Spring IOC 容器源码分析 - 余下的初始化工作
1. 简介
本篇文章是“Spring IOC 容器源码分析”系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bean 做最后的初始化工作。相较于之前几篇文章所分析的源码,initializeBean 的源码相对比较简单,大家可以愉快的阅读。好了,其他的不多说了,我们直入主题吧。
2. 源码分析
本章我们来分析一下 initializeBean 方法的源码。在完成分析后,还是像往常一样,把方法的执行流程列出来。好了,看源码吧:
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
invokeAwareMethods(beanName, bean);
return null;
}
}, getAccessControlContext());
}
else {
// 若 bean 实现了 BeanNameAware、BeanFactoryAware、BeanClassLoaderAware 等接口,则向 bean 中注入相关对象
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 执行 bean 初始化前置操作
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
/*
* 调用初始化方法:
* 1. 若 bean 实现了 InitializingBean 接口,则调用 afterPropertiesSet 方法
* 2. 若用户配置了 bean 的 init-method 属性,则调用用户在配置中指定的方法
*/
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
// 执行 bean 初始化后置操作,AOP 会在此处向目标对象中织入切面逻辑
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
以上就是 initializeBean 方法的逻辑,很简单是不是。该方法做了如下几件事情:
- 检测 bean 是否实现了 *Aware 类型接口,若实现,则向 bean 中注入相应的对象
- 执行 bean 初始化前置操作
- 执行初始化操作
- 执行 bean 初始化后置操作
在上面的流程中,我们又发现了后置处理器的踪影。如果大家阅读过 Spring 的源码,会发现后置处理器在 Spring 源码中多次出现过。后置处理器是 Spring 拓展点之一,通过实现后置处理器 BeanPostProcessor 接口,我们就可以插手 bean 的初始化过程。比如大家所熟悉的 AOP 就是在后置处理 postProcessAfterInitialization 方法中向目标对象中织如切面逻辑的。关于“前置处理”和“后置处理”相关的源码,这里就不分析了,大家有兴趣自己去看一下。接下来分析一下 invokeAwareMethods 和 invokeInitMethods 方法,如下:
private void invokeAwareMethods(final String beanName, final Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
// 注入 beanName 字符串
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
// 注入 ClassLoader 对象
((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
}
if (bean instanceof BeanFactoryAware) {
// 注入 BeanFactory 对象
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
invokeAwareMethods 方法的逻辑很简单,一句话总结:根据 bean 所实现的 Aware 的类型,向 bean 中注入不同类型的对象。
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
// 检测 bean 是否是 InitializingBean 类型的
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
@Override
public Object run() throws Exception {
((InitializingBean) bean).afterPropertiesSet();
return null;
}
}, getAccessControlContext());
}
catch (PrivilegedActionException pae) {
throw pae.getException();
}
}
else {
// 如果 bean 实现了 InitializingBean,则调用 afterPropertiesSet 方法执行初始化逻辑
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null) {
String initMethodName = mbd.getInitMethodName();
if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
// 调用用户自定义的初始化方法
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
invokeInitMethods 方法用于执行初始化方法,也不复杂,就不多说了。
3. 总结
本篇文章到这里差不多就分析完了,总的来说本文的内容比较简单,很容易看懂。正如简介一章中所说,本篇文章是我的“Spring IOC 容器源码分析”系列文章的最后一篇文章。写完这本篇文章,有种如释重负的感觉。我在5月15号写完 Java CAS 原理分析 文章后,次日开始阅读 Spring IOC 部分的源码,阅读该部分源码花了大概两周的时间。然后在5月30号发布了“Spring IOC 容器源码分析”系列文章的第一篇文章 Spring IOC 容器源码分析系列文章导读。在写完第一篇文章后,就开启了快速更新模式,以平均2天一篇的速度进行更新。终于在今天,也就是6月11号写完了最后一篇。这一段时间写文章写的很累,经常熬夜。主要的原因在于,在自己看懂源码的同时,通过写文章的方式尽量保证别人也能看懂的话,这个就比较难了。比如我在阅读源码的时候,在源码上面写了一些简单的注释。这些注释我可以看懂,但如果想写成文章,则需要把注释写的尽量详细,必要的背景知识也要介绍一下。总的来说,认真写一篇技术文章还是不容易的。写文章尚如此,那写书呢,想必更加辛苦了。我在阅读源码和写文章的过程中,也参考了一些资料(相关资料在“导读”一文中指明了出处,本文就不再次说明)。在这里,向这些资料的作者表示感谢!
好了,本篇文章就到这里了,感谢大家的阅读。
附录:Spring 源码分析文章列表
Ⅰ. IOC
更新时间 | 标题 |
---|---|
2018-05-30 | Spring IOC 容器源码分析系列文章导读 |
2018-06-01 | Spring IOC 容器源码分析 - 获取单例 bean |
2018-06-04 | Spring IOC 容器源码分析 - 创建单例 bean 的过程 |
2018-06-06 | Spring IOC 容器源码分析 - 创建原始 bean 对象 |
2018-06-08 | Spring IOC 容器源码分析 - 循环依赖的解决办法 |
2018-06-11 | Spring IOC 容器源码分析 - 填充属性到 bean 原始对象 |
2018-06-11 | Spring IOC 容器源码分析 - 余下的初始化工作 |
Ⅱ. AOP
更新时间 | 标题 |
---|---|
2018-06-17 | Spring AOP 源码分析系列文章导读 |
2018-06-20 | Spring AOP 源码分析 - 筛选合适的通知器 |
2018-06-20 | Spring AOP 源码分析 - 创建代理对象 |
2018-06-22 | Spring AOP 源码分析 - 拦截器链的执行过程 |
Ⅲ. MVC
更新时间 | 标题 |
---|---|
2018-06-29 | Spring MVC 原理探秘 - 一个请求的旅行过程 |
2018-06-30 | Spring MVC 原理探秘 - 容器的创建过程 |
本文在知识共享许可协议 4.0 下发布,转载需在明显位置处注明出处
作者:田小波
本文同步发布在我的个人博客:http://www.tianxiaobo.com
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。
Spring IOC 容器源码分析 - 余下的初始化工作的更多相关文章
- Spring IOC 容器源码分析 - 填充属性到 bean 原始对象
1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...
- Spring IOC 容器源码分析 - 循环依赖的解决办法
1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...
- Spring IOC 容器源码分析 - 创建原始 bean 对象
1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...
- Spring IOC 容器源码分析 - 创建单例 bean 的过程
1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...
- Spring IOC 容器源码分析 - 获取单例 bean
1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...
- Spring IOC 容器源码分析系列文章导读
1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...
- 最简 Spring IOC 容器源码分析
前言 BeanDefinition BeanFactory 简介 Web 容器启动过程 bean 的加载 FactoryBean 循环依赖 bean 生命周期 公众号 前言 许多文章都是分析的 xml ...
- Spring IOC 容器源码分析
声明!非原创,本文出处 Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 S ...
- Spring IOC 容器源码分析(转)
原文地址 Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢 ...
随机推荐
- JDK8集合类源码解析 - ArrayList
ArrayList主要要注意以下几点: 1构造方法 2添加add(E e) 3 获取 get(int index) 4 删除 remove(int index) , remove(Objec ...
- 【Linux】ApacheBench(ab)压力测试工具
AB的简介 ab是apachebench命令的缩写. ab是apache自带的压力测试工具.ab非常实用,它不仅可以对apache服务器进行网站访问压力测试,也可以对或其它类型的服务器进行压力测试.比 ...
- [cmd]如何设置 Windows 默认命令行窗口大小和缓冲区大小
Windows 命令行 cmd 窗口系统默认的大小(80*40)对于现在的屏幕配置已经跟不上时代了,我们总是要把它改大些,而且缓冲区大小也想改得大大的.单纯的为当前的 Windows 命令行窗口修改显 ...
- 深入C# String类
深入C# String类 C#中的String类 他是专门处理字符串的(String),他在System的命名空间下,在C#中我们使用的是string 小写的string只是大写的String的一个别 ...
- unity中的构造函数
避免使用构造函数 不要在构造函数中初始化任何变量,使用Awake或Start实现这个目的.即使是在编辑模式中Unity也自动调用构造函数,这通常发生在一个脚本被编译之后,因为需要调用构造函数来取向一个 ...
- IOS 在一个透明视图上添加不透明的子控件
环境: 在一个透明的view中添加一个tableview,tableview也变透明了. 解决: 不要这样设置view的透明度 view.backgroundColor = [UIColor clea ...
- MySQL API函数
MySQL提供了很多函数来对数据库进行操作,大致可以分为以下几类: 第一部分 控制类函数 mysql_init()初始化MySQL对象 mysql_options( ...
- math.net 拟合
参考:http://blog.csdn.net/ztmsimon/article/details/50524392 在论坛中总看到有人在说Math.NET Iridium,查了一下,现在被整合到Mat ...
- POJ 3110 Jenny's First Exam (贪心)
题意:告诉你n 个科目的考试日期,在考试当天不能复习,每一个科目的最早复习时间不能早于考试时间的t天,每一天你可以复习完一科,也只能复习一科,求最晚的复习时间!. 析:由于题目给定的时间都在1900 ...
- mysql 入门 jdbc
在java程序中连接mysql,先要到mysql的网站上面去下载驱动,并且安装,默认安装在c盘(我的都是默认安装,目录为C:\Program Files\MySQL\MySQL Connector J ...