Spring原始碼解析系列一:配置類的初始化過程
從今天開始,準備寫關於Spring原始碼的部落格,那麼廢話不多說,咱們開始搞!
1.環境準備
1).看圖:
PersonService類:
@Component
public class PersonService {
public void run(){
System.out.println("run方法執行了");
}
}
複製程式碼
SpringConfiguration類:
@ComponentScan("my.blog")
public class SpringConfiguration {
}
複製程式碼
Test01類:
public class Test01 {
public static void main(String[] args) {
//這個構造方法會把Spring所有的環境都準備好
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
PersonService person = ac.getBean(PersonService.class);
person.run();
}
}
複製程式碼
2.介紹:註解配置應用上下文
AnnotationConfigApplicationContext
所以需要 new AnnotationConfigApplicationContext
這個物件.
如果採用的是xml的配置方式 則需要 new ClassPathXmlApplicationContext
,這個應該我不用多說,我想你應該懂的!
那麼這個例項化物件的過程,Spring到底中幹了哪些見不得人的事呢? 接下來跟著我一起去揭開他的神祕面紗!
我們點選 new AnnotationConfigApplicationContext
看一下他的構造方法:
public AnnotationConfigApplicationContext (Class<?>... annotatedClasses) {
//這個類有父類,所以會先初始化父類的構造方法,接著初始化自己的構造方法
//呼叫無參構造方法進行初始化一個讀取器和掃描器
this();
//這個方法的作用:主要是把配置類的資訊載入進工廠中
//在這裡需要你記住一個類:DefaultListableBeanFactory,後面會非常的重要
register(annotatedClasses);
//例項化所有被加了元件的物件
refresh();
}
複製程式碼
我們發現這個建構函式的引數可以一次性傳多個配置類,後面程式碼中其實會遍歷annotatedClasses
這個陣列
我們看一下 this()
幹了哪些事?
public AnnotationConfigApplicationContext() {
//這裡也會先初始化父類的構造方法
//建立一個讀取被加了註解的bean讀取器,這個讀取器到底什麼鬼,不是這節的重點,可以先忽略
//你就知道他建立了一個讀取器就完事了
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
複製程式碼
我們回到 register(annotatedClasses);
這個方法,這個方法就是本節的大哥,我們現在就要去看看,他為Spring幹了哪些髒活!
3.載入配置類
我們點選 register(annotatedClasses)
嗯....,好像沒啥用!
點選 this.reader.register(annotatedClasses);
方法
這時候,我們發現這個方法開始遍歷annotatedClasses
陣列,由此我們可以一次性寫多個配置檔案傳給構造方法的
開始遍歷annotatedClasses
(注意:本次演示中,我只添加了一個配置類),呼叫 registerBean(annotatedClass);
我們這時候點選 registerBean(annotatedClass);
點選 doRegisterBean(annotatedClass,null,null);
<T> void doRegisterBean(Class<T> annotatedClass,@Nullable Supplier<T> instanceSupplier,@Nullable String name,@Nullable Class<? extends Annotation>[] qualifiers,BeanDefinitionCustomizer... definitionCustomizers) {
//(1)(解析:檢視下面圖)
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
//這個不是重點,跳過
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
//這個不是重點,跳過 instanceSupplier為null值
abd.setInstanceSupplier(instanceSupplier);
//(2)得到類的作用域 單例還是多例(解析:檢視下面圖)
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
//把類的作用域賦值給AnnotatedGenericBeanDefinition物件
abd.setScope(scopeMetadata.getScopeName());
//生成配置類的名稱,如果@Component沒有對應的名稱 (我沒有加名稱)
//預設是類的小駝峰式命名稱 (所有此時beanName為springConfiguration)
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd,this.registry));
/**
* (3)把AnnotatedGenericBeanDefinition物件傳進
* 然後獲取獲取元資料metadata物件,判斷元資料物件中是否存在lazy,DependsOn,Primary Role 等註解
* 如果有這些註解,則在AnnotatedGenericBeanDefinition物件中記錄
*/
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
//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));
}
}
}
//這個不是重點
for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
customizer.customize(abd);
}
//(4)把abd放進去,賦值給了成員變數beanDefinition
//把BeanName賦值進去,可以說是增強版的abd物件
//檢視後面的程式碼發現,其實definitionHolder就只是起到一個臨時容器的作用
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd,beanName);
//這個比較複雜,以後可以講 和本節無關
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata,definitionHolder,this.registry);
//(5)現在又把增強版的 definitionHolder 放到registry這個容器中
//BeanDefinitionRegistry 顧名思義 就是註冊BeanDefinition的
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder,this.registry);
}
複製程式碼
1).我們檢視程式碼AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
幹了哪些事 ?
通過debug 檢視 abd 物件內容
總結:
- 根據指定的配置類,建立一個GenericBeanDefinition物件
- 這個GenericBeanDefinition物件包含了該類的一些元資訊,例如註解資訊: scope,lazy,ComponentScan等註解
- 這些註解儲存在 GenericBeanDefinition中的 元資料(metadata)物件中
- 元資料物件有一個註解集合 annotations,儲存所有的註解資訊
2).接下來檢視 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
執行完這個abd.setScope(scopeMetadata.getScopeName());
方法後
3).檢視 AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
點選進入
額...,這個方法好像也沒啥...
把傳進的abd物件拆開,又傳了abd物件和abd.getMetadata()元資料物件
點選 processCommonDefinitionAnnotations(abd,abd.getMetadata());
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd,AnnotatedTypeMetadata metadata) {
//判斷當前的類是否加了lazy註解
AnnotationAttributes lazy = attributesFor(metadata,Lazy.class);
//如果不為null,則把AnnotatedBeanDefinition中的 lazyInit 預設的false 修改為true
if (lazy != null) {
abd.setLazyInit(lazy.getBoolean("value"));
}
else if (abd.getMetadata() != metadata) {
lazy = attributesFor(abd.getMetadata(),Lazy.class);
if (lazy != null) {
abd.setLazyInit(lazy.getBoolean("value"));
}
}
//判斷元資料物件中時候有@Primary,預設時候primary是 false,如果有則該為true
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"));
}
}
複製程式碼
總結:
- 把AnnotatedGenericBeanDefinition物件傳進去
- 獲取元資料metdata物件,Primary Role 等註解
- 如果有這些註解,則在AnnotatedGenericBeanDefinition物件中記錄
4).檢視 BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd,beanName);
debug 發現
5).檢視 BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder,this.registry);
點選進入
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder,BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
//在這裡又獲取beanName的名稱
String beanName = definitionHolder.getBeanName();
//現在把definitionHolder拆分了,又把abd物件拿出來了
//似乎definitionHolder就只是封裝了一下,然後又給拆分,可以理解為一個臨時的容器
registry.registerBeanDefinition(beanName,definitionHolder.getBeanDefinition());
//這個不重要,spring當中處理別名的
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName,alias);
}
}
}
複製程式碼
我們點選 registry.registerBeanDefinition(beanName,definitionHolder.getBeanDefinition());
發現是一個介面,按住快捷鍵 Ctrl + Alt + B
,有三個實現類,前面叫你留意的 DefaultListableBeanFactory
在這裡出現了,
選擇 DefaultListableBeanFactory
@Override
public void registerBeanDefinition(String beanName,BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
//-------------------------------------------------------------------------------
Assert.hasText(beanName,"Bean name must not be empty");
Assert.notNull(beanDefinition,"BeanDefinition must not be null");
if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(),beanName,"Validation of bean definition failed",ex);
}
}
//檢視該bean時候在map集合中儲存過
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName,beanDefinition,existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION,now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
this.beanDefinitionMap.put(beanName,beanDefinition);
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
this.beanDefinitionMap.put(beanName,beanDefinition);
List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
removeManualSingletonName(beanName);
}
}
//---------------------------------------------------------------------------------------
//前面的程式碼都是一些判斷,驗證不是重點
//重點是這裡的程式碼,前方高能
else {
//在這個方法中就把 beanDefinition 儲存在 DefaultListableBeanFactory的map集合中
//顧名思義,beanDefinitionMap就是一個儲存beanDefinition的map集合
//在這個集合當中還有Spring當中本身已經初始好的物件
this.beanDefinitionMap.put(beanName,beanDefinition);
//把beanName儲存在這個list集合中
this.beanDefinitionNames.add(beanName);
//這個是去重的,不是重點
removeManualSingletonName(beanName);
}
this.frozenBeanDefinitionNames = null;
}
if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
複製程式碼
this.beanDefinitionMap.put(beanName,beanDefinition);
之前
this.beanDefinitionMap.put(beanName,beanDefinition);
之後
this.beanDefinitionNames.add(beanName);
之前
this.beanDefinitionNames.add(beanName);
之後
整個配置類的載入過程就執行完了,最後總結一下 register(annotatedClasses);
都幹了哪些事?
- 載入配置類的元資料
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
- 給abd物件設定作用域
- 遍歷元資料註解,判斷時候存在某註解
- 把配置類資訊物件儲存到
DefaultListableBeanFactory
的beanDefinitionMap集合中 - 把配置類的名稱儲存到
DefaultListableBeanFactory
的 beanDefinitionNames 集合中
4.視訊講解:
視訊為自己學習的時候錄製的,也方便自己以後看
原始碼地址:
github.com/zouchangfu/… gitee.com/zouchangfu/…