版本 Spring Framework 6.0.9
1. 定义
Spring IoC容器在无需显式定义每个依赖关系的情况下,根据指定的策略,自动为指定的bean中所依赖的类类型或接口类型属性赋值。
2. 关键配置元素
BeanDefinitionParserDelegate类定义了autowire属性的属性值,代表了不同的自动装配策略。
2.1 < bean>元素的autowire属性
在XML配置文件中定义时,可以为其设置autowire属性来启用自动装配。该属性可取以下值:
- byType:
Spring容器会查找容器中与目标bean属性类型相匹配的所有bean,并将其中的一个(如果有多个同类型bean,则选择其中一个,具体选择规则取决于容器的处理逻辑)注入到该属性。 - byName:
Spring容器尝试按属性名作为bean的ID在容器中查找相应的bean。即如果目标bean有一个名为car的属性,那么容器会查找ID为car的bean并将其注入。 - constructor:
Spring容器会查找与bean构造器参数类型相匹配的bean,并通过构造器注入的方式创建bean实例。 - default 或 no:
禁用自动装配,default 会转换成 no,no 是Spring的默认设置。所有依赖项必须通过显式配置(如或标签)来注入。
2.2 < beans>标签的default-autowire属性
在< beans>元素上可以设置default-autowire属性,用于指定整个配置文件中所有未显式指定autowire属性的的默认自动装配策略。该属性可选值与 < bean>元素的autowire属性一样(byType、byName、constructor、default、no)。
3. 原理
3.1 获取autowire的默认值
在基于xml配置的ioc容器中,spring会创建一个DocumentDefaultsDefinition对象,并通过BeanDefinitionParserDelegate#populateDefaults方法进行初始化,使用默认的 lazy-init、autowire、依赖项检查设置、init-method、destroy-method 和 merge 填充,以防未在本地显式设置默认值。
红圈部分的代码在处理Spring XML配置文件中 标签的默认自动装配属性。
- 从当前 < beans> 标签节点中获取名为DEFAULT_AUTOWIRE_ATTRIBUTE(default-autowire)的属性值,
- 如果获取到的autowire值为空或等于default,则从父级获取自动装配模式(如果有);否则,使用预定义的常量AUTOWIRE_NO_VALUE(no)作为默认值,即不进行自动装配。
- 最后将计算得到的autowire值(即当前标签的默认自动装配模式)设置到defaults对象中,作为默认设置的对象,以便后续处理或传递给其他方法。
这样做的目的是确保每个< beans>标签都有一个明确的默认自动装配设置,以便在解析其内部bean定义时使用。
- DEFAULT_AUTOWIRE_ATTRIBUTE
- AUTOWIRE_NO_VALUE
- isDefaultValue
3.2 解析autowire的属性值
红圈部分的代码在处理Spring XML配置文件中 标签的自动装配属性。
- 从当前 < bean> 标签节点中获取名为AUTOWIRE_ATTRIBUTE(autowire)的属性值。
- 用刚刚获取的属性值作为入参attrValue调用autowire方法
- 如果attrValue为空或者default,从defaults对象中获取默认的自动装配模式字符串,并将其赋值给attr。即上面提到的默认值(一般情况下为no)。
- 初始化整型变量autowire,将其值设为AbstractBeanDefinition.AUTOWIRE_NO,表示默认不进行自动装配。
- 根据attr值的不同,分别设置autowire变量为不同的自动装配模式整数值(byName=1,byType=2,constructor=3,autodetect=4)。另外,autodetect模式已被标记为@Deprecated,提示用户应尽量避免使用。
- 最后将计算得到的autowire整型值设置到对应的BeanDefinition对象中(autowireMode属性)。
3.3 使用autowire的属性值
根据autowire属性值执行自动装配:
- byType:容器遍历bean的所有setter方法(或无参构造器后有setter的字段),查找与setter参数类型相匹配的bean。找到匹配项后,调用对应的setter方法进行注入。
- byName:容器查找bean的所有setter方法(或无参构造器后有setter的字段),根据setter方法名(去掉set前缀,将首字母小写)或字段名作为bean ID在容器中查找bean。找到匹配项后,调用setter方法或直接赋值进行注入。
- constructor:容器分析bean的构造器参数,查找与每个参数类型相匹配的bean。找到匹配项后,通过构造器注入方式创建bean实例。
3.3.1 byType
在bean声明周期的属性填充(populateBean方法)阶段,从bean定义中获取自动装配模式(autowireMode),如果值为autowireMode=2(AUTOWIRE_BY_TYPE=2),调用autowireByType方法执行按类型装配。
autowireByType的方法,用于按类型自动装配给定bean的属性。获取当前bean中所有待自动装配的非简单属性名称。遍历这些属性名称,通过resolveDependency方法获取满足依赖关系的bean实例,注入到对应属性。通过该方法,Spring IoC容器能够根据bean的类型自动为其属性注入合适的依赖bean实例。
unsatisfiedNonSimpleProperties(mbd, bw);
unsatisfiedNonSimpleProperties的方法为了找出给定AbstractBeanDefinition和BeanWrapper对象中未被显式设置且非简单的待自动装配属性名称。条件如下:
- 具有写方法
- 未被排除在依赖检查中
- PropertyValues中未已包含该属性的名称,即该属性尚未被显式设置。
- 非简单类型
bw.getPropertyDescriptor(propertyName)
BeanWrapper#getPropertyDescriptor方法获取属性的描述器,如下例子,定义一个UserController类,其包含一个userService属性,在xml配置文件中配置根据类型自动装配。getPropertyDescriptor方法返回两个类型为GenericTypeAwarePropertyDescriptor的描述器。
- propertyType为class,包含读方法getClass(),没有写方法
- propertyType为userService,没有读方法,包含写方法public void org.springframework.learn.ioc.auto.UserController.setUserService(org.springframework.learn.ioc.auto.UserService)
BeanUtils.getWriteMethodParameter(pd);
根据传入的PropertyDescriptor对象类型,分别从GenericTypeAwarePropertyDescriptor子类实例或普通PropertyDescriptor实例中提取与属性关联的写方法,并封装为MethodParameter对象返回。从上面getPropertyDescriptor方法中可知,返回的是GenericTypeAwarePropertyDescriptor子类实例,提取与属性关联的写方法。即是上面案例中的setUserService方法.。
resolveDependency(desc, beanName, autowiredBeanNames, converter);
resolveDependency方法可以分成三种解析情况
- 特殊类型的依赖解析:Optional类型、ObjectFactory/ObjectProvider类、javax.inject.Provider类。
- 支持延迟解析:尝试获取一个用于延迟解析依赖的代理对象
- 常规依赖解析:调用doResolveDependency方法解析依赖(一般情况下解析出符合所需类型的名称后,调用bean工厂的getBean方法获取bean实例)
resolveDependency方法根据依赖类型的差异采用不同的解析策略,包括创建适配特定类型的依赖提供者、支持延迟解析以及进行常规依赖解析。最终,该方法返回解析后的依赖对象。
doResolveDependency方法作用是解析依赖项。一般情况下,逻辑可以简单划分两部分
- 解析依赖值(descriptor包含依赖项),获取bean名称。
- 通过bean工厂获取bean实例并返回。
具体逻辑该方法会处理多种场景以解析由DependencyDescriptor描述的依赖项,同时考虑bean名称、自动装配规则及类型转换。它根据依赖项是否成功解析返回解析后的依赖项对象或针对各种错误条件抛出异常。
registerDependentBean(autowiredBeanName, beanName);
registerDependentBean方法用于注册两个相互依赖的bean之间的关系(两个内部数据结构dependentBeanMap 和 dependenciesForBeanMap),比如上述案例
- dependentBeanMap:key=依赖Bean(属性userService)
- dependenciesForBeanMap:key=依赖Bean的依赖项(userController)
3.3.2 byName
在bean声明周期的属性填充(populateBean方法)阶段,从bean定义中获取自动装配模式(autowireMode),如果值为autowireMode=1(AUTOWIRE_BY_NAME=1),调用autowireByName方法执行按名称装配。
autowireByName方法则比较简单,相较于autowireByType少了类型匹配的捕捉,先判断bean工厂是否包含bean名称,若包含则按照bean名称自动装配bean的属性(getBean方法获取bean实例)。
3.3.3 constructor
在bean声明周期的实例化(createBeanInstance方法)阶段,从bean定义中获取自动装配模式(autowireMode),如果值为autowireMode=3(AUTOWIRE_CONSTRUCTOR=2),调用autowireConstructor方法查找与bean构造器参数类型相匹配的bean,并通过构造器注入的方式创建bean实例。
autowireConstructor方法创建了一个包含当前bean工厂的构造函数解析器ConstructorResolver,并调用其autowireConstructor方法,完成实际的构造函数自动装配工作…
ConstructorResolver#autowireConstructor方法根据给定的bean名称、bean定义、用户指定构造器(若有)和显式参数值(若有),选择合适的构造器及其参数,完成bean的实例化过程。简单的过程可以理解成确认最佳构造器和所需参数,然后完成bean实例化。
上面的方法是获取最佳构造器和所需参数,通过构造器实例化bean。没有自动装配的具体使用逻辑(只判断了是否自动装配)。实际上,构造器类型的自动装配体现在createArgumentArray方法中。
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames,
getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
步骤如下:
- 定义局部变量,如类型转换器converter、构造参数结果值args等。
- 遍历给定参数数组,从resolvedValues获取匹配的构造器参数,转换后(如果未转换)保存到结果args中。获取不到则进行自动匹配。
- 往bean工厂注册依赖bean关系。
- 返回解析后的参数结果args。
例子
以下面配置为例子,看createArgumentArray方法
- 定义一个名称为userController的UserController,有两个属性
- name属性在配置文件的value值是一个spel表达式 #{userControllerNameUtil.getUserControllerName()},name属性的类型为String。
- userService属性未配置,使用自动装配
- 定义一个名称为userControllerNameUtil的UserControllerNameUtil类,用来提供name,其中getUserControllerName方法返回的是Interger类型。
- 定义一个名称为userService的UserServiceImpl类。
进入org.springframework.beans.factory.support.ConstructorResolver#autowireConstructor方法,获取到bean定义的构造函数参数name的原始配置值 #{userControllerNameUtil.getUserControllerName()}
resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);方法解析spel表达式,调用UserControllerNameUtil类的getUserControllerName方法,获取到Integer类型的 123456 解析值。
调用createArgumentArray方法构造参数的结果值。
- 处理构造参数userService
- 处理构造参数name
参考
- Spring使用有参构造器创建对象autowireConstructor方法