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 方法的邏輯,很簡單是不是。該方法做了如下幾件事情:
- 檢測 bean 是否實作了 *Aware 類型接口,若實作,則向 bean 中注入相應的對象
- 執行 bean 初始化前置操作
- 執行初始化操作
- 執行 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

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