Spring 標簽<context:annotation-config/>的探索

前言


在學習Spring的過程中,自己算是好好體會了一把Spring配置文件的繁瑣。

在學習Spring的時候,過多的標簽我們不容易記住。而且Spring官方也積極的給我們提供了示例項目供我們參考,可見官方也不推薦我們去記住那么眾多的配置文件,只是記住有這個東西存在就好,我們用到的時候只要去查閱引入配置標簽即可。

今天在搞Spring項目的時候,由于自己最近都在開發SpringBoot相關的內容。所以長期不接觸Spring最明顯的問題就是配置文件不會寫了。過去自己寫的時候也一直都是能自己寫出來的自己寫,寫不出來忘記了的的copy原來項目的配置文件。因為具體到業務代碼的編寫,很明顯業務代碼才是我們一個程序員應該關注的事情而不是去花大量的時間去搞配置文件,尤其是對于用過SpringBoot的我來說,真的再也受不了去死記硬背那些眾多的配置文件了。

問題


開啟Spring項目第一件事就是先配置不然你什么都做不了。首先我想要在項目中用我常用的注解@Autowired,我需要在配置文件中聲明AutowiredAnnotationBeanPostProcessor這個bean。我又想要在項目中用到@Required這個注解,那我還需要再配置RequiredAnnotationBeanPostProcesso這個bean。一個功能一個bean搞得自己有點兒頭大。后來查閱到要想支持這兩個注解只需要在配置文件中聲明<context:annotation-config/>該標簽即可。這個標簽自己以前常用,只知道它能為我提供一些注解的支持,但是具體到他能做多少我自己卻沒有深入研究過。今天就來研究一下,也算是為后續的的Spring標簽的探索先踩踩坑。

深入源碼


這里我只是簡單記錄一下我好奇的地方,并記錄自己解惑的過程。先上一段代碼:

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <context:annotation-config/>

</beans>

我們來看一下官方對該標簽的說明:

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)

翻譯過來:

與往常一樣,您可以將它們注冊為單獨的bean定義,但也可以通過在基于XML的Spring配置中包含以下標記來隱式注冊它們(注意包含上下文名稱空間)

img

既然官網這么說,可是那么<context:annotation-config/>這個標簽什么時候起作用的呢?哪兒體現了他實例化了這幾個bean了?

在Spring容器解析配置文件中時,會加載beans標簽中配置的URL,如圖:

<?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:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/context                            http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>

我們按住Ctrl點擊該標簽進入到該目錄下:

image

可以看到代碼提供的注釋這里寫的很清楚,該標簽提供了哪些注解的支持

image

然后我們再根據配置文件中beans標簽中配置的xsi:schemaLocation的URL到spring.handlers文件中,找到對應的NamespaceHandler

比如我上面的是<http://www.springframework.org/schema/context>那么將對應spring.handlers文件中的ContextNamespaceHandler。然后Ctrl B進入

image
image

那么這個Handler是做什么的呢?就一個方法,在該方法中,可以看到它的作用就是創建這些標簽所對應的解析類。什么是解析類呢?也就是所有的自定義命名空間(像mvccontext等)下的標簽解析都是由BeanDefinitionParser接口的子類來完成的。可以看到annotation-config的解析類是AnnotationConfigBeanDefinitionParser

我們繼續Ctrl B進入該類,看一下這個類如何解析這個標簽的。

image

根據上面的代碼可以看出真正的解析是在AnnotationConfigUtils.registerAnnotationConfigProcessors()方法中進行的
那就進去這個方法看看

這里代碼篇幅太長,我們貼了代碼:

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);
        // ConfigurationClassPostProcessor
        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));
        }
        // 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));
        }
        // 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));
        }
        // CommonAnnotationBeanPostProcessor
        // Check for JSR-250 support, and if present add the 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));
        }
        // PersistenceAnnotationBeanPostProcessor
        // Check for JPA support, and if present add the PersistenceAnnotationBeanPostProcessor.
        if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
            RootBeanDefinition def = new RootBeanDefinition();
            try {
                ClassLoader cl = AnnotationConfigUtils.class.getClassLoader();
                def.setBeanClass(cl.loadClass(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME));
            }
            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));
        }

        return beanDefs;
}

到這里我們的問題就完美解決了。由代碼可以看出,分別注冊了AutowiredAnnotationBeanPostProcessorRequiredAnnotationBeanPostProcessorCommonAnnotationBeanPostProcessor以及PersistenceAnnotationBeanPostProcessorConfigurationClassPostProcessor這5個BeanPostProcessor。

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

推薦閱讀更多精彩內容