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 方法的逻辑,很简单是不是。该方法做了如下几件事情:

  1. 检测 bean 是否实现了 *Aware 类型接口,若实现,则向 bean 中注入相应的对象
  2. 执行 bean 初始化前置操作
  3. 执行初始化操作
  4. 执行 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 容器源码分析 - 余下的初始化工作的更多相关文章

  1. Spring IOC 容器源码分析 - 填充属性到 bean 原始对象

    1. 简介 本篇文章,我们来一起了解一下 Spring 是如何将配置文件中的属性值填充到 bean 对象中的.我在前面几篇文章中介绍过 Spring 创建 bean 的流程,即 Spring 先通过反 ...

  2. Spring IOC 容器源码分析 - 循环依赖的解决办法

    1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的.在本篇文章中,我会首先向大家介绍一下什么是循环依赖.然后,进入源码分析阶段.为了更好的说明 Spring 解决循环依赖的办法,我 ...

  3. Spring IOC 容器源码分析 - 创建原始 bean 对象

    1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续.在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程.本篇文章,我们就从战术的层面上,详细分析doCreat ...

  4. Spring IOC 容器源码分析 - 创建单例 bean 的过程

    1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑.对于已实例化好的单例 bean,getBean(String) 方法并不会再一次去 ...

  5. Spring IOC 容器源码分析 - 获取单例 bean

    1. 简介 为了写 Spring IOC 容器源码分析系列的文章,我特地写了一篇 Spring IOC 容器的导读文章.在导读一文中,我介绍了 Spring 的一些特性以及阅读 Spring 源码的一 ...

  6. Spring IOC 容器源码分析系列文章导读

    1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本.经过十几年的迭代,现在的 Spring 框架已经非常成熟了.Spring ...

  7. 最简 Spring IOC 容器源码分析

    前言 BeanDefinition BeanFactory 简介 Web 容器启动过程 bean 的加载 FactoryBean 循环依赖 bean 生命周期 公众号 前言 许多文章都是分析的 xml ...

  8. Spring IOC 容器源码分析

    声明!非原创,本文出处 Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 S ...

  9. Spring IOC 容器源码分析(转)

    原文地址 Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器.既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢 ...

随机推荐

  1. Nodejs+Mongo+WebAPI

    Nodejs+Mongo+WebAPI集成 1.[ 目录]: |- models/bear.js |- node_modules/ |- express |- mongoose |- body-par ...

  2. HTML-入门篇day01

    HTML-入门篇day01 1.web     C/S:Client Server    客户端 服务器    QQ,...    B/S:Browser Server    浏览器 服务器 PC机: ...

  3. Hadoop3集群搭建之——虚拟机安装

    现在做的项目是个大数据报表系统,刚开始的时候,负责做Java方面的接口(项目前端为独立的Java web 系统,后端也是Java web的系统,前后端系统通过接口传输数据),后来领导觉得大家需要多元化 ...

  4. Svn项目管理工具

    1       svn介绍 1.1     项目管理中的版本控制问题 通常软件开发由多人协作开发,如果对代码文件.配置文件.文档等没有进行版本控制,将会出现很多问题: 备份多个版本,占用磁盘空间大 解 ...

  5. latex字体

    强调 方式:声明:\em 或者 命令\emph,后者是latex2e的命令 区别:声明与命令的作用范围不同:\em改变当前字体直到被其他相应的声明取消(也可以是\em本身),或者当前的环境结束为止,当 ...

  6. activemq部署

    系统环境 IP salt-master-1:192.168.0.156 salt-master-2:192.168.0.157 node-test-1:192.168.0.158 node-test- ...

  7. Java,数据库中的数据导入到Excel

    private static void executeMethod(JobExecutionContext arg0) throws Exception{ try { TContrastService ...

  8. IntelliJ IDEA 2017版 编译器使用学习笔记(七) (图文详尽版);IDE快捷键使用;IDE代码重构(编写高质量代码)

    一.重构 重构变量:将语义模糊的变量名称改为更易理解的名称       修改变量名称,快键键 shift + F6 (输入要改的名字,所有位置相同的名字都会改变)               重构方法 ...

  9. i2c总线驱动,总线设备(适配器),从设备,从设备驱动的注册以及匹配

    常用链接 我的随笔 我的评论 我的参与 最新评论 我的标签 随笔分类 ARM裸机(13) C(8) C++(8) GNU-ARM汇编 Linux驱动(24) Linux应用编程(5) Makefile ...

  10. Ng第八课:神经网络表述(Neural Networks: Representation)

    8.1  非线性假设 8.2  神经元和大脑 8.3  模型表示 1 8.4  模型表示 2 8.5  特征和直观理解 1 8.6  样本和直观理解 II 8.7  多类分类 8.1  非线性假设 无 ...