追蹤解析Spring ioc啓動源碼(2)

接上篇

3 reader 註冊配置類

該 part 的起點:java

public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
    this();    
    register(annotatedClasses);        // 3 reader 註冊配置類
    refresh();
}

該行代碼會將 iocConfig bean 註冊到 reader 中
AnnotationConfigApplicationContext 的 register 方法:spring

//AnnotationConfigApplicationContext.class
public void register(Class<?>... annotatedClasses) {
    //參數非空效驗
    Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified"); 
    //調用 AnnotatedBeanDefinitionReader 的 register 方法
    this.reader.register(annotatedClasses); 
}

上述方法主要是調用了 AnnotatedBeanDefinitionReader 的 register 方法:api

//AnnotatedBeanDefinitionReader.class
public void register(Class<?>... annotatedClasses) {
    for (Class<?> annotatedClass : annotatedClasses) {
        registerBean(annotatedClass);
    }
}

上述方法循環調用了 AnnotatedBeanDefinitionReader 的 registerBean 方法:數組

//AnnotatedBeanDefinitionReader.class
public void registerBean(Class<?> annotatedClass) {
    doRegisterBean(annotatedClass, null, null, null);
}

上述方法調用了 AnnotatedBeanDefinitionReader 的 doRegisterBean 方法,這個方法比較長,須要重點關注:數據結構

//AnnotatedBeanDefinitionReader.class
<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
        @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {

    //用 BeanDefinition 包裝 iocConfig
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); 
    
    //此段代碼用於處理 Conditional 註解,在特定條件下阻斷 bean 的註冊
    //本例中此處不會 return
    //3.1
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { 
        return;
    }

    //用來建立 bean 的 supplier,會替代掉 bean 自己的建立方法
    //instanceSupplier 通常狀況下爲 null
    abd.setInstanceSupplier(instanceSupplier); 

    //此行代碼處理 scope 註解,本例中 scope 是默認值 singleton
    //3.2
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    abd.setScope(scopeMetadata.getScopeName()); 

    //bean name 在本例中爲自動生成的 iocConfig
    //3.3
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); 

    //特定註解解析,本例中均不作操做
    //3.4
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); 

    //本例中 qualifiers 傳入的是 null
    //3.5
    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));
            }
        }
    }

    //本例中 definitionCustomizers 傳入的是 null
    //3.6
    for (BeanDefinitionCustomizer customizer : definitionCustomizers) { 
        customizer.customize(abd);
    }

    //用 BeanDefinitionHolder 包裝 BeanDefinition
    BeanDefinitionHolder definitionHolder 
        = new BeanDefinitionHolder(abd,beanName); 
    
    //此行代碼與動態代理和 scope 註解有關,可是在本案例中沒有作任何操做,只是返回了傳入的 definitionHolder
    //3.7
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); 

    //iocConfig 註冊
    // 3.8
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); 
}

3.1

看一下上述方法的片斷:app

if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) { 
    return;
}

首先須要瞭解到 abd 的 getMetadata() 方法會獲取到 abd 中的 metadata 對象。ui

該對象是一個 StandardAnnotationMetadata 的實例化對象,在建立的時候會利用 java.Class 中的 api 獲取 bean 中全部的註解,並保存爲一個數組:this

//StandardAnnotationMetadata.class
public StandardAnnotationMetadata(Class<?> introspectedClass, boolean nestedAnnotationsAsMap) {
    //此處的 introspectedClass 即爲 bean 的 class
    //父類的構造器用於內部保存 bean 的 class
    super(introspectedClass);
    //獲取全部的註解
    this.annotations = introspectedClass.getAnnotations();
    //nestedAnnotationsAsMap 暫時用不上,按下不表
    //nestedAnnotationsAsMap = true
    this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
}

conditionEvaluator 是一個註解解析器,在 AnnotatedBeanDefinitionReader 建立的時候在其構造方法內被建立:lua

this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);

追蹤 conditionEvaluator 的 shouldSkip(...) 方法:代理

//ConditionEvaluator.class
public boolean shouldSkip(AnnotatedTypeMetadata metadata) {
    return shouldSkip(metadata, null); //調用自身的重載方法
}

//ConditionEvaluator.class
public boolean shouldSkip(@Nullable AnnotatedTypeMetadata metadata, @Nullable ConfigurationPhase phase) {
    //metadata 在此處不爲 null
    //判斷 bean 是否使用了 Conditional 註解
    if (metadata == null || !metadata.isAnnotated(Conditional.class.getName())) {
        //若是 metadata爲空或者 bean 沒有使用 Conditional 註解,就會返回 false
        return false; 
    }

    //第一次調用該方法的時候,phase 爲 null
    if (phase == null) {
        //下列源碼規整一下,實際上是四個條件:
        //1 bean.metadata 是 AnnotationMetadata 或其子類
        //2 bean 使用了 Configuration 註解
        //3 bean 不是一個接口
        //4 bean 使用了 Component、ComponentScan、Import、ImportResource 這四個註解之一,或者使用了 Bean 註解
        //這四個條件中知足 一、2 或者 一、三、4 就會進入 if 語句中
        //請注意,對於 config bean 來講,只要使用了 Conditional 註解,必然會進入到語句中
        if (metadata instanceof AnnotationMetadata &&
                ConfigurationClassUtils.isConfigurationCandidate((AnnotationMetadata) metadata)) {
            return shouldSkip(metadata, ConfigurationPhase.PARSE_CONFIGURATION);
        }
        return shouldSkip(metadata, ConfigurationPhase.REGISTER_BEAN);
    }

    List<Condition> conditions = new ArrayList<>();
    //getConditionClasses(metadata) 會獲取到 Conditional 註解中的 value 數組
    for (String[] conditionClasses : getConditionClasses(metadata)) {
        //遍歷數組
        for (String conditionClass : conditionClasses) {
            //利用反射獲取實例化數組內的 class
            Condition condition = getCondition(conditionClass, this.context.getClassLoader());
            conditions.add(condition); //獲取全部的 canditionClass 並以次存入到列表中
        }
    }

    //利用了 List 自帶的排序 api 進行排序
    AnnotationAwareOrderComparator.sort(conditions);

    for (Condition condition : conditions) {
        ConfigurationPhase requiredPhase = null;
        if (condition instanceof ConfigurationCondition) {
            requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
        }

        //對於 Conditional 內的 value 並不是是實現 ConfigurationCondition 接口的 class,requiredPhase == null 必然爲 true;對於實現了該接口的 class,requiredPhase == phase 必然爲 true
        //因此要注意,若是 value class 的 matches(...) 方法返回 false,則會在此處阻斷 bean 的註冊
        if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
            return true;
        }
    }

    //正常狀況下,作完全部檢查工做以後仍是會返回 false
    return false;
}

能夠看到這個方法實際上是 Conditional 註解的解析器,對於未使用這個註解的 bean,直接就返回了,不會繼續往下走。

先來看一下 Conditional 的源碼:

@Target({ElementType.TYPE, ElementType.METHOD}) //能夠標註在類和方法上方
@Retention(RetentionPolicy.RUNTIME) //註解生命週期
@Documented //javadoc 相關
public @interface Conditional {
    //class 數組
    //這個數組裏的值必需要是實現了 Condition 接口的類
    //注意這個 value 沒有默認值,若是要使用該註解就必需要填入
    Class<? extends Condition>[] value();
}

順便來看一下 Condition 接口:

public interface Condition {

    //這個方法會返回一個 boolean 值,若是爲 true,則將繼承該接口的類注入到 Conditional 修飾的 bean 中
    boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}

conditional 的具體內容有待研究,不展開。

3.2

看下方代碼片斷:

ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());

scopeMetadataResolver 是一個定義在 AnnotatedBeanDefinitionReader 裏的 AnnotationScopeMetadataResolver 對象。顧名思義,其主要做用是解析 scope 標籤。

先來看一下 Scope 註解的定義源碼:

@Target({ElementType.TYPE, ElementType.METHOD}) //能夠標註在類和方法上方
@Retention(RetentionPolicy.RUNTIME) //註解生命週期
@Documented //javadoc 相關
public @interface Scope {

    //value 是平時開發中最經常使用到的 scope 屬性,用來設置是不是單例模式
    //在處理註解的時候 value 屬性會被轉化成 scopeName 屬性來看待
    //因此兩個屬性實際上是同樣的
    String value() default "";
    String scopeName() default "";
    //代理模式設置,默認爲無代理
    ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}

ScopedProxyMode 是一個枚舉類,沒有任何處理業務邏輯的代碼,一同放在這裏:

public enum ScopedProxyMode {
    DEFAULT,        //不使用代理,default 和 no 是等價的
    NO,
    INTERFACES,        //使用 jdk 自帶的動態代理 api 進行建立
    TARGET_CLASS;    //target-class 模式,須要使用 cglib 進行 bean 的建立
 }

AnnotationScopeMetadataResolver 的 resolveScopeMetadata(...) 方法具體實現以下:

//AnnotationScopeMetadataResolver.class
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
    //建立一個 metadata 對象用於返回
    ScopeMetadata metadata = new ScopeMetadata();

    if (definition instanceof AnnotatedBeanDefinition) {
        AnnotatedBeanDefinition annDef = (AnnotatedBeanDefinition) definition;
        //從 bean 的註解裏尋找 scope 這個註解
        AnnotationAttributes attributes = AnnotationConfigUtils.attributesFor(
                annDef.getMetadata(), this.scopeAnnotationType);

        //若是 bean 確實是用了 scope 註解
        if (attributes != null) {
            metadata.setScopeName(attributes.getString("value")); //存入 scope 的 value 屬性值
            //獲取 proxyMode 屬性值
            ScopedProxyMode proxyMode = attributes.getEnum("proxyMode");
            //default 和 no 是等同的,默認會轉化成 no 進行處理
            if (proxyMode == ScopedProxyMode.DEFAULT) {
                //this.defaultProxyMode = ScopedProxyMode.NO
                proxyMode = this.defaultProxyMode;
            }
            metadata.setScopedProxyMode(proxyMode); //存入 scope 的 proxyMode 屬性值
        }
    }
    //沒有使用 scope 的狀況下會返回一個新建的 metadata
    return metadata;
}

annDef.getMetadata() 會獲取到一個 AnnotationMetadata 對象,裏面包含了 bean 的全部註解信息。

scopeAnnotationType 是一個定義在 AnnotationScopeMetadataResolver 裏的 Class 對象:

protected Class<? extends Annotation> scopeAnnotationType = Scope.class;

可見 AnnotationConfigUtils 的 attributesFor(...) 就是去註解集裏查找 scope 註解,而且封裝成一個 AnnotationAttributes 返回。
AnnotationAttributes 是 Spring 用來存儲註解所定義的一種數據結構,本質上是一個 LinkedHashMap。

再回到本小節最上方的代碼:

abd.setScope(scopeMetadata.getScopeName());

最後其實 BeanDefinition 只接收了 scopeName,而沒有接收 proxyMode。proxyMode 屬性會在後面代碼中用到。

3.3

看下方代碼片斷:

String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));

beanNameGenerator 是一個定義在 AnnotatedBeanDefinitionReader 裏的 AnnotationBeanNameGenerator 對象,顧名思義用來生成 bean 的名稱:

private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();

追蹤一下 generateBeanName(...) 方法:

//AnnotationBeanNameGenerator.class
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    
    if (definition instanceof AnnotatedBeanDefinition) {
        //determineBeanNameFromAnnotation(...) 方法會從 bean 的全部註解裏去遍歷搜尋 bean 名稱
        String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition);
        //若是此處的 beanName 非空,則代表在註解裏找到了定義的 bean 名稱
        if (StringUtils.hasText(beanName)) {
            // Explicit bean name found.
            return beanName;
        }
    }
    //沒有在前面 return,證實 bean 沒有被設置名稱,則在此處默認生成一個名稱
    return buildDefaultBeanName(definition, registry);
}

看一眼 buildDefaultBeanName(...) 方法:

//AnnotationBeanNameGenerator.class
protected String buildDefaultBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
    return buildDefaultBeanName(definition);
}

其實這個方法只用到了 definition,而沒有使用到傳入的 registry。

繼續追蹤代碼實現:

//AnnotationBeanNameGenerator.class
protected String buildDefaultBeanName(BeanDefinition definition) {
    //該處返回的是 bean 的整個 class 路徑和名稱
    String beanClassName = definition.getBeanClassName();
    //beanClassName 非空判斷
    Assert.state(beanClassName != null, "No bean class name set");
    //截掉 class 的路徑,只取 class 名稱
    String shortClassName = ClassUtils.getShortName(beanClassName);
    //將首字母小寫並返回
    return Introspector.decapitalize(shortClassName);
}

3.4

看下方代碼實現:

AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

追蹤這行代碼:

//AnnotationConfigUtils.class
public static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd) {
    //調用重載方法
    processCommonDefinitionAnnotations(abd, abd.getMetadata());
}

//AnnotationConfigUtils.class
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) {
    //檢查 lazy 註解
    AnnotationAttributes lazy = attributesFor(metadata, Lazy.class);
    if (lazy != null) {
        abd.setLazyInit(lazy.getBoolean("value"));
    }else if (abd.getMetadata() != metadata) {
        //這裏還有一個補充檢查,若是傳入的 metadata 不是 abd 內部的 metadata的話,還會繼續進來判斷一次
        //在本例中沒什麼必要
        lazy = attributesFor(abd.getMetadata(), Lazy.class);
        if (lazy != null) {
            abd.setLazyInit(lazy.getBoolean("value"));
        }
    }

    //檢查 primary 註解
    if (metadata.isAnnotated(Primary.class.getName())) {
        abd.setPrimary(true);
    }

    //檢查 dependsOn 註解
    AnnotationAttributes dependsOn = attributesFor(metadata, DependsOn.class);
    if (dependsOn != null) {
        abd.setDependsOn(dependsOn.getStringArray("value"));
    }

    //檢查 role 註解
    AnnotationAttributes role = attributesFor(metadata, Role.class);
    if (role != null) {
        abd.setRole(role.getNumber("value").intValue());
    }

    //檢查 description 註解
    AnnotationAttributes description = attributesFor(metadata, Description.class);
    if (description != null) {
        abd.setDescription(description.getString("value"));
    }
}

其實上述代碼的主體都是相似的,步驟都是嘗試從 metadata 中獲取特定註解,若是獲取到了就將其做爲一個屬性值 set 進 abd 中。

這裏須要強調 abd 就是要註冊的 bean 的 BeanDefinition 包裝對象。

本例中沒有用到上述的註解,因此均爲 null。

3.5

看下方代碼:

if (qualifiers != null) {
    //在 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));
        }
    }
}

上述代碼是針對 qualifier 註解的解析,和 3.4 很相似。

AutowireCandidateQualifier 是註解的包裝類,儲存了一個特定註解的名字和 value。abd 的 addQualifier(...) 方法會將這個建立出來的包裝類存儲到一個 map 對象裏。

3.6

看下方代碼:

for (BeanDefinitionCustomizer customizer : definitionCustomizers) { 
    customizer.customize(abd);
}

這段代碼是 Spring5 中新加入的。根據註釋,官方應該是留下這個接口用以讓開發者經過 lambda 表達式去定義 bean。

3.7

看下方代碼:

definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

先來追蹤 applyScopedProxyMode(...) 方法:

//AnnotationConfigUtils.class
static BeanDefinitionHolder applyScopedProxyMode(
            ScopeMetadata metadata, BeanDefinitionHolder definition, BeanDefinitionRegistry registry) {
    //先判斷 scope 註解的使用
    ScopedProxyMode scopedProxyMode = metadata.getScopedProxyMode();
    if (scopedProxyMode.equals(ScopedProxyMode.NO)) {
        return definition;
    }
    //判斷具體使用哪一種模式
    boolean proxyTargetClass = scopedProxyMode.equals(ScopedProxyMode.TARGET_CLASS);
    //此行代碼會連向 spring-aop 包下的類來處理
    return ScopedProxyCreator.createScopedProxy(definition, registry, proxyTargetClass);
}

注意,此處傳入的 metadata 是上述 3.2 小節中新建出來並返回的對象:

ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);

對於通常沒有使用 scope 註解或者 scope 註解爲默認的 bean,此時 scopedProxyMode 是等於 ScopedProxyMode.NO 的。

對於 scopedProxyMode 不爲 NO 的 bean,均爲須要使用動態代理進行建立的對象,區別只是使用 jdk 自帶的 api 仍是使用 cglib 包。

追蹤一下 ScopedProxyCreator 的 createScopedProxy(...) 方法:

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

繼續追蹤:

//ScopedProxyUtils.class
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition,
            BeanDefinitionRegistry registry, boolean proxyTargetClass) {
    //bean 的名稱
    String originalBeanName = definition.getBeanName();
    //bean 的 BeanDefinition 包裝類
    BeanDefinition targetDefinition = definition.getBeanDefinition();
    //在 bean 的名稱前面加上字符串 "scopedTarget." ,拼成 targetBeanName
    //好比 scopedTarget.iocConfig
    String targetBeanName = getTargetBeanName(originalBeanName);

    //如下代碼用來組裝一個動態代理的工廠 bean,這個 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);
    }else {
        proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
    }
    proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
    proxyDefinition.setPrimary(targetDefinition.isPrimary());
    if (targetDefinition instanceof AbstractBeanDefinition) {
        proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition) targetDefinition);
    }
    targetDefinition.setAutowireCandidate(false);
    targetDefinition.setPrimary(false);

    //此處的 targetDefinition 是傳入的 bean 的包裝類
    //這一步會提早將該 bean 進行註冊
    //註冊過程見 2.2
    registry.registerBeanDefinition(targetBeanName, targetDefinition);

    //返回的實際上是動態代理所須要的工廠 bean
    return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}

scope 的具體內容有待研究,不展開。

3.8

在上例代碼中的這一行中:

BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);

傳入的 definitionHolder 就是 iocConfig bean 的包裝對象;而傳入的 registry 就是在 ApplicationContext 中實例化的 BeanFactory,此處具體而言就是DefaultListableBeanFactory。

繼續追蹤這行代碼的內部實現:

//BeanDefinitionReaderUtils.class
public static void registerBeanDefinition(
        BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
        throws BeanDefinitionStoreException {

    //獲取bean的名稱
    String beanName = definitionHolder.getBeanName();

    //調用 AnnotationConfigApplicationContext 的 registerBeanDefinition 方法
    //註冊過程見 2.2
    registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

    //處理bean的別名,本例中沒有別名,不進入循環
    String[] aliases = definitionHolder.getAliases();
    if (aliases != null) {
        for (String alias : aliases) {
            registry.registerAlias(beanName, alias);
        }
    }
}

alias 的具體內容有待研究,不展開。

到此爲止,iocConfig bean 已經被註冊到 bean factory 中。

To Be Continued ...

相關文章
相關標籤/搜索