文章目录
一、前言
本文,是本人在阅读源码过程中的一些内容整理和总结。
全文仅代表个人理解和意见,故难免出现错误,如若发现,欢迎指正。十分谢谢。
本文所涉及的文章内容均在 : Spring源码分析:全集整理
关于 :ConfigurationClassPostProcessor
和 AutowiredAnnotationBeanPostProcessor
的详细分析,可以在 Spring源码分析:全集整理 中找到对应的分析文章。
二、正文
1. Spring IOC 实现个人感觉最重要的两个类
-
ConfigurationClassPostProcessor
: 完成了类的扫描和配置了的解析 -
AutowiredAnnotationBeanPostProcessor
: 完成了非 Property 的属性的注入
-
ConfigurationClassPostProcessor
是BeanFactoryPostProcessor
子类。完成了对Bean的解析,生成的对应的BeanDefinition。postProcessBeanDefinitionRegistry
: 解析所有需要注册的配置类
postProcessBeanFactory
:对Full类型进行代理增强,保证@Bean方法正确语意。 -
AutowiredAnnotationBeanPostProcessor
是BeanPostProcessor
子类,完成了@Autowired、@Value、@Inject
注解的解析注入功能。也就是说AutowiredAnnotationBeanPostProcessor
中才完成了属性的注入。
2. Spring什么时候扫描的包路径?
Spring在启动时候会将启动类注册到Spring容器中。在ConfigurationClassPostProcessor
中,会将启动类作为配置类进行解析。由于启动类上面有@ComponentScan
注解,所以在ConfigurationClassParser# doProcessConfigurationClass中
会解析这个注解, 根据解析后的路径通过
his.componentScanParser, parse (componentScan,sourceClass.getMetadata () .getClassName())
进行扫描,注册扫描到的需要注入的类(被@Component
修饰的类)并进行注入,如果有配置类再进行递归的配置类解析。(扫描是在 方法 org.springframework.context. annotation.ClassPathBeanDefinitionScanner#doScan
中进行的扫描,但是我偷懒了没看具体实现)
3. ConfigurationClassPostProcessor 的解析流程
.ConfigurationClassPostProcessor
完成了对各种配置类的扫描,并将其引入的bean注册到BeanFactory中。
-
postProcessBeanDefinitionRegistry
方法逻辑(完成了各种配置类的解析和注册) -
获取所有
BeanFactory
中的BeanDefinition
。 -
遍历所有
BeanDefinition
,筛选出full或者lite 配置类,等待下一步处理。Full
规则 :被@Configuration
注解修饰 &&proxyBeanMethods
属性为true (默认为true)Lite
规则:被@Component. @ComponentScan, @Import. @ImportResource
修饰的类或者类中有被@Bean修饰的方法(该方法会被包装成一个 BeanDefinition)
-
遍历所有full 和lite配置类,解析各种注解,对配置类进行进一步的解析(因为Lite的配置类需要二次解析,所以这里主要是二次解析)
- 对
@Component
进行解析:即如果当前配置类被@Component注解修饰,则去解析其内部类(包括静态内部类和非静态内部类)是否是配置类,如果是,递归解析发现的配置类。 - :对
@PropertySource
进行解析:解析出其指定的配置文件内容,并保存到environment 中 - :对
@ComponentScan
注解的解析:获取注解指定路径,进行扫描注册。对新扫描出来的BeanDefinition进行判定是否是配置类,如果是,则递归去解析配置类 - 对
@Import、ImportSelector、ImportBeanDefinitionRegistrar
的解析:因为这三个注解或接口,本质上完成的功能是.一样的,都是引入类。所以在这一部分的处理上只是区分了不同的类型 - 对
@ImportResource
注解的解析:提取出@ImportResource
注解的属性,保存到configClass
的importedResources
属性中 - 对被
@Bean修饰的方法
的解析:提取出@Bean修饰的方法,保存的到configClass
的beanMethods
属性中. - 对接口的默认方法解析:保存的到
configClass
的beanMethods
属性中 - 对父类的解析:如果存在父类,则递归其父类进行解析。
- 对
-
遍历所有的配置类,进行注册
- 处理引入的配置类:将配置类信息封装成
BeanDefinition
,并注册到BeanFactory
- 处理
@Bean修饰的方法
:将@Bean修饰的方法封装成BeanDefinition,并注册到BeanFactory
中 - 处理
@ImportResource
注解引入的资源文件,获取到BeanDefinition并注册。 - 处理
@Import、ImportSelector、ImportBeanDefinitionRegistrar
引入的类,注册到BeanFactory
中。
- 处理引入的配置类:将配置类信息封装成
补充:4.1 中为什么要解析@Component
?
- 因为过滤出来的配置类可能没有被
@Component
修饰,比如仅仅是,可能是Spring硬编码注入或者我们通过编码注入的bean。如果没有使用@Component
注解修饰,就没必要解析其内部类是否是配置类。比如最简单的就是spring.factories
文件中引入的类。可能满足配置类判定,但并没有被@Component
修饰
4. AutowiredAnnotationBeanPostProcessor 的解析流程
AutowiredAnnotationBeanPostProcessor
完成@Autowired. @Value、 @Inject
注解的功能, 完成了
Spring的注入
-
determineCandidateConstructors
方法 :筛选出当前bean中能作为构造注入的构造函数列表。用于构造注入- 处理了
@Lookup
注解,将@Lookup
注解标注的方法保存了起来。这一步和构造函数的筛选没有任何关系 - 挑选候选构造函数。简单逻辑就是有注解修饰选注解修饰的构造函数,没有的话使用默认无参构造函数没有无参构造函数,再没有返回null.
- 处理了
-
postProcessProperties
方法 :筛选Bean中需要注入的属性,进行属性注入。用于设置注入和注解注入遍历每个属性,找到被注入注解修饰的属性。保存到currElements
中。遍历每个方法,找到被注入注解修饰的方法, 保存到currElements
中。随后遍历currElements
, 通过resolveDependency
方法将方法或者属性注入(resolveDependency
方法是在populateBean
方法中按照类型注入时调用的方法)这里可以知道,按照顺序遍历,方法的属性在属性的后面,自动注入时,方法的注入结果会把注解属性的注入覆盖掉。
注: Spring注入的方式分为三种 :构造注入、设值注入、注解注入。在AutowiredAnnotationBeanPostProcessor
中处理的实际是设置注入和注解注入。
5. Bean 创建的过程
个人认为,在Spring容器中Bean的 创建时可以认为分为两个过程:
- Bean对应的BeanDefinition 的创建。
- Bean 实例的创建。
那么提出下面的问题:
-
BeanDefinition 的作用是什么?为什么需要创建 BeanDefinition?
因为在 Spring容器中,Bean的创建并非仅仅通过反射创建就结束了。在创建过程中,需要考虑到Bean针对Spring容器中的一些属性,如 Bean的作用域(单例、多例等),是否需要懒加载、Bean是否使用别名等。所以BeanDefinition 中不仅仅包含了 Bean Class 文件信息,还包含了这些容器属性。当Bean 进行实例化创建时需要根据对应的BeanDefinition 提供对应的信息来完成创建过程。
-
BeanDefinition 是在什么时候创建的?
ConfigurationClassPostProcessor 在解析出需要注入的Bean的同时会生成对应的 BeanDefinition,并保存到
DefaultListableBeanFactory#beanDefinitionMap
中 -
Bean 的实例在什么时候创建的?
这里需要分为两种情况:BeanPostProcessor 和普通的Bean。BeanPostProcessor 因为参与在普通Bean的创建过程,所以其创建时机在普通Bean之前。不过都在AbstractApplicationContext#refresh 中创建,详细实现我们在Spring源码分析一:容器的刷新 - refresh() 中讲过。其大概逻辑如下:
public void refresh() throws BeansException, IllegalStateException { ... // 激活 BeanFactoryPostProcessors invokeBeanFactoryPostProcessors(beanFactory); ... // 创建 BeanPostProcessor 实例并注册到 BeanFactory.beanPostProcessors 集合中 registerBeanPostProcessors(beanFactory); ... // 完成非惰性普通bean的创建工作 finishBeanFactoryInitialization(beanFactory); ... }