接着分析doCreateBean()的第6步——初始化 bean 实例对象

首先回顾下CreateBean的主流程:

  1. 如果是单例模式,从factoryBeanInstanceCache 缓存中获取BeanWrapper 实例对象并删除缓存
  2. 调用 createBeanInstance() 实例化 bean
  3. 后置处理
  4. 单例模式的循环依赖处理
  5. 属性填充
  6. 初始化 bean 实例对象
  7. 依赖检查
  8. 注册bean的销毁方法

一、初始化

Spring在对Bean进行属性填充之后,会对Bean进行初始化,代码如下:

//AbstractAutowireCapableBeanFactory.javaprotected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {//JDK的安全机制验证权限if (System.getSecurityManager() != null) {// <1> 激活 Aware 方法,对特殊的 bean 处理:Aware、BeanClassLoaderAware、BeanFactoryAwareAccessController.doPrivileged((PrivilegedAction<Object>) () -> {invokeAwareMethods(beanName, bean);return null;}, getAccessControlContext());}else {// <1> 激活 Aware 方法,对特殊的 bean 处理:Aware、BeanClassLoaderAware、BeanFactoryAwareinvokeAwareMethods(beanName, bean);}Object wrappedBean = bean;// <2> 后置处理器,beforeif (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);}// <3> 激活用户自定义的 init 方法try {invokeInitMethods(beanName, wrappedBean, mbd);}catch (Throwable ex) {throw new BeanCreationException((mbd != null ? mbd.getResourceDescription() : null),beanName, "Invocation of init method failed", ex);}// <2> 后置处理器,afterif (mbd == null || !mbd.isSynthetic()) {wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);}return wrappedBean;}

初始化 bean 的方法其实就是三个步骤的处理,而这三个步骤主要还是根据用户设定的来进行初始化,这三个过程为:

  1. <1> 激活 Aware 方法。
  2. <2> 后置处理器。
  3. <3> 自定义的 init 方法。
  • 1.1、Aware

Aware ,英文翻译是意识到的,感知的。Spring 提供了诸多 Aware 接口,用于辅助 Spring Bean 以编程的方式调用 Spring 容器,通过实现这些接口,可以增强 Spring Bean 的功能。

Spring 提供了如下系列的 Aware 接口:

  1. LoadTimeWeaverAware:加载Spring Bean时织入第三方模块,如AspectJ
  2. BeanClassLoaderAware:加载Spring Bean的类加载器
  3. BootstrapContextAware:资源适配器BootstrapContext,如JCA,CCI
  4. ResourceLoaderAware:底层访问资源的加载器
  5. BeanFactoryAware:声明BeanFactory
  6. PortletConfigAware:PortletConfig
  7. PortletContextAware:PortletContext
  8. ServletConfigAware:ServletConfig
  9. ServletContextAware:ServletContext
  10. MessageSourceAware:国际化
  11. ApplicationEventPublisherAware:应用事件
  12. NotificationPublisherAware:JMX通知
  13. BeanNameAware:声明Spring Bean的名字
  • 1.2、后置处理器
    BeanPostProcessor 在前面介绍 bean 加载的过程曾多次遇到,
    它的作用是:
    如果我们想要在 Spring 容器完成 Bean 的实例化,配置和其他的初始化后添加一些自己的逻辑处理,那么请使用该接口,这个接口给与了用户充足的权限去更改或者扩展 Spring,是我们对 Spring 进行扩展和增强处理一个必不可少的接口。
    applyBeanPostProcessorsBeforeInitialization() 方法,代码如下:
// AbstractAutowireCapableBeanFactory.java@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;// 遍历 BeanPostProcessor 数组for (BeanPostProcessor processor : getBeanPostProcessors()) {// 处理Object current = processor.postProcessBeforeInitialization(result, beanName);// 返回空,则返回 resultif (current == null) {return result;}// 修改 resultresult = current;}return result;
}

applyBeanPostProcessorsAfterInitialization() 方法,代码如下:

// AbstractAutowireCapableBeanFactory.java
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)throws BeansException {Object result = existingBean;// 遍历 BeanPostProcessorfor (BeanPostProcessor processor : getBeanPostProcessors()) {// 处理Object current = processor.postProcessAfterInitialization(result, beanName);// 返回空,则返回 resultif (current == null) {return result;}// 修改 resultresult = current;}return result;
}

其逻辑就是通过 getBeanPostProcessors() 方法,获取定义的 BeanPostProcessor ,然后分别调用其 postProcessBeforeInitialization() 和 postProcessAfterInitialization() 方法,进行自定义的业务处理。

  • 1.3、自定义init方法
    在xml中有一个< bean >标签的配置, init-method 方法,是可以让我们在Bean初始化的时候,先执行我们自定义的一些逻辑。
    其实就是在这里被触发的,代码如下:
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)throws Throwable {// 首先会检查是否是 InitializingBean ,如果是的话需要调用 afterPropertiesSet()boolean isInitializingBean = (bean instanceof InitializingBean);if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {if (logger.isTraceEnabled()) {logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");}if (System.getSecurityManager() != null) { // 安全模式try {AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {// <1> 属性初始化的处理((InitializingBean) bean).afterPropertiesSet();return null;}, getAccessControlContext());} catch (PrivilegedActionException pae) {throw pae.getException();}} else {// <1> 属性初始化的处理((InitializingBean) bean).afterPropertiesSet();}}if (mbd != null && bean.getClass() != NullBean.class) {String initMethodName = mbd.getInitMethodName();if (StringUtils.hasLength(initMethodName) &&!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&!mbd.isExternallyManagedInitMethod(initMethodName)) {// <2> 激活用户自定义的初始化方法invokeCustomInitMethod(beanName, bean, mbd);}}
}

首先,检查是否为 InitializingBean 。如果是的话,需要执行 afterPropertiesSet() 方法,因为我们除了可以使用 init-method 来自定初始化方法外,还可以实现 InitializingBean 接口。接口仅有一个 afterPropertiesSet() 方法。
两者的执行先后顺序是先 :<1> 的 #afterPropertiesSet() 方法
后 <2> 的 init-method 对应的方法。

本文转自:https://cloud.tencent.com/developer/article/1521203

Spring Ioc源码分析 之 Bean的加载(7):初始化相关推荐

  1. Spring Ioc源码分析 之 Bean的加载(6):属性填充(populateBean())

    "属性填充",也是在populateBean()方法中. 首先回顾下CreateBean的主流程: 如果是单例模式,从factoryBeanInstanceCache 缓存中获取B ...

  2. Spring Ioc源码分析 之 Bean的加载(4):实例化Bean(createBeanInstance()方法)

    实例化 Bean 在doCreateBean()代码 <2> 处,有一行代码instanceWrapper = createBeanInstance(beanName, mbd, args ...

  3. Spring Ioc源码分析 之 Bean的加载(5):循环依赖处理(populateBean())

    首先回顾下Bean加载的主流程: 1.如果是单例模式,从factoryBeanInstanceCache 缓存中获取BeanWrapper 实例对象并删除缓存 2.调用 createBeanInsta ...

  4. Spring Ioc 源码分析(一)--Spring Ioc容器的加载

    1.目标:熟练使用spring,并分析其源码,了解其中的思想.这篇主要介绍spring ioc 容器的加载 2.前提条件:会使用debug 3.源码分析方法:Intellj idea debug 模式 ...

  5. java获取当前周一_Java互联网架构-Spring IOC源码分析

    欢迎关注头条号:java小马哥 周一至周日下午三点半!精品技术文章准时送上!!! 精品学习资料获取通道,参见文末 源码介绍之前,看几个问题: Bean的承载对象是什么? Bean的定义如何存储的? B ...

  6. Spring Ioc 源码分析(一)- XML 解析

    2019独角兽企业重金招聘Python工程师标准>>> 注 :源码对应版本为 4.2.8.RELEASE 引入Spring依赖的时候,是否发现spring依赖有spring-bean ...

  7. Myabtis源码分析五-Mybatis配置加载完全图解,建造者模式的使用,涵盖Java各种技术栈

    private SqlSessionFactory sqlSessionFactory; @Before public void init() throws IOException { //----- ...

  8. nhibernate源码分析之六: Criteria数据加载

    ICriteria是使用Expression进行数据加载的接口, 提供了设置表达式(Expression), 排序方式(Order), 分页记录等操作. 它使用一种类似于SQL语句where表达表的方 ...

  9. MPF源码分析之资源文件加载

    本文将分析MPF客户端框架中资源文件相关的源代码,以github包中提供的qq界面demo作为 起点,一步一步分析程序的运行原理: 主程序很简单,代码如下: int APIENTRY _tWinMai ...

最新文章

  1. 数学知识--Methods for Non-Linear Least Squares Problems(第二章)
  2. 无毛刺的时钟切换电路(Glitch-free clock switching circuit)设计(Verilog)
  3. wegame饥荒一直连接中_怪诞画风下的异世界生存,一款让你吃到撑的游戏——《饥荒》...
  4. SAP Spartacus Page Layout - 页面布局设计
  5. struct sockaddr_nl 结构体 由来、含义以及使用——获取Linux路由表
  6. [react] React为什么不要直接修改state?如果想修改怎么做?
  7. 200 行代码实现一个滑动验证码
  8. 预处理器loader总结
  9. Java进阶:GIT
  10. jQuery计算时间差和阴阳历转换
  11. 搭建一个misskey实例
  12. 微信小程序自定义switch中带文字效果
  13. 行测-图形推理-7-相异图形类
  14. linux技术基础教程 [转载]
  15. pdksh-5.2.14-36.el5.i386.rpm
  16. 没有可用软件包。错误:无须任何处理
  17. debian更新apt源报错 ...is not valid yet (invalid for another 722d 21h 13min 35s).
  18. 把excel转换成html格式的文件,极强PDF转换器将Excel表格转换成HTML网页格式的方法...
  19. COI 2020 Semafor(矩阵乘法+优化)
  20. 【时间序列】001-推断统计

热门文章

  1. Party at Hali-Bula(树形DP+判断方案数是否唯一)
  2. JTS基本概念和使用
  3. weblogic配置domain和删除domain
  4. 20个优秀的JavaScript 键盘事件处理库
  5. typedefnbsp;struct与struct的区别
  6. MySql数据库操作遇到的蛋疼二三事
  7. 在VS2005下使用libjson -- wangj
  8. 头发一周洗几次才适宜? - 生活至上,美容至尚!
  9. 中文语音情感识别python实现(一)
  10. YOLO v3解析与实现