基于 Spring Framework v5.2.6.RELEASE
前情提要
上一篇分析了 Spring 完成早期 Bean 实例的创建后的一部分流程,其中包括很重要的属性注入的部分,这一篇接着分析之后的代码。在doCreateBean
方法中,属性注入的部分在populateBean
方法中完成,本文分析initializeBean
方法的源码,它在populateBean
执行完成之后执行,完成 Bean 实例的初始化。
// Initialize the bean instance.ObjectexposedObject=bean; try { populateBean(beanName, mbd, instanceWrapper); exposedObject=initializeBean(beanName, exposedObject, mbd); } catch (Throwableex) { if (exinstanceofBeanCreationException&&beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { thrownewBeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } }
Bean 实例的初始化
进入initializeBean
方法查看源码。
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean(java.lang.String, java.lang.Object, org.springframework.beans.factory.support.RootBeanDefinition)protectedObjectinitializeBean(finalStringbeanName, finalObjectbean, RootBeanDefinitionmbd) { if (System.getSecurityManager() !=null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); returnnull; }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); } ObjectwrappedBean=bean; if (mbd==null||!mbd.isSynthetic()) { wrappedBean=applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwableex) { thrownewBeanCreationException( (mbd!=null?mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd==null||!mbd.isSynthetic()) { wrappedBean=applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } returnwrappedBean; }
虽然也是一个很重要的步骤,但是其中的代码并不多,而且仔细分析之后,发现关键代码就是以下的 4 行,从调用的方法名称,也能知道它们的具体作用:
invokeAwareMethods(beanName, bean)
执行感知接口的方法。applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)
执行后处理器中初始化之前的处理invokeInitMethods(beanName, wrappedBean, mbd)
执行初始化方法applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName)
执行后处理器中初始化之后的处理
接下来逐一分析。
执行感知接口的方法
先看invokeAwareMethods
方法。
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeAwareMethodsprivatevoidinvokeAwareMethods(finalStringbeanName, finalObjectbean) { if (beaninstanceofAware) { if (beaninstanceofBeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); } if (beaninstanceofBeanClassLoaderAware) { ClassLoaderbcl=getBeanClassLoader(); if (bcl!=null) { ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); } } if (beaninstanceofBeanFactoryAware) { ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); } } }
这里的逻辑很简单,如果 Bean 实现了 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware 三个接口中的一个或多个,就调用接口的方法,将对应的属性注入到 Bean 实例中。这里只处理了这三个跟 Bean 本身有关的感知接口,其他的比如 ApplicationContextAware 等感知接口的处理逻辑,其实在容器初始化的过程中,已经通过 BeanPostProcessor 的方式注册到了容器中,可以参考 Spring 源码阅读 12:BeanFactory 预处理 中的「添加感知接口的后处理器」小节。
执行后处理器在初始化前后的处理逻辑
接下来看后处理器的处理逻辑,分别在执行 Bean 初始化方法的前后执行了 BeanPostProcessor 的postProcessBeforeInitialization
和postProcessAfterInitialization
方法。
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsBeforeInitializationpublicObjectapplyBeanPostProcessorsBeforeInitialization(ObjectexistingBean, StringbeanName) throwsBeansException { Objectresult=existingBean; for (BeanPostProcessorprocessor : getBeanPostProcessors()) { Objectcurrent=processor.postProcessBeforeInitialization(result, beanName); if (current==null) { returnresult; } result=current; } returnresult; }
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#applyBeanPostProcessorsAfterInitializationpublicObjectapplyBeanPostProcessorsAfterInitialization(ObjectexistingBean, StringbeanName) throwsBeansException { Objectresult=existingBean; for (BeanPostProcessorprocessor : getBeanPostProcessors()) { Objectcurrent=processor.postProcessAfterInitialization(result, beanName); if (current==null) { returnresult; } result=current; } returnresult; }
这两个方法的逻辑除了调用了后处理器的不同方法以外,其他都没什么区别。
在分析 Spring 容器初始化的代码时,曾经详细介绍过 BeanPostProcessor,如果我们需要自定义一些 Bean 初始化前后的处理逻辑,可以开发一个 BeanPostProcessor 的实现类,并将其作为 Bean 添加到配置文件中,在 Spring 初始化容器的过程中,会将所有 BeanPostProcessor 类型的 Bean 都作为后处理器注册到容器中,并且在注册前还进行了排序。因此,在这里执行后处理器逻辑的时候,直接按列表中的顺序执行就可以了。
关于 BeanPostProcessor 的更详细内容,以及 BeanPostProcessor 是如何注册到容器中的,可以参考这篇:Spring 源码阅读 14:注册 BeanPostProcessor
执行 Bean 的初始化方法
接下来看invokeInitMethods
方法。
// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#invokeInitMethodsprotectedvoidinvokeInitMethods(StringbeanName, finalObjectbean, RootBeanDefinitionmbd) throwsThrowable { booleanisInitializingBean= (beaninstanceofInitializingBean); 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>) () -> { ((InitializingBean) bean).afterPropertiesSet(); returnnull; }, getAccessControlContext()); } catch (PrivilegedActionExceptionpae) { throwpae.getException(); } } else { ((InitializingBean) bean).afterPropertiesSet(); } } if (mbd!=null&&bean.getClass() !=NullBean.class) { StringinitMethodName=mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) &&!(isInitializingBean&&"afterPropertiesSet".equals(initMethodName)) &&!mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } } }
这段代码的逻辑也很简单,主要是两部分:
- 如果当前处理的 Bean 实例实现了 InitializingBean 接口,那么调用它的
afterPropertiesSet
方法。 - 从 BeanDefinition 中获取initMethodName,也就是初始化方法名称,在 Bean 实例上执行这个方法。
这两部分分开来讲。
InitializingBean
先看 InitializingBean 接口是什么。
publicinterfaceInitializingBean { voidafterPropertiesSet() throwsException; }
接口中只定义了一个afterPropertiesSet
方法,也就是说,如果一个 Bean 实现了 InitializingBean 接口,那么它的afterPropertiesSet
将会在这个时候被调用。因此,如果我们需要一个 Bean 在初始化的时候执行一段特定的逻辑,可以通过实现 InitializingBean 接口,在afterPropertiesSet
实现这些逻辑。
这一部分只针对实现了 InitializingBean 接口的 Bean 实例。
自定义初始化方法
接下来看第二部分。首先会获取 BeanDefinition 的initMethodName
成员变量,它其实对应的 XML 配置文件中bean
标签的init-method
属性,这个属性的值对应类中的一个同名的无参方法。配置方法如下:
<beanid="user"class="xxx.User"init-method="init"/>
获取到方法名,在执行之前,还要进行以下一系列判断:
- 方法名不能是空的
- 如果当前的 Bean 实现了 InitializingBean 接口,且这里配置的方法名是afterPropertiesSet,那么,这个方法在之前已经执行过了,这里不在执行。
- 通过
isExternallyManagedInitMethod
方法判断,如果是外部管理的初始化方法,则不在此处执行。
判断无误后,会通过invokeCustomInitMethod
执行这个自定义的初始化方法。这个方法的代码比较多,但是逻辑比较简单,就是通过反射,执行方法。
总结
至此,initializeBean
方法中的源码就分析完了。完成了初始化的方法之后,Spring 创建和初始化 Bean 实例的流程也接近了尾声,后面还有一些最后的处理工作,留到下一篇中进行分析。