2018-05-12

spring源碼分析(四)



目錄

五、spring源碼分析
--5.5、IOC 容器的高級特性
----1、介紹
----2、Spring IOC 容器的 lazy-init 屬性實(shí)現(xiàn)預(yù)實(shí)例化
------(1).refresh()
------(2).finishBeanFactoryInitialization 處理預(yù)實(shí)例化 Bean
------(3).DefaultListableBeanFactory 對配置 lazy-init 屬性單態(tài) Bean 的預(yù)實(shí)例化
----3、FactoryBean 的實(shí)現(xiàn)
------(1)FactoryBean的源碼
------(2). AbstractBeanFactory 的 getBean 方法調(diào)用 FactoryBean
------(3).AbstractBeanFactory 生產(chǎn) Bean 實(shí)例對象
------(4).工廠 Bean 的實(shí)現(xiàn)類 getObject 方法創(chuàng)建 Bean 實(shí)例對象
----4、BeanPostProcessor 后置處理器的實(shí)現(xiàn)
------(1).BeanPostProcessor 的源碼
------(2).AbstractAutowireCapableBeanFactory 類對容器生成的 Bean 添加后置處理器
------(3).initializeBean 方法為容器產(chǎn)生的 Bean 實(shí)例對象添加 BeanPostProcessor 后置處理器
------(4).AdvisorAdapterRegistrationManager 在 Bean 對象初始化后注冊通知適配器
----5、Spring IOC 容器 autowiring 實(shí)現(xiàn)原理
------(1). AbstractAutoWireCapableBeanFactory 對 Bean 實(shí)例進(jìn)行屬性依賴注入
------(2).Spring IOC 容器根據(jù) Bean 名稱或者類型進(jìn)行 autowiring 自動依賴注入
------(3).DefaultSingletonBeanRegistry 的 registerDependentBean 方法對屬性注入



五、spring源碼分析

5.5、IOC 容器的高級特性

1、介紹

通過前面 4 節(jié)對 Spring IOC 容器的源碼分析,我們已經(jīng)基本上了解了 Spring IOC 容器對 Bean 定 義資源的定位、讀入和解析過程,同時也清楚了當(dāng)用戶通過 getBean 方法向 IOC 容器獲取被管理的 Bean 時,IOC 容器對 Bean 進(jìn)行的初始化和依賴注入過程,這些是 Spring IOC 容器的基本功能特性。 Spring IOC 容器還有一些高級特性,如使用 lazy-init 屬性對 Bean 預(yù)初始化、FactoryBean 產(chǎn)生或者 修飾 Bean 對象的生成、 IOC 容器初始化 Bean 過程中使用 BeanPostProcessor 后置處理器對 Bean 聲明周 期事件管理和 IOC 容器的 autowiring 自動裝配功能等。

2、Spring IOC 容器的 lazy-init 屬性實(shí)現(xiàn)預(yù)實(shí)例化:

通過前面我們對 IOC 容器的實(shí)現(xiàn)和工作原理分析,我們知道 IOC 容器的初始化過程就是對 Bean 定義 資源的定位、載入和注冊,此時容器對 Bean 的依賴注入并沒有發(fā)生,依賴注入主要是在應(yīng)用程序第一 次向容器索取 Bean 時,通過 getBean 方法的調(diào)用完成。

當(dāng) Bean 定義資源的<Bean>元素中配置了 lazy-init 屬性時,容器將會在初始化的時候?qū)λ渲玫?Bean 進(jìn)行預(yù)實(shí)例化,Bean 的依賴注入在容器初始化的時候就已經(jīng)完成。這樣,當(dāng)應(yīng)用程序第一次向容器索取 被管理的 Bean 時,就不用再初始化和對 Bean 進(jìn)行依賴注入了,直接從容器中獲取已經(jīng)完成依賴注入的 現(xiàn)成 Bean,可以提高應(yīng)用第一次向容器獲取 Bean 的性能。

下面我們通過代碼分析容器預(yù)實(shí)例化的實(shí)現(xiàn)過程:

(1).refresh()

先從 IOC 容器的初始會過程開始,通過前面文章分析,我們知道 IOC 容器讀入已經(jīng)定位的 Bean 定義資 源是從 refresh 方法開始的,我們首先從 AbstractApplicationContext 類的 refresh 方法入手分析, 源碼如下:

//容器初始化的過程,讀入 Bean 定義資源,并解析注冊
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        //調(diào)用容器準(zhǔn)備刷新的方法,獲取容器的當(dāng)時時間,同時給容器設(shè)置同步標(biāo)識
        prepareRefresh();
        //告訴子類啟動 refreshBeanFactory()方法,Bean 定義資源文件的載入從
        //子類的 refreshBeanFactory()方法啟動
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        //為 BeanFactory 配置容器特性,例如類加載器、事件處理器等
        prepareBeanFactory(beanFactory);
        try {
            //為容器的某些子類指定特殊的 BeanPost 事件處理器
            postProcessBeanFactory(beanFactory);
            //調(diào)用所有注冊的 BeanFactoryPostProcessor 的 Bean
            invokeBeanFactoryPostProcessors(beanFactory);
            //為 BeanFactory 注冊 BeanPost 事件處理器.
            //BeanPostProcessor 是 Bean 后置處理器,用于監(jiān)聽容器觸發(fā)的事件
            registerBeanPostProcessors(beanFactory);
            //初始化信息源,和國際化相關(guān).
            initMessageSource();
            //初始化容器事件傳播器.
            initApplicationEventMulticaster();
            //調(diào)用子類的某些特殊 Bean 初始化方法
            onRefresh();
            //為事件傳播器注冊事件監(jiān)聽器.
            registerListeners();
            //這里是對容器 lazy-init 屬性進(jìn)行處理的入口方法
            finishBeanFactoryInitialization(beanFactory);
            //初始化容器的生命周期事件處理器,并發(fā)布容器的生命周期事件
            finishRefresh();
        }catch (BeansException ex) {
            //銷毀以創(chuàng)建的單態(tài) Bean
            destroyBeans();
            //取消 refresh 操作,重置容器的同步標(biāo)識.
            cancelRefresh(ex);
            throw ex;
        }
    }
}

在 refresh 方法中 ConfigurableListableBeanFactorybeanFactory = obtainFreshBeanFactory(); 啟動了 Bean 定義資源的載入、注冊過程,而 finishBeanFactoryInitialization 方法是對注冊后的 Bean 定義中的預(yù)實(shí)例化(lazy-init=false,Spring 默認(rèn)就是預(yù)實(shí)例化,即為 true)的 Bean 進(jìn)行處理的地方。

(2).finishBeanFactoryInitialization 處理預(yù)實(shí)例化 Bean:

當(dāng)Bean定義資源被載入IOC容器之后,容器將Bean定義資源解析為容器內(nèi)部的數(shù)據(jù)結(jié)構(gòu)BeanDefinition 注冊到容器中, AbstractApplicationContext 類中的 finishBeanFactoryInitialization 方法對配置了 預(yù)實(shí)例化屬性的 Bean 進(jìn)行預(yù)初始化過程,源碼如下:

//對配置了 lazy-init 屬性的 Bean 進(jìn)行預(yù)實(shí)例化處理
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    //這是 Spring3 以后新加的代碼,為容器指定一個轉(zhuǎn)換服務(wù)(ConversionService)
    //在對某些 Bean 屬性進(jìn)行轉(zhuǎn)換時使用
    if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
    beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
        beanFactory.setConversionService(
            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
    }
    //為了類型匹配,停止使用臨時的類加載器
    beanFactory.setTempClassLoader(null);
    //緩存容器中所有注冊的 BeanDefinition 元數(shù)據(jù),以防被修改
    beanFactory.freezeConfiguration();
    //對配置了 lazy-init 屬性的單態(tài)模式 Bean 進(jìn)行預(yù)實(shí)例化處理
    beanFactory.preInstantiateSingletons();
}

ConfigurableListableBeanFactory 是一個接口,其 preInstantiateSingletons 方法由其子類 DefaultListableBeanFactory 提供。

(3).DefaultListableBeanFactory 對配置 lazy-init 屬性單態(tài) Bean 的預(yù)實(shí)例化:

public void preInstantiateSingletons() throws BeansException {
    if (this.logger.isInfoEnabled()) {
     this.logger.info("Pre-instantiating singletons in " + this);
    }
    //在對配置 lazy-init 屬性單態(tài) Bean 的預(yù)實(shí)例化過程中,必須多線程同步,以確保數(shù)據(jù)一致性
    synchronized (this.beanDefinitionMap) {
        for (String beanName : this.beanDefinitionNames) {
            //獲取指定名稱的 Bean 定義
            RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
            //Bean 不是抽象的,是單態(tài)模式的,且 lazy-init 屬性配置為 false
                if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
                    //如果指定名稱的 bean 是創(chuàng)建容器的 Bean
                    if (isFactoryBean(beanName)) {
                    //FACTORY_BEAN_PREFIX=”&”,當(dāng) Bean 名稱前面加”&”符號
                    //時,獲取的是產(chǎn)生容器對象本身,而不是容器產(chǎn)生的 Bean.
                    //調(diào)用 getBean 方法,觸發(fā)容器對 Bean 實(shí)例化和依賴注入過程
                    final FactoryBean factory = (FactoryBean) getBean(FACTORY_BEAN_PREFIX +
                    beanName);
                    //標(biāo)識是否需要預(yù)實(shí)例化
                    boolean isEagerInit;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean){
                        //一個匿名內(nèi)部類
                        isEagerInit = AccessController.doPrivileged(new
                            PrivilegedAction<Boolean>() {
                                public Boolean run() {
                                    return ((SmartFactoryBean) factory).isEagerInit();
                                }
                            }, getAccessControlContext());
                    }else {
                        isEagerInit = factory instanceof SmartFactoryBean && ((SmartFactoryBean)
                        factory).isEagerInit();
                    }
                    if (isEagerInit) {
                        //調(diào)用 getBean 方法,觸發(fā)容器對 Bean 實(shí)例化和依賴注入過程
                        getBean(beanName);
                    }
                }else {
                    //調(diào)用 getBean 方法,觸發(fā)容器對 Bean 實(shí)例化和依賴注入過程
                    getBean(beanName);
                }
            }
        }
    }
}

通過對 lazy-init 處理源碼的分析,我們可以看出,如果設(shè)置了 lazy-init 屬性,則容器在完成 Bean 定義的注冊之后,會通過 getBean 方法,觸發(fā)對指定 Bean 的初始化和依賴注入過程,這樣當(dāng)應(yīng)用第一 次向容器索取所需的 Bean 時,容器不再需要對 Bean 進(jìn)行初始化和依賴注入,直接從已經(jīng)完成實(shí)例化和 依賴注入的 Bean 中取一個現(xiàn)成的 Bean,這樣就提高了第一次獲取 Bean 的性能。

3、FactoryBean 的實(shí)現(xiàn):

在 Spring 中,有兩個很容易混淆的類:BeanFactory 和 FactoryBean。

BeanFactory:Bean 工廠,是一個工廠(Factory),我們 Spring IOC 容器的最頂層接口就是這個 BeanFactory,它的作用是管理 Bean,即實(shí)例化、定位、配置應(yīng)用程序中的對象及建立這些對象間的依 賴。

FactoryBean:工廠 Bean,是一個 Bean,作用是產(chǎn)生其他 bean 實(shí)例。通常情況下,這種 bean 沒有什么 特別的要求,僅需要提供一個工廠方法,該方法用來返回其他 bean 實(shí)例。通常情況下,bean 無須自己 實(shí)現(xiàn)工廠模式,Spring 容器擔(dān)任工廠角色;但少數(shù)情況下,容器中的 bean 本身就是工廠,其作用是產(chǎn) 生其它 bean 實(shí)例。

當(dāng)用戶使用容器本身時,可以使用轉(zhuǎn)義字符”&”來得到 FactoryBean 本身,以區(qū)別通過 FactoryBean 產(chǎn)生的實(shí)例對象和 FactoryBean 對象本身。在 BeanFactory 中通過如下代碼定義了該轉(zhuǎn)義字符:

StringFACTORY_BEAN_PREFIX = "&";

如果 myJndiObject 是一個 FactoryBean,則使用&myJndiObject 得到的是 myJndiObject 對象,而不是 myJndiObject 產(chǎn)生出來的對象。

(1).FactoryBean 的源碼:

//工廠 Bean,用于產(chǎn)生其他對象
public interface FactoryBean<T> {
    //獲取容器管理的對象實(shí)例
    T getObject() throws Exception;
    //獲取 Bean 工廠創(chuàng)建的對象的類型
    Class<?> getObjectType();
    //Bean 工廠創(chuàng)建的對象是否是單態(tài)模式,如果是單態(tài)模式,則整個容器中只有一個實(shí)例
    //對象,每次請求都返回同一個實(shí)例對象
    boolean isSingleton();
}

(2). AbstractBeanFactory 的 getBean 方法調(diào)用 FactoryBean:

在前面我們分析 Spring IOC 容器實(shí)例化 Bean 并進(jìn)行依賴注入過程的源碼時,提到在 getBean 方法觸 發(fā)容器實(shí)例化 Bean 的時候會調(diào)用 AbstractBeanFactory 的 doGetBean 方法來進(jìn)行實(shí)例化的過程,源碼 如下:

//真正實(shí)現(xiàn)向 IOC 容器獲取 Bean 的功能,也是觸發(fā)依賴注入功能的地方
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean
typeCheckOnly) throws BeansException {
    //根據(jù)指定的名稱獲取被管理 Bean 的名稱,剝離指定名稱中對容器的相關(guān)依賴
    //如果指定的是別名,將別名轉(zhuǎn)換為規(guī)范的 Bean 名稱
    final String beanName = transformedBeanName(name);
    Object bean;
    //先從緩存中取是否已經(jīng)有被創(chuàng)建過的單態(tài)類型的 Bean,對于單態(tài)模式的 Bean 整
    //個 IoC 容器中只創(chuàng)建一次,不需要重復(fù)創(chuàng)建
    Object sharedInstance = getSingleton(beanName);
    //IoC 容器創(chuàng)建單態(tài)模式 Bean 實(shí)例對象
    if (sharedInstance != null && args == null) {
        if (logger.isDebugEnabled()) {
            //如果指定名稱的 Bean 在容器中已有單態(tài)模式的 Bean 被創(chuàng)建,直接返回
            //已經(jīng)創(chuàng)建的 Bean
            if (isSingletonCurrentlyInCreation(beanName)) {
                logger.debug("Returning eagerly cached instance of singleton bean '" + beanName
                +
                "' that is not fully initialized yet - a consequence of a circular
                reference");
            }else {
             logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
            }
        }
        //獲取給定 Bean 的實(shí)例對象,主要是完成 FactoryBean 的相關(guān)處理
        bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    ……
}
//獲取給定 Bean 的實(shí)例對象,主要是完成 FactoryBean 的相關(guān)處理
protected Object getObjectForBeanInstance(
Object beanInstance, String name, String beanName, RootBeanDefinition mbd) {
    //容器已經(jīng)得到了 Bean 實(shí)例對象,這個實(shí)例對象可能是一個普通的 Bean,
    //也可能是一個工廠 Bean,如果是一個工廠 Bean,則使用它創(chuàng)建一個 Bean 實(shí)例對象,
    //如果調(diào)用本身就想獲得一個容器的引用,則指定返回這個工廠 Bean 實(shí)例對象
    //如果指定的名稱是容器的解引用(dereference,即是對象本身而非內(nèi)存地址),
    //且 Bean 實(shí)例也不是創(chuàng)建 Bean 實(shí)例對象的工廠 Bean
    if (BeanFactoryUtils.isFactoryDereference(name) && !(beanInstance instanceof FactoryBean)) {
        throw new BeanIsNotAFactoryException(transformedBeanName(name),
            beanInstance.getClass());
    }
    //如果 Bean 實(shí)例不是工廠 Bean,或者指定名稱是容器的解引用,
    //調(diào)用者向獲取對容器的引用,則直接返回當(dāng)前的 Bean 實(shí)例
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
     return beanInstance;
    }
    //處理指定名稱不是容器的解引用,或者根據(jù)名稱獲取的 Bean 實(shí)例對象是一個工廠 Bean
    //使用工廠 Bean 創(chuàng)建一個 Bean 的實(shí)例對象
    Object object = null;
    if (mbd == null) {
        //從 Bean 工廠緩存中獲取給定名稱的 Bean 實(shí)例對象
        object = getCachedObjectForFactoryBean(beanName);
    }
    //讓 Bean 工廠生產(chǎn)給定名稱的 Bean 對象實(shí)例
    if (object == null) {
        FactoryBean factory = (FactoryBean) beanInstance;
        //如果從 Bean 工廠生產(chǎn)的 Bean 是單態(tài)模式的,則緩存
        if (mbd == null && containsBeanDefinition(beanName)) {
            //從容器中獲取指定名稱的 Bean 定義,如果繼承基類,則合并基類相關(guān)屬性
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        //如果從容器得到 Bean 定義信息,并且 Bean 定義信息不是虛構(gòu)的,
        //則讓工廠 Bean 生產(chǎn) Bean 實(shí)例對象
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        //調(diào)用 FactoryBeanRegistrySupport 類的 getObjectFromFactoryBean 方法,
        //實(shí)現(xiàn)工廠 Bean 生產(chǎn) Bean 對象實(shí)例的過程
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

在上面獲取給定 Bean 的實(shí)例對象的 getObjectForBeanInstance 方法中,會調(diào)用 FactoryBeanRegistrySupport 類的 getObjectFromFactoryBean 方法,該方法實(shí)現(xiàn)了 Bean 工廠生產(chǎn) Bean 實(shí)例對象。

Dereference(解引用):一個在 C/C++中應(yīng)用比較多的術(shù)語,在 C++中,”*”是解引用符號,而”&”是 引用符號,解引用是指變量指向的是所引用對象的本身數(shù)據(jù),而不是引用對象的內(nèi)存地址。

(3).AbstractBeanFactory 生產(chǎn) Bean 實(shí)例對象:

AbstractBeanFactory 類中生產(chǎn) Bean 實(shí)例對象的主要源碼如下

//Bean 工廠生產(chǎn) Bean 實(shí)例對象
protected Object getObjectFromFactoryBean(FactoryBean factory, String beanName, boolean
shouldPostProcess) {
    //Bean 工廠是單態(tài)模式,并且 Bean 工廠緩存中存在指定名稱的 Bean 實(shí)例對象
    if (factory.isSingleton() && containsSingleton(beanName)) {
        //多線程同步,以防止數(shù)據(jù)不一致
        synchronized (getSingletonMutex()) {
            //直接從 Bean 工廠緩存中獲取指定名稱的 Bean 實(shí)例對象
            Object object = this.factoryBeanObjectCache.get(beanName);
            //Bean 工廠緩存中沒有指定名稱的實(shí)例對象,則生產(chǎn)該實(shí)例對象
            if (object == null) {
                //調(diào)用 Bean 工廠的 getObject 方法生產(chǎn)指定 Bean 的實(shí)例對象
                object = doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
                //將生產(chǎn)的實(shí)例對象添加到 Bean 工廠緩存中
                this.factoryBeanObjectCache.put(beanName, (object != null ? object : NULL_OBJECT));
            }
            return (object != NULL_OBJECT ? object : null);
        }
    }
    //調(diào)用 Bean 工廠的 getObject 方法生產(chǎn)指定 Bean 的實(shí)例對象
    else {
        return doGetObjectFromFactoryBean(factory, beanName, shouldPostProcess);
    }
}
//調(diào)用 Bean 工廠的 getObject 方法生產(chǎn)指定 Bean 的實(shí)例對象
private Object doGetObjectFromFactoryBean(
final FactoryBean factory, final String beanName, final boolean shouldPostProcess)
throws BeanCreationException {
    Object object;
    try {
        if (System.getSecurityManager() != null) {
            AccessControlContext acc = getAccessControlContext();
            try {
                //實(shí)現(xiàn) PrivilegedExceptionAction 接口的匿名內(nèi)置類
                //根據(jù) JVM 檢查權(quán)限,然后決定 BeanFactory 創(chuàng)建實(shí)例對象
                object = AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                    public Object run() throws Exception {
                        //調(diào)用 BeanFactory 接口實(shí)現(xiàn)類的創(chuàng)建對象方法
                        return factory.getObject();
                    }}, acc);
            }catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }else {
            //調(diào)用 BeanFactory 接口實(shí)現(xiàn)類的創(chuàng)建對象方法
            object = factory.getObject();
        }
    }catch (FactoryBeanNotInitializedException ex) {
        throw new BeanCurrentlyInCreationException(beanName, ex.toString());
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation",ex);
    }
    //創(chuàng)建出來的實(shí)例對象為 null,或者因?yàn)閱螒B(tài)對象正在創(chuàng)建而返回 null
    if (object == null && isSingletonCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(
        beanName, "FactoryBean which is currently in creation returned null from getObject");
    }
    //為創(chuàng)建出來的 Bean 實(shí)例對象添加 BeanPostProcessor 后置處理器
    if (object != null && shouldPostProcess) {
        try {
            object = postProcessObjectFromFactoryBean(object, beanName);
        }catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Post-processing of the FactoryBean's object
            failed", ex);
        }
    }
    return object;
}

從上面的源碼分析中,我們可以看出, BeanFactory 接口調(diào)用其實(shí)現(xiàn)類的 getObject 方法來實(shí)現(xiàn)創(chuàng)建 Bean 實(shí)例對象的功能。

(4).工廠 Bean 的實(shí)現(xiàn)類 getObject 方法創(chuàng)建 Bean 實(shí)例對象:

FactoryBean的實(shí)現(xiàn)類有非常多,比如: Proxy、 RMI、 JNDI、 ServletContextFactoryBean 等等, FactoryBean 接口為 Spring 容器提供了一個很好的封裝機(jī)制,具體的 getObject 有不同的實(shí)現(xiàn)類根據(jù)不同的實(shí)現(xiàn)策 略來具體提供,我們分析一個最簡單的 AnnotationTestFactoryBean 的實(shí)現(xiàn)源碼:

public class AnnotationTestBeanFactory implements FactoryBean<IJmxTestBean> {

    private final FactoryCreatedAnnotationTestBean instance = new
            FactoryCreatedAnnotationTestBean();

    public AnnotationTestBeanFactory() {
        this.instance.setName("FACTORY");
    }
    //AnnotationTestBeanFactory 產(chǎn)生 Bean 實(shí)例對象的實(shí)現(xiàn)
    public IJmxTestBean getObject() throws Exception {
         return this.instance;
    }
    public Class<? extends IJmxTestBean> getObjectType() {
        return FactoryCreatedAnnotationTestBean.class;
    }
    public boolean isSingleton() {
         return true;
    }
}

其他的 Proxy,RMI,JNDI 等等,都是根據(jù)相應(yīng)的策略提供 getObject 的實(shí)現(xiàn)。這里不做一一分析,這 已經(jīng)不是 Spring 的核心功能,有需要的時候再去深入研究。

4、BeanPostProcessor 后置處理器的實(shí)現(xiàn):

BeanPostProcessor 后置處理器是 Spring IOC 容器經(jīng)常使用到的一個特性,這個 Bean 后置處理器是一 個監(jiān)聽器,可以監(jiān)聽容器觸發(fā)的 Bean 聲明周期事件。后置處理器向容器注冊以后,容器中管理的 Bean 就具備了接收 IOC 容器事件回調(diào)的能力。

BeanPostProcessor 的使用非常簡單,只需要提供一個實(shí)現(xiàn)接口 BeanPostProcessor 的實(shí)現(xiàn)類,然后在 Bean 的配置文件中設(shè)置即可。

(1).BeanPostProcessor 的源碼:

package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
public interface BeanPostProcessor {
    //為在 Bean 的初始化前提供回調(diào)入口
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    //為在 Bean 的初始化之后提供回調(diào)入口
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

這兩個回調(diào)的入口都是和容器管理的 Bean 的生命周期事件緊密相關(guān),可以為用戶提供在 Spring IOC 容器初始化 Bean 過程中自定義的處理操作。

(2).AbstractAutowireCapableBeanFactory 類對容器生成的 Bean 添加后置處理器:

BeanPostProcessor后置處理器的調(diào)用發(fā)生在Spring IOC容器完成對Bean實(shí)例對象的創(chuàng)建和屬性的依 賴注入完成之后,在對 Spring 依賴注入的源碼分析過程中我們知道,當(dāng)應(yīng)用程序第一次調(diào)用 getBean 方法(lazy-init 預(yù)實(shí)例化除外)向 Spring IOC 容器索取指定 Bean 時觸發(fā) Spring IOC 容器創(chuàng)建 Bean 實(shí)例對象并進(jìn)行依賴注入的過程,其中真正實(shí)現(xiàn)創(chuàng)建 Bean 對象并進(jìn)行依賴注入的方法是 AbstractAutowireCapableBeanFactory 類的 doCreateBean 方法,主要源碼如下

//真正創(chuàng)建 Bean 的方法
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {
    //創(chuàng)建 Bean 實(shí)例對象
    ……
    try {
        //對 Bean 屬性進(jìn)行依賴注入
        populateBean(beanName, mbd, instanceWrapper);
        if (exposedObject != null) {
            //在對 Bean 實(shí)例對象生成和依賴注入完成以后,開始對 Bean 實(shí)例對象
            //進(jìn)行初始化 ,為 Bean 實(shí)例對象應(yīng)用 BeanPostProcessor 后置處理器
            exposedObject = initializeBean(beanName, exposedObject, mbd);
        }
    }catch (Throwable ex) {
        if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException)
        ex).getBeanName())) {
            throw (BeanCreationException) ex;
        }
    ……
    //為應(yīng)用返回所需要的實(shí)例對象
    return exposedObject;
}

從上面的代碼中我們知道,為 Bean 實(shí)例對象添加 BeanPostProcessor 后置處理器的入口的是 initializeBean 方法。

(3).initializeBean 方法為容器產(chǎn)生的 Bean 實(shí)例對象添加 BeanPostProcessor 后置處理器:

同樣在 AbstractAutowireCapableBeanFactory 類中, initializeBean 方法實(shí)現(xiàn)為容器創(chuàng)建的 Bean 實(shí)例 對象添加 BeanPostProcessor 后置處理器,源碼如下:

//初始容器創(chuàng)建的 Bean 實(shí)例對象,為其添加 BeanPostProcessor 后置處理器
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd){
    //JDK 的安全機(jī)制驗(yàn)證權(quán)限
    if (System.getSecurityManager() != null) {
        //實(shí)現(xiàn) PrivilegedAction 接口的匿名內(nèi)部類
        AccessController.doPrivileged(new PrivilegedAction<Object>() {
            public Object run() {
                invokeAwareMethods(beanName, bean);
                return null;
            }
        }, getAccessControlContext());
    }else {
        //為 Bean 實(shí)例對象包裝相關(guān)屬性,如名稱,類加載器,所屬容器等信息
        invokeAwareMethods(beanName, bean);
    }
    Object wrappedBean = bean;
    //對 BeanPostProcessor 后置處理器的 postProcessBeforeInitialization
    //回調(diào)方法的調(diào)用,為 Bean 實(shí)例初始化前做一些處理
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }
    //調(diào)用 Bean 實(shí)例對象初始化的方法,這個初始化方法是在 Spring Bean 定義配置
    //文件中通過 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);
    }
    //對 BeanPostProcessor 后置處理器的 postProcessAfterInitialization
    //回調(diào)方法的調(diào)用,為 Bean 實(shí)例初始化之后做一些處理
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}
//調(diào)用 BeanPostProcessor 后置處理器實(shí)例對象初始化之前的處理方法
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException {
    Object result = existingBean;
    //遍歷容器為所創(chuàng)建的 Bean 添加的所有 BeanPostProcessor 后置處理器
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        //調(diào)用 Bean 實(shí)例所有的后置處理中的初始化前處理方法,為 Bean 實(shí)例對象在
        //初始化之前做一些自定義的處理操作
        result = beanProcessor.postProcessBeforeInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
    return result;
}
//調(diào)用 BeanPostProcessor 后置處理器實(shí)例對象初始化之后的處理方法
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException {
    Object result = existingBean;
    //遍歷容器為所創(chuàng)建的 Bean 添加的所有 BeanPostProcessor 后置處理器
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        //調(diào)用 Bean 實(shí)例所有的后置處理中的初始化后處理方法,為 Bean 實(shí)例對象在
        //初始化之后做一些自定義的處理操作
        result = beanProcessor.postProcessAfterInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
    return result;
}

BeanPostProcessor 是一個接口,其初始化前的操作方法和初始化后的操作方法均委托其實(shí)現(xiàn)子類來實(shí) 現(xiàn),在 Spring 中,BeanPostProcessor 的實(shí)現(xiàn)子類非常的多,分別完成不同的操作,如:AOP 面向切面編程的注冊通知適配器、Bean 對象的數(shù)據(jù)校驗(yàn)、Bean 繼承屬性/方法的合并等等,我們以最簡單的 AOP 切面織入來簡單了解其主要的功能。

(4).AdvisorAdapterRegistrationManager 在 Bean 對象初始化后注冊通知適配器:

AdvisorAdapterRegistrationManager 是 BeanPostProcessor 的一個實(shí)現(xiàn)類,其主要的作用為容器中管 理的 Bean 注冊一個面向切面編程的通知適配器,以便在 Spring 容器為所管理的 Bean 進(jìn)行面向切面編 程時提供方便,其源碼如下:

//為容器中管理的 Bean 注冊一個面向切面編程的通知適配器
public class AdvisorAdapterRegistrationManager implements BeanPostProcessor {
    //容器中負(fù)責(zé)管理切面通知適配器注冊的對象
    private AdvisorAdapterRegistry advisorAdapterRegistry =
        GlobalAdvisorAdapterRegistry.getInstance();
    
    public void setAdvisorAdapterRegistry(AdvisorAdapterRegistry advisorAdapterRegistry) {
         this.advisorAdapterRegistry = advisorAdapterRegistry;
    }
    //BeanPostProcessor 在 Bean 對象初始化前的操作
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException
    {
        //沒有做任何操作,直接返回容器創(chuàng)建的 Bean 對象
        return bean;
    }
    //BeanPostProcessor 在 Bean 對象初始化后的操作
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException
    {
        if (bean instanceof AdvisorAdapter){
            //如果容器創(chuàng)建的 Bean 實(shí)例對象是一個切面通知適配器,則向容器的注冊
            this.advisorAdapterRegistry.registerAdvisorAdapter((AdvisorAdapter) bean);
        }
        return bean;
    }
}

其他的 BeanPostProcessor 接口實(shí)現(xiàn)類的也類似,都是對 Bean 對象使用到的一些特性進(jìn)行處理,或者 向 IOC 容器中注冊,為創(chuàng)建的 Bean 實(shí)例對象做一些自定義的功能增加,這些操作是容器初始化 Bean 時 自動觸發(fā)的,不需要認(rèn)為的干預(yù)。

5、Spring IOC 容器 autowiring 實(shí)現(xiàn)原理:

Spring IOC 容器提供了兩種管理 Bean 依賴關(guān)系的方式:

a.顯式管理:通過 BeanDefinition 的屬性值和構(gòu)造方法實(shí)現(xiàn) Bean 依賴關(guān)系管理。

b.autowiring:Spring IOC 容器的依賴自動裝配功能,不需要對 Bean 屬性的依賴關(guān)系做顯式的聲明, 只需要在配置好 autowiring 屬性,IOC 容器會自動使用反射查找屬性的類型和名稱,然后基于屬性的類 型或者名稱來自動匹配容器中管理的 Bean,從而自動地完成依賴注入。

通過對 autowiring 自動裝配特性的理解,我們知道容器對 Bean 的自動裝配發(fā)生在容器對 Bean 依賴注 入的過程中。在前面對 Spring IOC 容器的依賴注入過程源碼分析中,我們已經(jīng)知道了容器對 Bean 實(shí) 例對象的屬性注入的處理發(fā)生在 AbstractAutoWireCapableBeanFactory 類中的 populateBean 方法中, 我們通過程序流程分析 autowiring 的實(shí)現(xiàn)原理:

(1). AbstractAutoWireCapableBeanFactory 對 Bean 實(shí)例進(jìn)行屬性依賴注入:

應(yīng)用第一次通過 getBean 方法(配置了 lazy-init 預(yù)實(shí)例化屬性的除外)向 IoC 容器索取 Bean 時,容器 創(chuàng)建 Bean 實(shí)例對象,并且對 Bean 實(shí)例對象進(jìn)行屬性依賴注入,AbstractAutoWireCapableBeanFactory 的 populateBean 方法就是實(shí)現(xiàn) Bean 屬性依賴注入的功能,其主要源碼如下:

protected void populateBean(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw) {
    //獲取 Bean 定義的屬性值,并對屬性值進(jìn)行處理
    PropertyValues pvs = mbd.getPropertyValues();
    ……
    //對依賴注入處理,首先處理 autowiring 自動裝配的依賴注入
    if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
     mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
        //根據(jù) Bean 名稱進(jìn)行 autowiring 自動裝配處理
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
            autowireByName(beanName, mbd, bw, newPvs);
        }
        //根據(jù) Bean 類型進(jìn)行 autowiring 自動裝配處理
        if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
            autowireByType(beanName, mbd, bw, newPvs);
        }
    }
    //對非 autowiring 的屬性進(jìn)行依賴注入處理
    ……
}

(2).Spring IOC 容器根據(jù) Bean 名稱或者類型進(jìn)行 autowiring 自動依賴注入:

//根據(jù)名稱對屬性進(jìn)行自動依賴注入
protected void autowireByName(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs){
    //對 Bean 對象中非簡單屬性(不是簡單繼承的對象,如 8 中原始類型,字符串,URL 等都是簡單屬性)進(jìn)行處理
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        //如果 Spring IOC 容器中包含指定名稱的 Bean
        if (containsBean(propertyName)) {
            //調(diào)用 getBean 方法向 IoC 容器索取指定名稱的 Bean 實(shí)例,迭代觸發(fā)屬性的初始化和依賴注入
            Object bean = getBean(propertyName);
            //為指定名稱的屬性賦予屬性值
            pvs.add(propertyName, bean);
            //指定名稱屬性注冊依賴 Bean 名稱,進(jìn)行屬性依賴注入
            registerDependentBean(propertyName, beanName);
            if (logger.isDebugEnabled()) {
                logger.debug("Added autowiring by name from bean name '" + beanName +
                "' via property '" + propertyName + "' to bean named '" + propertyName + "'");
            }
        }else {
            if (logger.isTraceEnabled()) {
                logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName + "' by name: no matching bean found");
            }
        }
    }
}
//根據(jù)類型對屬性進(jìn)行自動依賴注入
protected void autowireByType(
String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs)
{
    //獲取用戶定義的類型轉(zhuǎn)換器
    TypeConverter converter = getCustomTypeConverter();
    if (converter == null) {
         converter = bw;
    }
    //存放解析的要注入的屬性
    Set<String> autowiredBeanNames = new LinkedHashSet<String>(4);
    //對 Bean 對象中非簡單屬性(不是簡單繼承的對象,如 8 中原始類型,字符
    //URL 等都是簡單屬性)進(jìn)行處理
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        try {
            //獲取指定屬性名稱的屬性描述器
            PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
            //不對 Object 類型的屬性進(jìn)行 autowiring 自動依賴注入
            if (!Object.class.equals(pd.getPropertyType())) {
                //獲取屬性的 setter 方法
                MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
                //檢查指定類型是否可以被轉(zhuǎn)換為目標(biāo)對象的類型
                boolean eager = !PriorityOrdered.class.isAssignableFrom(bw.getWrappedClass());
                //創(chuàng)建一個要被注入的依賴描述
                DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam,
                eager);
                //根據(jù)容器的 Bean 定義解析依賴關(guān)系,返回所有要被注入的 Bean 對象
                Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames,
                converter);
                if (autowiredArgument != null) {
                    //為屬性賦值所引用的對象
                    pvs.add(propertyName, autowiredArgument);
                }
                for (String autowiredBeanName : autowiredBeanNames) {
                //指定名稱屬性注冊依賴 Bean 名稱,進(jìn)行屬性依賴注入
                    registerDependentBean(autowiredBeanName, beanName);
                    if (logger.isDebugEnabled()) {
                        logger.debug("Autowiring by type from bean name '" + beanName + "' via
                        property '" +
                        propertyName + "' to bean named '" + autowiredBeanName + "'");
                    }
                }
                //釋放已自動注入的屬性
                autowiredBeanNames.clear();
            }
        }catch (BeansException ex) {
            throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName,
             propertyName, ex);
        }
    }
}

通過上面的源碼分析,我們可以看出來通過屬性名進(jìn)行自動依賴注入的相對比通過屬性類型進(jìn)行自動依 賴注入要稍微簡單一些,但是真正實(shí)現(xiàn)屬性注入的是 DefaultSingletonBeanRegistry 類的 registerDependentBean 方法。

(3).DefaultSingletonBeanRegistry 的 registerDependentBean 方法對屬性注入:

//為指定的 Bean 注入依賴的 Bean
public void registerDependentBean(String beanName, String dependentBeanName) {
    //處理 Bean 名稱,將別名轉(zhuǎn)換為規(guī)范的 Bean 名稱
    String canonicalName = canonicalName(beanName);
    //多線程同步,保證容器內(nèi)數(shù)據(jù)的一致性
    //先從容器中:bean 名稱-->全部依賴 Bean 名稱集合找查找給定名稱 Bean 的依賴 Bean
    synchronized (this.dependentBeanMap) {
        //獲取給定名稱 Bean 的所有依賴 Bean 名稱
        Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
        if (dependentBeans == null) {
            //為 Bean 設(shè)置依賴 Bean 信息
            dependentBeans = new LinkedHashSet<String>(8);
            this.dependentBeanMap.put(canonicalName, dependentBeans);
        }
        //向容器中:bean 名稱-->全部依賴 Bean 名稱集合添加 Bean 的依賴信息
        //即,將 Bean 所依賴的 Bean 添加到容器的集合中
        dependentBeans.add(dependentBeanName);
    }
    //從容器中:bean 名稱-->指定名稱 Bean 的依賴 Bean 集合找查找給定名稱 Bean 的依賴 Bean
    synchronized (this.dependenciesForBeanMap) {
        Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName);
        if (dependenciesForBean == null) {
            dependenciesForBean = new LinkedHashSet<String>(8);
            this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);
        }
        //向容器中:bean 名稱-->指定 Bean 的依賴 Bean 名稱集合添加 Bean 的依賴信息
        //即,將 Bean 所依賴的 Bean 添加到容器的集合中
        dependenciesForBean.add(canonicalName);
    }
}

通過對 autowiring 的源碼分析,我們可以看出,autowiring 的實(shí)現(xiàn)過程:

a.對 Bean 的屬性代調(diào)用 getBean 方法,完成依賴 Bean 的初始化和依賴注入。

b.將依賴 Bean 的屬性引用設(shè)置到被依賴的 Bean 屬性上。

c.將依賴 Bean 的名稱和被依賴 Bean 的名稱存儲在 IOC 容器的集合中。

Spring IOC 容器的 autowiring 屬性自動依賴注入是一個很方便的特性,可以簡化開發(fā)時的配置,但是凡是都有兩面性, 自動屬性依賴注入也有不足,首先,Bean 的依賴關(guān)系在配置文件中無法很清楚地看出來,對于維護(hù)造成一定困難。其 次,由于自動依賴注入是 Spring 容器自動執(zhí)行的,容器是不會智能判斷的,如果配置不當(dāng),將會帶來無法預(yù)料的后果, 所以自動依賴注入特性在使用時還是綜合考慮。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,345評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,494評論 3 416
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,283評論 0 374
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,953評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,714評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,186評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,255評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,410評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,940評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,776評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,976評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,518評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,210評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,642評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,878評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,654評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,958評論 2 373

推薦閱讀更多精彩內(nèi)容

  • 文章作者:Tyan博客:noahsnail.com | CSDN | 簡書 3.8 Container Exten...
    SnailTyan閱讀 1,240評論 0 6
  • 注意LifecycleProcessor接口繼承了Lifcycle接口。同時,增加了2個方法,用于處理容器的ref...
    google666s閱讀 1,119評論 0 51
  • 什么是Spring Spring是一個開源的Java EE開發(fā)框架。Spring框架的核心功能可以應(yīng)用在任何Jav...
    jemmm閱讀 16,503評論 1 133
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,781評論 18 139
  • 這是關(guān)于藏客小伙伴的第一個故事 我叫陳咪咪,陳外是我的藝名,西藏這個地方嘛!沒來的時候想的抓心撓肺,來了之后,發(fā)現(xiàn)...
    藏客旅行閱讀 553評論 0 9