javascript
Spring IOC 容器源码分析 - 余下的初始化工作
1. 簡介
本篇文章是“Spring IOC 容器源碼分析”系列文章的最后一篇文章,本篇文章所分析的對象是 initializeBean 方法,該方法用于對已完成屬性填充的 bean 做最后的初始化工作。相較于之前幾篇文章所分析的源碼,initializeBean 的源碼相對比較簡單,大家可以愉快的閱讀。好了,其他的不多說了,我們直入主題吧。
?2. 源碼分析
本章我們來分析一下 initializeBean 方法的源碼。在完成分析后,還是像往常一樣,把方法的執行流程列出來。好了,看源碼吧:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 | protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {if (System.getSecurityManager() != null) {AccessController.doPrivileged(new PrivilegedAction<Object>() {@Overridepublic 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 方法的邏輯,很簡單是不是。該方法做了如下幾件事情:
在上面的流程中,我們又發現了后置處理器的蹤影。如果大家閱讀過 Spring 的源碼,會發現后置處理器在 Spring 源碼中多次出現過。后置處理器是 Spring 拓展點之一,通過實現后置處理器 BeanPostProcessor 接口,我們就可以插手 bean 的初始化過程。比如大家所熟悉的 AOP 就是在后置處理 postProcessAfterInitialization 方法中向目標對象中織如切面邏輯的。關于“前置處理”和“后置處理”相關的源碼,這里就不分析了,大家有興趣自己去看一下。接下來分析一下 invokeAwareMethods 和 invokeInitMethods 方法,如下:
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | 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 中注入不同類型的對象。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | 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>() {@Overridepublic 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號寫完了最后一篇。這一段時間寫文章寫的很累,經常熬夜。主要的原因在于,在自己看懂源碼的同時,通過寫文章的方式盡量保證別人也能看懂的話,這個就比較難了。比如我在閱讀源碼的時候,在源碼上面寫了一些簡單的注釋。這些注釋我可以看懂,但如果想寫成文章,則需要把注釋寫的盡量詳細,必要的背景知識也要介紹一下??偟膩碚f,認真寫一篇技術文章還是不容易的。寫文章尚如此,那寫書呢,想必更加辛苦了。我在閱讀源碼和寫文章的過程中,也參考了一些資料(相關資料在“導讀”一文中指明了出處,本文就不再次說明)。在這里,向這些資料的作者致以崇高的敬意,感謝!
好了,本篇文章就到這里了,感謝大家的閱讀。
?附錄: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 原始對象 |
| 2018-06-11 | Spring IOC 容器源碼分析 - 余下的初始化工作 |
?Ⅱ. AOP
| 2018-06-17 | Spring AOP 源碼分析系列文章導讀 |
| 2018-06-20 | Spring AOP 源碼分析 - 篩選合適的通知器 |
| 2018-06-20 | Spring AOP 源碼分析 - 創建代理對象 |
| 2018-06-22 | Spring AOP 源碼分析 - 攔截器鏈的執行過程 |
?Ⅲ. MVC
| 2018-06-29 | Spring MVC 原理探秘 - 一個請求的旅行過程 |
| 2018-06-30 | Spring MVC 原理探秘 - 容器的創建過程 |
- 本文鏈接:?https://www.tianxiaobo.com/2018/06/11/Spring-IOC-容器源碼分析-余下的初始化工作/
from:?http://www.tianxiaobo.com/2018/06/11/Spring-IOC-%E5%AE%B9%E5%99%A8%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90-%E4%BD%99%E4%B8%8B%E7%9A%84%E5%88%9D%E5%A7%8B%E5%8C%96%E5%B7%A5%E4%BD%9C/?
總結
以上是生活随笔為你收集整理的Spring IOC 容器源码分析 - 余下的初始化工作的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Spring IOC 容器源码分析 -
- 下一篇: Spring IOC 容器源码分析系列文