当前位置: 首页 > ds >正文

Spring依赖注入源码学习:基于XML配置的DI源码解析

1.什么是依赖注入

​   控制反转(IoC)和依赖注入(DI)是Spring框架的核心设计思想,具体来说DI其实是对IoC的一种实现。抛开八股,依赖注入给使用者最直观的感受是Spring会自动给类(bean)的属性赋值,不需要我们手动地调用setter方法。所以,Spring不仅会自动地创建bean,并且还会给bean里面的属性赋值。给属性赋值的过程我们称之为依赖注入。

1.1 构造器注入和setter方法注入

​   我们先来看看在Java中如何给一个类的实例设置属性:

public class SimpleBased {private String property1;private int property2;public SimpleBased() {}public SimpleBased(String property1, int property2) {this.property1 = property1;this.property2 = property2;}public String getProperty1() {return property1;}public void setProperty1(String property1) {this.property1 = property1;}public int getProperty2() {return property2;}public void setProperty2(int property2) {this.property2 = property2;}@Overridepublic String toString() {return "SimpleBased{" +"property1='" + property1 + '\'' +", property2=" + property2 +'}';}
}
public class IOCTest {@Testpublic void simpleTest(){// 通过构造器注入SimpleBased simpleConstructorBased = new SimpleBased("property1", 2);System.out.println(simpleConstructorBased);// 通过setter注入SimpleBased simpleSetterBased = new SimpleBased();simpleSetterBased.setProperty1("property1.1");simpleSetterBased.setProperty2(3);System.out.println(simpleSetterBased);}
}

​   可以看到,我们设置一个对象的属性通过两种途径,一种是创建对象时通过构造方法指定属性值;另一种是在对象创建好后调用setter方法。

​   Spring框架的依赖注入(DI),本质上也是通过这两种方式实现,我们通常这两种方式为:构造器注入setter方法注入

​   我们以XML配置为例,来看看Spring是怎么实现这两种方式的属性注入:

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--构造器注入--><bean class="com.source.ioc.SimpleBased" name="simpleConstructorBased"><constructor-arg value="constructor" name="property1" type="java.lang.String"/><constructor-arg value="1" name="property2" type="int"/></bean><!--setter注入--><bean class="com.source.ioc.SimpleBased" name="simpleSetterBased"><property value="setter" name="property1"/><property value="1" name="property2"/></bean>
</beans>
@Test
public void simpleXmlTest(){ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("spring-di.xml");SimpleBased simpleSetterBased = ac.getBean("simpleSetterBased", SimpleBased.class);System.out.println(simpleSetterBased);SimpleBased simpleConstructorBased = ac.getBean("simpleConstructorBased", SimpleBased.class);System.out.println(simpleConstructorBased);
}

​   运行结果如下:

在这里插入图片描述

在配置xml文件时,无论bean的属性是什么类型,标签中的value属性的类型均是String,Spring会自动帮我们做类型转换。

​   如果当前bean依赖其他bean时该怎么配置呢?

​   constructor-arg标签和property标签都支持ref属性,用于将bean的指定属性的值设置为对容器管理的另一个bean的引用。我们可以通过一个简单的例子来看看,首先我们定义好相关的bean:

public class SpringIOC {public static class SpringIOC1 {private String property;public SpringIOC1() {this.property = "i am SpringIOC1";}@Overridepublic String toString() {return "SpringIOC1{" +"property1='" + property + '\'' +'}';}}public static class SpringIOC2 {private String property;public SpringIOC2() {this.property = "i am SpringIOC2";}@Overridepublic String toString() {return "SpringIOC2{" +"property1='" + property + '\'' +'}';}}public static class SpringIOC3 {private String property;private SpringIOC1 springIOC1;private SpringIOC2 springIOC2;public SpringIOC3(SpringIOC1 springIOC1) {this.property = "i am SpringIOC3";this.springIOC1 = springIOC1;}public void setSpringIOC2(SpringIOC2 springIOC2) {this.springIOC2 = springIOC2;}@Overridepublic String toString() {return "SpringIOC3{" +"property='" + property + '\'' +", springIOC1=" + springIOC1 +", springIOC2=" + springIOC2 +'}';}}
}

​   接着我们在XML文件中配置这些bean:

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean class="com.source.ioc.SpringIOC.SpringIOC1" name="springIOC1"/><bean class="com.source.ioc.SpringIOC.SpringIOC2" name="springIOC2"/><bean id="springIOC3" class="com.source.ioc.SpringIOC.SpringIOC3"><constructor-arg name="springIOC1" ref="springIOC1"/><property name="springIOC2" ref="springIOC2"/></bean>
</beans>

​   最后编写测试方法:

@Test
public void simpleXmlBeanTest(){ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("ioc/spring-di-bean.xml");SpringIOC.SpringIOC3 springIOC3 = ac.getBean("springIOC3", SpringIOC.SpringIOC3.class);System.out.println(springIOC3);
}

​   控制台打印结果如下:

在这里插入图片描述

​   可以看到通过ref属性,成功将XML配置文件中的bean注入到目标bean当中。

1.2 自动注入

​   从上面的demo中,通过配置XML文件Spring能很好地帮助我们完成依赖注入。

​   但是这又产生了另一个问题,如果bean依赖很多其他bean,那么配置文件就会非常复杂,为此Spring提供了自动注入的功能。通过配置bean标签的autowire属性来设置自动注入的模式,一共有四种模式:

1.no:默认值。不启用自动装配。bean必须引用由ref定义的元素。虽然配置要多一点,但是这样可以让系统结构和bean之间的关系更加清晰。2.byName:通过属性的名字的方式查找bean依赖的对象并为其注入。比如说某个bean的autowire属性为byName,并且有个属性名为a,有个setA方法,那么IoC容器会在配置文件中查找id/name的值包含a的bean,然后使用setter方法模式为其注入。注意这里的自动setter相比前面讲的手动setter注入有限制,即setXXX方法的XXX一定要对应一个容器中实例的name才行,否则不会注入。3.byType:通过属性的类型查找bean依赖的对象并为其注入。比如说某个bean的autowire属性为byType,并且有个属性名为a,有个setA方法,这个属性a的类型为com.A,那么IoC容器会在配置文件中查找class为com.A的bean,然后使用setter方法模式为其注入。注意这里的自动setter注入相比手动setter配注入有限制,即setXXX方法的XXX一定是属性名才行(第一个字母可以改变大小写)。如果容器有多个同类型的bean,那么抛出异常:expected single matching bean but found x: xxx。4.constructor:同byType一样,也是通过类型查找依赖对象。与byType的区别在于它不是使用setter方法注入,而是使用构造器注入。如果容器有多个同类型的bean,并且不能找到最合适的依赖,那么同样会抛出异常:expected single matching bean but found x: xxx。

​   如果使用自动注入模式,我们将SpringIOC3去掉有参构造,增加无参构造:

public static class SpringIOC3 {private String property;private SpringIOC1 springIOC1;private SpringIOC2 springIOC2;public SpringIOC3() {}public void setSpringIOC2(SpringIOC2 springIOC2) {this.springIOC2 = springIOC2;}@Overridepublic String toString() {return "SpringIOC3{" +"property='" + property + '\'' +", springIOC1=" + springIOC1 +", springIOC2=" + springIOC2 +'}';}
}

​   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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean class="com.source.ioc.SpringIOC.SpringIOC1" name="springIOC1"/><bean class="com.source.ioc.SpringIOC.SpringIOC2" name="springIOC2"/><bean id="springIOC3" class="com.source.ioc.SpringIOC.SpringIOC3" autowire="byType"/></beans>

​   最后运行结果如下:

在这里插入图片描述

​   因为我们将autowire属性设置为byTypeSpringIOC3虽然有springIOC1springIOC2两个属性,但是只有springIOC2有set方法,因此最后只有springIOC2自动注入成功。

[!IMPORTANT]

由于byType和byName均是通过set方法实现自动注入的,因此需要一个空参的构造方法来实例化bean。

​   这里顺带提一嘴,在最开始接触Spring框架时觉着自动注入功能很高级,如果从源码的角度来看,Spring在部分功能的实现确实挺复杂的,但是仅通过实现逻辑来看其实并没有那么复杂,我们简单来分析一下:

​   在前面的博客中我们有分析过,Spring会根据XML文件和注解来帮我们实例化bean,并将这些bean保存到Spring容器中。Spring 容器本质是一个大的Map,key为bean的name,value为bean的实例对象。

​   对于byTypebyName,Spring只需要根据name或者类型去查这个大Map,取出bean的实例,通过反射调用setter方法完成依赖注入;

​   而对于constructor,则首先通过反射得到构造器的参数类型,然后根据类型查大Map取出bean实例,最后通过反射调用构造方法实例化bean并完成依赖注入。

​   上面只是简单的介绍整个自动注入的流程,Spring实际的实现肯定比这复杂得多。

2.基于XML配置的DI源码解析

​   想要了解依赖注入的源码实现,一定是绕不开Spring初始化bean的流程,这部分内容在之前的博客中已经详细讲述了,为保证前后文的连贯性,这里就简单过一下大致流程。

​   依赖注入的实现主要在于两点:保存XML的依赖配置信息取出依赖配置信息完成依赖注入

​   那么Spring会见XML的bean的依赖配置信息放在哪里呢?答:beanDefinition。我们先来看看Spring初始化bean的过程中,beanDefinition是怎么随着XML配置的变化而变化的。

​   我们先来看看方法入口:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

2.1 加载beanDefinition

​   我们先来看看加载beanDefintion的粗略过程:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

​   从两个角度来看看beanDefinition保存的信息:手动指定依赖注入信息和自动注入。

2.1.1 手动注入-setter

  我们先来看看手动指定依赖注入信息:

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean class="com.source.ioc.SpringIOC.SpringIOC1" name="springIOC1"/><bean class="com.source.ioc.SpringIOC.SpringIOC2" name="springIOC2"/><bean id="springIOC3" class="com.source.ioc.SpringIOC.SpringIOC3"><property name="springIOC2" ref="springIOC2"/></bean>
</beans>

​   这时springIOC3的beanDefinition如下:

在这里插入图片描述

2.1.2 手动注入-构造器

​   我们再看看通过constructor-arg标签通过构造器注入:

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean class="com.source.ioc.SpringIOC.SpringIOC1" name="springIOC1"/><bean class="com.source.ioc.SpringIOC.SpringIOC2" name="springIOC2"/><bean id="springIOC3" class="com.source.ioc.SpringIOC.SpringIOC3"><constructor-arg name="springIOC1" ref="springIOC1"/></bean>
</beans>

​   这时springIOC3的beanDefinition如下:
在这里插入图片描述

2.1.3 自动注入

​   我们把依赖注入改为自动注入:

<?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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean class="com.source.ioc.SpringIOC.SpringIOC1" name="springIOC1"/><bean class="com.source.ioc.SpringIOC.SpringIOC2" name="springIOC2"/><bean id="springIOC3" class="com.source.ioc.SpringIOC.SpringIOC3" autowire="byName"/>
</beans>

​   继续打断点看看自动注入模式下的springIOC3的beanDefinition:

在这里插入图片描述

​   自动注入虽然有四种模式,但是反应在beanDefinition上面只会影响到autowireMode一个属性,autowireMode通过一个整型来区分不同的自动注入模式。

​   现在我们知道了,依赖注入的配置信息主要是通过beanDefinition的propertyValues属性、autowireMode属性和constructorArgumentValues属性完成的,这三个属性也是完成依赖注入的依据。

​   1.2节中我们提到过自动注入有四种模式,这四种模式和autowireMode的值对应如下:

no -> autowireMode=0
byName -> autowireMode=1
byType -> autowireMode=2
constructor -> autowireMode=3

​   由于自动注入没有明确指定需要注入哪些属性,Spring只能从设置的自动注入模式来推测,我们来看看四种自动注入模式下beanDefinition的propertyValuesconstructorArgumentValues属性值:

public static class SpringIOC3 {private String property;private SpringIOC1 springIOC1;private SpringIOC2 springIOC2;public SpringIOC3() {}public SpringIOC3(SpringIOC1 springIOC1) {this.springIOC1 = springIOC1;}public void setSpringIOC2(SpringIOC2 springIOC2) {this.springIOC2 = springIOC2;}@Overridepublic String toString() {return "SpringIOC3{" +"property='" + property + '\'' +", springIOC1=" + springIOC1 +", springIOC2=" + springIOC2 +'}';        }}

2.2 依赖注入源码分析

​   前面我们讨论了Spring是如何保存XML中关于依赖注入的配置信息,接下来我们来看看Spring是如何完成依赖注入的。

​   我们先来分析一下依赖注入的时机,根据前文的介绍得知,依赖注入(无论是手动注入还是自动注入)只会通过两个途径:构造器注入或者setter方法注入。结合之前Spring初始化bean的博客来看,依赖注入会发生在bean的实例化阶段和属性填充阶段。

​   bean的实例化本质是通过得到Class对象,然后利用反射调用构造器完成实例化,因此当我们设置构造器注入时,会在这个阶段通过有参构造完成依赖注入。

​   属性填充则会调用对应字段的setter方法完成依赖注入。

​   前面的博客中我们已经详细分析了Spring初始化bean的全过程,这里就不再赘述,为了保证行文的流畅性,简单概括一下bean的初始化过程。

​   我们从refresh方法来看:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

2.2.1 手动注入-构造器

​   我们重新调整一下Demo:

public class SpringIOC {public static class SpringIOC1 {private String property;public SpringIOC1() {this.property = "i am SpringIOC1";}@Overridepublic String toString() {return "SpringIOC1{" +"property1='" + property + '\'' +'}';}}public static class SpringIOC2 {private String property;public SpringIOC2() {this.property = "i am SpringIOC2";}@Overridepublic String toString() {return "SpringIOC2{" +"property1='" + property + '\'' +'}';}}public static class SpringIOC3 {private String property;private SpringIOC1 springIOC1;private SpringIOC2 springIOC2;public SpringIOC3() {}public SpringIOC3(SpringIOC1 springIOC1) {this.springIOC1 = springIOC1;}public void setSpringIOC2(SpringIOC2 springIOC2) {this.springIOC2 = springIOC2;}@Overridepublic String toString() {return "SpringIOC3{" +"property='" + property + '\'' +", springIOC1=" + springIOC1 +", springIOC2=" + springIOC2 +'}';}}
}

​   重新调整一下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/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean class="com.source.ioc.SpringIOC.SpringIOC1" name="springIOC1"/><bean class="com.source.ioc.SpringIOC.SpringIOC2" name="springIOC2"/><bean id="springIOC3" class="com.source.ioc.SpringIOC.SpringIOC3"><constructor-arg name="springIOC1" ref="springIOC1"/><property name="springIOC2" ref="springIOC2"/></bean>
</beans>

​   可以看到,SpringIOC3有两个构造器:空参构造和包含SpringIOC1的构造,我们继续进到AbstractAutowireCapableBeanFactorycreateBeanInstance方法,关于这个方法的具体讲解在之前的博客(Spring源码学习(三):finishBeanFactoryInitialization-CSDN博客)中有提到,这里我们只聚焦于与依赖注入相关的关键部分:

// AbstractAutowireCapableBeanFactory.java的第1200行到1204行
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {return autowireConstructor(beanName, mbd, ctors, args);
}

​   determineConstructorsFromBeanPostProcessors方法是通过Spring后处理器来获得被@Autowire注解修饰的构造器,这个方法只会在开启注解后才会生效,如果使用XML配置则该方法返回为null

​   在本小结给出的demo中,会进入if判断来调用autowireConstructor方法,我们来逐个分析一下这四个判断条件:

  • ctors != null:false,因为没开启注解,determineConstructorsFromBeanPostProcessors返回为null
  • mbd.getResolvedAutowireMode():false,因为没开启自动注入,beanDefinition的autowireMode的值为0,AUTOWIRE_CONSTRUCTOR的值为3;
  • mbd.hasConstructorArgumentValues():true,在2.1.2中有提到过,使用了constructor-arg注解后,beanDefinition的constructorArgumentValues属性不为空;
  • !ObjectUtils.isEmpty(args):false,这是判断实例化bean时是否使用外部参数,默认是不适用所以args是null

​   if条件成立,接着我们进入到AbstractAutowireCapableBeanFactoryautowireConstructor方法:

protected BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] ctors, @Nullable Object[] explicitArgs) {return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs);
}

​   autowireConstructor方法里面会创建一个ConstructorResolver的对象,然后再调用ConstructorResolverautowireConstructor方法,这里的this其实就是beanFactory的本身,保存了所有bean的beanDefinition。

​   我们继续进到autowireConstructor的方法,注意这里入参ctorsexplicitArgs均为nullautowireConstructor方法较长,且在之前的博客中有详细讲述,我们这里只关注关键代码部分:

在这里插入图片描述

在这里插入图片描述

​   我们结合本小节的demo,看看Spring是怎么计算autowiring属性的值:

  • chosenCtors != null:false,这里的chosenCtors 就是autowireConstructor的入参,这里的入参值为null
  • mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR:false,不使用自动注入时beanDefinition的autowireMode的值为0,AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR的值为3。

[!NOTE]

这里的autowiring的其实是注解和xml配置两个方便综合考虑的结果。

​   再来看看resolveConstructorArguments方法,该方法做了两件事,计算构造函数最少参数数量属性minNrOfArgs解析依赖的bean,在进到resolveConstructorArguments方法之前,先来回顾一下beanDefinition的constructorArgumentValues属性:

在这里插入图片描述

​   可以看到constructorArgumentValuesindexedArgumentValuesgenericArgumentValues两个属性构成,当我们使用constructor-arg标签时可以设置index属性,表示构造器的第几个入参,可以看个简单的例子:

<bean id="exampleBean" class="com.example.ExampleClass"><constructor-arg index="0" value="First Argument"/><constructor-arg index="1" value="Second Argument"/>
</bean>

​   Spring会将设置了index属性的constructor-arg标签信息保存到indexedArgumentValues,而没设置index属性的constructor-arg标签信息保存到genericArgumentValues属性当中。

​   在本小节的例子中,没有设置index属性,因此constructor-arg标签信息会被保存到genericArgumentValues属性中。

​   在了解了constructorArgumentValues的基本构成之后,我们再来看看resolveConstructorArguments方法:

在这里插入图片描述

在这里插入图片描述

constructorArgumentValuesindexedArgumentValuesgenericArgumentValues两个属性构成,minNrOfArgs则是这两个属性的size之和:

在这里插入图片描述

​   关于Spring将beanName解析为对应的bean的方法resolveValueIfNecessary,底层其实是通过beanFactory的getBean方法(套娃了属于是)完成的,这里就不再详细展开:

在这里插入图片描述

​   让我们重新回到autowireConstructor方法继续往下看:

在这里插入图片描述

​   Spring对候选构造器的排序规则如下:

  • 访问权限修饰符越高的越靠前
  • 参数越多的越靠前

​   排序完成后,会遍历这些候选构造器,选出最合适的一个构造器,这部分代码在之前的博客中已经讲解过了,这里就不再赘述,我们只看看minNrOfArgs的过滤作用:

在这里插入图片描述

​   在本小节的demo中,SpringIOC3虽然有空参构造,但是minNrOfArgs的值为1,0<1,空参构造就会被过滤掉。

​   在找到了合适的构造函数,已经入参对应的解析值后就可以根据反射来创建实例对象了,同时完成了依赖注入:

在这里插入图片描述

​   让我们来小结一下构造器依赖注入的整个流程,关键其实在于constructorArgumentValues属性:

  1. Spring读取XML文件,将constructor-arg标签的配置信息解析到beanDefinition的constructorArgumentValues属性中
  2. Spring会根据constructorArgumentValues计算构造器最少参数数量minNrOfArgs,根据minNrOfArgs来过滤构造器
  3. Spring会根据constructorArgumentValues将依赖关系解析成对应的bean(这里特指依赖其他bean的情况)
  4. 根据2和3的结果得到了构造器和构造器参数对应的值,通过反射创建对象,完成了依赖注入

2.2.2 手动注入-setter

​   我们继续沿用2.2.1的demo,Spring在实例化SpringIOC3的对象时通过构造器注入成功注入了SpringIOC1的依赖,接下来我们继续来看看SpringIOC2是如何注入的。

​   前文有说过,setter注入是在bean的属性填充阶段完成的也就是populateBean方法,我们进入到populateBean方法来看看:

在这里插入图片描述

在这里插入图片描述

​   Spring到这一步是已经完成了SpringIOC3的实例化,因此要想完成setter注入需要需要干两件事:将beanName解析为bean,调用对应属性的set方法将依赖注入到实例bean中。

  我们进入到applyPropertyValues方法里面看看关键的部分:

在这里插入图片描述

在这里插入图片描述

​   我们继续进到setPropertyValues方法里面看看,这个方法位于AbstractPropertyAccessor

在这里插入图片描述

​   setPropertyValue方法调用链较多,我们来看看最后属性注入的代码:

在这里插入图片描述

2.2.3 自动注入

​   在1.2节中讲到了自动注入的四种方式,在2.1.3节中又讲到了四种自动注入模式在beanDefinition中对应的autowireMode的值,接下来我们来看看Spring是如何处理这四种模式的自动注入。

​   Demo:

public static class SpringIOC3 {private String property;private SpringIOC1 springIOC1;private SpringIOC2 springIOC2;public SpringIOC3() {}public SpringIOC3(SpringIOC1 springIOC1) {this.springIOC1 = springIOC1;}public void setSpringIOC2(SpringIOC2 springIOC2) {this.springIOC2 = springIOC2;}@Overridepublic String toString() {return "SpringIOC3{" +"property='" + property + '\'' +", springIOC1=" + springIOC1 +", springIOC2=" + springIOC2 +'}';}
}
2.2.3.1 autowire=“no”

在这里插入图片描述

​ 创建bean实例的createBeanInstance方法:

在这里插入图片描述

​   可以看到最后使用的无参构造来实例化SpringIOC3,如果我们把Demo中的无参构造去掉会怎么样呢?程序会报错:

在这里插入图片描述

在这里插入图片描述

​   接着来看看属性填充部分的代码populateBean,可以看到核心逻辑一个没走,压根没触发属性填充:

在这里插入图片描述

2.2.3.2 autowire=“byName”

​   先来看看beanDefinition

在这里插入图片描述

​   再来看看实例化bean方法:

在这里插入图片描述

​   其实和 autowire=“no”一样,最后也是采用无参构造实例化bean,也就是说使用byName自动注入,如果没有无参构造程序会报错。

​   这个其实也比较好理解,因为byName的本质是通过set方法完成依赖注入,而调用set方法的前提是能实例化bean,没有无参构造就无法实例化bean。

​   关键来看看属性填充方法:

在这里插入图片描述

​   继续进到autowireByName方法:

在这里插入图片描述

​   什么是符合要求的属性名称?,让我们进到unsatisfiedNonSimpleProperties方法来看看:

在这里插入图片描述

​   我们来逐行分析:

PropertyValues pvs = mbd.getPropertyValues();

​   因为我们没有主动在xml中配置property标签,因此beanDefinition的propertyValues属性是个空数组,这里的pvs也是一个空数组。

PropertyDescriptor[] pds = bw.getPropertyDescriptors();

​   获得当前bean的属性描述符,Spring会通过反射解析bean的结构,并缓存符合要求的属性信息。简单来说,这一步可以获得有setter方法或者getter方法的属性信息。

pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) && !BeanUtils.isSimpleProperty(pd.getPropertyType())

​   这一步是从pds中筛选出哪些属性要完成属性填充:

pd.getWriteMethod() != null:该属性有setter方法的
!isExcludedFromDependencyCheck(pd):该属性没有排除依赖检查之外,也就是不能为null
!pvs.contains(pd.getName()):xml中的property标签没有配置该属性
!BeanUtils.isSimpleProperty(pd.getPropertyType()):该属性不是简单属性

​   unsatisfiedNonSimpleProperties其实完成了一个属性检查的工作,它会筛选出类里面非简单类型的有setter方法的属性。

​   unsatisfiedNonSimpleProperties方法找到符合要求的属性名称后,通过getBean方法找到/创建bean作为该属性对应的属性值,最后完成属性注入。

[!IMPORTANT]

通过这里我们知道,byName并不会对所有属性进行依赖注入,它会有两步过滤,首先过滤出有setter/getter方法的属性,然后再过滤出里面有setter方法的非简单类型的属性。

2.2.3.3 autowire=“byType”

​   先来看看beanDefinition

在这里插入图片描述

​   对应的,进到autowireByType方法:

在这里插入图片描述

​   可以看到,byTypebyName的大体流程一致,这里一个有意思的点是byType会对Object类型的属性进行过滤,而byName并没有。

​   这是因为byName通过beanName找到的bean是唯一的,而byType通过类型找到的bean可能不唯一,特别是对于Object类型,几乎可以接收所有类型的bean(子类的值可以赋值给父类)。

2.2.3.4 autowire=“constructor”

​   先来看看beanDefinition:

在这里插入图片描述

​   通过构造器注入,需要重点看看createBeanInstance方法:

在这里插入图片描述

​   可以看到,if判断为true,所以接下来的流程和2.2.1类似,这里就不再赘述,需要注意的一点是:

在这里插入图片描述

http://www.xdnf.cn/news/14222.html

相关文章:

  • PGCP:用于比较基因组学的植物基因组综合数据库-文献精读144
  • 信息学奥赛一本通 1543:【例 3】与众不同
  • ubuntu之坑(十四)——安装FFmpeg进行本地视频推流(在海思平台上运行)
  • UVM同步的方法
  • RPT:预训练新范式,用强化学习做预训练!
  • 生成式AI如何与RPA融合?
  • Cursor-1.0安装Jupyter-Notebook,可视化运行.ipynb文件中Python分片代码
  • 使用麒麟V10操作系统的KVM服务,但麒麟V10存在高危漏洞无法修复?
  • 【运维】iDRAC、Lifecycle Controller、Unified Server Configurator 的区别
  • 【1/2, 2/3, 3/5, 5/8, 8/13, ...写一个函数,计算以下数列的前10项之和,在主函数中调用该函数并输出结果。】2022-5-19
  • 成都鼎讯短波通信信号模拟设备:短波频段的电磁模拟王者​
  • 【iSAQB软件架构】良好的设计技术
  • spring:使用注解@Configuration、@ComponentScan创建配置类(未完待续)
  • mysql8数据库本地能连上但是远程连不上
  • AI作画提示词:Prompts工程技巧与最佳实践
  • GEO指南之内容创业者:AI时代的“品牌大模型种草”与IP推荐力打造
  • OSPF基础实验案例
  • Java登录验证后台实现详解
  • 【QSoundEffect QT 音频文件的播放】
  • 岛屿周长问题的三种解法:直接计数法、数学计算法与深度优先搜索
  • 精益数据分析(100/126):SaaS行业付费注册率优化与商业模式选择策略
  • Vue3本地存储实现方案
  • java通过hutool工具生成二维码实现扫码跳转功能
  • 【C/C++ 为什么 unique_ptr 不支持拷贝构造、赋值构造等操作】
  • SpringBoot项目接入DeepSeek指南:从零开始实现AI能力整合
  • PyTorch优化器总结
  • JS进阶 Day01
  • 前端面经整理【1】
  • 人工智能嵌入公共服务治理的风险挑战(一)
  • meshgpt 笔记2