Spring之@Autowired依賴注入探究

前言

使用Spring,@Autowired注解肯定再熟悉不過了,今天徹底探究一下@Autowired實現的源碼細節

實現

其實這個實現方式其實思路很簡單:

就是在bean容器中找到type==@Autowired修飾的類型的bean,然后通過反射給屬性賦值即可

道理很簡單,但還是看代碼證實一下,并關注一些實現細節

例子

寫一個簡單的例子,為方便后續說明

// B Service
@Service
public class BService {
}
// A Service 通過@Autowired依賴注入 B Service
@Service
public class AService {
    @Autowired
    private BService bService;
}

@Autowired實現步驟

spring內部的默認bean工廠實現為DefaultListableBeanFactory,然而該BeanFactory并不支持@Autowired注解

實際上@Autowired注解的支持是依靠于ApplicationContext向DefaultListableBeanFactory注冊了Autowired后置處理器:AutowiredAnnotationBeanPostProcessor

在bean創建周期的populateBean(填充屬性)中會執行該后置處理器的postProcessProperties方法來完成依賴注入

postProcessProperties

步驟如下

1.查找帶有@Autowired的屬性

findAutowiringMetadata方法查找了當前類屬性中帶有@Autowired,@Value的屬性,包裝成InjectionMetadata

findAutowiringMetadata

buildAutowiringMetadata

其中autowiredAnnotationTypes主要包含兩個注解
autowiredAnnotationTypes

二.通過beanFactory的resolveDependency方法獲取需要依賴Bean

InjectionMetadata.inject方法中,調用beanFactory的resolveDependency方法

InjectionMetadata.inject1

其中desc即查找的目標,包含了目標的類型

三.給屬性賦值

inject方法最終獲取到依賴的bean后,反射完成屬性賦值

InjectionMetadata.inject2

到此,依賴注入的功能就實現了

深入resolveDependency

上面的步驟都很好理解,重點是第二步通過beanFactory的resolveDependency查找依賴的bean,所以再次深入探究beanFactory是如何獲取到依賴的bean的

以上例debugger一下調用resolveDependency時的參數

debugger

beanName為“AService”即被注入的bean, desc中存放BService的一些信息: BService的類型

所以resolveDependency實際上是根據type獲取bean,即getBeanByType,再深入看一下(以下代碼都是省略了分支邏輯和緩存邏輯,只保留重點邏輯)

resolveDependency:

public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException {
    // spring一般doXXX就是實際的主干代碼,如createBean和doCreateBean
    Object result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
    return result;
}

再來doResolveDependency:

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
                                  @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    // 獲取依賴的type,使用type去bean容器查找
    Class<?> type = descriptor.getDependencyType();
    // 查找所有符合的bean
    Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
    // 依賴的beanName
    String autowiredBeanName;
    // 依賴的bean實體
    Object instanceCandidate;
    // 一般情況只有一個,但多個符合的也要處理
    if (matchingBeans.size() > 1) {
        // 從多個里按優先級選擇一個,determine即下決定
        autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
        instanceCandidate = matchingBeans.get(autowiredBeanName);
    }
    else {
        // 只有一個的情況(大部分情況),直接取第一個
        Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
        autowiredBeanName = entry.getKey();
        instanceCandidate = entry.getValue();
    }
    // 如果返回的是class, 轉換為實體
    if (instanceCandidate instanceof Class) {
        instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
    }
    // 返回以來的bean實體
    Object result = instanceCandidate;
    return result;
}

其中重點是:

  • findAutowireCandidates:根據type查找依賴注入候選者
  • determineAutowireCandidate: 當出現多候選者,選擇一個(畢竟依賴注入只能注入一個對象)

findAutowireCandidates

根據type查找依賴注入候選者,先看一下它的定義

protected Map<String, Object> findAutowireCandidates(
            @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {            

其中:

  • beanName:將被填充屬性的bean的name
  • requiredType:填充的類型,即要查找的依賴
  • DependencyDescriptor:依賴的一些描述
  • 返回一個Map<String, Object>,即beanName和bean實體的映射map(value不一定都是bean實體,有可能是實體的類),一般情況下返回的map.size==1,但也會有多個的情況

再來看下具體代碼

protected Map<String, Object> findAutowireCandidates(
        @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
    // 使用BeanFactoryUtils篩選Bean容器中的候選者
    String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
            this, requiredType, true, descriptor.isEager());
    // 初始化返回結構
    Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
    // 1.從resolvableDependencies中篩選候選者
    for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {
        Class<?> autowiringType = classObjectEntry.getKey();
        if (autowiringType.isAssignableFrom(requiredType)) {
            Object autowiringValue = classObjectEntry.getValue();
            autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
            if (requiredType.isInstance(autowiringValue)) {
                result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                break;
            }
        }
    }
    // 2.從bean容器中進一步篩選候選者
    for (String candidate : candidateNames) {
        if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
            addCandidateEntry(result, candidate, descriptor, requiredType);
        }
    }
    // 返回結果
    return result;
}

通過代碼可以看出,依賴的候選者來源有兩個地方:

  • bean容器(使用addCandidateEntry方法加入返回map)
  • resolvableDependencies(直接加入返回map)

其中bean容器的查找,交給方法BeanFactoryUtils.beanNamesForTypeIncludingAncestors,那么resolvableDependencies是一個什么東西?

通過查看源碼,發現resolvableDependencies也是一個容器,存儲的是type->實體的map

resolvableDependencies

再次查找發現容器內容添加基本只有一處,即registerResolvableDependency

registerResolvableDependency

registerResolvableDependency是一個public接口,實際上他的存在是為了實現依賴注入非Bean容器中的Bean,可以叫spring托管的外部bean

比如現在又個單例工具對象,你想讓其他bean可以依賴注入這個對象,同時又不想把這個對象加入bean容器,則可以使用registerResolvableDependency加入到resolvableDependencies(相當于給spring中加入一個創建好的對象,供其他bean依賴注入,但不需要spring去創建管理它)

再ApplicationContext初始化階段,會把ApplicationContext對象,BeanFactory對象通過registerResolvableDependency加入到spring中托管

ApplicationContext.refresh

這就是為什么我們的bean能依賴注入上下文對象和bean工廠本身,如下

注入

當然resolvableDependencies依然只是支線邏輯,重點和大部分情況還是從bean容器中查找依賴,即BeanFactoryUtils.beanNamesForTypeIncludingAncestors方法,這個方法返回的是一個字符串數組,也就是beanName的數組,那怎么返回候選項Object的吶?

答案在addCandidateEntry方法里,看一下它的內部

addCandidateEntry

可以看見它往findAutowireCandidates的result里面的value塞了一個根據字符串getType獲取的type(Class)而不是bean的實體,這也是為什么上一步doResolveDependency會有這么一步代碼

// 如果返回的是class, 轉換為實體
if (instanceCandidate instanceof Class) {
    instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
}

所以findAutowireCandidates返回的map可能是beanName->bean實體,但大部分情況下是beanName->beanClass

所以重點又回到了descriptor.resolveCandidate方法,當通過BeanFactoryUtils獲取候選注入bean時,返回的是一個beanName->beanClass時,spring調用descriptor.resolveCandidate方法通過beanName獲取實際注入的實體(此時beanClass基本就沒啥用了),而resolveCandidate方法內部也很簡單,既然有了beanName,直接根據beanName獲取bean即可:

public Object resolveCandidate(String beanName, Class<?> requiredType, BeanFactory beanFactory) throws BeansException {
    return beanFactory.getBean(beanName);
}

getBeanByName就不展開了,這個是最基礎的,即通過beanName獲取bean定義,初始化創建bean

beanNamesForTypeIncludingAncestors

先看下定義

public static String[] beanNamesForTypeIncludingAncestors(
            ListableBeanFactory lbf, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {

根據命名:通過type查詢beanName數組,參數type即查找的類型,IncludingAncestors代表如果lbf有父級,要遞歸去祖先bean工廠中查找,看下代碼

public static String[] beanNamesForTypeIncludingAncestors(
        ListableBeanFactory lbf, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {

    Assert.notNull(lbf, "ListableBeanFactory must not be null");
    // 使用beanFactory的getBeanNamesForType方法查找
    String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
    // 如果有父級,遞歸查找
    if (lbf instanceof HierarchicalBeanFactory) {
        HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
        if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
            String[] parentResult = beanNamesForTypeIncludingAncestors(
                    (ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
            result = mergeNamesWithParent(result, parentResult, hbf);
        }
    }
    return result;
}

所以其實最核心的方法就是BeanFactory.getBeanNamesForType,也就是常用的方法getBean(Class<T> requiredType)內部調用的方法,即根據type在bean容器中獲取bean,很好理解,本文就不展開了~

總結

總結一下@Autowired的整個過程,畫個示意圖如下


@Autowired過程
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容