您的位置:首页 > 编程语言 > Java开发

spring启动component-scan类扫描加载,以及@Resource,postConstruct等等注解的解析生效源码

2018-02-01 15:17 871 查看
spring里IOC的原理就不详细写了, 如果想要搞清楚自动扫描组件是如何实现的,还有@Resouce @PostConstruct等注解的工作原理,最好可以先搞清楚整个IOC容器的运作原理再来分析这个过程.

IOC容器里的bean的生命周期如下:

1. beanDefinition 的解析

2. beanDefinition 的注册

3. bean的实例化

4. bean 的依赖注入

5. bean的初始化 (在初始化前后分别有对BeanPostProcessor的应用)

当然,中整个过程中还有很多细节的部分, 比如循环依赖的解决, 是否惰性注入, 等等, 还少不了很多地方都有BeanPostProcessor的埋点调用.

以上都是对IOC的简单背景介绍, 下面开始说正文.

在解析xml的时候, 如果发现了context:component-scan 这个标签, 在解析xml的过程中,就会找到这个标签的parsre类, 也就是ComponentScanBeanDefinitionParser, 具体怎么找到这个解析器的, 请大家自行回顾下beanDefinition 的解析过程.

然后我们看下ComponentScanBeanDefinitionParser 里的parse方法, 一直追下去, 会看到把包下各个类当作resource返回, 然后使用ASM对类信息进行解析. 大家自己可以看下关于ASM的内容 , 在看源码的时候,会更容易理解. 可以看到在对类信息进行解析的时候, 其实只是记录了类级别的信息, 比较类名, 类上的注解 等等, 在解析到类内的方法和域的时候, 使用的都是一个空的visitor, 也就意味着, 在解析的过程中, 并没有对方法和域进行任何实质性的解析,也没有记录任何关于方法和域的信息. 然后就拿着类级别的信息, 生成了对应的beanDefinition, 然后将这些beanDefinition注册到容器里. 看到这里, 我本来是很困惑的, 没有对域和方法进行解析, 那这个类内部的依赖是怎么注入的呢?

下面开始解释@Resouce @PostConstruct 这类注解的生效原理

首先看@Resouce这个注解是怎么生效的. 我们可以看下CommonAnnotationBeanPostProcessor 这个类, 在postProcessPropertyValues 方法里往下追, 会看到对@Resouce 这个注解的识别, 还有相应的域信息的记录, 然后还有相应bean的获取, 还有进行注入等操作. 但是postProcessPropertyValues 这个方法是什么时候被调用的呢? 再分析一下调用链, 发现是在bean的依赖注入的过程中, 掉用了所有的InstantiationAwareBeanPostProcessor 的postProcessPropertyValues 方法, 来对使用了@Resouce注解的类进行依赖注入. 总结一下, 在整个bean的生命周期里, 都没有保持关于@Resouce这个注解的任何信息, 它的生效, 完全是同过一类BeanPostProcessor来实现. 这也是为什么在解析beanDefinition的时候, 没有解析任何关于类内部域和方法的原因

下面再说@PostConstruct 这个注解是怎么生效的. 原理是一样的, 这次我们看下InitDestroyAnnotationBeanPostProcessor 这个类, 内部的postProcessBeforeInitialization 方法, 可以看到, 在这个方法里 , 首先对@PostConstruct 这个注解进行识别, 然后通过反射, 对这个方法进行了调用. 而postProcessBeforeInitialization 这个方法的被调用的地方就是上面在bean的生命周期里提到的 5. bean的初始化 (在初始化前后分别有对BeanPostProcessor的应用)

好, 这个时候, 其实可以看到IOC的设计思路了, 很多的注解的功能的生效, 并不是在解析beanDefinition的时候, 先对这些注解的信息进行解析, 保留, 然后到对应的点直接调用. 而是在对应的点, 通过不同的BeanPostProcessor开始对类内部进行解析, 如果解析到了感兴趣的注解或别的元素的话, 直接开始调用.

那么我们还剩下最后一个问题, 这些BeanPostProcessor 是什么地方注册进去的呢? 我们回到ComponentScanBeanDefinitionParser 这个类里, 看下parse 这个方法里调用了registerComponents 这个方法,往下追, 会看到AnnotationConfigUtils.registerAnnotationConfigProcessors 的调用,在这里注册了很多的BeanPostProcessor, 其中就有CommonAnnotationBeanPostProcessor, 还有别的一些注解, 包括一些会@Autowired等注解处理的BeanPostProcessor. 这也就是为什么说  <context:component-scan base-package=”XX.XX”/> 这个配置其实包含了类如<context:annotation-config/> 这些配置的原因.....

好, 但是还有一点不对劲, 这里的对BeanPostProcessor的注册, 只是把这写BeanPostProcessor当作普通的beanDefinition向容器进行了注册, 这写beanDefinition是什么时候生成真正的bean实例, 并被注册成为容器的BeanPostProcessor的呢? 这时就需要看下AbstractApplicationContext 类里的refresh方法了. 在这里完成了很多的操作, 包括对容器的初始化, beandefinition的注册等等一些操作. 在这个方法里可以看到对registerBeanPostProcessors的调用, 这这里, 会找到已经注册了的beandefinition里的BeanPostProcessor, 然后对它们进行实例话, 然后把bean实例注册为容器真正的BeanPostProcessor

也就是说, 如果我们想要把自己写的BeanPostProcessor 在spring内生效的话, 只要继承BeanPostProcessor 这个接口, 然后把这个类向spring注册就好了

好了, 以上, 就是对自动扫描相关的解说, 希望对大家有帮助

转载请注明出处: http://www.cnblogs.com/zhaoxinshanwei/p/8399176.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: