AnnotationConfigApplicationContext 源碼分析(二):注冊配置類

AnnotationConfigApplicationContext 源碼分析(二):注冊配置類

本文是作者的個人學(xué)習(xí)筆記缅叠,僅做參考肤粱,Spring代碼版本5.2.2

AnnotationConfigApplicationContext 源碼分析(一)

注冊配置類

注冊配置類將要解析由AnnotationConfigRegistry接口所提供的#register(Class<?>... componentClasses)方法领曼,該方法用于注冊一個或多個組件類(標注了@component的類)悯森,注意的是注冊完了之后容器必須調(diào)用#refresh()方法瓢姻,不然將會導(dǎo)致未知的問題幻碱。

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
  /**
     * Register one or more component classes to be processed.
     * <p>Note that {@link #refresh()} must be called in order for the context
     * to fully process the new classes.
     * @param componentClasses one or more component classes &mdash; for example,
     * {@link Configuration @Configuration} classes
     *
     * 注冊一個或多個要處理的組件類褥傍。
     * <p>請注意恍风,必須調(diào)用{@link #refresh()}朋贬,以便容器完全處理新類锦募。
     * @param componentClasses 一個或多個組件類糠亩,例如赎线,{@link Configuration@Configuration}類
     */
    @Override
    public void register(Class<?>... componentClasses) {
        Assert.notEmpty(componentClasses, "At least one component class must be specified");
        this.reader.register(componentClasses);
    }
}

根據(jù)代碼可以知道垂寥,實際上調(diào)用的是AnnotatedBeanDefinitionReader#register(Class<?>... componentClasses)方法

public class AnnotatedBeanDefinitionReader {
  /**
     * Register one or more component classes to be processed.
     * <p>Calls to {@code register} are idempotent; adding the same
     * component class more than once has no additional effect.
     * @param componentClasses one or more component classes,
     * e.g. {@link Configuration @Configuration} classes
     *
     * 注冊一個或多個要處理的組件類盏缤。
     * <p>對{@code register}的調(diào)用是等冪的蓖扑;多次添加同一個組件類沒有額外的效果律杠。
     * @param componentClasses 一個或多個組件類,例如{@link Configuration @Configuration}類
     */
    public void register(Class<?>... componentClasses) {
        for (Class<?> componentClass : componentClasses) {
            registerBean(componentClass);
        }
    }
    public void registerBean(Class<?> beanClass) {
        doRegisterBean(beanClass, null, null, null, null);
    }
}

for循環(huán)依次調(diào)用注冊組件類灰嫉,這里就不過多解析

public class AnnotatedBeanDefinitionReader {
  /**  **/
  private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
  
  /**
     * Register a bean from the given bean class, deriving its metadata from
     * class-declared annotations.
     * @param beanClass the class of the bean
     * @param name an explicit name for the bean
     * @param qualifiers specific qualifier annotations to consider, if any,
     * in addition to qualifiers at the bean class level
     * @param supplier a callback for creating an instance of the bean
     * (may be {@code null})
     * @param customizers one or more callbacks for customizing the factory's
     * {@link BeanDefinition}, e.g. setting a lazy-init or primary flag
     *
     * 從給定的bean類中注冊一個bean,從類聲明的注釋中派生其元數(shù)據(jù)股耽。
     * @param beanClass 注冊的Bean的Class對象
     * @param name  bean的顯式名稱
     * @param qualifiers 除了bean類級別的限定符之外物蝙,要考慮的特定限定符注釋(如果有的話)
     * @param supplier 用于創(chuàng)建bean實例的回調(diào)(可以是{@code null})
     * @param customizers 一個或多個回調(diào)册赛,用于自定義工廠的{@link BeanDefinition}震嫉,例如設(shè)置lazy init或primary標志
     * @since 5.0
     */
    private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
            @Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
            @Nullable BeanDefinitionCustomizer[] customizers) {

    //1柜砾、根據(jù)beanClass生成AnnotatedGenericBeanDefinition
        AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
        //2换衬、
        if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
            return;
        }
    //3、supplier==null叫潦,略
        abd.setInstanceSupplier(supplier);
    //4官硝、解析BeanDefinition上的@Scope標簽,設(shè)置BeanDefinition的scope
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
        abd.setScope(scopeMetadata.getScopeName());
    //5朋魔、解析BeanDefinition獲得beanName(Bean的名稱)
        String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
        //6、分析類上的多個注解害淤,在BeanDefinition上設(shè)置注解上設(shè)置的配置元數(shù)據(jù)
        AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    //7窥摄、qualifiers==null崭放,略
        if (qualifiers != null) {
            for (Class<? extends Annotation> qualifier : qualifiers) {
                if (Primary.class == qualifier) {
                    abd.setPrimary(true);
                }
                else if (Lazy.class == qualifier) {
                    abd.setLazyInit(true);
                }
                else {
                    abd.addQualifier(new AutowireCandidateQualifier(qualifier));
                }
            }
        }
    //8移国、customizers==null道伟,略
        if (customizers != null) {
            for (BeanDefinitionCustomizer customizer : customizers) {
                customizer.customize(abd);
            }
        }
    
        //9蜜徽、生成BeanDefinitionHolder對象
        BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    //10砚蓬、作用域代理策略灰蛙,針對@Scope標簽的proxyMode屬性
        definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    //11摩梧、注冊definitionHolder到容器中
        BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
    }
}

往容器中注冊一個bean

參數(shù)解析:

  1. beanClass:解析的目標類
  2. name:這里為空,注冊的名字
  3. qualifiers:這里為空宣旱,暫不分析
  4. supplier:這里為空仅父,暫不分析
  5. customizers:這里為空,暫不分析

步驟:

  1. 生成AnnotatedGenericBeanDefinition對象(存儲Bean的描述信息的對象),持有beanClass笙纤,同時解析beanClass獲取部分配置元數(shù)據(jù)(配置元數(shù)據(jù)即描述這個Bean的配置信息)耗溜,存儲到該BeanDefinition對象中;
  2. 暫時跳過省容;
  3. 這里無作用抖拴,略;
  4. 使用scopeMetadataResolver解析BeanDefinition上的@Scope標簽蓉冈,確定作用域元數(shù)據(jù)城舞。然后設(shè)置到BeanDefinition上
  5. 解析BeanDefinition獲得beanName(Bean的名稱)
  6. 通過AnnotatedBeanDefinition上的配置元數(shù)據(jù)家夺,解析存儲在該配置元數(shù)據(jù)上的以下的注解信息煌茴,獲取注解上的值,存儲到該AnnotatedBeanDefinition對象上
    1. @Lazy
    2. @Primary
    3. @DependsOn
    4. @Role
    5. @Description
  7. 這里無作用,略;
  8. 這里無作用,略;
  9. 生成BeanDefinitionHolder對象,持有BeanDefinition和Bean的名稱
  10. 作用域代理策略,將根據(jù)參數(shù)判斷是否需要代理,使用jdk代理還是CGLIB代理
  11. 往容器中注冊BeanDefinitionHolder

生成AnnotatedGenericBeanDefinition對象

public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {
  /**
     * Create a new AnnotatedGenericBeanDefinition for the given bean class.
     * @param beanClass the loaded bean class
     * 為給定的bean類創(chuàng)建一個新的AnnotatedGenericBeanDefinition。
     * @param beanClass 加載的bean類
     */
    public AnnotatedGenericBeanDefinition(Class<?> beanClass) {
        setBeanClass(beanClass);
        this.metadata = AnnotationMetadata.introspect(beanClass);
    }
}

這個類是GenericBeanDefinition(GenericBeanDefinition是通用的BeanDefinition)的基礎(chǔ)上實現(xiàn)AnnotatedBeanDefinition接口杉畜,該接口提供了用于暴露注解相關(guān)的配置元數(shù)據(jù),即AnnotationMetadata的接口。

AnnotatedGenericBeanDefinition類型的BeanDefinition通常只在注冊組件類的時候被定義软瞎。

步驟:

1. BeanDefinition持有類對象
2. 通過內(nèi)省(java機制)類對象生成StandardAnnotationMetadata對象(配置元數(shù)據(jù)對象)并被當前BeanDefinition持有。相關(guān)類將另起文章分析

解析BeanDefinition獲取ScopeMetadata

/**
 * A {@link ScopeMetadataResolver} implementation that by default checks for
 * the presence of Spring's {@link Scope @Scope} annotation on the bean class.
 *
 * <p>The exact type of annotation that is checked for is configurable via
 * {@link #setScopeAnnotationType(Class)}.
 * 默認情況下檢查bean類上是否存在Spring的{@link Scope@Scope}注釋的{@link ScopeMetadataResolver}實現(xiàn)。
 * <p>檢查的注釋的確切類型可以通過{@link #setScopeAnnotationType(Class)}配置
 */
public class AnnotationScopeMetadataResolver implements ScopeMetadataResolver {

  
  @Override
    public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
        ScopeMetadata metadata = new ScopeMetadata();
        if (definition instanceof AnnotatedBeanDefinition) {
            AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
            AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
                    annDef.getMetadata(), this.scopeAnnotationType);
            if (attributes != null) {
                metadata.setScopeName(attributes.getString("value"));
                ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
                if (proxyMode == ScopedProxyMode.DEFAULT) {
                    proxyMode = this.defaultProxyMode;
                }
                metadata.setScopedProxyMode(proxyMode);
            }
        }
        return metadata;
    }
}

步驟:

  1. 新建ScopeMetadata對象
  2. 使用AnnotationConfigUtils工具類獲取標記了@Scope的注解對象(AnnotationAttributes)
  3. 如果注解對象不為空幻林,則解析注解對象的屬性整葡,并設(shè)置給配置ScopeMetadata(保存Scope注解信息的配置元數(shù)據(jù))
    1. value—>setScopeName
    2. proxyMode—>設(shè)置setScopedProxyMode:作用域代理模式俱萍,用于決定是否要代理這個類腥寇,代理的模式是jdk代理還是CGLIB代理
  4. 返回ScopeMetadata對象后,通過BeanDefinition#setScope(String scopeName)設(shè)置到BeanDefinition中持有

生成BeanName策略

public class AnnotationBeanNameGenerator implements BeanNameGenerator {
    public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
        if (definition instanceof AnnotatedBeanDefinition) {
            String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
            if (StringUtils.hasText(beanName)) {
                // Explicit bean name found.
                return beanName;
            }
        }
        // Fallback: generate a unique default bean name.
        return buildDefaultBeanName(definition, registry);
    }
}

BeanNameGenerator是Spring中為bean definitions生成名字策略接口。

AnnotationBeanNameGenerator生成BeanName的策略:

  1. 如果definition是AnnotatedBeanDefinition寥殖,則會通過解析類上的注解粤策,獲取以下注解(包括繼承的注解類)上value的值作為BeanName
    1. @Component
    2. @ManagedBean
    3. @Named
  2. 如果以上注解中value的值為null,將會獲取類名萍膛,轉(zhuǎn)化為beanName,轉(zhuǎn)換方式如下
    1. 常規(guī)類:package.MyConfiguration—>myConfiguration
    2. 靜態(tài)內(nèi)部類:package.MyConfiguration$Config—>myConfiguration.Config

作用域代理策略

public abstract class AnnotationConfigUtils {
    static BeanDefinitionHolder applyScopedProxyMode(
            ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {

        ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
        if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
            return definition;
        }
        boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
        return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
    }
}

如果代理模式為ScopedProxyMode.NO,則不做任何處理僻爽,否則進行ScopedProxyCreator#createScopedProxy方法返回一個新的BeanDefinitionHolder對象

獲取ScopedProxyFactoryBean類的BeanDefinitionHolder

final class ScopedProxyCreator {
  
    public static BeanDefinitionHolder createScopedProxy(
            BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry, boolean proxyTargetClass) {
    
        return ScopedProxyUtils.createScopedProxy(definitionHolder, registry, proxyTargetClass);
    }
}


public abstract class ScopedProxyUtils {
    /**
     * Generate a scoped proxy for the supplied target bean, registering the target
     * bean with an internal name and setting 'targetBeanName' on the scoped proxy.
     * @param definition the original bean definition
     * @param registry the bean definition registry
     * @param proxyTargetClass whether to create a target class proxy
     * @return the scoped proxy definition
     *
     * 為提供的目標bean生成作用域代理,用內(nèi)部名稱注冊目標bean雁乡,并在作用域代理上設(shè)置“target bean name”扩淀。
     */
    public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
            BeanDefinitionRegistry registry, boolean proxyTargetClass) {
    
        String originalBeanName = definition.getBeanName();
        BeanDefinition targetDefinition = definition.getBeanDefinition();
    //targetBeanName = "scopedTarget."+ originalBeanName
        String targetBeanName = getTargetBeanName(originalBeanName);

        // Create a scoped proxy definition for the original bean name,
        // "hiding" the target bean in an internal target definition.
        // 為原始bean名稱創(chuàng)建一個作用域代理定義,
        // 在內(nèi)部目標定義中“隱藏”目標bean菇曲。
        RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
        proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
        proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
        proxyDefinition.setSource(definition.getSource());
        proxyDefinition.setRole(targetDefinition.getRole());

        proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
        if (proxyTargetClass) {
            targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
            // ScopedProxyFactoryBean's "proxyTargetClass" default is TRUE, so we don't need to set it explicitly here.
            // ScopedProxyFactoryBean的“proxyTargetClass”默認值為TRUE,因此不需要在這里顯式設(shè)置它酱固。
        }
        else {
            proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
        }

        // Copy autowire settings from original bean definition.
        // 從原始bean定義復(fù)制autowire設(shè)置烁巫。
        proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
        proxyDefinition.setPrimary(targetDefinition.isPrimary());
        if (targetDefinition instanceof AbstractBeanDefinition) {
            proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
        }

        // The target bean should be ignored in favor of the scoped proxy.
        // 為了支持作用域代理,應(yīng)該忽略目標bean赛蔫。
        targetDefinition.setAutowireCandidate(false);
        targetDefinition.setPrimary(false);

        // Register the target bean as separate bean in the factory.
        // 在工廠中將目標bean注冊為單獨的bean鳄橘。
        registry.registerBeanDefinition(targetBeanName, targetDefinition);

        // Return the scoped proxy definition as primary bean definition
        // (potentially an inner bean).
        return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
    }
}

在上述過程中炸裆,主要做了三件事

  1. 新建一個ScopedProxyFactoryBean類的BeanDefinition,用于生成代理原來的Bean的代理Bean浪漠,設(shè)置所需的參數(shù),確定代理類使用jdk代理還是CGLIB代理律适。
  2. 處理原來的BeanDefinition
    1. 原來的BeanDefinition中autowireCandidate屬性和primary屬性設(shè)置為false
    2. 將原來的BeanDefinition的以("scopedTarget."+原BeanName)作為新名稱注冊到容器中
  3. 返回包含原BeanName和ScopedProxyFactoryBean類的BeanDefinition信息的BeanDefinitionHolder。
    1. 以后再調(diào)用原來的beanName去獲取bean的時候福荸,獲取的將是在ScopedProxyFactoryBean對象中獲取的BeanDefinition的代理對象。

總結(jié)

在注冊組件類的時候主要做了以下事情:

  1. 分析組件類生成BeanDefinition對象前域,將以下注解的配置元信息設(shè)置到BeanDefinition對象
    1. @Scope
    2. @Lazy
    3. @Primary
    4. @DependsOn
    5. @Role
    6. @Description
  2. 確定BeanName
  3. 根據(jù)@Scope的proxyMode屬性绢掰,進行不同的作用域代理策略
  4. 將組件類的BeanDefinition或ScopedProxyFactoryBean類的BeanDefinition注冊到容器中
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末假丧,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子姨夹,更是在濱河造成了極大的恐慌逃糟,老刑警劉巖取募,帶你破解...
    沈念sama閱讀 218,284評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異眶蕉,居然都是意外死亡厘贼,警方通過查閱死者的電腦和手機凭疮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,115評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來规惰,“玉大人,你說我怎么就攤上這事泉蝌∏渌” “怎么了?”我有些...
    開封第一講書人閱讀 164,614評論 0 354
  • 文/不壞的土叔 我叫張陵梨与,是天一觀的道長。 經(jīng)常有香客問我文狱,道長粥鞋,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,671評論 1 293
  • 正文 為了忘掉前任瞄崇,我火速辦了婚禮呻粹,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘苏研。我一直安慰自己等浊,他們只是感情好,可當我...
    茶點故事閱讀 67,699評論 6 392
  • 文/花漫 我一把揭開白布摹蘑。 她就那樣靜靜地躺著筹燕,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上撒踪,一...
    開封第一講書人閱讀 51,562評論 1 305
  • 那天过咬,我揣著相機與錄音,去河邊找鬼制妄。 笑死掸绞,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的耕捞。 我是一名探鬼主播衔掸,決...
    沈念sama閱讀 40,309評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼俺抽!你這毒婦竟也來了敞映?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,223評論 0 276
  • 序言:老撾萬榮一對情侶失蹤凌埂,失蹤者是張志新(化名)和其女友劉穎驱显,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瞳抓,經(jīng)...
    沈念sama閱讀 45,668評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡埃疫,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,859評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了孩哑。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片栓霜。...
    茶點故事閱讀 39,981評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖横蜒,靈堂內(nèi)的尸體忽然破棺而出胳蛮,到底是詐尸還是另有隱情,我是刑警寧澤丛晌,帶...
    沈念sama閱讀 35,705評論 5 347
  • 正文 年R本政府宣布仅炊,位于F島的核電站,受9級特大地震影響澎蛛,放射性物質(zhì)發(fā)生泄漏抚垄。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,310評論 3 330
  • 文/蒙蒙 一谋逻、第九天 我趴在偏房一處隱蔽的房頂上張望呆馁。 院中可真熱鬧,春花似錦毁兆、人聲如沸浙滤。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,904評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纺腊。三九已至畔咧,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間摹菠,已是汗流浹背盒卸。 一陣腳步聲響...
    開封第一講書人閱讀 33,023評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留次氨,地道東北人蔽介。 一個月前我還...
    沈念sama閱讀 48,146評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像煮寡,于是被迫代替她去往敵國和親虹蓄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,933評論 2 355