3-bean 实例化——3-5 初始化
2020-02-25 本文已影响0人
鹏程1995
概要
过度
我们上文介绍了创建 Bean 实例并进行依赖注入、初始化的整体逻辑,我们介绍的是doCreateBean
方法。还画了一个流程图,如下图所示:
其中,我们分别在3-3,3-4中介绍了创建Bean实例、依赖注入的操作。现在我们拿到了 Bean 的一个实例引用,而且完成了对 Bean 实例中的所有需要的属性的依赖注入。我们之前说过在得到 Bean 实例后有两条路:
- 看如果是单例的话就提前暴露出去方便解决循环依赖,在最后完成创建后看是否成功解决了循环依赖
- 继续进行 Bean 的创建,包括依赖注入和调用初始化函数
其中,第一条路我们放在最后,在收束整个创建 Bean 实例流程时进行讲解。本节从第二条路入手,继续讲解 Bean 实例创建的整个流程。
我们本节介绍调用初始化钩子,对 Bean 实例进行最后的包装。
内容简介
本节介绍对得到的 Bean 实例进行初始化钩子调用的步骤。
所属环节
调用初始化相关钩子。
上下环节
上文: 创建 Bean 实例【根据配置和入参获得创建实例的函数并获得实例。然后进行依赖注入】
下午: 获得实例,进行循环依赖是否成功解决的判断。
源码解析
入口
我们看一下doCreateBean
中的相关调用
try {
// 填充一些 bean 的属性
populateBean(beanName, mbd, instanceWrapper);
// 初始化 bean
// 这里才叫初始化,初始化特指调用初始化和那些钩子方法,其他的包括填充属性,都叫创建实例
exposedObject = initializeBean(beanName, exposedObject, mbd);
} catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
} else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
其实就一行:
exposedObject = initializeBean(beanName, exposedObject, mbd);
我们接下来看看里面的逻辑:
初始化钩子调用的逻辑
// 初始化 bean ,主要调用 bean 的初始化方法【如果这个 bean 实现了对应的接口】以及工厂的一些回调函数
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
// 根据 bean 实现的 Aware 接口【aware ---> 知道的,也即是说这个 bean 想要获得的一些信息】,将对应的信息填充进 bean 实例
// 其实就是约定了一些接口,你实现一下,后面 Spring 会专门把一些你想知道的东西给你扔进去,比如 id、类加载器、创建你这个Bean的工厂啥的
// TODO 专门介绍一下下
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
} else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
// bean 是系统生成的,不是合成的,就调用工厂注册的那些在初始化之前要调用的钩子
// TODO mbd.isSynthetic() 这个一直有些懵逼
if (mbd == null || !mbd.isSynthetic()) { // 调用 Factory 中的那些个钩子
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
// 调用初始化方法
try {
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()) {// 调用 Factory 中的那些个钩子
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
思路明确:
- 把你想知道的关于上下文的信息给你
- 调用注册在
Factory
中的钩子 - 调用你配置的那些初始化钩子
- 调用注册在
Factory
中的钩子
都调用了哪些我们配置的初始化钩子
// 调用 bean 的初始化方法,初始化方法的指定有两种:
// 1. 用 init-method 指定【目前只关注了 xml 的配置,注解的那个我们后面再分析一遍】
// 2. bean 实现 InitializingBean 接口,直接调用接口下面的初始化方法
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
// mbs == null 用 或的短路去理解
// TODO : mbd.isExternallyManagedInitMethod 这个的设置和对应的使用后面可以瞅一下
if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (logger.isDebugEnabled()) {
logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
}
// 调用 afterPropertiesSet
if (System.getSecurityManager() != null) {
try {
AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> {
((InitializingBean) bean).afterPropertiesSet();
return null;
}, getAccessControlContext());
} catch (PrivilegedActionException pae) {
throw pae.getException();
}
} else {
((InitializingBean) bean).afterPropertiesSet();
}
}
if (mbd != null && bean.getClass() != NullBean.class) {
String initMethodName = mbd.getInitMethodName();
// 1. 有配置对应的 initMethod,就继续调用
if (StringUtils.hasLength(initMethodName) &&
!(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
!mbd.isExternallyManagedInitMethod(initMethodName)) {
invokeCustomInitMethod(beanName, bean, mbd);
}
}
}
- 如果实现了
InitializingBean
接口,就先调用它定义的初始化钩子 - 看你还有没有配置哪个函数要初始化的,调用一下
总结
思路整体来说极其顺畅。一杆子到底,没得说。
扩展
问题遗留
Aware
系列接口统一介绍
后处理器接口统一介绍
mbd
的一些属性
mbd.isExternallyManagedInitMethod
干啥的?
mbd.isSynthetic()
出现了好几次了,干啥的?