@[toc] 1. Spring註解的源碼分析 1.1 我如何開始分析源碼的? ==這一部分可以略過直接看第1.2節== 想必程式員都會經過這樣一個階段,當 已經能夠熟練運用。並且它的 也能夠用到 ,程式員就會找進階的入口,這時候就想到了去瞭解源碼。 作為Java程式員,首先想到的一定是瞭解Spri ...
@[toc]
1. Spring註解的源碼分析
1.1 我如何開始分析源碼的?
這一部分可以略過直接看第1.2節
想必程式員都會經過這樣一個階段,當一門編程語言的語法
已經能夠熟練運用。並且它的流行框架
也能夠用到五分熟
,程式員就會找進階的入口,這時候就想到了去瞭解源碼。
作為Java程式員,首先想到的一定是瞭解Spring源碼,但是Spring這個東西龐然大物,從哪裡開始都是個問題!這時候我就想起了唐曾那句話:“貧僧從東土大唐而來,往西天取經而去”。真的很羡慕他,知道自己從哪來,還知道自己該往哪去。這樣的人已經不多了。那麼對Spring源碼的剖析該從哪來呢?
我經歷過這幾個階段:
- 網上看視頻(像B站里有很多好的關於spring源碼的視頻,但是視頻有個缺點就是如果忘記了,想複習一遍,沒那麼容易。)
- 看網上的博客,這就不用說了。網上的博客五花八門,魚龍混雜,想要找到一篇適合自己的很不容易。(放棄了)
- 啃書本,最開始我看的是《Spring源碼深度剖析》,但這本書打著Spring5的旗號,講解xml的內容,作為跟進潮流的程式員表示,我想看關於註解方式的源碼解析。然後我又看了《Spring揭秘》《Spirng 技術內幕》等等。沒有一本是符合自己的。
- 黃天不負有心人,最終我還是找到了一本合適的書籍《Spring5 核心原理——手寫Spirng 30 個類實戰》不得不說,這本書是我進階Spring的入門書籍。強烈推薦。以下博文內容參考這本書和我自己微薄的只是而成。
1.2 Spring的模塊
Spirng的模塊有很多,這裡我不必多說。想要瞭解spring那最好是從Spring的Ioc容器,DI依賴註入,AOP切麵編程,MVC,資料庫處理這幾個入手。廢話不多說,直接上手Spring Ioc容器
,請看下節。
1.3 找到一個入口
1.3.1 Spring容器
一說到Spring容器,會想到什麼呢?有些人會想到ApplicationContext
,BeanFactory
,好像沒錯。但是理解的太狹隘了。我理解的Spring 容器是包含Bean的生命周期,從一個Bean裝載到這個Bean被初始化並註冊到容器中的過程中所涉及到的容器和類的總體成為Ioc容器。從ApplicationContext
這個名字也能看出來,它有上下文
的意思。
隨著Spring版本的更新,最新的Spring5 配上Springboot 2.x已經全面推薦使用註解開發,AnnotationConfigWebApplicationContext
只是AnnotationConfigApplicationContext
的Web版本。所以接下來講到的就以AnnotationConfigApplicationContext
這個類為例分析源碼。
2. Ioc容器分析
在spring環境下,先準備一個名為AppConfig
的類。名字自己起。通過AnnotationConfigApplicationContext
容器對它的掃描Bean、解析Bean、註冊Bean的過程來分析容器的整個創建過程。
AppConfig.java文件源碼:
package com.abc;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
@Configuration
@ComponentScan("com.abc")
@Scope("singleton")
public class AppConfig {
public AppConfig() {
System.out.println("com.abc.AppConfig");
}
}
TestAppConfig.java文件源碼:
public class TestAppConfig {
@Test
public void testAnno() {
// 創建註解上下文容器(Ioc容器)
AnnotationConfigApplicationContext atx = new AnnotationConfigApplicationContext(AppConfig.class);
System.out.println(atx);
// xml載入容器方式,在這裡不做介紹。
// ApplicationContext app = new ClassPathXmlApplicationContext("application.xml");
}
}
看上面的測試類。想要觀察AnnotationConfigApplicationContext
容器的創建過程,實際上就是看這個對象怎麼被new出來的。通過以下代碼,我們查看他的構造函數。
// 最常用的構造函數
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
// 實例化了一個DefaultListableBeanFactory
this();
// AppConfig是沒有Component註解的。但它被掃描是必要的。
// 將AppConfig這個類註冊成一個BeanDefinition添加到Map中
// BeanDefinition是bean容器的一個重要的類,它是bean的定義。你可以先記住,不需要理解
register(componentClasses);
// refresh是SpringFramework中最重要的方法,沒有之一
refresh();
}
其中,
- this()方法是調用預設構造函數。
- register()方法是將bean註冊到容器中。
- refresh()方法是刷新容器,因為註冊的bean在這裡就需要手動刷新一下容器。
接下來,順著構造方法一起走,來看register的源碼:
// 註冊一個註解Bean
// 新註冊的註解Bean必須手動調用refresh方法,來刷新容器對Bean的處理。
@Override
public void register(Class<?>... componentClasses) {
...省略斷言語句
this.reader.register(componentClasses);
}
在構造方法中的register()方法原來是委派了reader的register方法。這個reader是什麼呢?reader是AnnotationConfigApplicationContext的一個屬性。從名字看它是註解bean定義讀取器
(同學們斷句一定要註意啊,把註解bean
當成一個整體名詞,說白了reader就是一個讀取器,用來讀取註解bean定義的讀取器)
public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
// 用來讀取註解的Bean定義讀取器
// 將該讀取器存放到容器中
private final AnnotatedBeanDefinitionReader reader;
// Bean定義的掃描器,掃描指定路勁下的註解Bean
private final ClassPathBeanDefinitionScanner scanner;
......
}
順著讀取器。來看一下上面提到的this.reader.register(componentClasses);
,
// 註冊多個{註解Bean定義}類
public void register(Class<?>... componentClasses) {
for (Class<?> componentClass : componentClasses) {
registerBean(componentClass);
}
}
// 註冊一個{註解Bean定義}類
public void registerBean(Class<?> beanClass) {
doRegisterBean(beanClass, null, null, null);
}
從源碼中可以看出,在register方法中調用了四個參數的doRegisterBean方法。 doRegisterBean:
<T> void doRegisterBean(Class<T> beanClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
// 註解生成bean定義
// 根據指定的註解類,創建一個數據結構——封裝註解bean定義的數據結構
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
return;
}
// 設置實例提供者
abd.setInstanceSupplier(instanceSupplier);
// 1. 解析bean作用域
// 獲取 Scope註解元數據 對象
// 解析bean的作用域
// prototype為原型模式,singleton為單例
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
// 設置註解bean定義的作用域
abd.setScope(scopeMetadata.getScopeName());
// 獲取beanName ,如果name沒有傳值就從beanNameGenerator生成一個
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
// 2. 處理通用註解
// 處理註解bean定義中的通用註解(如:@Lazy @Primary等)
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
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);
}
// 創建一個指定bean名稱的Bean定義對象。封裝 註解bean定義數據
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
// 3. 創建作用域的代理對象
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// 4. 通過BeanDefinitionReaderUtils向容器註冊bean
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
到此AnnotationConfigApplicationContext
這個容器註冊Bean的過程就完成了。
看過來看過來!!!!劃重點了
過多的源碼看起來眼花繚亂,看不懂就對了!沒關係,源碼的東西別想一遍能看懂,需要反覆的看
第一遍瞭解大概,認識基本的概念比如上下文-ApplicationContext
,註解Bean-定義AnnotatedBeanDefinition
,Bean定義-BeanDefinition
,Bean讀取器-AnnotatedBeanDefinitionReader
,Bean掃描器
。
第二遍鞏固概念,理解一下什麼叫做Bean定義-BeanDefinition
等這些概念。然後你就能一步一步的入門了。
3. IoC總結
還是重!點! 回頭再來看IoC容器,現在你可以說實現BeanFactory的都可以叫做IoC容器。(包括中間的Bean定義、存儲Bean的數據結構等等)
IoC只是Spring這座大山的一角而已,IoC的功能就是,讀取了Bean放在自己的容器中。
我們都知道Bean是一個簡單對象,現在把Bean理解成一個汽車輪子,裡面的屬性都是輪子的組件——輪胎、輪軸、輪盤等等。BeanDefinition
就是存儲輪子的數據結構的一種描述!
而Bean讀取器-AnnotatedBeanDefinitionReader
則是讀取這種描述的,相當於把造輪子的圖紙讀了一下,而AnnotatedBeanDefinitionReader
是applicationContext的一個屬性,也就是說applicationContext讀取了造輪子的圖紙,那麼applicationContext就可以造輪子了。
第二節applicationContext構造方法中的register()
方法就是讀取輪子圖紙的過程。refresh是刷新的作用。
經過下麵 三個方法後,IoC容器算是真的可以造輪子(製造Bean)了。註意是可以造,但還沒造呢! 但是它並沒有執行造輪子的請求。想要造輪子就需要依賴註入DI
或者getBean顯示調用了
。
- this
- register
- refresh
// 最常用的構造函數
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
// 實例化了一個DefaultListableBeanFactory
this();
register(componentClasses);
// refresh是SpringFramework中最重要的方法,沒有之一
refresh();
}
好了到此,IoC的簡單分析已經完畢。如有錯誤,請留言更正,謝謝!!