概述
在 AnnotationConfigApplicationContext 上下文初始化的时候,会初始化一个 AnnotatedBeanDefinitionReader 类型的成员变量,在此期间,会通过 AnnotationConfigUtils 类中的registerAnnotationConfigProcessors
方法,注册一些与注解配置相关的处理器。ConfigurationClassPostProcessor 就是其中之一。
本文通过阅读源码的方式,分析在这里注册的另外一个重要的处理器 AutowiredAnnotationBeanPostProcessor。
AutowiredAnnotationBeanPostProcessor 分析
先看 AutowiredAnnotationBeanPostProcessor 类的继承关系。
可以看出,它是一个 BeanPostProcessor,也就是 Bean 的后处理器。它还实现了一些 BeanPostProcessor 的字接口,BeanPostProcessor 和它的字接口中的方法,会在 Bean 实例初始化的过程中某些特定的时机被调用。所以,我们顺便了解一下这几个接口中包含的方法。
这里大概介绍一下其中一些关键方法的调用时机。
- BeanPostProcessor
-
postProcessBeforeInitialization
在 Bean 实例的初始化方法执行前被调用。 -
postProcessAfterInitialization
在 Bean 实例的初始化方法执行后被调用。
- MergedBeanDefinitionPostProcessor
-
postProcessMergedBeanDefinition
在通过doCreateBean
使用反射机制创建 Bean 实例之后调用。
- InstantiationAwareBeanPostProcessor
-
postProcessBeforeInstantiation
在doCreateBean
执行前自定义 Bean 实例创建的扩展点。 -
postProcessAfterInstantiation
在postProcessBeforeInstantiation
创建 Bean 实例不为空的情况下对 Bean 实例进行处理。 -
postProcessProperties
在装配 Bean 实例的属性前对属性进行处理。
- SmartInstantiationAwareBeanPostProcessor
-
getEarlyBeanReference
用于在早期 Bean 实例被获取之前对其进行处理。 -
determineCandidateConstructors
用于在通过反射创建 Bean 实例是获取候选的构造函数。
再结合前面的类关系图,可以知道 AutowiredAnnotationBeanPostProcessor 对这些方法的实现,都可以从它本身和它的父类 InstantiationAwareBeanPostProcessorAdapter 中找到。在这两个类中,大部分的方法都继承了接口的默认实现,除了determineCandidateConstructors、postProcessMergedBeanDefinition
、postProcessProperties
三个方法。下面以这三个方法被调用的时机为顺序,分析这三个方法都做了什么。
determineCandidateConstructors
方法分析
首先我们来分析determineCandidateConstructors
方法,这个方法被调用的时机,是 Spring 通过反射创建 Bean 实例对象的时候,会通过这个方法来获取候选的构造方法。我们查看方法的源码。
代码量相当可观,我们接下来会挑重点分析。
在第一个if
语句块中,处理了@``Lookup
注解相关的逻辑,这一部分不太重要,也很少会用到,因此不过介绍,我们重点看后面的部分。
首先,会从缓存集合candidateConstructorsCache中查询候选构造方法数组,如果缓存中获取的值不为空,则返回数组(数组中有元素)或者空(数组中没有元素)。另外,在if
语句块中,还会在synchronized
语句块中再次判断一遍。
确保缓存中确实没有内容的情况下,进入之后的流程。也就是上述代码中被省略的部分。
首先,会从当前 Bean 的类型中,获取到所有的构造方法,放到提前声明好的rawCandidates
数组中。
然后声明了一系列的变量,供后续的流程使用。这里的primaryConstructor
主要是针对 Kotlin 的,如果是 Kotlin 类型,则会返回其主构造方法对应的 Java 构造方法,如果是 Java 类型,则为空。这里可以通过findPrimaryConstructor
方法来看一下。
回到之前的方法中,之后开始对rawCandidates
中的构造方法进行遍历。
接下来看for
循环语句块中的内容。
首先判断了当前的构造方法是不是「合成」的构造方法,不是的话nonSyntheticConstructors
变量自增,如果是,且primaryConstructor
不是空,则跳过档次循环。
这一步是为了获取构造方法上的@``Autowired
注解,不仅会从当前的构造方法上查找,如果当前的类是CGLIB代理类型,还会从其userClass
,也就是用户声明的类中查找。
如果当前的构造方法没有被标记@``Autowired
注解,则ann
变量为空,反之则不为空。获取完成之后,会进入一个if-else
语句,针对ann
是否为空的情况,执行不同的流程。
如果ann
不为空,则进入以上的代码块中。首先会通过其中的第一个if
语句判断requiredConstructor
是否为空,如果不是空的话会抛出异常。当循环第一次走到这里的时候,它一定是空的,我们接着往下看。
接下来会判断当前构造方法的@``Autowired
注解的required
属性是否为true
。如果是true的话,则会进入下一个if
语句块。因为如果有构造方法的@``Autowired
注解的required
属性是否为true
的时候,被注解标记的构造方法只能有一个,所以,如果当前的构造方法如果符合条件,但是candidates
不为空,则会抛出异常。确保没有异常之后,会将当前的构造方法赋值给requiredConstructor
变量。
最后,只要当前的构造方法被标记了@``Autowired
注解,都会被添加到candidates
集合中。
如果当前的构造方法没有被标记@``Autowired
注解,且构造方法是无参构造方法,则将其赋值给defaultConstructor
变量。
至此,遍历所有构造方法的for
循环就执行完了。接下来会进入一系列的条件判断语句,它们的主要作用是对之前遍历过程中得到的结果进行处理。
如果candidates
不为空,也就是存在被标记@``Autowired
注解的构造方法,则进入当前的语句块。
再次判断,如果requiredConstructor
为空,也就是不存在@``Autowired
注解的required
属性是否为true
的构造方法,则将默认构造方法(也就是空参的构造发方法)defaultConstructor
添加到candidates
中。
当前if语句块的最后,将candidates
转换为数组赋值给candidateConstructors
作为候选的构造方法数组。
如果candidates
为空,则进入后续的条件判断语句块。
后续的几个条件连起来看。
- 如果当前类型只有一个构造方法,且是有参数的构造方法,则
candidateConstructors
为之包含这个构造方法的数组。 - 如果只有两个非合成的构造方法,其中一个是默认构造方法
defaultConstructor
,另一个是primaryConstructor
,且两者不是同一个构造方法,则candidateConstructors
为包含这两者的数组,primaryConstructor
在defaultConstructor
之前。 - 如果只有一个
primaryConstructor
,则candidateConstructors
是之包含它的数组。 - 如果之前的条件都不符合,则
candidateConstructors
为空数组。
处理完之后,会将结果放到缓存中。
在方法的最后,会判断candidateConstructors
是否有元素,如果有则返回,没有就返回空。
总结
本文介绍了 AutowiredAnnotationBeanPostProcessor 后处理器中的determineCandidateConstructors
如何为 Bean 类型确定候选的构造函数。