摘要:从源码角度剖析
@Autowire
注解实现自动注入的原理,透彻理解Spring
中依赖注入实现的底层逻辑。
前言
在日常开发中,@Autowire
注解可以说是我们日常开发中使用最频繁的一个注解。通过@Autowire
注解我们可以顺利实现相关Bean
的自动注入,而无需我们那在手动通过new
关键字来构建一个对象。
但最熟悉的往往是最陌生的,不妨思考一下,虽然@Autowire
是我们开发中使用最频繁的一个,但你是否明白其内部的原理呢?换言之,你是否有真正了解过@Autowired
注解背后的实现原理?
不了解也没关系,本文会重点对@Autowired
原理进行深入剖析。相信读完本文能加深你对于 @Autowired
注解底层原理有深刻认识。
@Autowire
简介
@Autowired
注解是 Spring
框架中的一种依赖注入机制,用于自动装配 Bean
。它可以应用于构造函数、字段和方法上。当 Spring
容器启动时,它会自动寻找与被注入类型匹配的 Bean
,并将其注入到标注了 @Autowired
的位置。
具体来看,其可用于:构造器注入,字段注入,方法注入等。其使用方式如下:
-
构造器注入 :在类的构造函数中使用
@Autowired
。Spring
会在创建实例时注入所需的依赖。其使用如下:java@Component public class MyService { private final MyRepository repository; @Autowired public MyService(MyRepository repository) { this.repository = repository; } }
-
字段注入 :直接在字段上使用
@Autowired
。虽然代码更简洁,但不利于单元测试,因为需要通过反射或其他方式进行注入。java@Component public class MyService { @Autowired private MyRepository repository; }
-
方法注入 :在
setter
方法上使用@Autowired
,可以实现可选的依赖注入,适合需要修改依赖的场景。java@Component public class MyService { private MyRepository repository; @Autowired public void setRepository(MyRepository repository) { this.repository = repository; } }
总之,@Autowired
注解是 Spring
框架中用于自动装配 Bean
的关键工具。它可以应用于构造器、字段和方法,简化了依赖注入的过程,增强了代码的灵活性与可维护性。
@Autowire
解析原理
众所周知,在容器启动阶段Spring
首先会实例化 Bean
,然后进行初始化操作。在初始化阶段,而通过调用 Bean
后置处理器可以完成对Bean
属性的赋值等操作。进一步,对于 @Autowired
而言,其在功能的实现上也依赖于------AutowiredAnnotationBeanPostProcessor
这一后置处理器。
具体来看,AutowiredAnnotationBeanPostProcessor
是 Spring
框架中内置的一个后置处理器,主要负责处理标注了 @Autowired
注解标注属性的注入。其主要在Bean
的初始化过程中介入,以实现依赖注入。
更进一步,在 Spring
容器启动时,AutowiredAnnotationBeanPostProcessor
会被注册为一个后置处理器。每当一个Bean
被创建并初始化时,它会在 postProcessProperties
方法中进行扫描,识别并注入标注了 @Autowired
的属性。 通过这一机制,AutowiredAnnotationBeanPostProcessor
有效地实现了 Spring
的依赖注入,提升了代码的灵活性和可维护性。
接下来,我们便来AutowiredAnnotationBeanPostProcessor
看看这个类的源码究竟是如何来完成对@Autowire
注解进行解析的。
@Autowire
解析的时机
下图展示了Spring
中一个Bean
的加载全过程,具体来看,Spring
在创建bean的
过程中,岂会调用 AbstractAutowireCapableBeanFactory
中的doCreateBean
方法,进而在doCreateBea
中进一步调用populateBean()
方法完成Bean
中属性的填充,而Bean
中所依赖bean
信息自动装配也全部在populateBean()
完成。
其中,populateBean()
方法的部分源码如下。
java
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
// ... 省略无关代码
PropertyDescriptor[] filteredPds = null;
if (hasInstAwareBpps) {
if (pvs == null) {
pvs = mbd.getPropertyValues();
}
// 遍历容器中全部的后置处理器,执行其中的postProcessProperties方法
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
if (filteredPds == null) {
filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
}
pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvsToUse == null) {
return;
}
}
pvs = pvsToUse;
}
}
}
// ... 省略无关代码
}
可以看看到,在populateBean
其内部会循环遍历容器中的后置并判断是否需要属性填充,如果不需要进行属性填充,那么就会直接进行return
,反之就会调用容器中的后置处理的postProcessPropertyValues()方法
来实现Bean
的依赖注入。
进一步,AutowiredAnnotationBeanPostProcessor
中postProcessProperties
的内部逻辑如下:
AutowiredAnnotationBeanPostProcessor # postProcessProperties
java
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// <1> 解析出bean中带有@Autowired注解、@Inject和@Value注解的属性和方法
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
// <2> 遍历标有 @Autowire注解的字段,完成反射的注入
metadata.inject(bean, beanName, pvs);
return pvs;
}
其中<1> findAutowiringMetadata()
方法主要对Bean
对象中的@Autowired
注解进行解析。具体来看, 对于属性上加了Autowired注解的,经过findAutowiringMetadata
的处理后,会将字段解析为AutowiredFieldElement类型 ;而如果是方法上加了@Autowired
注解,则会解析为AutowiredMethodElement
类型。
事实上,无论其扩展于InjectedElement
而<2> metadata.inject()
方法则最终会调用InjectedElement
类的inject()
方法。
具体来看,以@Autowire
标注在字段上为例来进行分析。正如之前所说,对于@Autowire
标注在字段上的情况,其会将标有@Autowire
字段的属性解析为AutowiredFieldElement
进而调用其内部的inject
方法来完成属性的注入。
其中,AutowiredFieldElement
内部inject()
的实现逻辑如下:
java
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
Object value;
// 判断当前被注入类是否第一次注入,如果不是则从缓存中进行获取
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
} else {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
// <1> 通过beanFactory.resolveDependency()方法,来从容器中找到对应的bean信息
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
//<2> 借助反射来完成属性注入
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
}
概览inject
方法中,不难发现其核心逻辑在于如下两点:
- 通过
BeanFactory
中的resolveDependency
找出待注入的value
对象- 利用反射机制,将获取
value
对象通过反射注入到bean
成员中
进一步,对于其中的resolveDependency
的逻辑如下。
java
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// ..... 省略其他无关逻辑
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
return result;
}
}
不难发现在resolveDependency
内部,其主要依赖doResolveDependency
方法来完成bean
对象的筛选,其具体逻辑如下:
java
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
... 省略无关代码
// <1> 解析属性,这里筛选出的属性可能是多个。即对于数组、集合、Map类型
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
// <2> 根据需要注入的类型type,从容器中找到有哪些匹配的Bean
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
}
String autowiredBeanName;
Object instanceCandidate;
if (matchingBeans.size() > 1) {
//<3> 判断应该使用哪一个bean
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
// ... 省略无关代码
if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
if (instanceCandidate instanceof Class) {
instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}
Object result = instanceCandidate;
if (result instanceof NullBean) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
result = null;
}
if (!ClassUtils.isAssignableValue(type, result)) {
throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
}
return result;
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}
-
对于
<1>
处逻辑,如果需要注入的属性是普通类型(即非数组、集合、Map ),那么方法会继续向下执行,会调用下面一行代码,根据属性的类型来查找bean
。进而走到<2>
。 -
此处
findAutowireCandidates()
方法,会从容器中找到对应类型的bean
。如果此时无法找到,且Autowired
注解的required
属性为true
那么就会抛出异常。也就是我们常见的:NoSuchBeanDefinitionException
。 -
如果能找到,则会继续执行。首先,
matchingBeans
这个map
的大小可能大于1,因为在spring
容器中是有可能找到多个满足类型的Bean
信息的。针对这种情况,就需要进行额外的判断了。其逻辑即可能找到多个UserService类型的bean,那么这个时候就需要判断这多个bean中,究竟应该注入哪一个。
因此,其会调用determineAutowireCandidate()
方法,判断应该使用哪一个Bean
。具体来看, determineAutowireCandidate()
其会首先查找标有 **@Primary注解的Bean
作为候选Bean
信息。 而如果都没有加@Primary
注解,那么就会找标有@Priority
注解的Bean
,进而将优先级高的Bean
选中。反之,如果都没有加@Priority
,@Primary
。那么此时则会先查找类型相匹配的类,例如,如果期待注入一个UserService
类型的属性,那么其在注入时则会首先寻找UserService
类型的类信息,如果找不到则会根据属性名进行查找。
这也就是我们平常所说的@Autowired
注解在注入时的核心逻辑,即其会先根据类型注入,当碰到多个相同类型时,就会根据属性名注入。
总结
本文首先介绍了@Autowired
注解的作用及使用方式,接着对@Autowired
实现原理进行深入剖析。具体来看,@Autowired
自动注入的通过AutowiredAnnotationBeanPostProcessor
这一后置处理器来完成的。随后,我们对其调用时机及实现逻辑进行深入剖析。简单来看,@Autowired
自动注入的本质就是反射实现Bean
内部实行的赋值。而在筛选候选Bean
时所采取的策略则是先根据类型查找,在根据你属性名查找候选Bean
。