spring源碼日記16: @Autowired實現(xiàn)原理

所有文章已遷移至csdn栽燕,csdn個人主頁https://blog.csdn.net/chaitoudaren
在閱讀本篇之前客给,如果對后置處理器還不夠熟悉的先參考下spring BeanPostProcessor 生命周期

@Autowired使用

  1. 構(gòu)造函數(shù)注入
public Class Outer {
    private Inner inner;
    @Autowired
    public Outer(Inner inner) {
        this.inner = inner;
    }
}
  1. 屬性注入
public Class Outer {
    @Autowired
    private Inner inner;
}
  1. 方法注入
public Class Outer {
    private Inner inner;
    public Inner getInner() {
        return inner;
    }
    @Autowired
    public void setInner(Inner inner) {
        this.inner = inner;
    }
}

目前絕大部分的代碼都使用第2、第3種惰说。第1種在bean實例化時完成撕蔼,而第2窿吩、第3種的實現(xiàn)原理都是一樣的杨蛋,在屬性填充時完成兜材。本篇將介紹第二第三種的是實現(xiàn)原理

在開始之前,如果我們自己設(shè)計@Autowired逞力,我們應(yīng)該怎么實現(xiàn)曙寡?我想做法還是比較簡單的

  1. 通過反射查找bean的class下所有注解了@Autowired的字段和方法
  2. 獲取到字段,通過getBean(字段)獲取到對應(yīng)bean寇荧,然后再通過反射調(diào)用field的set將bean注入

@Autowired源碼分析

AutowiredAnnotationBeanPostProcessor類

該類是@Autowired的具體實現(xiàn)類举庶,先預(yù)覽一下類方法

AutowiredAnnotationBeanPostProcessor預(yù)覽.png

發(fā)現(xiàn)實際有機會介入bean的創(chuàng)建操作只有可能是后置處理器,用于后置處理的有3個方法揩抡,其中一個過時不用户侥,分別是postProcessMergedBeanDefinition镀琉、postProcessProperties后置處理,我們再看一下這2個方法的具體代碼

public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
        implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {

    ...

    @Override
    public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
        // 1. 尋找bean中所有被@Autowired注釋的屬性添祸,并將屬性封裝成InjectedElement類型
        InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
        metadata.checkConfigMembers(beanDefinition);
    }

    ...

    @Override
    public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
        // 1. 尋找通過@Autowired注解的屬性或者方法
        InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
        try {
            // 2. 注入
            metadata.inject(bean, beanName, pvs);
        }
        catch (BeanCreationException ex) {
            throw ex;
        }
        catch (Throwable ex) {
            throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
        }
        return pvs;
    }

    ...
}

跟我們的猜想是一樣的滚粟,首先先找出所有注解了@Autowired的屬性或者方法寻仗,然后進行注入刃泌,當然postProcessMergedBeanDefinition后置處理器的調(diào)用肯定是在postProcessProperties之前的,這里我們回顧一下spring bean的創(chuàng)建過程署尤。2個處理器我已用黃色標出

spring @Autowired注入時機.jpg

1. 查找所有@Autowired

// 尋找bean中所有被@Autowired注釋的屬性耙替,并將屬性封裝成InjectedElement類型
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
        // Fall back to class name as cache key, for backwards compatibility with custom callers.
        // 獲取緩存的key值,一般以beanName做key
        String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
        // Quick check on the concurrent map first, with minimal locking.
        // 從緩存中獲取metadata
        InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
        // 檢測metadata是否需要更新
        if (InjectionMetadata.needsRefresh(metadata, clazz)) {
            synchronized (this.injectionMetadataCache) {
                metadata = this.injectionMetadataCache.get(cacheKey);
                if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                    if (metadata != null) {
                        metadata.clear(pvs);
                    }
                    // 通過clazz類曹体,查找所有@Autowired的屬性或者方法俗扇,并封裝成InjectionMetadata類型
                    metadata = buildAutowiringMetadata(clazz);
                    // 將metadata加入緩存
                    this.injectionMetadataCache.put(cacheKey, metadata);
                }
            }
        }
        return metadata;
    }

可以看到spring依然在用緩存的方式提高性能,繼續(xù)跟蹤核心代碼buildAutowiringMetadata(clazz)

    private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
        // 查看clazz是否有Autowired注解
        if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
            return InjectionMetadata.EMPTY;
        }
        // 這里需要注意AutowiredFieldElement箕别,AutowiredMethodElement均繼承了InjectionMetadata.InjectedElement
        // 因此這個列表是可以保存注解的屬性和被注解的方法的
        List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
        Class<?> targetClass = clazz;

        // 1. 通過do while循環(huán)铜幽,遞歸的往直接繼承的父類尋找@Autowired
        do {
            final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
            
            // 2. 通過反射,獲取所有屬性串稀,doWithLocalFields則是循環(huán)的對每個屬性應(yīng)用以下匿名方法
            ReflectionUtils.doWithLocalFields(targetClass, field -> {
                // 判斷當前field屬性是否含有@Autowired的注解
                MergedAnnotation<?> ann = findAutowiredAnnotation(field);
                if (ann != null) {
                    // 返回該屬性在類中的修飾符除抛,如果等于static常量,則拋出異常母截,@Autowired不允許注解在靜態(tài)屬性上
                    if (Modifier.isStatic(field.getModifiers())) {
                        if (logger.isInfoEnabled()) {
                            logger.info("Autowired annotation is not supported on static fields: " + field);
                        }
                        return;
                    }
                    // @Autowired有required屬性到忽,獲取required的值,默認為true
                    boolean required = determineRequiredStatus(ann);
                    // 3. 將field封裝成InjectedElement清寇,并添加到集合中喘漏,這里用的是AutowiredFieldElement
                    currElements.add(new AutowiredFieldElement(field, required));
                }
            });

            // 4. @Autowired可以注解在方法上
            ReflectionUtils.doWithLocalMethods(targetClass, method -> {
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
                if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                    return;
                }
                MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
                if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                    if (Modifier.isStatic(method.getModifiers())) {
                        if (logger.isInfoEnabled()) {
                            logger.info("Autowired annotation is not supported on static methods: " + method);
                        }
                        return;
                    }
                    if (method.getParameterCount() == 0) {
                        if (logger.isInfoEnabled()) {
                            logger.info("Autowired annotation should only be used on methods with parameters: " +
                                    method);
                        }
                    }
                    boolean required = determineRequiredStatus(ann);
                    PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                    // 5. 將方法封裝成InjectedElement,并添加到集合中华烟,這里用的是AutowiredMethodElement
                    currElements.add(new AutowiredMethodElement(method, required, pd));
                }
            });

            elements.addAll(0, currElements);
            // 返回直接繼承的父類
            targetClass = targetClass.getSuperclass();
        }
        // 如果父類不為空則需要把父類的@Autowired屬性或方法也找出
        while (targetClass != null && targetClass != Object.class);
        // 6. new InjectionMetadata(clazz, elements)翩迈,將找到的所有的待注入屬性或方法生成metadata返回
        return InjectionMetadata.forElements(elements, clazz);
    }
  1. 外層 do ... while ... 的循環(huán)被用于遞歸的查找父類的@Autowired屬性或方法

  2. 通過反射的方式獲取到所有屬性并循環(huán)驗證每一個屬性是否被@Autowired注解

  3. 將查找到包含@Autowired注解的filed封裝成AutowiredFieldElement,加入到列表中

  4. 循環(huán)查找在方法上的注解

  5. 將找到的方法封裝成AutowiredMethodElement盔夜,并加入列表
    這里需要特別強調(diào)一點负饲,InjectedElement被AutowiredFieldElement、AutowiredMethodElement比吭,他們都有各自的inject函數(shù)绽族,實現(xiàn)各自的注入。因此改ArrayList elements是擁有2種類型的屬性


    InjectedElement.png
  6. 將找到的所有元素列表和clazz作為參數(shù)生成metadata數(shù)據(jù)返回

2. 注入

// 注入
metadata.inject(bean, beanName, pvs);
    public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
        // 獲取所有需要被注入的元素
        Collection<InjectedElement> checkedElements = this.checkedElements;
        Collection<InjectedElement> elementsToIterate =
                (checkedElements != null ? checkedElements : this.injectedElements);
        // 迭代的元素不為空
        if (!elementsToIterate.isEmpty()) {
            for (InjectedElement element : elementsToIterate) {
                if (logger.isTraceEnabled()) {
                    logger.trace("Processing injected element of bean '" + beanName + "': " + element);
                }
                // 循環(huán)注入衩藤,這里有可能是AutowiredFieldElement也可能AutowiredMethodElement吧慢,因此調(diào)用的inject是2個不同的方法
                element.inject(target, beanName, pvs);
            }
        }
    }

利用for循環(huán),遍歷剛剛我們查到到的elements列表赏表,進行注入检诗。在上面有特別提醒匈仗,這里的element有可能是AutowiredFieldElement類型、或AutowiredMethodElement類型逢慌。各自代表@Autowired注解在屬性上悠轩、以及注解在方法上的2種不同元素。因此他們調(diào)用的element.inject(target, beanName, pvs);也是不一樣的

字段注入(AutowiredFieldElement)

    private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
        @Override
        protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
            Field field = (Field) this.member;
            Object value;
            if (this.cached) {
                value = resolvedCachedArgument(beanName, this.cachedFieldValue);
            }
            else {
                // 專門用于注入的包裝類攻泼,包裝構(gòu)造函數(shù)參數(shù)火架,方法參數(shù)或字段
                DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
                // 設(shè)置class
                desc.setContainingClass(bean.getClass());
                // 需要被自動注入的beanNames,這里只有可能 = 1忙菠,方法注入時才有可能為多個
                Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
                Assert.state(beanFactory != null, "No BeanFactory available");
                TypeConverter typeConverter = beanFactory.getTypeConverter();// 獲取類型轉(zhuǎn)換器
                try {
                    // 通過beanFactory獲取屬性對應(yīng)的值何鸡,比如需要調(diào)用getBean("b")獲取依賴的屬性單例,并且通過自動轉(zhuǎn)型轉(zhuǎn)為需要的類型
                    value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
                }
                catch (BeansException ex) {
                    throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
                }
                synchronized (this) {
                    if (!this.cached) {
                        if (value != null || this.required) {
                            this.cachedFieldValue = desc;
                            // 注冊依賴牛欢,
                            registerDependentBeans(beanName, autowiredBeanNames);
                            // 因為是屬性注入骡男,因此這里只有可能等于1
                            if (autowiredBeanNames.size() == 1) {
                                String autowiredBeanName = autowiredBeanNames.iterator().next();
                                if (beanFactory.containsBean(autowiredBeanName) &&
                                        beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                                    // 緩存當前value
                                    this.cachedFieldValue = new ShortcutDependencyDescriptor(
                                            desc, autowiredBeanName, field.getType());
                                }
                            }
                        }
                        else {
                            this.cachedFieldValue = null;
                        }
                        this.cached = true;
                    }
                }
            }
            if (value != null) {
                // 通過反射,將value值設(shè)置到bean中
                ReflectionUtils.makeAccessible(field);
                field.set(bean, value);
            }
        }
    }

上方大部分的工作都在做待注入bean的獲取以及類型的轉(zhuǎn)換傍睹,如果深究下去可以再把spring Ioc講一遍隔盛,但是核心還是getBean(字段)獲取到對應(yīng)bean...我們這里就關(guān)心核心的語句,就是這2句

            if (value != null) {
                // 通過反射拾稳,將value值設(shè)置到bean中
                ReflectionUtils.makeAccessible(field);
                field.set(bean, value);
            }

spring通過反射的方式吮炕,調(diào)用field的set進行屬性的注入

方法注入(AutowiredMethodElement)

    private class AutowiredMethodElement extends InjectionMetadata.InjectedElement {

        
        @Override
        protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
            if (checkPropertySkipping(pvs)) {
                return;
            }
            // @Autowired標注在方法上
            Method method = (Method) this.member;
            Object[] arguments;
            if (this.cached) {
                // Shortcut for avoiding synchronization...
                // 有緩存
                arguments = resolveCachedArguments(beanName);
            }
            else {
                // 沒緩存,直接獲取方法上所有的參數(shù)
                int argumentCount = method.getParameterCount();
                arguments = new Object[argumentCount];
                DependencyDescriptor[] descriptors = new DependencyDescriptor[argumentCount];
                Set<String> autowiredBeans = new LinkedHashSet<>(argumentCount);
                Assert.state(beanFactory != null, "No BeanFactory available");
                TypeConverter typeConverter = beanFactory.getTypeConverter();
                // 循環(huán)所有參數(shù)
                for (int i = 0; i < arguments.length; i++) {
                    MethodParameter methodParam = new MethodParameter(method, i);
                    DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
                    currDesc.setContainingClass(bean.getClass());
                    descriptors[i] = currDesc;
                    try {
                        // 通過beanFactory熊赖,獲取代注入的bean来屠,并進行類型轉(zhuǎn)換
                        Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
                        if (arg == null && !this.required) {
                            arguments = null;
                            break;
                        }
                        arguments[i] = arg;
                    }
                    catch (BeansException ex) {
                        throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
                    }
                }
                synchronized (this) {
                    if (!this.cached) {
                        if (arguments != null) {
                            DependencyDescriptor[] cachedMethodArguments = Arrays.copyOf(descriptors, arguments.length);
                            // 注冊依賴
                            registerDependentBeans(beanName, autowiredBeans);
                            // 如果自動注入的個數(shù) = 參數(shù)個數(shù),則緩存
                            if (autowiredBeans.size() == argumentCount) {
                                Iterator<String> it = autowiredBeans.iterator();
                                Class<?>[] paramTypes = method.getParameterTypes();
                                for (int i = 0; i < paramTypes.length; i++) {
                                    String autowiredBeanName = it.next();
                                    if (beanFactory.containsBean(autowiredBeanName) &&
                                            beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
                                        // 緩存
                                        cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
                                                descriptors[i], autowiredBeanName, paramTypes[i]);
                                    }
                                }
                            }
                            // 緩存方法
                            this.cachedMethodArguments = cachedMethodArguments;
                        }
                        else {
                            this.cachedMethodArguments = null;
                        }
                        this.cached = true;
                    }
                }
            }
            if (arguments != null) {
                try {
                    // 反射調(diào)用注入方法震鹉,將獲取到的所有bean作為參數(shù)
                    ReflectionUtils.makeAccessible(method);
                    method.invoke(bean, arguments);
                }
                catch (InvocationTargetException ex) {
                    throw ex.getTargetException();
                }
            }
        }

    }

這里與屬性注入最大的區(qū)別在于俱笛,@Autowired注解在方法上,方法可以擁有多個參數(shù)传趾,因此這里需要通過循環(huán)將一個個獲取迎膜,而獲取bean的方式于上面一樣,本質(zhì)都是通過getBean獲取浆兰。而核心語句還是2句

// 反射調(diào)用注入方法磕仅,將獲取到的所有bean作為參數(shù)
ReflectionUtils.makeAccessible(method);
method.invoke(bean, arguments);

與屬性注入不同的是,當@Autowired注解在方法上簸呈,例如我們注解在setter方法上榕订,則只需要直接調(diào)用該setter方法將參數(shù)數(shù)組傳入即可以,即使用invoke觸發(fā)方法蜕便,具體屬性賦值的過程在setter方法中由用戶自行編寫

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末劫恒,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌两嘴,老刑警劉巖丛楚,帶你破解...
    沈念sama閱讀 222,104評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異憔辫,居然都是意外死亡趣些,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,816評論 3 399
  • 文/潘曉璐 我一進店門贰您,熙熙樓的掌柜王于貴愁眉苦臉地迎上來坏平,“玉大人,你說我怎么就攤上這事枉圃」睿” “怎么了庐冯?”我有些...
    開封第一講書人閱讀 168,697評論 0 360
  • 文/不壞的土叔 我叫張陵孽亲,是天一觀的道長。 經(jīng)常有香客問我展父,道長返劲,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,836評論 1 298
  • 正文 為了忘掉前任栖茉,我火速辦了婚禮篮绿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘吕漂。我一直安慰自己亲配,他們只是感情好,可當我...
    茶點故事閱讀 68,851評論 6 397
  • 文/花漫 我一把揭開白布惶凝。 她就那樣靜靜地躺著吼虎,像睡著了一般。 火紅的嫁衣襯著肌膚如雪苍鲜。 梳的紋絲不亂的頭發(fā)上思灰,一...
    開封第一講書人閱讀 52,441評論 1 310
  • 那天,我揣著相機與錄音混滔,去河邊找鬼洒疚。 笑死,一個胖子當著我的面吹牛坯屿,可吹牛的內(nèi)容都是我干的油湖。 我是一名探鬼主播,決...
    沈念sama閱讀 40,992評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼领跛,長吁一口氣:“原來是場噩夢啊……” “哼乏德!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起隔节,我...
    開封第一講書人閱讀 39,899評論 0 276
  • 序言:老撾萬榮一對情侶失蹤鹅经,失蹤者是張志新(化名)和其女友劉穎寂呛,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瘾晃,經(jīng)...
    沈念sama閱讀 46,457評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡贷痪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,529評論 3 341
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蹦误。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片劫拢。...
    茶點故事閱讀 40,664評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖强胰,靈堂內(nèi)的尸體忽然破棺而出舱沧,到底是詐尸還是另有隱情,我是刑警寧澤偶洋,帶...
    沈念sama閱讀 36,346評論 5 350
  • 正文 年R本政府宣布熟吏,位于F島的核電站,受9級特大地震影響玄窝,放射性物質(zhì)發(fā)生泄漏牵寺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,025評論 3 334
  • 文/蒙蒙 一恩脂、第九天 我趴在偏房一處隱蔽的房頂上張望帽氓。 院中可真熱鬧,春花似錦俩块、人聲如沸黎休。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,511評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽势腮。三九已至,卻和暖如春壮啊,著一層夾襖步出監(jiān)牢的瞬間嫉鲸,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,611評論 1 272
  • 我被黑心中介騙來泰國打工歹啼, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留玄渗,地道東北人。 一個月前我還...
    沈念sama閱讀 49,081評論 3 377
  • 正文 我出身青樓狸眼,卻偏偏與公主長得像藤树,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子拓萌,可洞房花燭夜當晚...
    茶點故事閱讀 45,675評論 2 359

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