Spring源碼初探-IOC(5)-ApplicationContext功能擴展及其擴展點

前言

前面幾篇關于Spring的文章簡單闡述了使用BeanFactory作為容器時bean的初始化過程。然而在實際使用中,我們并不會直接接觸和編碼BeanFactory,我們通常會使用另外一個功能更強、更完善的容器ApplicationContext。本文粗略講述了ApplicationContext對于BeanFactory的功能擴展,并將重點放在了Spring在容器啟動和初始化過程中提供的擴展點和事件發布上。擴展點讓我們能夠“插手和干預”Bean的初始化,通過容器發布的事件得以了解容器的一些內部過程。

ApplicationContext的功能擴展

ApplicationContext是“事實上”的容器標準,它基于BeanFactory并對其做了一些功能上的擴展。例如:

  • 通過MessageResource支持國際化
  • 提供了容器內部的消息發布機制
  • 自動添加BeanFactoryPostProcessor、BeanPostProcessor到容器中
    等。
    由于ApplicationContext對于BeanFactory的擴展不是本文闡述的重點,所以略過。

Spring容器初始化中的擴展點

Spring容器初始化中的擴展點不僅包括了Beanfactory提供的也包含了ApplictionContext增強的。

從前面幾篇關于容器初始化的文章可以得出,從xml到實例化并初始化完bean大體上經歷了兩個過程:

  • 容器啟動過程:這個過程包括了讀取xml文件,并替換一些系統或者自定義變量,將xml標簽解析成BeanDefinitionwrapper。
  • 容器初始化過程:這個過程包括了解析BeanDefinition中包含的Bean信息,完成Bean的實例化(Instantiation)和初始化(Initialization)過程。

而ApplicationContext將容器啟動和初始化過程細化成了一些模板函數,構成以下步驟。

        public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();

            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);

            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);

                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);

                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);

                // Initialize message source for this context.
                initMessageSource();

                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();

                // Initialize other special beans in specific context subclasses.
                onRefresh();

                // Check for listener beans and register them.
                registerListeners();

                // Instantiate all remaining (non-lazy-init) singletons.
                finishBeanFactoryInitialization(beanFactory);

                // Last step: publish corresponding event.
                finishRefresh();
            }

            catch (BeansException ex) {
                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();

                // Reset 'active' flag.
                cancelRefresh(ex);

                // Propagate exception to caller.
                throw ex;
            }
        }
    }

整個過程及其中的擴展點可以表示成下圖:


容器啟動及初始化過程.jpg

圖中表示出了Spring容器中設計到的很多擴展點,主要可以分為以下幾類:

  • BeanFactoryPostProcessor
  • 各種Aware
  • BeanPostProcessor
  • 隱藏的一些特殊功能
    下文將一項一項地進行梳理

BeanFactoryPostProcessor

簡介

BeanFactoryPostProcessor是一個很重要的接口,其中只有一個方法

void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

它起作用的時機發生在解析成BeanDefinition后,實例化之前。從名字可以看出來,BeanFactoryPostProcessor針對的應該是容器級別的擴展,名為“BeanFactory PostProcessor”即對容器中所有的BeanDefinition都起普遍作用。BeanFactoryPostProcessor有幾個我們比較常用的子類PropertyPlaceholderConfigurer、CustomEditorConfigurer,前者用于配置文件中的${var}變量替換,后者用于自定義編輯BeanDefinition中的屬性值,合理利用CustomEditorConfigurer會有一些意想不到的效果(例如可以通過修改某些屬性實現類似aop的功能)。

使用

如果是在BeanFactory中使用,我們需要

PropertyPlaceholderConfigurer  configurer = new PropertyPlaceholderConfigurer();
configurer.setLaction("xxx");
configurer.postProcessBeanFactory(beanFactory);

很繁瑣,很麻煩,而且通常我們并不喜歡硬編碼直接使用BeanFactory。在ApplicationContext中得到了改善,使用BeanFactoryPostProcessor只需要在xml文件中進行相應的配置就行,因為ApplicationContext在初始化過程中會調用invokeBeanFactoryPostProcessors(beanFactory),該函數會找出所有BeanFactoryPostProcessor類型的bean,調用postProcessBeanFactory方法。

BeanPostProcessor

簡介

BeanPostProcessor很容易和BeanFactoryPostProcessor混淆,但從名字上來說,BeanPostProcessor是“Bean PostProcessor”,主要針對于Bean這一級別,關注的主要是Bean實例化后,初始化前后的。為什么說主要呢?因為存在特例,有一個BeanPostProcessor的調用并不是發生在實例化后,初始化前后。BeanPostProcessor接口存在兩個方法,從名字可以看粗一個調用在初始化之前,一個調用在初始化之后。

  Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

BeanPostProcessor在BeanFactory的初始化bean的函數initializeBean中,主要代碼為,基本就是取出所有的BeanPostProcessor,然后遍歷調用其postProcessBeforeInitialization或者postProcessAfterInitialization方法。

if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }
     try {
            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()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }

  public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
            throws BeansException {
       Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            result = beanProcessor.postProcessBeforeInitialization(result, beanName);
            if (result == null) {
                return result;
            }
        }
        return result;
    }

使用

Spring利用BeanPostProcessor給了我們在bean初始化前后“doSomething”的機會。
在BeanFactory中使用需要編碼

 beanFactory.addBeanPostProcessor(new SomeBeanPostProcessor);

但是在ApplicationContext中,我們只需要將自定義的BeanPostProcessor配置到xml文件中即可。ApplicationContext在初始化過程中會識別所有的BeanPostProcessors并添加到BeanFactory中。

各種Aware

簡介

這種類型的擴展點是我們比較熟悉的了,例如ApplicationContextAware、BeanFactoryAware等,Aware的接口用于標識“我需要這個對象”,例如ApplicationContextAware通知容器我需要“當前的ApplicationContext對象”。這個Spring給我們提供的一種用于獲取有用對象的一種好的方式。

使用

需要注意的是,Aware起作用的時機是在Bean已經完成實例化之后,初始化Bean的同時。而且需要注意的是BeanFactory對于Aware的處理和ApplicationContext是不同的。

先看BeanFactory中的處理方式,各種Aware被調用的地方是在初始化bean的函數
initializeBean中

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
        if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                public Object run() {
                    invokeAwareMethods(beanName, bean);
                    return null;
                }
            }, getAccessControlContext());
        }
        else {
            invokeAwareMethods(beanName, bean);
        }

        Object wrappedBean = bean;
        if (mbd == null || !mbd.isSynthetic()) {
            wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        }

        try {
            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()) {
            wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        }
        return wrappedBean;
    }

    private void invokeAwareMethods(final String beanName, final Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof BeanNameAware) {
                ((BeanNameAware) bean).setBeanName(beanName);
            }
            if (bean instanceof BeanClassLoaderAware) {
                ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
            }
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
            }
        }
    }

可以看到其發生在invokeInitMethods之前。而ApplicationContext怎么處理呢?ApplicationContext本身是利用BeanFactory進行容器的初始化,而BeanFactory卻硬編碼了invokeAwareMethods中Aware的類型,那ApplicationContext究竟是怎么調用ApplicationContextAware的呢,答案就在函數prepareBeanFactory中,

beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));

ApplicationContext為beanFactory增加了ApplicationContextAwareProcessor,ApplicationContextAwareProcessor是一種BeanPostProcessor。前文提到BeanPostProcessor發生在bean初始化前后,在bean初始化之前將調用postProcessBeforeInitialization方法,ApplicationContextAwareProcessor#postProcessBeforeInitialization如下:

public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
        AccessControlContext acc = null;

        if (System.getSecurityManager() != null &&
                (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
                        bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
                        bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
            acc = this.applicationContext.getBeanFactory().getAccessControlContext();
        }

        if (acc != null) {
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
                public Object run() {
                    invokeAwareInterfaces(bean);
                    return null;
                }
            }, acc);
        }
        else {
            invokeAwareInterfaces(bean);
        }

        return bean;
    }

    private void invokeAwareInterfaces(Object bean) {
        if (bean instanceof Aware) {
            if (bean instanceof EnvironmentAware) {
                ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
            }
            if (bean instanceof EmbeddedValueResolverAware) {
                ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
                        new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
            }
            if (bean instanceof ResourceLoaderAware) {
                ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
            }
            if (bean instanceof ApplicationEventPublisherAware) {
                ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
            }
            if (bean instanceof MessageSourceAware) {
                ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
            }
            if (bean instanceof ApplicationContextAware) {
                ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
            }
        }
    }

可以看出ApplicationContext利用ApplicationContextAwareProcessor完成了ApplicationContext中特有的一些Aware的調用,發生的時機在初bean始化之前。

一種特殊的BeanPostProcessor-InstantiationAwareBeanPostProcessor

前文提到BeanPostProcessor主要作用于Bean實例化后,初始化前后,但是存在特例,InstantiationAwareBeanPostProcessor就是特例,其發生在Bean實例化前,
在真正調用doCreate()創建bean實例化之前,調用了resolveBeforeInstantiation進行了短路操作,如果此方法返回值不為空則直接返回bean。spring注釋“Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. ”給BeanPostProcessors一個機會返回代理proxy對象。

try {   
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.  
 Object bean = resolveBeforeInstantiation(beanName, mbd); 
  if (bean != null) {
      return bean;
   }
}



    protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
        Object bean = null;
        if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
            // Make sure bean class is actually resolved at this point.
            if (mbd.hasBeanClass() && !mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
                bean = applyBeanPostProcessorsBeforeInstantiation(mbd.getBeanClass(), beanName);
                if (bean != null) {
                    bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
                }
            }
            mbd.beforeInstantiationResolved = (bean != null);
        }
        return bean;
    }

我們熟知的AOP,對target對象進行代理就是實現了InstantiationAwareBeanPostProcessor接口,在Bean實例化之前短路操作返回代理Proxy對象。

ApplicationContext的事件發布

--待續

總結

本文總結了Spring容器中幾種使用較多的擴展機制,Spring作為一個設計良好的框架,遵循了“對修改封閉,對擴展開放”的原則,我們可以根據自己的實際需要來自定義BeanFactoryPostProcessor,BeanPostProcessor來在bean的生命周期里doSomething,實現各種Aware接口拿到容器中提供的一些有用對象,通過自定義監聽器監聽容器的事件等。

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

推薦閱讀更多精彩內容

  • 文章作者:Tyan博客:noahsnail.com | CSDN | 簡書 3.8 Container Exten...
    SnailTyan閱讀 1,240評論 0 6
  • Spring容器高層視圖 Spring 啟動時讀取應用程序提供的Bean配置信息,并在Spring容器中生成一份相...
    Theriseof閱讀 2,832評論 1 24
  • 注意LifecycleProcessor接口繼承了Lifcycle接口。同時,增加了2個方法,用于處理容器的ref...
    google666s閱讀 1,119評論 0 51
  • 馬云出現在王菲演唱會第一排,票價100萬 成功的人都是互相幫襯,馬云與趙薇是最好的朋友,而趙薇是王菲最好的閨蜜,所...
    李恩億的書屋閱讀 208評論 0 1
  • 我有兩個愿望,一個是成為中餐廚師,一個是律師。我很愛做飯,不愛做傳統的菜,某人說我做的是黑暗料理,我認了,因為我還...
    大牙妹的搖擺青春閱讀 301評論 1 1