最近有時(shí)間讀了一下Spring的源碼柳沙,順便記錄一下筆記,加深理解妓柜。關(guān)于Spring IoC源碼分析箱季,網(wǎng)上有一篇文章寫得很詳細(xì),叫Spring IOC 容器源碼分析棍掐,大家可以去看看藏雏。這篇文章是基于XML的,因?yàn)楣ぷ髦谢谧⒔獾姆绞接玫枚嘁恍┳骰停跃妥x了一下Spring IoC注解版本的源代碼掘殴。為了更方便的分析源碼,有時(shí)候需要對(duì)代碼一步一步進(jìn)行調(diào)試最疆,所以我寫了一個(gè)簡(jiǎn)單的spring-ioc-analysis杯巨,放在github上蚤告,供大家參考努酸。
整個(gè)分析過(guò)程大概分為4篇(上,中杜恰,下和總結(jié)篇)获诈,因?yàn)闀r(shí)間和水平有限,筆記中如果有錯(cuò)誤之處心褐,歡迎大家指正舔涎。
核心概念
在分析Spring IoC源碼之前,需要弄清楚一些核心概念逗爹。
BeanDefinition
這個(gè)是一個(gè)接口亡嫌,它是用來(lái)存儲(chǔ)Bean定義的一些信息的,比如ClassName掘而,Scope挟冠,init-methon,等等袍睡。它的實(shí)現(xiàn)有RootBeanDefination知染,AnnotatedGenericBeanDefinition等。
BeanDefinitionHolder
這是BeanDefination的包裝類斑胜,用來(lái)存儲(chǔ)BeanDefinition控淡,name以及aliases等嫌吠。
BeanFactory & AbstractBeanFactory
BeanFactory就是Bean工廠啦,所有的Bean都由BeanFactory統(tǒng)一創(chuàng)建和管理掺炭,Spring提供了一個(gè)AbstractBeanFactory辫诅,它繼承了BeanFactory接口,并且實(shí)現(xiàn)了大部分BeanFactory的功能竹伸。
AbstractBeanFactory有一個(gè)非常重要的方法叫g(shù)etBean()
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return doGetBean(name, requiredType, null, false);
}
它是用來(lái)根據(jù)名字和類型獲取Bean對(duì)象的泥栖。它獲取對(duì)象的原理是,如果BeanFactory中存在Bean勋篓,則從緩存中取Bean吧享,否則創(chuàng)建并返回該Bean,并且將該Bean添加到緩存中(這里指Singleton類型的Bean)譬嚣。所以確切的講钢颂,Bean是在什么時(shí)候創(chuàng)建的,要看什么時(shí)候調(diào)用getBean方法獲取這個(gè)Bean拜银,根據(jù)Bean定義的不同殊鞭,getBean方法會(huì)在不同時(shí)候被調(diào)用。Spring中Bean大致可以分為兩類尼桶,應(yīng)用類Bean(被應(yīng)用程序使用)以及功能性Bean(被Spring使用)操灿。Spring提供了一些功能性接口如BeanFactoryPostProcessor和BeanPostProcessor,實(shí)現(xiàn)了這些接口的Bean可以成為功能性Bean泵督。如果Bean實(shí)現(xiàn)了BeanFactoryPostProcessor趾盐,則在refresh方法中調(diào)用invokeBeanFactoryPostProcessors方法時(shí)創(chuàng)建Bean,如果實(shí)現(xiàn)了BeanPostProcessor方法小腊,則在registerBeanPostProcessors方法中創(chuàng)建Bean救鲤。其它的Bean應(yīng)該屬于應(yīng)用類Bean,在默認(rèn)的finishBeanFactoryInitialization方法中創(chuàng)建Bean秩冈,這在后邊還會(huì)講到本缠。
AnnotationConfigApplicationContext & BeanFactory & BeanDefinitionRegistry & DefaultListableBeanFactory
這幾個(gè)類的關(guān)系也是很重要的,我們來(lái)捋一捋入问。
AnnotationConfigApplicationContext這個(gè)類是基于注解的容器類丹锹,它實(shí)現(xiàn)了BeanFactory和BeanDefinitionRegistry兩個(gè)接口,擁有Bean對(duì)象的管理和BeanDefinition注冊(cè)功能芬失。同時(shí)這個(gè)類擁有一個(gè)DefaultListableBeanFactory的對(duì)象楣黍。
DefaultListableBeanFactory也實(shí)現(xiàn)了BeanFactory和BeanDefinitionRegistry接口,擁有Bean對(duì)象管理和BeanDefinition注冊(cè)功能麸折。AnnotationConfigApplicationContext是委托DefaultListableBeanFactory來(lái)實(shí)現(xiàn)對(duì)象管理和BeanDefinition注冊(cè)的锡凝。這里使用的是代理模式。
介紹完了核心概念垢啼,我們來(lái)開(kāi)始分析源碼窜锯,剖析Spring 容器的加載過(guò)程张肾。Spring容器啟動(dòng)過(guò)程主要做了兩件事情:
- 加載BeanDefinition
- 創(chuàng)建Bean
我們先來(lái)看BeanDefinition的加載過(guò)程。
加載BeanDefinition
我們創(chuàng)建一個(gè)簡(jiǎn)單Maven工程锚扎,在main函數(shù)中輸入如下代碼吞瞪,就啟動(dòng)了一個(gè)基于注解的Spring容器,并從容器中獲取helloService對(duì)象驾孔。
public static void main(String[] args) {
AbstractApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
context.registerShutdownHook();
HelloService helloService = context.getBean(HelloService.class);
helloService.sayHello();
}
之所以要使用AbstractApplicationContext類芍秆,是因?yàn)槲覀兊腍elloService實(shí)現(xiàn)了一些BeanPostProcessor這樣的接口,需要調(diào)用AnnotationConfigApplicationContext對(duì)象的registerShutdownHook方法翠勉。
現(xiàn)在來(lái)分析源碼妖啥,進(jìn)入AnnotationConfigApplicationContext的構(gòu)造函數(shù):
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
this();
register(annotatedClasses);
refresh();
}
兩個(gè)主方法,resister和refresh对碌。
register
進(jìn)入register方法荆虱,它調(diào)用了reader.register方法。
public void register(Class<?>... annotatedClasses) {
Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
this.reader.register(annotatedClasses);
}
這個(gè)reader是AnnotatedBeanDefinitionReader類的一個(gè)實(shí)例朽们,在AnnotationConfigApplicationContext的構(gòu)造函數(shù)中創(chuàng)建的怀读。構(gòu)造函數(shù)還創(chuàng)建了一個(gè)scanner,用于包掃描骑脱。
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
進(jìn)入reader的register方法:
public void register(Class<?>... annotatedClasses) {
for (Class<?> annotatedClass : annotatedClasses) {
registerBean(annotatedClass);
}
}
public void registerBean(Class<?> annotatedClass) {
doRegisterBean(annotatedClass, null, null, null);
}
<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
// 創(chuàng)建BeanDefinition
AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
// 生成bean name
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
// ...省去了其它代碼
// 創(chuàng)建BeanDefinitionHolder菜枷,它是BeanDefiniton的一個(gè)包裝類,包含BeanDefiniton對(duì)象和它的名字
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
// 最后調(diào)用registerBeanDefinition
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}
限于篇幅叁丧,我們只分析一些比較重要的源代碼啤誊,其它的源代碼都被我刪除了。進(jìn)入BeanDefinitionReaderUtils.registerBeanDefinition
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
// 調(diào)用registry的registerBeanDefinition方法歹袁,registry是我們傳進(jìn)來(lái)的AnnotationConfigApplicationContext坷衍,因?yàn)樗鼘?shí)現(xiàn)了BeanDefinitionRegistry寝优,所以我們進(jìn)入AnnotationConfigApplicationContext的registerBeanDefinition方法条舔。
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
進(jìn)入AnnotationConfigApplicationContext的registerBeanDefinition方法。好奇怪乏矾,怎么沒(méi)有呢孟抗,原來(lái)在它的父類GenericApplicationContext中。來(lái)看代碼:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
// 調(diào)用beanFactory的registerBeanDefinition钻心。我們之前講過(guò)凄硼,AnnotationConfigApplicationContext是委托DefaultListableBeanFactory來(lái)注冊(cè)BeanDefinition和管理Bean的,所以調(diào)用了beanFactory的registerBeanDefinition
this.beanFactory.registerBeanDefinition(beanName, beanDefinition);
}
在GenericApplicationContext的構(gòu)造函數(shù)中捷沸,創(chuàng)建了這個(gè)beanFactory摊沉。
public GenericApplicationContext() {
// 前面也講過(guò),DefaultListableBeanFactory也實(shí)現(xiàn)了BeanDefinitionRegistry和BeanFactory接口的痒给。
this.beanFactory = new DefaultListableBeanFactory();
}
既然beanFactory是DefaultListableBeanFactory的實(shí)例说墨,所以進(jìn)入DefaultListableBeanFactory的registerBeanDefinition方法咯骏全。
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
//... 省去重復(fù)注冊(cè)代碼邏輯以及其它一些邏輯判斷,
//最終執(zhí)行以下代碼尼斧,可以看出姜贡,最終是將beanDefinition放到beanDefinitionMap中。所有的beanDefinition都會(huì)放到這個(gè)map中棺棵,以beanName為主鍵楼咳。如果一個(gè)beanDefinition有多個(gè)別名,那個(gè)它會(huì)被注冊(cè)多次烛恤。
this.beanDefinitionMap.put(beanName, beanDefinition);
}
好啦母怜,到這里為止,register方法就分析完成了缚柏。但是bean定義全部注冊(cè)完了嗎糙申,還沒(méi)有。這里只是注冊(cè)完了配置文件的bean definition船惨。至于我們自己注入的類柜裸,或者通過(guò)掃描注入的類,在refresh方法中粱锐,我們會(huì)在下一篇分析疙挺。
總結(jié)
我們今天講了Spring IoC容器源碼分析的第一部分,主要講了一些核心概念怜浅,以及BeanDefinition的加載過(guò)程铐然。
在Spring中,所有用戶定義的Bean都會(huì)以BeanDefinition的方式注冊(cè)到BeanFactory中恶座。BeanDefinition相當(dāng)于Bean的元數(shù)據(jù)搀暑,描述了Bean定義的一些屬性。
我們講了AnnotationConfigApplicationContext跨琳,BeanFactory自点,BeanDefinitionRegistry, DefaultListableBeanFactory脉让。AnnotationConfigApplicationContext實(shí)現(xiàn)了BeanFactory和BeanDefinitionRegistry接口桂敛,DefaultListableBeanFactory也實(shí)現(xiàn)了DefaultListableBeanFactory接口。而AnnotationConfigApplicationContext中BeanDefinition的注冊(cè)和Bean的管理溅潜,是委托給DefaultListableBeanFactory去完成的术唬。
另外我們也分析了BeanDefinition的register過(guò)程,也確認(rèn)了AnnotationConfigApplicationContext注冊(cè)BeanDefinition確實(shí)是委托給DefaultListableBeanFactory來(lái)完成的滚澜。最終BeanDefinition被存儲(chǔ)在一個(gè)Map中粗仓,以beanName為key,以BeanDefinition對(duì)象為值。
我們下一篇會(huì)繼續(xù)講refresh方法借浊。