從源碼的角度查找Spring @Autowired注解不能依賴注入靜態(tài)變量的原因

前言

  • 本文將總結(jié)下Spring依賴注入靜態(tài)屬性失敗以及添加set方法就能解決的原理

一川蒙、測(cè)試項(xiàng)目

  • AppConfig.java
    @Configuration
    @ComponentScan("com.eugene.sumarry.csdn.autowiredstatic")
    public class AppConfig {
    }
    
  • UserDao.java
    @Repository
    public class UserDao {
    }
    
  • UserService.java
    @Service
    public class UserService {
    
        @Autowired
        private UserDao userDao;
    
    
        @Override
        public String toString() {
            return "UserService{" +
                    "userDao=" + userDao +
                    '}';
        }
    }
    
  • Entry.java
    public class Entry {
    
        public static void main(String[] args) {
            AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    
            System.out.println(context.getBean(UserService.class));
        }
    }
    

二己单、問(wèn)題重現(xiàn)及解決方案

  • UserService.java中依賴注入U(xiǎn)serDao靜態(tài)變量及運(yùn)行結(jié)果(靜態(tài)變量注入失敗):
    在這里插入圖片描述
  • UserService.java中依賴注入U(xiǎn)serDao實(shí)例實(shí)例變量及運(yùn)行結(jié)果(實(shí)例變量注入成功):
    在這里插入圖片描述
  • 添加set方法完成靜態(tài)變量的依賴注入(靜態(tài)變量注入成功):
    在這里插入圖片描述

    注意: 使用構(gòu)造方法也可以完成注入纽绍,但是使用構(gòu)造方法來(lái)注入并不是@Autowired注解完成的功能撩荣,因?yàn)槟銜?huì)發(fā)現(xiàn),你加與不加@Autowired注解都會(huì)完成注入肉微。此時(shí)是通過(guò)構(gòu)造方法進(jìn)行依賴注入的(如果讀者不信遵馆,讀完下面的 【原理】章節(jié)后可以自己debug調(diào)試)。本文講解的是@Autowired注解的原理赦肃,所以不考慮構(gòu)造方法的依賴注入

三溅蛉、原理

  • 背景知識(shí): Spring的依賴注入方式有很多種,對(duì)Spring而言常見(jiàn)的有如下四種
    常見(jiàn)依賴注入類型 備注
    AbstractBeanDefinition.AUTOWIRE_NO 不開啟自動(dòng)裝配功能
    AbstractBeanDefinition.AUTOWIRE_BY_NAME 根據(jù)變量名來(lái)自動(dòng)裝配
    AbstractBeanDefinition.AUTOWIRE_BY_TYPE 根據(jù)類型自動(dòng)裝配
    AbstractBeanDefinition.AUTOWIRE_CONSTRUCTOR 根據(jù)構(gòu)造方法自動(dòng)裝配
    而針對(duì)于@Autowired注解的依賴注入他宛,最終都會(huì)通過(guò)AutowiredAnnotationBeanPostProcessor后置處理器來(lái)處理船侧,針對(duì)于此篇博客而言,是它的MergedBeanDefinitionPostProcessor身份起的作用關(guān)于此后置處理器的作用可以查看我之前發(fā)布的博客:spring 5.0.x源碼學(xué)習(xí)系列八: 實(shí)例化bean之使用構(gòu)造方法創(chuàng)建bean厅各、自動(dòng)裝配與循環(huán)依賴的第三章: spring bean實(shí)例化過(guò)程中涉及到的后置處理器和執(zhí)行順序镜撩。最終,經(jīng)過(guò)AutowiredAnnotationBeanPostProcessor后置處理器的處理队塘,會(huì)將當(dāng)前類的所有支持自動(dòng)裝配屬性以InjectionMetadata類型的對(duì)象保存袁梗,那到底是支持哪些屬性的自動(dòng)裝配的呢?繼續(xù)往下看憔古。遮怜。

3.1 spring如何選擇@Autowired注解標(biāo)識(shí)的變量進(jìn)行依賴注入

  • 請(qǐng)先看下圖中的注釋及源碼解釋
    在這里插入圖片描述
  • org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata源碼分析

    private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
        // 存放當(dāng)前類包括父類中帶@Autowired注解的字段和方法
        List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
        Class<?> targetClass = clazz;
    
        do {
            // 存放targetClass中所有帶了@Autowired注解的字段和方法
            final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
    
            ReflectionUtils.doWithLocalFields(targetClass, field -> {
                // JDK1.8 新特性,傳入一個(gè)方法進(jìn)去投放,在方法內(nèi)部就是獲取當(dāng)前類的所有字段(不包括父類奈泪,
                // 包括自己定義的私有變量)适贸,并循環(huán)調(diào)用傳入的方法灸芳,
                // 即當(dāng)前方法
    
                // 判斷當(dāng)前字段是否有@Autowired注解
                AnnotationAttributes ann = findAutowiredAnnotation(field);
                if (ann != null) {
                    // 判斷當(dāng)前字段是否為static修飾  ===>  靜態(tài)變量, 如果是涝桅,則只是返回了
                    if (Modifier.isStatic(field.getModifiers())) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("Autowired annotation is not supported on static fields: " + field);
                        }
                        return;
                    }
                    boolean required = determineRequiredStatus(ann);
                    // 將字段包裝成AutowiredFieldElement對(duì)象,并存入一開始創(chuàng)建的list中
                    currElements.add(new AutowiredFieldElement(field, required));
                }
            });
    
            ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                // 同上烙样,此時(shí)獲取的是當(dāng)前class內(nèi)部的method(不包括父類冯遂, 包括自己定義的私有方法
                // ),并挨個(gè)遍歷執(zhí)行當(dāng)前傳入的方法
    
                // 判斷遍歷的方法是否為橋接方法
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
                if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                    return;
                }
    
                // 拿到當(dāng)前方法的@Autowired注解谒获,并進(jìn)行校驗(yàn)?zāi)玫秸鎸?shí)方法(因?yàn)橛锌赡墚?dāng)前要處理的類是一個(gè)代理對(duì)象蛤肌,或者接口等等)
                AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
                if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                    // 判斷當(dāng)前方法是否為靜態(tài)方法
                    if (Modifier.isStatic(method.getModifiers())) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("Autowired annotation is not supported on static methods: " + method);
                        }
                        return;
                    }
                    if (method.getParameterCount() == 0) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("Autowired annotation should only be used on methods with parameters: " +
                                    method);
                        }
                    }
                    boolean required = determineRequiredStatus(ann);
    
                    // 找到當(dāng)前方法中的參數(shù)描述器
                    PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                    // 將set方法和要傳入的屬性的描述器包裝成AutowiredMethodElement類并添加至一開始創(chuàng)建的list集合中
                    currElements.add(new AutowiredMethodElement(method, required, pd));
                }
            });
    
            elements.addAll(0, currElements);
            // 獲取父類的class,針對(duì)父類的屬性和方法在做篩選
            targetClass = targetClass.getSuperclass();
        }
        while (targetClass != null && targetClass != Object.class);
    
        // 最終返回一個(gè)InjectionMetadata對(duì)象批狱,其中包含當(dāng)前類及其父類(不包含Object類)的所有帶
        // @Autowired注解的方法和子彈
        return new InjectionMetadata(clazz, elements);
    }   
    
  • 綜上裸准,AutowiredAnnotationBeanPostProcessor后置處理器的MergedBeanDefinitionPostProcessors后置處理器的作用就是將當(dāng)前類及其父類(不包含Object類)的所有包含@Autowired注解的非靜態(tài)字段和非靜態(tài)帶參方法以InjectionMetadata對(duì)象的方式保存在injectionMetadataCache屬性中

3.2 spring在進(jìn)行依賴注入時(shí)的邏輯

  • 在進(jìn)行依賴注入時(shí),肯定是執(zhí)行了populateBean方法赔硫,具體結(jié)果如下圖所示:
    在這里插入圖片描述
    在這里插入圖片描述

    至此炒俱,spring針對(duì)依賴注入的功能的準(zhǔn)備工作算是完成了。為什么說(shuō)是準(zhǔn)備工作呢爪膊?因?yàn)楹罄m(xù)還要執(zhí)行真正的inject注入屬性方法权悟,最后會(huì)通過(guò)Spring的beanFacotry或者直接從cache中拿依賴對(duì)象,最后進(jìn)行屬性賦值推盛。至此峦阁,Spring @Autowired注解的處理流程就結(jié)束了。

四耘成、總結(jié)

  • 綜上所述榔昔,針對(duì)@Autowired注解的處理流程主要核心為AutowiredAnnotationBeanPostProcessor后置處理器的MergedBeanDefinitionPostProcessor身份,它會(huì)去篩選出所有帶@Autowired注解的非靜態(tài)字段和非靜態(tài)方法作為候選者凿跳,最終再通過(guò)spring的bean工廠去獲取依賴的對(duì)象件豌,使用反射的技術(shù)完成注入
  • I am a slow walker, but I never walk backwards.
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市控嗜,隨后出現(xiàn)的幾起案子茧彤,更是在濱河造成了極大的恐慌,老刑警劉巖疆栏,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件曾掂,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡壁顶,警方通過(guò)查閱死者的電腦和手機(jī)珠洗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)若专,“玉大人许蓖,你說(shuō)我怎么就攤上這事。” “怎么了膊爪?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵自阱,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我米酬,道長(zhǎng)沛豌,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任赃额,我火速辦了婚禮加派,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘跳芳。我一直安慰自己芍锦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布飞盆。 她就那樣靜靜地躺著醉旦,像睡著了一般。 火紅的嫁衣襯著肌膚如雪桨啃。 梳的紋絲不亂的頭發(fā)上车胡,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音照瘾,去河邊找鬼匈棘。 笑死,一個(gè)胖子當(dāng)著我的面吹牛析命,可吹牛的內(nèi)容都是我干的主卫。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼鹃愤,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼簇搅!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起软吐,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤瘩将,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后凹耙,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體姿现,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年肖抱,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了备典。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡意述,死狀恐怖提佣,靈堂內(nèi)的尸體忽然破棺而出吮蛹,到底是詐尸還是另有隱情,我是刑警寧澤拌屏,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布匹涮,位于F島的核電站,受9級(jí)特大地震影響槐壳,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜喜每,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一务唐、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧带兜,春花似錦枫笛、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至无畔,卻和暖如春啊楚,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浑彰。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工恭理, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人郭变。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓颜价,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親诉濒。 傳聞我的和親對(duì)象是個(gè)殘疾皇子周伦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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