面試官:請你描述下 Spring Bean 的生命週期?





1. 引言

“請你描述下 Spring Bean 的生命週期?”,這是面試官考察 Spring 的常用問題,可見是 Spring 中很重要的知識點。

我之前在準備面試時,去網上搜過答案,大多以下圖給出的流程作為答案。

但是當我第一次看到該圖時,就產生了很多困擾,“Aware,BeanPostProcessor......這些都是什麼啊?而且這麼多步驟,太多了,該怎麼記啊?”。

其實要記憶該過程,還是需要我們先去理解,本文將從以下兩方面去幫助理解 Bean 的生命週期:

生命週期的概要流程:對 Bean 的生命週期進行概括,並且結合代碼來理解;擴展點的作用:詳細介紹 Bean 生命週期中所涉及到的擴展點的作用。

2. 生命週期的概要流程

Bean 的生命週期概括起來就是 4 個階段

實例化(Instantiation)屬性賦值(Populate)初始化(Initialization)銷燬(Destruction)


實例化:第 1 步,實例化一個 bean 對象;屬性賦值:第 2 步,為 bean 設置相關屬性和依賴;初始化:第 3~7 步,步驟較多,其中第 5、6 步為初始化操作,第 3、4 步為在初始化前執行,第 7 步在初始化後執行,該階段結束,才能被用戶使用;銷燬:第 8~10步,第8步不是真正意義上的銷燬(還沒使用呢),而是先在使用前註冊了銷燬的相關調用接口,為了後面第9、10步真正銷燬 bean 時再執行相應的方法。

下面我們結合代碼來直觀的看下,在 doCreateBean() 方法中能看到依次執行了這 4 個階段:

<code>// AbstractAutowireCapableBeanFactory.javaprotected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // 1. 實例化 BeanWrapper instanceWrapper = null; if (instanceWrapper == null) { instanceWrapper = createBeanInstance(beanName, mbd, args); } Object exposedObject = bean; try { // 2. 屬性賦值 populateBean(beanName, mbd, instanceWrapper); // 3. 初始化 exposedObject = initializeBean(beanName, exposedObject, mbd); } // 4. 銷燬-註冊回調接口 try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } return exposedObject;}/<code>

由於初始化包含了第 3~7步,較複雜,所以我們進到 initializeBean() 方法裡具體看下其過程(註釋的序號對應圖中序號):

<code>// AbstractAutowireCapableBeanFactory.javaprotected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) { // 3. 檢查 Aware 相關接口並設置相關依賴 if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); } // 4. BeanPostProcessor 前置處理 Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } // 5. 若實現 InitializingBean 接口,調用 afterPropertiesSet() 方法 // 6. 若配置自定義的 init-method方法,則執行 try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } // 7. BeanPostProceesor 後置處理 if (mbd == null || !mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean;}/<object>/<code>

在 invokInitMethods() 方法中會檢查 InitializingBean 接口和 init-method 方法,銷燬的過程也與其類似:

<code>// DisposableBeanAdapter.javapublic void destroy() { // 9. 若實現 DisposableBean 接口,則執行 destory()方法 if (this.invokeDisposableBean) { try { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedExceptionAction<object>) () -> { ((DisposableBean) this.bean).destroy(); return null; }, this.acc); } else { ((DisposableBean) this.bean).destroy(); } } } // 10. 若配置自定義的 detory-method 方法,則執行 if (this.destroyMethod != null) { invokeCustomDestroyMethod(this.destroyMethod); } else if (this.destroyMethodName != null) { Method methodToInvoke = determineDestroyMethod(this.destroyMethodName); if (methodToInvoke != null) { invokeCustomDestroyMethod(ClassUtils.getInterfaceMethodIfPossible(methodToInvoke)); } }}/<object>/<code>

從 Spring 的源碼我們可以直觀的看到其執行過程,而我們記憶其過程便可以從這 4 個階段出發,實例化、屬性賦值、初始化、銷燬。其中細節較多的便是初始化,涉及了 Aware、BeanPostProcessor、InitializingBean、init-method 的概念。這些都是 Spring 提供的擴展點,其具體作用將在下一節講述。

3. 擴展點的作用

3.1 Aware 接口

若 Spring 檢測到 bean 實現了 Aware 接口,則會為其注入相應的依賴。所以通過讓bean 實現 Aware 接口,則能在 bean 中獲得相應的 Spring 容器資源

Spring 中提供的 Aware 接口有:

BeanNameAware:注入當前 bean 對應 beanName;BeanClassLoaderAware:注入加載當前 bean 的 ClassLoader;BeanFactoryAware:注入 當前BeanFactory容器 的引用。

其代碼實現如下:

<code>// AbstractAutowireCapableBeanFactory.javaprivate void invokeAwareMethods(final String beanName, final Object bean) { if (bean instanceof Aware) { if (bean instanceof BeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); } if (bean instanceof BeanClassLoaderAware) { ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); } if (bean instanceof BeanFactoryAware) { ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); } }}/<code>

以上是針對 BeanFactory 類型的容器,而對於 ApplicationContext 類型的容器,也提供了 Aware 接口,只不過這些 Aware 接口的注入實現,是通過 BeanPostProcessor 的方式注入的,但其作用仍是注入依賴。

EnvironmentAware:注入 Enviroment,一般用於獲取配置屬性;EmbeddedValueResolverAware:注入 EmbeddedValueResolver(Spring EL解析器),一般用於參數解析;ApplicationContextAware(ResourceLoader、ApplicationEventPublisherAware、MessageSourceAware):注入 ApplicationContext 容器本身。

其代碼實現如下:

<code>// ApplicationContextAwareProcessor.javaprivate void invokeAwareInterfaces(Object bean) { if (bean instanceof EnvironmentAware) { ((EnvironmentAware)bean).setEnvironment(this.applicationContext.getEnvironment()); } if (bean instanceof EmbeddedValueResolverAware) { ((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(this.embeddedValueResolver); } if (bean instanceof ResourceLoaderAware) { ((ResourceLoaderAware)bean).setResourceLoader(this.applicationContext); } if (bean instanceof ApplicationEventPublisherAware) { ((ApplicationEventPublisherAware)bean).setApplicationEventPublisher(this.applicationContext); } if (bean instanceof MessageSourceAware) { ((MessageSourceAware)bean).setMessageSource(this.applicationContext); } if (bean instanceof ApplicationContextAware) { ((ApplicationContextAware)bean).setApplicationContext(this.applicationContext); }}/<code>

3.2 BeanPostProcessor

BeanPostProcessor 是 Spring 為修改 bean提供的強大擴展點,其可作用於容器中所有 bean,其定義如下:

<code>public interface BeanPostProcessor {// 初始化前置處理default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}// 初始化後置處理default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {return bean;}}/<code>

常用場景有:

對於標記接口的實現類,進行自定義處理。例如3.1節中所說的ApplicationContextAwareProcessor,為其注入相應依賴;再舉個例子,自定義對實現解密接口的類,將對其屬性進行解密處理;為當前對象提供代理實現。例如 Spring AOP 功能,生成對象的代理類,然後返回。

<code>// AbstractAutoProxyCreator.javapublic Object postProcessBeforeInstantiation(Class> beanClass, String beanName) { TargetSource targetSource = getCustomTargetSource(beanClass, beanName); if (targetSource != null) { if (StringUtils.hasLength(beanName)) { this.targetSourcedBeans.add(beanName); } Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource); Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource); this.proxyTypes.put(cacheKey, proxy.getClass()); // 返回代理類 return proxy; } return null;}/<code>

3.3 InitializingBean 和 init-method

InitializingBean 和 init-method 是 Spring 為 bean 初始化提供的擴展點。

InitializingBean接口 的定義如下:

<code>public interface InitializingBean {void afterPropertiesSet() throws Exception;}/<code>

在 afterPropertiesSet() 方法寫初始化邏輯。

指定 init-method 方法,指定初始化方法:

<code><beans> <bean> /<beans>/<code>

DisposableBean 和 destory-method 與上述類似,就不描述了。

4. 總結

最後總結下如何記憶 Spring Bean 的生命週期:

首先是實例化、屬性賦值、初始化、銷燬這 4 個大階段;再是初始化的具體操作,有 Aware 接口的依賴注入、BeanPostProcessor 在初始化前後的處理以及 InitializingBean 和 init-method 的初始化操作;銷燬的具體操作,有註冊相關銷燬回調接口,最後通過DisposableBean 和 destory-method 進行銷燬。


作者:草捏子
原文鏈接:https://juejin.im/post/5e4791a7f265da5715630629