1. 程式人生 > >Spring-Framework 原始碼閱讀之@Autowired和AutowiredAnnotationBeanPostProcessor

Spring-Framework 原始碼閱讀之@Autowired和AutowiredAnnotationBeanPostProcessor

  今天接下去講我們的內容,上次的解析了AnnotationBeanUtils這個類的運用和原始碼。今天主要關注的是Autowired和 AutowiredAnnotationBeanPostProcessor這2個類。首先我們來看一下Autowired標籤的定義。

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired { /** * * <p>Defaults to {@code true}. */ boolean required() default true; }

  從標籤的定義和上面的註釋可以知道,該標籤可以用於建構函式、方法、引數、標籤上。為了使這個標籤生效,我們需要一個解析這個標籤的類,Spring 為我們提供解析的類就是AutowiredAnnotationBeanPostProcessor,這是個BeanPostProcessor類。關於BeanPostProcessor大家可以查閱相關資料,之後等我讀到這個介面的時候,會具體的和大家一起探討。這裡我們以引數注入為例,來分析了一下,這個類到底做了哪些事情。這個引數注入主要是它的一個內部類AutowiredFieldElement來處理。

  而這個類的inject()方法被呼叫是在AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues方法上。

@Override
public PropertyValues postProcessPropertyValues(
        PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {

    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    
try { 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; }

  從這裡我們知道 findAutowiringMetadata()方法這裡獲取注入元資料資訊,然後呼叫InjectionMetadata.inject()的方法。在以引數注入就是呼叫AutowiredFieldElement.inject()方法。

這些型別的關係,將在之後解釋。讓我們具體來看一下 AutowiredFieldElement.inject()方法。

@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
    Field field = (Field) this.member;
    Object value;
    if (this.cached) {
        value = resolvedCachedArgument(beanName, this.cachedFieldValue);
    }
    else {
        DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
        desc.setContainingClass(bean.getClass());
        Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
        TypeConverter typeConverter = beanFactory.getTypeConverter();
        try {
            //通過BeanFactory的resolveDependency()方法解決依賴的值。也就是這個引數需要注入的值
            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);
                    if (autowiredBeanNames.size() == 1) {
                        String autowiredBeanName = autowiredBeanNames.iterator().next();
                        if (beanFactory.containsBean(autowiredBeanName)) {
                            if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
                                this.cachedFieldValue = new ShortcutDependencyDescriptor(
                                        desc, autowiredBeanName, field.getType());
                            }
                        }
                    }
                }
                else {
                    this.cachedFieldValue = null;
                }
                this.cached = true;
            }
        }
    }
    if (value != null) {//這裡就是通過反射設定引數可見性,然後把值設定到該引數上。
        ReflectionUtils.makeAccessible(field);
        field.set(bean, value);
    }
}

  接下來,我們需要知道,AutowiredAnnotationBeanPostProcessor的postProcessPropertyValues()方法什麼時候被呼叫。在這個函式上,我們關注2個引數,一個是PropertyDescriptor

型別的陣列,一個beanName。beanName就是我們例項化的物件,其中PropertyDescriptor就是描述這個名為beanName的Bean的引數內容。在AbstractAutowireCapableBeanFactory的

doCreateBean()方法裡,有一個populateBean()裡面呼叫了postProcessPropertyValues()。從方法名稱上,我們知道建立Bean(doCreateBean)>填充Bean(populateBean)。在這個populateBean()裡面有如一下這麼一段程式碼:

if (hasInstAwareBpps || needsDepCheck) {
    PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
    if (hasInstAwareBpps) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
                if (pvs == null) {
                    return;
                }
            }
        }
    }
    if (needsDepCheck) {
        checkDependencies(beanName, mbd, filteredPds, pvs);
    }
}

  在for迴圈裡面,判斷每一個BeanPostProcessor ,看這個BeanPostProcessor 是否實現InstantiationAwareBeanPostProcessor這個介面,剛好,我們知道AutowiredAnnotationBeanPostProcessors實現了這個介面,過載了InstantiationAwareBeanPostProcessor的postProcessPropertyValues()方法。到此為止,我們知道Bean的Autowired的注入實現。那麼我們在回到postProcessPropertyValues的findAutowiringMetadata(),從上面已經點出這個方法是找出注入元資料資訊。那麼它是如何查詢的,看如下程式碼:

private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, PropertyValues pvs) {
    // Fall back to class name as cache key, for backwards compatibility with custom callers.
    String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
    // Quick check on the concurrent map first, with minimal locking.
    InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
    if (InjectionMetadata.needsRefresh(metadata, clazz)) {
        synchronized (this.injectionMetadataCache) {
            metadata = this.injectionMetadataCache.get(cacheKey);
            if (InjectionMetadata.needsRefresh(metadata, clazz)) {
                if (metadata != null) {
                    metadata.clear(pvs);
                }
                try {
                    metadata = buildAutowiringMetadata(clazz);
                    this.injectionMetadataCache.put(cacheKey, metadata);
                }
                catch (NoClassDefFoundError err) {
                    throw new IllegalStateException("Failed to introspect bean class [" + clazz.getName() +
                            "] for autowiring metadata: could not find class that it depends on", err);
                }
            }
        }
    }
    return metadata;
}

  其中的關鍵程式碼就是InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);這句話。從這裡我們知道他是從this.injectionMetadataCache,這是一個Map,key為beanName,value為注入元資料InjectionMetadata。即直接從這個Map中獲取,那麼接下來我們就要知道這個注入資訊是什麼時候放入到這個快取Map上的。從上面程式碼上,我們看到

this.injectionMetadataCache.put(cacheKey, metadata);這段程式碼,這程式碼就是把注入資訊放到this.injectionMetadataCache上。那麼,從這裡我們可以猜測,findAutowiringMetadata()這個方法肯定被呼叫了多次,在Bean例項化過程中。從檢視程式碼,印證了我的想法。

  再回到AutowiredAnnotationBeanPostProcessor的BeanPostProcessor,之前我們知道他實現InstantiationAwareBeanPostProcessor這個介面。在這裡,我要說AutowiredAnnotationBeanPostProcessor還實現了MergedBeanDefinitionPostProcessor這個介面,這個MergedBeanDefinitionPostProcessor介面只有一個函式postProcessMergedBeanDefinition(),該方法就是用來整合BeanDefinition。讓我們自己毛估估也知道,這個對Bean內部的引數描述PropertyDescriptor也應該在專門用來整合Bean定義的這種BeanPostProcessors。從 AutowiredAnnotationBeanPostProcessor的如下程式碼,它果然實現postProcessMergedBeanDefinition函式。在其中呼叫了findAutowiringMetadata()。

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
    if (beanType != null) {
        InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
        metadata.checkConfigMembers(beanDefinition);
    }
}

  第一次呼叫findAutowiringMetadata()的時候,我們this.injectionMetadataCache.get()得到metadata為null,這樣就會進入if{}段程式碼,接著 呼叫 buildAutowiringMetadata()從該Bean的位元組碼中得到注入元資訊。接著我們把得到的注入源資訊InjectionMetadata放到this.injectionMetadataCache上。那麼我們看一下這個方法。

private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
    LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
    Class<?> targetClass = clazz;

    do {
        final LinkedList<InjectionMetadata.InjectedElement> currElements =
                new LinkedList<InjectionMetadata.InjectedElement>();

        ReflectionUtils.doWithLocalFields(targetClass, new ReflectionUtils.FieldCallback() {
            @Override
            public void doWith(Field field) throws IllegalArgumentException, IllegalAccessException {
                AnnotationAttributes ann = findAutowiredAnnotation(field);
                if (ann != null) {
                    if (Modifier.isStatic(field.getModifiers())) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("Autowired annotation is not supported on static fields: " + field);
                        }
                        return;
                    }
                    boolean required = determineRequiredStatus(ann);
                    currElements.add(new AutowiredFieldElement(field, required));
                }
            }
        });

        ReflectionUtils.doWithLocalMethods(targetClass, new ReflectionUtils.MethodCallback() {
            @Override
            public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
                Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
                if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
                    return;
                }
                AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
                if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
                    if (Modifier.isStatic(method.getModifiers())) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("Autowired annotation is not supported on static methods: " + method);
                        }
                        return;
                    }
                    if (method.getParameterTypes().length == 0) {
                        if (logger.isWarnEnabled()) {
                            logger.warn("Autowired annotation should only be used on methods with parameters: " +
                                    method);
                        }
                    }
                    boolean required = determineRequiredStatus(ann);
                    PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
                    currElements.add(new AutowiredMethodElement(method, required, pd));
                }
            }
        });

        elements.addAll(0, currElements);
        targetClass = targetClass.getSuperclass();
    }
    while (targetClass != null && targetClass != Object.class);

    return new InjectionMetadata(clazz, elements);
}

從上面,我們知道這個方法主要通過ReflectionUtils.doWithLocalFields()和ReflectionUtils.doWithLocalMethods()來得到源注入資訊。到這裡,我們可以大致知道依賴注入了。瞎 main提供一下test,大家可以通過debug知道Bean的建立流程。

@Test
public void testResourceInjection2() {
    DefaultListableBeanFactory bf = new DefaultListableBeanFactory();
    AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor();
    bpp.setBeanFactory(bf);
    bf.addBeanPostProcessor(bpp);
    RootBeanDefinition bd1 = new RootBeanDefinition(Uss.class);

    bd1.setScope(RootBeanDefinition.SCOPE_SINGLETON);
    bf.registerBeanDefinition("uss", bd1);

    RootBeanDefinition bd2 = new RootBeanDefinition(Tss.class);

    bd2.setScope(RootBeanDefinition.SCOPE_SINGLETON);
    bf.registerBeanDefinition("tss", bd2);


    Uss uss = (Uss) bf.getBean("uss");
    Tss tss = (Tss) bf.getBean("tss");

    System.out.println(uss.getTss() == tss);
    System.out.println(tss.getUss() == uss);
}
public class Uss {
    private String id;

    @Autowired
    private Tss tss;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Tss getTss() {
        return tss;
    }

    public void setTss(Tss tss) {
        this.tss = tss;
    }
}
public class Tss {
    private String id;

    @Autowired
    private Uss uss;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Uss getUss() {
        return uss;
    }

    public void setUss(Uss uss) {
        this.uss = uss;
    }
}

  在這裡,還有一些東西沒有講的非常的清楚,第一,是我自己有些程式碼還不太清楚,整體把握不住,還有些要等到我看到了其他內容在和大家一起分享。

相關推薦

Spring-Framework 原始碼閱讀@AutowiredAutowiredAnnotationBeanPostProcessor

  今天接下去講我們的內容,上次的解析了AnnotationBeanUtils這個類的運用和原始碼。今天主要關注的是Autowired和 AutowiredAnnotationBeanPostProcessor這2個類。首先我們來看一下Autowired標籤的定義。 @Target({ElementType

Spring-Framework 原始碼閱讀AnnotationBeanUtils

  Java程式設計師,就是要學會一個名字叫做“春”的東西,這玩意運用的非常的廣泛,現在如果你的業務系統或者軟體沒有在這個東西上開發,都不要意思拿出來。因為你更不上時代了。在平時的工作的中基本都是簡單的運用,沒有深入的瞭解內部的肌理。這次我一定可以滿滿的看完裡面的骨架。加油!加油!加油!   在之前我也看過一

Spring Ioc 原始碼分析Bean的載入構造

我們都知道,Spring Ioc和Aop是Spring的核心的功能,因此花一點時間去研究還是很有意義的,如果僅僅是知其所以然,也就體會不到大師設計Spring的精華,還記得那句話,Spring為JavaEE開發帶來了春天。IOC就是Inversion of control 也就是控制反轉的意思,另一種稱呼叫做

SpringSpring Boot2.0原始碼閱讀環境搭建結構

一:Spring 第一步:安裝git和gradle,以及Java8,並配置環境變數。   第二步:開啟gitbash,cd到你要放置spring原始碼工程的目錄,輸入:git clone https://github.com/spring-projects/sprin

Spring原始碼閱讀Bean載入(xml)1

先上兩張圖,簡單的畫了一下beanFactory各個類之間的關係,XmlBeanFactory是bean載入的入口和核心。Spring中大量使用了設計模式和UML中的設計原則,比如單一職責原則,從類圖可以看出,BeanFactory派生的各個介面,根據名字的不同,都增加了

Spring原始碼學習BeanFactoryFactoryBean

今天在學習Spring原始碼的時候,發現了spring中不僅僅有BeanFactory,還有FactoryBean,突然覺得分不清這兩者之間有什麼不同,難道僅僅是名字嗎?但是從名字上我們也能看出一些端

Robot Framework 原始碼閱讀筆記

上次走到了具體測試執行的地方,感覺缺乏一個全域性觀,有點走不下去。 還是再回頭看看整個設計思路,所有的模組文件都可以從這裡訪問到: 使用文件: http://robotframework.org/robotframework/ 介面文件: http://robot-fra

Spring 原始碼閱讀 深入理解 finishBeanFactoryInitialization

原始碼入口 上篇博文中我們看到了將Spring環境中的 BeanPostProcessor找出來,新增到BeanFactory中的beanPostProcessors中,統一維護,本片博文繼續往下拓展,看下Spring如何例項化bean,以及如何實現在bean的例項化通過各種各樣的後置處理器完成bean的增強

Netty原始碼閱讀如何將TCP的讀寫操作指定執行緒繫結

**原文連結**:[http://xueliang.org/article/detail/20200712234015993](http://xueliang.org/article/detail/20200712234015993) # 前言 在Netty的執行緒模型中,對於一個TCP連線的讀寫操作,都是

Spring源碼閱讀Springs-beans(一)容器的基本實現

beans 閱讀 gin com -i add wid ans lock 一、Spring-beans Spring源碼閱讀之Springs-beans(一)容器的基本實現

Promise原始碼閱讀建構函式+then過程

前言 Promise是非同步程式設計的一種方案,ES6規範中將其寫入規範標準中,統一了用法。 考慮到瀏覽器的相容性,Vue專案中使用promise,就具體閱讀promise原始碼,看看內部的具體實現。 具體分析 通過具體例項來閱讀promise原始碼的實現,例項如下: new

Spring-web原始碼解析Filter-OncePerRequestFilter

轉自:  http://blog.csdn.net/ktlifeng/article/details/50630934 基於4.1.7.RELEASE 我們先看一個filter-mapping的配置 

Netty 原始碼閱讀初始環境搭建

推薦 netty 系列原始碼解析合集 http://www.iocoder.cn/Netty/Netty-collection/?aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3R6c18xMDQxMjE4MTI5L2FydGljbGUvZGV0YWlscy83OD

jdk原始碼閱讀——arraylist

首先看一下他的建構函式: public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } 其實arraylist還有其他的建構函式,可以指定陣列的長度,這裡先從最基本的入

netty原始碼閱讀效能優化工具類Recycle異執行緒獲取物件

在這篇《netty原始碼閱讀之效能優化工具類之Recycler獲取物件》文章裡面,我們還有一個scavenge()方法沒有解析,也就是在別的執行緒裡面回收物件。下面我們開始介紹,從這個方法開始進入: boolean scavenge() { // con

STL原始碼剖析mapset

之前分析二叉搜尋樹和平衡二叉樹時,真心感覺樹的實現真是難,特別是平衡二叉樹,不平衡之後需要調整,還要考慮各種情況,累感不愛.今天看到這個紅黑樹,發現比平衡二叉樹還難,但是紅黑樹比平衡二叉樹使用的場景更多,所以平常使用時,我們需要了解紅黑樹的實現原理,如果有能力,可以自己實現,但是如果實在做不出來,也

Spring文件閱讀AOP

Aspect-oriented Programming (AOP) 補充了Object-oriented Programming (OOP)。OOP最重要的概念模組是類(class),而AOP中則是切面。AOP可以在多種型別和多個類間進行操作,可以認為AOP串起了這些資料。OOP使用封裝,繼承和多型來定義物件

我的原始碼閱讀路:redux原始碼剖析

前言 用過react的小夥伴對redux其實並不陌生,基本大多數的React應用用到它。一般大家用redux的時候基本都不會單獨去使用它,而是配合react-redux一起去使用。剛學習redux的時候很容易弄混淆redux和react-redux,以為他倆是同一個

netty原始碼閱讀解碼值基於固定長度解碼器分析

固定長度解碼器FixedLengthFrameDecoder比較簡單,我們看下它類的註釋: /** * A decoder that splits the received {@link ByteBuf}s by the fixed number * of bytes.

netty原始碼閱讀解碼基於長度域解碼器引數分析

這篇文章我們放鬆一點,只分析基於長度域解碼器的幾個引數, lengthFieldOffset :長度域的偏移量,也就是長度域要從什麼地方開始 lengthFieldLength:長度域的長度,也就是長度域佔多少個位元組 lengthAdjustment:長度域的值的調整