接上篇
3 reader 註冊配置類
該 part 的起點:
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses); // 3 reader 註冊配置類
refresh();
}
該行程式碼會將 iocConfig bean 註冊到 reader 中
AnnotationConfigApplicationContext 的 register 方法:
//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 方法:
//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
看一下上述方法的片段:
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
首先需要了解到 abd 的 getMetadata() 方法會獲取到 abd 中的 metadata 物件。
該物件是一個 StandardAnnotationMetadata 的例項化物件,在建立的時候會利用 java.Class 中的 api 獲取 bean 中所有的註解,並儲存為一個陣列:
//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 建立的時候在其構造方法內被建立:
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 註解
//這四個條件中滿足 1、2 或者 1、3、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 中。