天天看點

Spring IOC 容器源碼分析 - 餘下的初始化工作

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 原始對象
Spring IOC 容器源碼分析 - 餘下的初始化工作

Ⅱ. AOP

2018-06-17 Spring AOP 源碼分析系列文章導讀
2018-06-20 Spring AOP 源碼分析 - 篩選合适的通知器
Spring AOP 源碼分析 - 建立代理對象
2018-06-22 Spring AOP 源碼分析 - 攔截器鍊的執行過程

Ⅲ. MVC

2018-06-29 Spring MVC 原理探秘 - 一個請求的旅行過程
2018-06-30 Spring MVC 原理探秘 - 容器的建立過程

本文在知識共享許可協定 4.0 下釋出,轉載需在明顯位置處注明出處

作者:田小波

本文同步釋出在我的個人部落格:http://www.tianxiaobo.com

Spring IOC 容器源碼分析 - 餘下的初始化工作

本作品采用知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協定進行許可。