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

1. 簡介

本篇文章是「Spring IOC 容器源碼分析」系列文章的最後一篇文章,本篇文章所分析的對象是 initializeBean 方法,該方法用於對已完成屬性填充的 bean 作最後的初始化工做。相較於以前幾篇文章所分析的源碼,initializeBean 的源碼相對比較簡單,你們能夠愉快的閱讀。好了,其餘的很少說了,咱們直入主題吧。java

2. 源碼分析

本章咱們來分析一下 initializeBean 方法的源碼。在完成分析後,仍是像往常同樣,把方法的執行流程列出來。好了,看源碼吧:app

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 方法的邏輯,很簡單是否是。該方法作了以下幾件事情:ide

  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 中注入不一樣類型的對象。post

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 方法用於執行初始化方法,也不復雜,就很少說了。this

3. 總結

本篇文章到這裏差很少就分析完了,總的來講本文的內容比較簡單,很容易看懂。正如簡介一章中所說,本篇文章是個人「Spring IOC 容器源碼分析」系列文章的最後一篇文章。寫完這本篇文章,有種如釋重負的感受。我在5月15號寫完 Java CAS 原理分析 文章後,第二天開始閱讀 Spring IOC 部分的源碼,閱讀該部分源碼花了大概兩週的時間。而後在5月30號發佈了「Spring IOC 容器源碼分析」系列文章的第一篇文章 Spring IOC 容器源碼分析系列文章導讀。在寫完第一篇文章後,就開啓了快速更新模式,以平均2天一篇的速度進行更新。終於在今天,也就是6月11號寫完了最後一篇。這一段時間寫文章寫的很累,常常熬夜。主要的緣由在於,在本身看懂源碼的同時,經過寫文章的方式儘可能保證別人也能看懂的話,這個就比較難了。好比我在閱讀源碼的時候,在源碼上面寫了一些簡單的註釋。這些註釋我能夠看懂,但若是想寫成文章,則須要把註釋寫的儘可能詳細,必要的背景知識也要介紹一下。總的來講,認真寫一篇技術文章仍是不容易的。寫文章尚如此,那寫書呢,想必更加辛苦了。我在閱讀源碼和寫文章的過程當中,也參考了一些資料(相關資料在「導讀」一文中指明瞭出處,本文就再也不次說明)。在這裏,向這些資料的做者表示感謝!spa

好了,本篇文章就到這裏了,感謝你們的閱讀。debug

本文在知識共享許可協議 4.0 下發布,轉載需在明顯位置處註明出處
做者:coolblog.xyz
本文同步發佈在個人我的博客: http://www.coolblog.xyz

附錄: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 原理探祕 - 容器的建立過程

cc
本做品採用知識共享署名-非商業性使用-禁止演繹 4.0 國際許可協議進行許可。代理

相關文章
相關標籤/搜索