Spring的属性注入是如何执行的?

  • Post author:
  • Post category:其他



上一篇文章

已经搞清楚了Spring是什么时候注册、创建、以及实例化bean的,那么对于Class A中存在属性 Class B,那么Class B是怎么注入到A中的呢?

public class UserServiceImpl {
    private SchoolServiceImpl schoolService;

    public String getSchoolServiceMethod(){
        return schoolService.getTimeStr();
    }

    public void setSchoolService(SchoolServiceImpl schoolService) {
        this.schoolService = schoolService;
    }

}


第一种方式:


这里通过set方法将SchoolServiceImpl注入到UserServiceImpl中

,userService.xml文件配置

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--set方法方式注入-->
   <bean id="userService" class="com.learn.spring.spring01.service.UserServiceImpl">
     <property name="schoolService" ref="schoolService"/>
   </bean>
    <bean id="schoolService" class="com.learn.spring.spring01.service.SchoolServiceImpl">
    </bean>
</beans>

回顾上一篇内容,spring在启动的时候,会解析xml文件,并将xml文件的bean标签解析为BeanDefinition对象,debug发现,这一步还是和上一篇文章说的一样,正常注册。

接下里是创建bean,

上一篇第二步、第三步中有一个beanName 的list—-beanDefinitionNames,spring循环这个list,对于每一个元素,调用getBean(beanName)方法,创建bean、填充bean的属性、实例化bean

debug发现:

当userServiceImpl被创建完毕之后,会进入

populateBean这个方法,它负责填充bean属性

,userServiceImpl中的

属性就是schoolServiceImpl,

populateBean->applyPropertyValues->

valueResolver.resolveValueIfNecessary(这个方法比较关键,会根据入参类型的不同执行不同的策略)->resolveReference-

>this.beanFactory.getBean(resolvedName)最终还是会调用getBean(beanName)方法,剩下的就和上一篇文章分析的一样了,如果schoolServiceImpl存在,则返回;如果不存在,依旧创建、填充属性、实例化,

然后返回到resolveValueIfNecessary这里,把属性值设置进去

。然后接着实例化,那么userServiceImpl这个bean就处于就绪待使用状态了。


以上步骤相当于:

创建A->创建完毕->填充A属性,B->发现B缓存中没有对应的实例->创建B、填充B属性、实例化B->将实例化好的B设置到A中的属性B中->实例化A->完毕


思考:


有没有可能当userServiceImpl需要注入schoolServiceImpl的时候,schoolServiceImpl已经准备好的时候?

要搞清楚这个问题,就要知道几个关键点:

1、当getBean(beanName)时候,只要getSingleton(beanName)能获取到schoolServiceImpl,那么就不会创建,getSingleton里负责找bean的map是在getBean方法执行中,put进去的

2、也就是说,schoolServiceImpl要比userServiceImpl更早的调用getBean(beanName)方法,而调用顺序是beanDefinitionNames里的元素顺序决定的

3、beanDefinitionNames里的元素是什么时候放进去的?就是解析xml的时候,解析xml文件也是从上到下、循环解析。所以,

只要将schoolServiceImpl的bean标签放到userServiceImp之前就行了。


第二种方式:构造器方法

public class UserServiceImpl {
    private SchoolServiceImpl schoolService;

    public UserServiceImpl(SchoolServiceImpl schoolService) {
       this.schoolService = schoolService;
    }

    public String getSchoolServiceMethod(){
        return schoolService.getTimeStr();
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--将schoolService放到userService前面实例化,这样userService在填充属性的时候直接拿缓存里面的就行-->
    <bean id="schoolService" class="com.learn.spring.spring01.service.SchoolServiceImpl">
    </bean>

   <bean id="userService" class="com.learn.spring.spring01.service.UserServiceImpl">
       <!--构造器方式注入-->
       <constructor-arg ref="schoolService"/>
   </bean>

</beans>

构造器和set方法执行流程一点点不同,

当getBean(beanName)最终createBeanInstance的时候,此时会调用autowireConstructor方法,->resolveConstructorArguments->resolveValueIfNecessary,最终还是会调用这个方法

,由于创建实例的时候,调用构造函数,

this.SchoolServiceImpl = schoolServiceImpl 算是提前把属性填充好了

,所以再调用populateBean这个方法时,基本没什么可执行的代码,此时在这个方法里面是没做任何事情的。它不像set方法注入,最终会调用方法。它不执行任何方法


以上步骤相当于:

创建A->发现A具有构造器,使用代码中的构造器进行创建A->创建A的构造器参数是B,此时代码把B设置到A的属性B上->A创建完毕->填充属性->实例化->完毕

更多详细使用或者原理,可参考spring官方文档对于

依赖注入

的文档部分



版权声明:本文为dghkgjlh原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。