Spring,相信每個Java開發都用過,而且是每天都在用,那強大又神祕的IoC,AOP,讓我們的開發變得越來越簡單,只需要一個註解搞定一切,但是它內部到底是什麼樣子的呢?跟著我,一起探究Spring原始碼把。
寫在前面的話:Spring專案距今已有15年左右的歷史了,是眾多Java大神們的傑作,由於我個人水平有限,時間有限,不保證我說的全部都是正確的,但是我可以保證每一句話都是反覆推敲,經過驗證,絕沒有複製貼上。當然在這裡,也不可能把每個方法都進行深層次的分析,只能把重點集中在重要的方法上,有些(其實是絕大部分)只能採取黑盒理論,即:不去探究方法內部到底做了什麼,只大概的知道執行這個方法後發生了什麼。
本文中採用的Spring版本是5.0.0
由於現在JavaConfig風格+註解的方式來使用Spring,是Spring官方主推的,也是現在的主流方式,所以我們從這裡出發:
AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(AppConfig.class);
複製程式碼
讓我們先來看看AnnotationConfigApplicationContext的關係圖:
可以看到這個關係夠複雜的,我們現在完全不需要特意全部記住,只要有一個大概的印象就可以了,後面隨著原始碼分析的深入,自然而然會記住其中的一些關係。
建立AnnotationConfigApplicationContext物件,首先會跑到這裡:
//根據引數型別可以知道,其實可以傳入多個annotatedClasses,但是這種情況出現的比較少
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
//呼叫無參建構函式,會先呼叫父類GenericApplicationContext的建構函式
//父類的建構函式裡面就是初始化DefaultListableBeanFactory,並且賦值給beanFactory
//本類的建構函式裡面,初始化了一個讀取器:AnnotatedBeanDefinitionReader read,一個掃描器ClassPathBeanDefinitionScanner scanner
//scanner的用處不是很大,它僅僅是在我們外部手動呼叫 .scan 等方法才有用,常規方式是不會用到scanner物件的
this();
//把傳入的類進行註冊,這裡有兩個情況,
//傳入傳統的配置類
//傳入bean(雖然一般沒有人會這麼做
//看到後面會知道spring把傳統的帶上@Configuration的配置類稱之為FULL配置類,不帶@Configuration的稱之為Lite配置類
//但是我們這裡先把帶上@Configuration的配置類稱之為傳統配置類,不帶的稱之為普通bean
register(annotatedClasses);
//重新整理
refresh();
}
複製程式碼
這個方法第一眼看上去,很簡單,無非就是三行程式碼,但是這三行程式碼包含了大千世界。
我們先來為構造方法做一個簡單的說明:
-
這是一個有參的構造方法,可以接收多個配置類,不過一般情況下,只會傳入一個配置類。
-
這個配置類有兩種情況,一種是傳統意義上的帶上@Configuration註解的配置類,還有一種是沒有帶上@Configuration,但是帶有@Component,@Import,@ImportResouce,@Service,@ComponentScan等註解的配置類,在Spring內部把前者稱為Full配置類,把後者稱之為Lite配置類。在本原始碼分析中,有些地方也把Lite配置類稱為普通Bean。
我們先來看一下第一行程式碼:通過this()呼叫此類無參的構造方法,程式碼會跑到下面:
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
//註解bean定義讀取器,主要作用是用來讀取被註解的了bean
private final AnnotatedBeanDefinitionReader reader;
//掃描器,它僅僅是在我們外部手動呼叫 .scan 等方法才有用,常規方式是不會用到scanner物件的
private final ClassPathBeanDefinitionScanner scanner;
/**
* Create a new AnnotationConfigApplicationContext that needs to be populated
* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
*/
public AnnotationConfigApplicationContext() {
//會隱式呼叫父類的構造方法,初始化DefaultListableBeanFactory
//初始化一個Bean讀取器
this.reader = new AnnotatedBeanDefinitionReader(this);
//初始化一個掃描器,它僅僅是在我們外部手動呼叫 .scan 等方法才有用,常規方式是不會用到scanner物件的
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
}
複製程式碼
首先映入眼簾的是reader和scanner,無參構造方法中就是對reader和scanner進行了例項化,reader的型別是AnnotatedBeanDefinitionReader,從字面意思就可以看出它是一個 “打了註解的Bean定義讀取器”,scanner的型別是ClassPathBeanDefinitionScanner,其實這個欄位並不重要,它僅僅是在我們外面手動呼叫.scan方法,或者呼叫引數為String的構造方法,傳入需要掃描的包名,才會用到,像我們這樣傳入配置類是不會用到這個scanner物件的。
AnnotationConfigApplicationContext類是有繼承關係的,會隱式呼叫父類的構造方法:
public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {
private final DefaultListableBeanFactory beanFactory;
public GenericApplicationContext() {
this.beanFactory = new DefaultListableBeanFactory();
}
}
複製程式碼
這個程式碼很簡單,就是初始化了DefaultListableBeanFactory。
我們再來看看DefaultListableBeanFactory的關係圖:
DefaultListableBeanFactory是相當重要的,從字面意思就可以看出它是一個Bean的工廠,什麼是Bean的工廠?當然就是用來生產和獲得Bean的。
讓我們把目光回到AnnotationConfigApplicationContext的無參構造方法,讓我們看看Spring在初始化AnnotatedBeanDefinitionReader的時候做了什麼:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}
複製程式碼
這裡的BeanDefinitionRegistry當然就是AnnotationConfigApplicationContext的例項了,這裡又直接呼叫了此類其他的構造方法:
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
Assert.notNull(environment, "Environment must not be null");
this.registry = registry;
this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
複製程式碼
讓我們把目光移動到這個方法的最後一行,進入registerAnnotationConfigProcessors方法:
public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
registerAnnotationConfigProcessors(registry, null);
}
複製程式碼
這又是一個門面方法,再點進去,這個方法的返回值Set,但是上游方法並沒有去接收這個返回值,所以這個方法的返回值也不是很重要了,當然方法內部給這個返回值賦值也不重要了。由於這個方法內容比較多,這裡就把最核心的貼出來,這個方法的核心就是註冊Spring內建的多個Bean:
if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
}
複製程式碼
-
判斷容器中是否已經存在了ConfigurationClassPostProcessor Bean
-
如果不存在(當然這裡肯定是不存在的),就通過RootBeanDefinition的構造方法獲得ConfigurationClassPostProcessor的BeanDefinition,RootBeanDefinition是BeanDefinition的子類:
- 執行registerPostProcessor方法,registerPostProcessor方法內部就是註冊Bean。
當然這裡註冊其他Bean也是一樣的流程。
BeanDefinition是什麼,顧名思義,它是用來描述Bean的,裡面存放著關於Bean的一系列資訊,比如Bean的作用域,Bean所對應的Class,是否懶載入,是否Primary等等,這個BeanDefinition也相當重要,我們以後會常常和它打交道。
registerPostProcessor方法:
private static BeanDefinitionHolder registerPostProcessor(
BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(beanName, definition);
return new BeanDefinitionHolder(definition, beanName);
}
複製程式碼
這方法為BeanDefinition設定了一個Role,ROLE_INFRASTRUCTURE代表這是spring內部的,並非使用者定義的,然後又呼叫了registerBeanDefinition方法,再點進去,Oh No,你會發現它是一個介面,沒辦法直接點進去了,首先要知道registry實現類是什麼,那麼它的實現是什麼呢?答案是DefaultListableBeanFactory:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
複製程式碼
這又是一個門面方法,再點進去,核心在於下面兩行程式碼:
//beanDefinitionMap是Map<String, BeanDefinition>,
//這裡就是把beanName作為key,ScopedProxyMode作為value,推到map裡面
this.beanDefinitionMap.put(beanName, beanDefinition);
//beanDefinitionNames就是一個List<String>,這裡就是把beanName放到List中去
this.beanDefinitionNames.add(beanName);
複製程式碼
從這裡可以看出DefaultListableBeanFactory就是我們所說的容器了,裡面放著beanDefinitionMap,beanDefinitionNames,beanDefinitionMap是一個hashMap,beanName作為Key,beanDefinition作為Value,beanDefinitionNames是一個集合,裡面存放了beanName。打個斷點,第一次執行到這裡,監視這兩個變數:
DefaultListableBeanFactory中的beanDefinitionMap,beanDefinitionNames也是相當重要的,以後會經常看到它,最好看到它,第一時間就可以反應出它裡面放了什麼資料
這裡僅僅是註冊,可以簡單的理解為把一些原料放入工廠,工廠還沒有真正的去生產。
上面已經介紹過,這裡會一連串註冊好幾個Bean,在這其中最重要的一個Bean(沒有之一)就是BeanDefinitionRegistryPostProcessor Bean。
ConfigurationClassPostProcessor實現BeanDefinitionRegistryPostProcessor介面,BeanDefinitionRegistryPostProcessor介面又擴充套件了BeanFactoryPostProcessor介面,BeanFactoryPostProcessor是Spring的擴充套件點之一,ConfigurationClassPostProcessor是Spring極為重要的一個類,必須牢牢的記住上面所說的這個類和它的繼承關係。
除了註冊了ConfigurationClassPostProcessor,還註冊了其他Bean,其他Bean也都實現了其他介面,比如BeanPostProcessor介面。
BeanPostProcessor介面也是Spring的擴充套件點之一。
至此,例項化AnnotatedBeanDefinitionReader reader分析完畢。
由於常規使用方式是不會用到AnnotationConfigApplicationContext裡面的scanner的,所以這裡就不看scanner是如何被例項化的了。
把目光回到最開始,再分析第二行程式碼:
register(annotatedClasses);
複製程式碼
這裡傳進去的是一個陣列,最終會迴圈呼叫如下方法:
<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
//AnnotatedGenericBeanDefinition可以理解為一種資料結構,是用來描述Bean的,這裡的作用就是把傳入的標記了註解的類
//轉為AnnotatedGenericBeanDefinition資料結構,裡面有一個getMetadata方法,可以拿到類上的註解
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
//判斷是否需要跳過註解,spring中有一個@Condition註解,當不滿足條件,這個bean就不會被解析
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
abd.setInstanceSupplier(instanceSupplier);
//解析bean的作用域,如果沒有設定的話,預設為單例
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
abd.setScope(scopeMetadata.getScopeName());
//獲得beanName
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
//解析通用註解,填充到AnnotatedGenericBeanDefinition,解析的註解為Lazy,Primary,DependsOn,Role,Description
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
//限定符處理,不是特指@Qualifier註解,也有可能是Primary,或者是Lazy,或者是其他(理論上是任何註解,這裡沒有判斷註解的有效性),如果我們在外面,以類似這種
//AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(Appconfig.class);常規方式去初始化spring,
//qualifiers永遠都是空的,包括上面的name和instanceSupplier都是同樣的道理
//但是spring提供了其他方式去註冊bean,就可能會傳入了
if (qualifiers != null) {
//可以傳入qualifier陣列,所以需要迴圈處理
for (Class<? extends Annotation> qualifier : qualifiers) {
//Primary註解優先
if (Primary.class == qualifier) {
abd.setPrimary(true);
}
//Lazy註解
else if (Lazy.class == qualifier) {
abd.setLazyInit(true);
}
//其他,AnnotatedGenericBeanDefinition有個Map<String,AutowireCandidateQualifier>屬性,直接push進去
else {
abd.addQualifier(new AutowireCandidateQualifier(qualifier));
}
}
}
for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
customizer.customize(abd);
}
//這個方法用處不大,就是把AnnotatedGenericBeanDefinition資料結構和beanName封裝到一個物件中
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
//註冊,最終會呼叫DefaultListableBeanFactory中的registerBeanDefinition方法去註冊,
//DefaultListableBeanFactory維護著一系列資訊,比如beanDefinitionNames,beanDefinitionMap
//beanDefinitionNames是一個List<String>,用來儲存beanName
//beanDefinitionMap是一個Map,用來儲存beanName和beanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
複製程式碼
在這裡又要說明下,以常規方式去註冊配置類,此方法中除了第一個引數,其他引數都是預設值。
-
通過AnnotatedGenericBeanDefinition的構造方法,獲得配置類的BeanDefinition,這裡是不是似曾相似,在註冊ConfigurationClassPostProcessor類的時候,也是通過構造方法去獲得BeanDefinition的,只不過當時是通過RootBeanDefinition去獲得,現在是通過AnnotatedGenericBeanDefinition去獲得。
-
判斷需不需要跳過註冊,Spring中有一個@Condition註解,如果不滿足條件,就會跳過這個類的註冊。
-
然後是解析作用域,如果沒有設定的話,預設為單例。
-
獲得BeanName。
-
解析通用註解,填充到AnnotatedGenericBeanDefinition,解析的註解為Lazy,Primary,DependsOn,Role,Description。
-
限定符處理,不是特指@Qualifier註解,也有可能是Primary,或者是Lazy,或者是其他(理論上是任何註解,這裡沒有判斷註解的有效性)。
-
把AnnotatedGenericBeanDefinition資料結構和beanName封裝到一個物件中(這個不是很重要,可以簡單的理解為方便傳參)。
-
註冊,最終會呼叫DefaultListableBeanFactory中的registerBeanDefinition方法去註冊:
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
//獲取beanName
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
//註冊bean
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//Spring支援別名
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
複製程式碼
這個registerBeanDefinition是不是又有一種似曾相似的感覺,沒錯,在上面註冊Spring內建的Bean的時候,已經解析過這個方法了,這裡就不重複了,此時,讓我們再觀察下beanDefinitionMap beanDefinitionNames兩個變數,除了Spring內建的Bean,還有我們傳進來的Bean,這裡的Bean當然就是我們的配置類了:
到這裡註冊配置類也分析完畢了。
大家可以看到其實到這裡,Spring還沒有進行掃描,只是例項化了一個工廠,註冊了一些內建的Bean和我們傳進去的配置類,真正的大頭是在第三行程式碼:
refresh();
複製程式碼
不過,這就是下一章的內容了。