<context:annotation-config/>自動(dòng)掃描標(biāo)簽詳解

當(dāng)我們需要使用BeanPostProcessor時(shí),直接在Spring配置文件中定義這些Bean顯得比較笨拙,例如:使用@Autowired注解酿炸,必須事先在Spring容器中聲明AutowiredAnnotationBeanPostProcessor的Bean:
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor "/>
  使用 @Required注解,就必須聲明RequiredAnnotationBeanPostProcessor的Bean:
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
  類似地宾添,使用@Resource、@PostConstruct、@PreDestroy等注解就必須聲明 CommonAnnotationBeanPostProcessor缕陕;使用@PersistenceContext注解粱锐,就必須聲明 PersistenceAnnotationBeanPostProcessor的Bean。
  這樣的聲明未免太麻煩了吧扛邑,所以Spring為我們提供了一種極為方便注冊(cè)這些BeanPostProcessor的方式怜浅,即使用<context:annotation- config/>隱式地向 Spring容器注冊(cè)AutowiredAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor蔬崩、CommonAnnotationBeanPostProcessor以及PersistenceAnnotationBeanPostProcessor這4個(gè)BeanPostProcessor恶座。如下:
<context:annotation-config/>

官方文檔的說明:

As always, you can register them as individual bean definitions, but they can also be implicitly registered by including the following tag in an XML-based Spring configuration (notice the inclusion of the context namespace):
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

</beans>

 那么<context:annotation-config/> 這個(gè)標(biāo)簽時(shí)何時(shí)注入這些BeanPostProcessor的呢?

下面進(jìn)行源碼分析

在Spring容器解析配置文件中時(shí)舱殿,會(huì)加載beans標(biāo)簽中配置的URL,如下圖

<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
                xsi:schemaLocation="http://www.springframework.org/schema/context
                            http://www.springframework.org/schema/context/spring-context-3.1.xsd
                            " >

(1)根據(jù)beans標(biāo)簽中配置的xsi:schemaLocation的URL到spring.handlers文件中奥裸,找到對(duì)應(yīng)的NamespaceHandler

Paste_Image.png

比如我上面的是http://www.springframework.org/schema/context
那么將對(duì)應(yīng)spring.handlers文件中的ContextNamespaceHandler

http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler

(2)由上面的可見,根據(jù)這個(gè)http://www.springframework.org/schema/context可以找到ContextNamespaceHandler類沪袭,這個(gè)類是干啥用的呢?

public class ContextNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser());
        registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser());
        registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser());
        registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser());
        registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser());
        registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser());
        registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser());
    }

}

就一個(gè)方法樟氢,在唯一一個(gè)方法中冈绊,可以看到一些很熟悉的單詞。沒錯(cuò)埠啃,它的作用就是創(chuàng)建這些標(biāo)簽所對(duì)應(yīng)的解析類死宣。
何為解析類?也就是所有的自定義命名空間(像mvc碴开,context等)下的標(biāo)簽解析都是由BeanDefinitionParser接口的子類來完成的毅该。
可以看到nnotation-config"的解析類是 AnnotationConfigBeanDefinitionParser
,那么進(jìn)入這個(gè)類中看它是如何解析這個(gè)標(biāo)簽的
(3)
解析的方法是在parse方法中

@Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        Object source = parserContext.extractSource(element);

        // Obtain bean definitions for all relevant BeanPostProcessors.
        Set<BeanDefinitionHolder> processorDefinitions =
//這里將注冊(cè)獲取到所有相關(guān)BeanPostProcessors的bean定義潦牛。    所以解析過程是在這個(gè)方法中
            AnnotationConfigUtils.registerAnnotationConfigProcessors(parserContext.getRegistry(), source);

        // Register component for the surrounding <context:annotation-config> element.
        CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
        parserContext.pushContainingComponent(compDefinition);

        // Nest the concrete beans in the surrounding component.
        for (BeanDefinitionHolder processorDefinition : processorDefinitions) {
            parserContext.registerComponent(new BeanComponentDefinition(processorDefinition));
        }

        // Finally register the composite component.
        parserContext.popAndRegisterContainingComponent();

        return null;
    }

根據(jù)上面代碼可以看出真正的解析是在AnnotationConfigUtils.registerAnnotationConfigProcessors()方法中進(jìn)行的
進(jìn)去這個(gè)方法看看

public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
            BeanDefinitionRegistry registry, Object source) {

        DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);
        if (beanFactory != null) {
            if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {
                beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);
            }
            if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {
                beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());
            }
        }

        Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<BeanDefinitionHolder>(4);

        if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
        }
//實(shí)例化AutowiredAnnotationBeanPostProcessor
        if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
        }
//實(shí)例化RequiredAnnotationBeanPostProcessor
        if (!registry.containsBeanDefinition(REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(RequiredAnnotationBeanPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, REQUIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
        }

        // 實(shí)例化CommonAnnotationBeanPostProcessor.
        if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
        }

        // 實(shí)例化PersistenceAnnotationBeanPostProcessor.
        if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition();
            try {
                def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
                        AnnotationConfigUtils.class.getClassLoader()));
            }
            catch (ClassNotFoundException ex) {
                throw new IllegalStateException(
                        "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
            }
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
        }

        if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
        }
        if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
            def.setSource(source);
            beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
        }

        return beanDefs;
    }

代碼可以看到眶掌,分別注冊(cè)了AutowiredAnnotationBeanPostProcessor、RequiredAnnotationBeanPostProcessor巴碗、CommonAnnotationBeanPostProcessor以及PersistenceAnnotationBeanPostProcessor這4個(gè)BeanPostProcessor朴爬。

后記

這是我自己看源碼總結(jié)的,錯(cuò)誤之處請(qǐng)幫忙指出橡淆,大家一起進(jìn)步召噩。

[朝陽區(qū)尼克楊]
轉(zhuǎn)載請(qǐng)注明原創(chuàng)出處,謝謝啦逸爵!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末具滴,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子师倔,更是在濱河造成了極大的恐慌构韵,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,080評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異贞绳,居然都是意外死亡谷醉,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,422評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門冈闭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俱尼,“玉大人,你說我怎么就攤上這事萎攒∮霭耍” “怎么了?”我有些...
    開封第一講書人閱讀 157,630評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵耍休,是天一觀的道長(zhǎng)刃永。 經(jīng)常有香客問我,道長(zhǎng)羊精,這世上最難降的妖魔是什么斯够? 我笑而不...
    開封第一講書人閱讀 56,554評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮喧锦,結(jié)果婚禮上读规,老公的妹妹穿的比我還像新娘。我一直安慰自己燃少,他們只是感情好束亏,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,662評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著阵具,像睡著了一般碍遍。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上阳液,一...
    開封第一講書人閱讀 49,856評(píng)論 1 290
  • 那天怕敬,我揣著相機(jī)與錄音,去河邊找鬼趁舀。 笑死赖捌,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的矮烹。 我是一名探鬼主播越庇,決...
    沈念sama閱讀 39,014評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼奉狈!你這毒婦竟也來了卤唉?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,752評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤仁期,失蹤者是張志新(化名)和其女友劉穎桑驱,沒想到半個(gè)月后竭恬,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,212評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡熬的,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,541評(píng)論 2 327
  • 正文 我和宋清朗相戀三年痊硕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片押框。...
    茶點(diǎn)故事閱讀 38,687評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡岔绸,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出橡伞,到底是詐尸還是另有隱情盒揉,我是刑警寧澤,帶...
    沈念sama閱讀 34,347評(píng)論 4 331
  • 正文 年R本政府宣布兑徘,位于F島的核電站刚盈,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏挂脑。R本人自食惡果不足惜藕漱,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,973評(píng)論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望崭闲。 院中可真熱鬧谴分,春花似錦、人聲如沸镀脂。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,777評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽薄翅。三九已至,卻和暖如春氓奈,著一層夾襖步出監(jiān)牢的瞬間翘魄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,006評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工舀奶, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留暑竟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,406評(píng)論 2 360
  • 正文 我出身青樓育勺,卻偏偏與公主長(zhǎng)得像但荤,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子涧至,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,576評(píng)論 2 349

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