spring启动component-scan类扫描加载,以及@Resource,postConstruct等等注解的解析生效源码
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
spring启动component-scan类扫描加载,以及@Resource,postConstruct等等注解的解析生效源码的更多相关文章
- 【Spring源码分析系列】启动component-scan类扫描加载过程
原文地址:http://blog.csdn.net/xieyuooo/article/details/9089441/ 在spring 3.0以上大家都一般会配置一个Servelet,如下所示: &l ...
- spring启动component-scan类扫描加载过程(转)
文章转自 http://www.it165.net/pro/html/201406/15205.html 有朋友最近问到了 spring 加载类的过程,尤其是基于 annotation 注解的加载过程 ...
- 在Spring Boot中从类路径加载文件
介绍 创建Spring Boot Web应用程序时,有时有时需要从类路径中加载文件:war和jar的加载文件格式是不一样的 在下面,您将找到在WAR和JAR中加载文件的解决方案. 资源加载器 使用Ja ...
- 鸿蒙内核源码分析(进程镜像篇)|ELF是如何被加载运行的? | 百篇博客分析OpenHarmony源码 | v56.01
百篇博客系列篇.本篇为: v56.xx 鸿蒙内核源码分析(进程映像篇) | ELF是如何被加载运行的? | 51.c.h.o 加载运行相关篇为: v51.xx 鸿蒙内核源码分析(ELF格式篇) | 应 ...
- spring启动component-scan类扫描加载过程---源码分析
http://blog.csdn.net/xieyuooo/article/details/9089441#comments
- Spring Boot 启动(二) Environment 加载
Spring Boot 启动(二) Environment 加载 Spring 系列目录(https://www.cnblogs.com/binarylei/p/10198698.html) 上一节中 ...
- 反射 类的加载 Schema DOM 解析方式和解析器 命名空间
Day15 反射 1.1 类的加载 当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载,连接,初始化三步来实现对这个类进行初始化. l 加载 就是指将class文件读入内存,并为之创建 ...
- 【转】怎么解决java.lang.NoClassDefFoundError错误 ,以及类的加载机制
转自http://blog.csdn.net/jamesjxin/article/details/46606307 前言 在日常Java开发中,我们经常碰到java.lang.NoClassDefFo ...
- jvm(1)类的加载(二)(自定义类加载器)
[深入Java虚拟机]之四:类加载机制 1,从Java虚拟机的角度,只存在两种不同的类加载器: 1,启动类加载器:它使用C++实现(这里仅限于Hotspot,也就是JDK1.5之后默认的虚拟机,有其他 ...
随机推荐
- Notification 通知传值
通知 是在跳转控制器之间常用的传值代理方式,除了代理模式,通知更方便.便捷,一个简单的Demo实现通知的跳转传值. 输入所要发送的信息 ,同时将label的值通过button方法调用传递, ...
- 大牛的距离(笑cry)精简算法
在一条数轴上有N头牛在不同的位置上,每头牛都计算到其它各头牛的距离.求这n*(n-1)个距离的总和.1<= N <= 10000.每头牛所在位置是一个范围在0到1,000,000,000之 ...
- 约束 CONSTRAINT
约束用于限制加入表中数据的类型 约束的种类: 非空约束(NOT NULL):约束列不接受NULL值,强制字段始终包含值. 唯一约束(UNIQUE):约束一列或一组列中的数据是唯一的.表中可以有多个唯一 ...
- Hive 系列(二)权限管理
Hive 系列(二)权限管理 一.关于 Hive Beeline 问题 启动 hiveserver2 服务,启动 beeline -u jdbc:hive2:// 正常 ,启动 beeline -u ...
- 配置nginx1.8支持thinkPHP3.2 pathinfo模式
nginx 下conf/nginx.conf 或者自己的vhosts更改以前的参数 location / { root html; index index.html index.htm inde ...
- 补课:Shell命令${}
Shell中的${}.##和%%使用范例: 代码如下:file=/dir1/dir2/dir3/my.file.txt可以用${ }分别替换得到不同的值:${file#*/}:删掉第一个 / 及其左边 ...
- NServiceBus SAGA 消息状态驱动
https://docs.particular.net/tutorials/nservicebus-sagas/1-getting-started/ 链接:https://pan.baidu.com/ ...
- 【NIFI】 实现数据库到数据库之间数据同步
本里需要基础知识:[NIFI] Apache NiFI 安装及简单的使用 数据同步 界面如下: 具体流程: 1.使用ExecuteSQL连接mysql数据库,通过写sql查询所需要的数据 2.nifi ...
- 初学者问题一oracle
问:(待解决)如何将纵向表改成横向表? (待解决)如何实现对大型数据范围差距不大的索引?(建什么索引树)
- 又一道区间DP的题 -- P3146 [USACO16OPEN]248
https://www.luogu.org/problemnew/show/P3146 一道区间dp的题,以区间长度为阶段; 但由于要处理相邻的问题,就变得有点麻烦; 最开始想了一个我知道有漏洞的方程 ...