Spring依賴注入查找過程

版本: Spring-beans-5.2.5.RELEASE

假定環(huán)境中存在兩個User對象描睦,分別是user眨业、superUser薪缆,其中superUser標(biāo)注了primary屬性烛缔,根據(jù)這個前提條件怒竿,來看看Spring如何查找要依賴注入的對象

@Configuration
public class AnnotationDependencyInjectionResolutionDemo {
    @Autowired          // 依賴查找(處理) + 延遲
    @Lazy
    private User lazyUser;

    @Autowired          // 依賴查找(處理)
    private User user;

    @Autowired          // 集合類型依賴注入
    private Map<String, User> users; // user superUser

     public static void main(String[] args) {
        // 創(chuàng)建 BeanFactory 容器
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
        // 注冊 Configuration Class(配置類) -> Spring Bean
        applicationContext.register(AnnotationDependencyInjectionResolutionDemo.class);
        XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(applicationContext);
        String xmlResourcePath = "classpath:/META-INF/dependency-lookup-context.xml";
        // 加載 XML 資源,解析并且生成 BeanDefinition
        beanDefinitionReader.loadBeanDefinitions(xmlResourcePath);
        // 啟動 Spring 應(yīng)用上下文
        applicationContext.refresh();
         // 顯示地關(guān)閉 Spring 應(yīng)用上下文
        applicationContext.close();
    }
}

Spring框架的依賴查找工作由DefaultListableBeanFactory類負(fù)責(zé)稳其,它實現(xiàn)了AutowireCapableBeanFactory接口驶赏,該接口中定義了如下解析依賴屬性的方法

public interface AutowireCapableBeanFactory extends BeanFactory {
    // ...

    /**
     * Resolve the specified dependency against the beans defined in this factory.
     * @param descriptor the descriptor for the dependency (field/method/constructor)
     * @param requestingBeanName the name of the bean which declares the given dependency
     */
    @Nullable
    Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName) 
            throws BeansException;

    /**
     * Resolve the specified dependency against the beans defined in this factory.
     * @param descriptor the descriptor for the dependency (field/method/constructor)
     * @param requestingBeanName the name of the bean which declares the given dependency
    */
    @Nullable
    Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
            @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) 
            throws BeansException;
}

從上面的resolveDependency`接口定義中可以了解都需要一個DependencyDescriptor``對象,這個類描述了一個依賴屬性的相關(guān)信息,如下:

public class DependencyDescriptor extends InjectionPoint implements Serializable {
    // 屬性定義在哪個類中,本例中值為AnnotationDependencyInjectionResolutionDemo 
    private final Class<?> declaringClass;

    @Nullable
    private String methodName;    // 通過方法注入可用的方法名稱

    @Nullable
    private Class<?>[] parameterTypes; 

    private int parameterIndex;

    @Nullable
    private String fieldName;  // 屬性名稱

    private final boolean required;  // 對應(yīng)@Autowired注解的required屬性

    private final boolean eager;    // 對應(yīng)@Lazy注解担映,是否懶加載,懶加載此項為false

    private int nestingLevel = 1;    // 嵌套層數(shù)

    @Nullable
    private Class<?> containingClass;    // 類似declaringClass屬性

    @Nullable
    private volatile ResolvableType resolvableType;    //  屬性的類型描述
    // ...
}

再來看看DefaultListableBeanFactory的實現(xiàn)蚯姆,針對普通類型的Bean,Spring會判斷當(dāng)前依賴屬性是單一類型還是多類型洒敏,針對單一類型龄恋,如果查找到多個候選bean,則判斷是否有首選Bean可以返回凶伙,否則拋出異常郭毕,而針對多類型的屬性依賴,直接遍歷所有符合的Bean返回

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {
    // ...

    @Override
    @Nullable
    public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
            @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) 
            throws BeansException {

        descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());

        // 首先函荣,可以看到Spring先判斷依賴注入的屬性是否為
        // Optional显押、ObjectFactory、javaxInjectProviderClass 類型之一
        if (Optional.class == descriptor.getDependencyType()) {
            return createOptionalDependency(descriptor, requestingBeanName);
        } else if (ObjectFactory.class == descriptor.getDependencyType() ||
            ObjectProvider.class == descriptor.getDependencyType()) {
            return new DependencyObjectProvider(descriptor, requestingBeanName);
        } else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
            return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
        } else {
            // 由于本例沒有上面的任何注入類型傻挂,會走這個分支煮落,
            // 同時,第一個LazyUser屬性由于標(biāo)注了@Lazy注解踊谋,那么會直接生成一個CGLIB代理對象返回
            Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
                    descriptor, requestingBeanName);
            if (result == null) {
                 // user和users屬性則進(jìn)入當(dāng)前方法
                result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
            }
            return result;
        }
    }

    @Nullable
    public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
        @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) 
        throws BeansException {

        InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
        try {
            // 省略和本例無關(guān)代碼...

            // users屬性將會通過這個方法直接返回查找到的對象
            // 顧名思義,這是一個解析多個bean集合屬性的方法
            Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
            if (multipleBeans != null) {
                return multipleBeans;
            }

            // user屬性將會通過這個方法獲得兩個候選bean信息旋讹,即user殖蚕、superUser
            Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
            if (matchingBeans.isEmpty()) {
                if (isRequired(descriptor)) {
                    raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
                }
                return null;
            }

            String autowiredBeanName;
            Object instanceCandidate;
    
            if (matchingBeans.size() > 1) {
                // 通過這個方法推斷如果有多個候選bean可以注入單一類型屬性,則嘗試推斷
                // 是否有哪個屬性標(biāo)注了@Primary之類的主要bean信息沉迹,通過這里得到superUser
                autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
                if (autowiredBeanName == null) {
                    if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
                        return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
                    } else {
                        // In case of an optional Collection/Map, silently ignore a non-unique case:
                        // possibly it was meant to be an empty collection of multiple regular beans
                        // (before 4.3 in particular when we didn't even look for collection beans).
                        return null;
                    }
                }
                instanceCandidate = matchingBeans.get(autowiredBeanName);
            } else {
                // We have exactly one match.
                Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
                autowiredBeanName = entry.getKey();
                instanceCandidate = entry.getValue();
            }

            if (autowiredBeanNames != null) {
                autowiredBeanNames.add(autowiredBeanName);
            }

            if (instanceCandidate instanceof Class) {
                // 最終通過這個方法從beanFactory中獲取目標(biāo)bean返回
                instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
            }

            Object result = instanceCandidate;

            // 省略...
            return result;
        } finally {
            ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
        }
    }

    // 處理多元素屬性的注入查找
    @Nullable
    private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
            @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {

        final Class<?> type = descriptor.getDependencyType();

        if (descriptor instanceof StreamDependencyDescriptor) {
            // ... 
        } else if (type.isArray()) {
            // ...
        } else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
            // ...
        } else if (Map.class == type) {
            ResolvableType mapType = descriptor.getResolvableType().asMap();
            Class<?> keyType = mapType.resolveGeneric(0);
            if (String.class != keyType) {
                return null;
            }
            Class<?> valueType = mapType.resolveGeneric(1);
            if (valueType == null) {
                return null;
            }
            Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType,
                    new MultiElementDescriptor(descriptor));
            if (matchingBeans.isEmpty()) {
                return null;
            }
            if (autowiredBeanNames != null) {
                autowiredBeanNames.addAll(matchingBeans.keySet());
            }
            return matchingBeans;
        } else {
            return null;
        }
    }
    
    protected Map<String, Object> findAutowireCandidates(
            @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

        String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this, requiredType, true, descriptor.isEager());   
        Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);

        // ... 省略

        // 注意本方法返回的是Map<String,Object>睦疫,
        // 當(dāng)解析多元素注入屬性的時候,value為具體的bean鞭呕,通過這個方法獲取具體的bean
        for (String candidate : candidateNames) {
            if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
                addCandidateEntry(result, candidate, descriptor, requiredType);
        }
        // ... 省略
    }
       
    private void addCandidateEntry(Map<String, Object> candidates, String candidateName,
            DependencyDescriptor descriptor, Class<?> requiredType) {

        if (descriptor instanceof MultiElementDescriptor) {
            // 通過這個方法向beanFactory獲取目標(biāo)bean
            Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
            if (!(beanInstance instanceof NullBean)) {
                candidates.put(candidateName, beanInstance);
            }
        }
        // ...
    }
}

參考圖

依賴注入處理過程(查找bean蛤育,無注入).png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子瓦糕,更是在濱河造成了極大的恐慌底洗,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咕娄,死亡現(xiàn)場離奇詭異亥揖,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)圣勒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門费变,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人圣贸,你說我怎么就攤上這事挚歧。” “怎么了吁峻?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵滑负,是天一觀的道長。 經(jīng)常有香客問我锡搜,道長橙困,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任耕餐,我火速辦了婚禮凡傅,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘肠缔。我一直安慰自己夏跷,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布明未。 她就那樣靜靜地躺著槽华,像睡著了一般。 火紅的嫁衣襯著肌膚如雪趟妥。 梳的紋絲不亂的頭發(fā)上猫态,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天,我揣著相機(jī)與錄音披摄,去河邊找鬼亲雪。 笑死,一個胖子當(dāng)著我的面吹牛疚膊,可吹牛的內(nèi)容都是我干的义辕。 我是一名探鬼主播,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼寓盗,長吁一口氣:“原來是場噩夢啊……” “哼灌砖!你這毒婦竟也來了璧函?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤基显,失蹤者是張志新(化名)和其女友劉穎蘸吓,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體续镇,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡美澳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了摸航。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片制跟。...
    茶點(diǎn)故事閱讀 40,488評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖酱虎,靈堂內(nèi)的尸體忽然破棺而出雨膨,到底是詐尸還是另有隱情,我是刑警寧澤读串,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布聊记,位于F島的核電站,受9級特大地震影響恢暖,放射性物質(zhì)發(fā)生泄漏排监。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一杰捂、第九天 我趴在偏房一處隱蔽的房頂上張望舆床。 院中可真熱鬧,春花似錦嫁佳、人聲如沸挨队。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽盛垦。三九已至,卻和暖如春瓤漏,著一層夾襖步出監(jiān)牢的瞬間腾夯,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工蔬充, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留俯在,地道東北人。 一個月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓娃惯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肥败。 傳聞我的和親對象是個殘疾皇子趾浅,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評論 2 359