日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問(wèn)題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
我竟然寫了三萬(wàn)字解析@Configuration注解

?一、學(xué)習(xí)指引

關(guān)于@Configuration注解,不能只停留在表面!

翻開Spring中@Configuration注解的源碼,在源碼上赫然標(biāo)注了Since: 3.0的字樣,也就是@Configuration注解是從Spring 3.0開始提供的注解。

大部讀者都知道@Configuration注解可以標(biāo)注到類上,當(dāng)標(biāo)注到類上時(shí),啟動(dòng)Spring就會(huì)自動(dòng)掃描@Configuration注解標(biāo)注的類,將其注冊(cè)到IOC容器中,并被實(shí)例化成Bean對(duì)象。

如果被@Configuration注解標(biāo)注的類中存在使用@Bean注解標(biāo)注的創(chuàng)建某個(gè)類對(duì)象的方法,那么,Spring也會(huì)自動(dòng)執(zhí)行使用@Bean注解標(biāo)注的方法,將對(duì)應(yīng)的Bean定義信息注冊(cè)到IOC容器,并進(jìn)行實(shí)例化。

如果你只想做CRUD操作,或者你只想做一名默默無(wú)聞的代碼工,關(guān)于@Configuration注解,你了解到這一步就可以了,因?yàn)樽鯟RUD不需要你對(duì)@Configuration注解了解的多么深入。

但是,如果你是一個(gè)不甘于做CRUD操作,想突破自己的瓶頸,想成為一名合格的架構(gòu)師或技術(shù)專家,那你只了解這些是遠(yuǎn)遠(yuǎn)不夠的,你必須對(duì)@Configuration注解有更進(jìn)一步的認(rèn)識(shí)。

二、注解說(shuō)明?

@Configuration注解的一點(diǎn)點(diǎn)說(shuō)明

@Configuration注解是從Spring 3.0版本開始加入的一個(gè)使Spring能夠支持注解驅(qū)動(dòng)開發(fā)的標(biāo)注型注解,主要用于標(biāo)注在類上。當(dāng)某個(gè)類標(biāo)注了@Configuration注解時(shí),表示這個(gè)類是Spring的一個(gè)配置類。@Configuration注解能夠替代Spring的applicationContext.xml文件,并且被@Configuration注解標(biāo)注的類,能夠自動(dòng)注冊(cè)到IOC容器并進(jìn)行實(shí)例化。

2.1 注解源碼

源碼詳見(jiàn):org.springframework.context.annotation.Configuration。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(annotation = Component.class)
String value() default "";
//Since: 5.2
boolean proxyBeanMethods() default true;
//Since: 6.0
boolean enforceUniqueMethods() default true;
}

@Configuration注解中每個(gè)屬性的含義如下所示。

value:存入到Spring IOC容器中的Bean的id。

proxyBeanMethods:從Spring 5.2版本開始加入到@Configuration注解,表示被@Configuration注解標(biāo)注的配置類是否會(huì)被代理,并且在配置類中使用@Bean注解生成的Bean對(duì)象在IOC容器中是否是單例對(duì)象,取值為true或者false。當(dāng)取值為true時(shí),表示full(全局)模式,此模式下被@Configuration注解標(biāo)注的配置類會(huì)被代理,在配置類中使用@Bean注解注入到IOC容器中的Bean對(duì)象是單例模式,無(wú)論調(diào)用多少次被@Bean注解標(biāo)注的方法,返回的都是同一個(gè)Bean對(duì)象。當(dāng)取值為false時(shí),表示lite(輕量級(jí))模式,此模式下被@Configuration注解標(biāo)注的配置類不會(huì)被代理,在配置類中使用@Bean注解注入到IOC容器中的Bean對(duì)象不是單例模式,每次調(diào)用被@Bean注解標(biāo)注的方法時(shí),都會(huì)返回一個(gè)新的Bean對(duì)象。默認(rèn)的取值為true。

enforceUniqueMethods:從Spring 6.0開始加入到@Configuration注解,指定使用@Bean注解標(biāo)注的方法是否需要具有唯一的方法名稱,取值為true或者false。當(dāng)取值為true時(shí),表示使用@Bean注解標(biāo)注的方法具有唯一的方法名稱,并且這些方法名稱不會(huì)重疊。當(dāng)取值為false時(shí),表示使用@Bean注解標(biāo)注的方法名稱不唯一,存在被重疊的風(fēng)險(xiǎn)。默認(rèn)取值為true。

從@Configuration注解的源碼也可以看出,@Configuration注解本質(zhì)上是一個(gè)@Component注解,所以,被@Configuration注解標(biāo)注的配置類本身也會(huì)被注冊(cè)到IOC容器中。同時(shí),@Configuration注解也會(huì)被@ComponentScan注解掃描到。

2.2 注解使用場(chǎng)景

基于Spring的注解開發(fā)應(yīng)用程序時(shí),可以將@Configuration注解標(biāo)注到某個(gè)類上。當(dāng)某個(gè)類被@Configuration注解標(biāo)注時(shí),說(shuō)明這個(gè)類是配置類,可以在這個(gè)類中使用@Bean注解向IOC容器中注入Bean對(duì)象,也可以使用@Autowired、@Inject和@Resource等注解來(lái)注入所需的Bean對(duì)象。

注意:基于Spring的注解模式開發(fā)應(yīng)用程序時(shí),在使用AnnotationConfigApplicationContext類創(chuàng)建IOC容器時(shí),需要注意如下事項(xiàng):

(1)如果調(diào)用的是AnnotationConfigApplicationContext類中傳入Class類型可變參數(shù)的構(gòu)造方法來(lái)創(chuàng)建IOC容器,表示傳入使用@Configuration注解標(biāo)注的配置類的Class對(duì)象來(lái)創(chuàng)建IOC容器,則標(biāo)注到配置類上的@Configuration注解可以省略。

AnnotationConfigApplicationContext類中傳入Class類型可變參數(shù)的構(gòu)造方法源碼如下所示。

public AnnotationConfigApplicationContext(Class... componentClasses) {
this();
register(componentClasses);
refresh();
}

由圖1-2可以看出,Spring IOC容器啟動(dòng)時(shí),向IOC容器中注冊(cè)ConfigurationAnnotationConfig類的Bean定義信息時(shí),會(huì)涉及到AnnotationConfigApplicationContext類、AnnotatedBeanDefinitionReader類、BeanDefinitionReaderUtils類和DefaultListableBeanFactory類的方法調(diào)用,具體的源碼調(diào)用細(xì)節(jié)見(jiàn)源碼解析部分。

(2)如果調(diào)用的是AnnotationConfigApplicationContext類中傳入String類型可變參數(shù)的構(gòu)造方法來(lái)創(chuàng)建IOC容器,表示傳入應(yīng)用程序的包名來(lái)創(chuàng)建IOC容器,則標(biāo)注到配置類上的@Configuration注解不能省略。

AnnotationConfigApplicationContext類中傳入String類型可變參數(shù)的構(gòu)造方法源碼如下所示。

public AnnotationConfigApplicationContext(String... basePackages) {
this();
scan(basePackages);
refresh();
}

另外,當(dāng)調(diào)用的是AnnotationConfigApplicationContext類中傳入Class類型可變參數(shù)的構(gòu)造方法來(lái)創(chuàng)建IOC容器時(shí),如果傳入的配置類上省略了@Configuration注解,則每次調(diào)用配置類中被@Bean注解標(biāo)注的方法時(shí),都會(huì)返回不同的Bean實(shí)例對(duì)象。

三、使用案例?

不給案例學(xué)起來(lái)挺枯燥的。

本節(jié),簡(jiǎn)單介紹使用@Configuration注解的幾個(gè)案例程序。

3.1  驗(yàn)證proxyBeanMethods屬性的作用

在2.1節(jié)已經(jīng)詳細(xì)介紹過(guò)@Configuration注解中proxyBeanMethods屬性的作用,proxyBeanMethods屬性可取值為true或者false。取值為true時(shí),無(wú)論調(diào)用多少次在被@Configuration注解標(biāo)注的類中被@Bean注解標(biāo)注的方法,返回的都是同一個(gè)Bean對(duì)象。取值為false時(shí),每次調(diào)用在被@Configuration注解標(biāo)注的類中被@Bean注解標(biāo)注的方法,都回返回不同的Bean對(duì)象。

3.1.1 驗(yàn)證proxyBeanMethods取值為true的情況

具體的案例實(shí)現(xiàn)步驟如下所示。

(1)創(chuàng)建Person類

Person類主要是用來(lái)注冊(cè)到IOC容器中,并實(shí)例化對(duì)象。

源碼詳見(jiàn):spring-annotation-chapter-01工程下的io.binghe.spring.annotation.chapter01.configuration.bean.Person,如下所示。

public class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

(2)創(chuàng)建ConfigurationAnnotationConfig類

ConfigurationAnnotationConfig類的作用就是充當(dāng)程序啟動(dòng)的配置類,會(huì)在ConfigurationAnnotationConfig類上標(biāo)注@Configuration注解,說(shuō)明ConfigurationAnnotationConfig類是Spring啟動(dòng)時(shí)的配置類。

源碼詳見(jiàn):spring-annotation-chapter-01工程下的io.binghe.spring.annotation.chapter01.configuration.config.ConfigurationAnnotationConfig,如下所示。

@Configuration
public class ConfigurationAnnotationConfig {
@Bean
public Person person(){
return new Person();
}
}

可以看到,在ConfigurationAnnotationConfig類上標(biāo)注了@Configuration注解,由于@Configuration注解中的proxyBeanMethods屬性默認(rèn)為true,所以在ConfigurationAnnotationConfig類上的@Configuration注解省略了proxyBeanMethods屬性。

(3)創(chuàng)建ConfigurationAnnotationTest類

ConfigurationAnnotationTest類的作用就是整個(gè)案例程序的啟動(dòng)類,對(duì)整個(gè)案例程序進(jìn)行測(cè)試。

源碼詳見(jiàn):spring-annotation-chapter-01工程下的io.binghe.spring.annotation.chapter01.configuration.ConfigurationAnnotationTest,如下所示。

public class ConfigurationAnnotationTest {

private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationAnnotationTest.class);

public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigurationAnnotationConfig.class);
ConfigurationAnnotationConfig config = context.getBean(ConfigurationAnnotationConfig.class);
Person person1 = config.person();
Person person2 = config.person();
LOGGER.info("person1 == person2 ===>> {}", (person1 == person2));
}
}

可以看到,在ConfigurationAnnotationTest類的main()方法中,首先基于AnnotationConfigApplicationContext常見(jiàn)了IOC容器context,從context中獲取了ConfigurationAnnotationConfig類的Bean實(shí)例對(duì)象config,接下來(lái),調(diào)用兩次config的person()方法分別賦值給Person類型的局部變量person1和person2,最后打印person1是否等于person2的日志。

(4)測(cè)試案例

運(yùn)行ConfigurationAnnotationTest類的main()方法,輸出的結(jié)果信息如下所示。

person1 是否等于 person2 ===>> true

通過(guò)輸出的結(jié)果信息可以看出,person1是否等于person2輸出的結(jié)果為true。說(shuō)明當(dāng)@Configuration注解中的proxyBeanMethods屬性為true時(shí),每次調(diào)用使用@Configuration注解標(biāo)注的類中被@Bean注解標(biāo)注的方法時(shí),都會(huì)返回同一個(gè)Bean實(shí)例對(duì)象。

3.1.2 驗(yàn)證proxyBeanMethods取值為false的情況

驗(yàn)證@Configuration注解中的proxyBeanMethods屬性為false的情況,與驗(yàn)證proxyBeanMethods屬性為true的情況的案例程序基本一致,只是將ConfigurationAnnotationConfig類上標(biāo)注的@Configuration注解的proxyBeanMethods屬性設(shè)置為false,案例實(shí)現(xiàn)的具體步驟如下所示。

(1)修改proxyBeanMethods屬性的值

修改后的ConfigurationAnnotationConfig類的源碼如下所示。

@Configuration(proxyBeanMethods = false)
public class ConfigurationAnnotationConfig {
@Bean
public Person person(){
return new Person();
}
}

可以看到,此時(shí)在ConfigurationAnnotationConfig類上標(biāo)注的@Configuration注解的proxyBeanMethods屬性為false。

(2)測(cè)試案例

運(yùn)行ConfigurationAnnotationTest類的main()方法,輸出的結(jié)果信息如下所示。

person1 是否等于 person2 ===>> false

從輸出的結(jié)果信息可以看出,person1是否等于person2輸出的結(jié)果為false。說(shuō)明當(dāng)@Configuration注解中的proxyBeanMethods屬性為false時(shí),每次調(diào)用使用@Configuration注解標(biāo)注的類中被@Bean注解標(biāo)注的方法時(shí),都會(huì)返回不同的Bean實(shí)例對(duì)象。

3.2 傳入配置類創(chuàng)建IOC容器

調(diào)用AnnotationConfigApplicationContext類的構(gòu)造方法傳入配置類的Class對(duì)象創(chuàng)建IOC容器時(shí),可以省略配置類上的@Configuration注解,案例的具體實(shí)現(xiàn)步驟如下所示。

(1)刪除@Configuration注解

刪除ConfigurationAnnotationConfig類上的@Configuration注解,源碼如下所示。

public class ConfigurationAnnotationConfig {
@Bean
public Person person(){
return new Person();
}
}

(2)測(cè)試案例

運(yùn)行ConfigurationAnnotationTest類的main()方法,輸出的結(jié)果信息如下所示。

person1 是否等于 person2 ===>> false

從輸出的結(jié)果信息可以看到,輸出了person1是否等于person2的結(jié)果為false。說(shuō)明調(diào)用AnnotationConfigApplicationContext類的構(gòu)造方法傳入配置類的Class對(duì)象創(chuàng)建IOC容器時(shí),可以省略配置類上的@Configuration注解,此時(shí)每次調(diào)用配置類中被@Bean注解標(biāo)注的方法時(shí),都會(huì)返回不同的Bean實(shí)例對(duì)象。

3.3 傳入包名創(chuàng)建IOC容器

調(diào)用AnnotationConfigApplicationContext類的構(gòu)造方法傳入包名創(chuàng)建IOC容器時(shí),不能省略配置類上的@Configuration注解,案例的具體實(shí)現(xiàn)步驟如下所示。

(1)修改測(cè)試類

修改ConfigurationAnnotationTest類的main()方法中,創(chuàng)建AnnotationConfigApplicationContext對(duì)象的代碼,將調(diào)用傳入Class對(duì)象的構(gòu)造方法修改為調(diào)用傳入String對(duì)象的方法,修改后的代碼如下所示。

public class ConfigurationAnnotationTest {

private static final Logger LOGGER = LoggerFactory.getLogger(ConfigurationAnnotationTest.class);

public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext("io.binghe.spring.annotation.chapter01.configuration");
ConfigurationAnnotationConfig config = context.getBean(ConfigurationAnnotationConfig.class);
Person person1 = config.person();
Person person2 = config.person();
LOGGER.info("person1 是否等于 person2 ===>> {}", (person1 == person2));
}
}

(2)刪除@Configuration注解

刪除ConfigurationAnnotationConfig類上的@Configuration注解,源碼如下所示。

public class ConfigurationAnnotationConfig {
@Bean
public Person person(){
return new Person();
}
}

(3)測(cè)試案例

運(yùn)行ConfigurationAnnotationTest類的main()方法,可以看到程序拋出了異常信息,如下所示。

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'io.binghe.spring.annotation.chapter01.configuration.config.ConfigurationAnnotationConfig' available

從輸出的結(jié)果信息可以看出,調(diào)用AnnotationConfigApplicationContext類的構(gòu)造方法傳入包名創(chuàng)建IOC容器時(shí),不能省略配置類上的@Configuration注解,否則會(huì)拋出NoSuchBeanDefinitionException。

(4)添加@Configuration注解

在ConfigurationAnnotationConfig類上添加@Configuration注解,源碼如下所示。

@Configuration
public class ConfigurationAnnotationConfig {
@Bean
public Person person(){
return new Person();
}
}

(5)再次測(cè)試案例

再次運(yùn)行ConfigurationAnnotationTest類的main()方法,輸出的結(jié)果信息如下所示。

person1 是否等于 person2 ===>> true

從輸出的結(jié)果信息可以看到,輸出了person1是否等于person2的結(jié)果為true,再次說(shuō)明調(diào)用AnnotationConfigApplicationContext類的構(gòu)造方法傳入包名創(chuàng)建IOC容器時(shí),不能省略配置類上的@Configuration注解。

四、源碼時(shí)序圖?

根據(jù)源碼執(zhí)行的流程圖分析源碼思路會(huì)更加清晰!

就@Configuration注解本身而言,在源碼層面的執(zhí)行流程涉及到注冊(cè)與實(shí)例化兩種執(zhí)行流程,就注冊(cè)流程而言,會(huì)涉及到Spring內(nèi)部的ConfigurationClassPostProcessor類的Bean定義信息的注冊(cè)流程,以及案例中標(biāo)注了@Configuration注解的ConfigurationAnnotationConfig配置類的Bean定義信息注冊(cè)流程。

本節(jié),就簡(jiǎn)單介紹下@Configuration注解在源碼層面的注冊(cè)與實(shí)例化兩種執(zhí)行時(shí)序圖。

注意:本章的源碼時(shí)序圖和源碼解析均以本章案例程序作為入口進(jìn)行分析,并且會(huì)在ConfigurationAnnotationConfig類上標(biāo)注@Configuration注解,同時(shí)在ConfigurationAnnotationTest測(cè)試類中,調(diào)用AnnotationConfigApplicationContext類的AnnotationConfigApplicationContext(Class... componentClasses)構(gòu)造方法來(lái)創(chuàng)建IOC容器。

4.1 注冊(cè)ConfigurationClassPostProcessor流程源碼時(shí)序圖

ConfigurationClassPostProcessor后置處理器是解析@Configuration注解的核心類,也是Spring中的一個(gè)非常重要的后置處理器類, Spring IOC容器啟動(dòng)時(shí),會(huì)向IOC容器中注冊(cè)ConfigurationClassPostProcessor類的Bean定義信息。向IOC容器中注冊(cè)ConfigurationClassPostProcessor類的Bean定義信息的時(shí)序圖如圖1-1所示。

圖1-1

由圖1-1可以看出,Spring IOC容器啟動(dòng)時(shí),向IOC容器中注冊(cè)ConfigurationClassPostProcessor類的Bean定義信息時(shí),會(huì)涉及到AnnotationConfigApplicationContext類、AnnotatedBeanDefinitionReader類和AnnotationConfigUtils類中方法的調(diào)用。具體源碼的調(diào)用細(xì)節(jié)見(jiàn)源碼解析部分。

4.2 注冊(cè)ConfigurationAnnotationConfig流程源碼時(shí)序圖

ConfigurationAnnotationConfig類是本章中案例程序的配置類,在ConfigurationAnnotationConfig類上標(biāo)注了@Configuration注解,當(dāng)Spring IOC容器啟動(dòng)時(shí),也會(huì)將ConfigurationAnnotationConfig類的Bean定義信息注冊(cè)到Spring IOC容器中,向Spring IOC容器中注冊(cè)ConfigurationAnnotationConfig類的Bean定義信息的時(shí)序圖如圖1-2所示。

圖1-2

由圖1-2可以看出,Spring IOC容器啟動(dòng)時(shí),向IOC容器中注冊(cè)ConfigurationAnnotationConfig類的Bean定義信息時(shí),會(huì)涉及到AnnotationConfigApplicationContext類、AnnotatedBeanDefinitionReader類、BeanDefinitionReaderUtils類和DefaultListableBeanFactory類的方法調(diào)用,具體的源碼調(diào)用細(xì)節(jié)見(jiàn)源碼解析部分。

注意:Spring IOC容器在啟動(dòng)時(shí),會(huì)向IOC容器中注冊(cè)ConfigurationClassPostProcessor類的bean定義信息和使用@Configuration注解標(biāo)注的ConfigurationAnnotationConfig配置類的Bean定義信息。當(dāng)Spring IOC容器在刷新時(shí),會(huì)遞歸處理所有使用@Configuration注解標(biāo)注的類,解析@Bean等注解標(biāo)注的方法,解析成一個(gè)個(gè)ConfigurationClassBeanDefinition類型的BeanDefinition對(duì)象,注冊(cè)到IOC容器中。Spring IOC容器刷新時(shí),解析@Bean等注解的時(shí)序圖和源碼執(zhí)行流程會(huì)在后續(xù)章節(jié)介紹@Bean等注解時(shí),詳細(xì)介紹,這里不再贅述。

4.3 實(shí)例化流程源碼時(shí)序圖

Spring IOC容器在啟動(dòng)過(guò)程中,最終會(huì)調(diào)用AnnotationConfigApplicationContext類的refresh()方法刷新IOC容器,刷新IOC容器的過(guò)程中就會(huì)對(duì)標(biāo)注了@Configuration注解的配置類進(jìn)行實(shí)例化。本節(jié),就結(jié)合案例程序簡(jiǎn)單分析下刷新IOC容器時(shí),對(duì)標(biāo)注了@Configuration注解的配置類進(jìn)行實(shí)例化的源碼時(shí)序圖,源碼時(shí)序圖如圖1-3-1和1-3-2所示。

圖1-3-1

圖1-3-2

由圖1-3-1和圖1-3-2可以看出,刷新IOC容器時(shí),對(duì)標(biāo)注了@Configuration注解的配置類進(jìn)行實(shí)例化時(shí),會(huì)涉及到AnnotationConfigApplicationContext類、AbstractApplicationContext類、PostProcessorRegistrationDelegate類、ConfigurationClassPostProcessor類和ConfigurationClassEnhancer類方法的調(diào)用,具體方法調(diào)用的細(xì)節(jié)見(jiàn)源碼解析部分。

五、源碼解析?

重點(diǎn)來(lái)了,源碼解析,跟上節(jié)奏,別走神!

本節(jié),同樣按照注冊(cè)流程和實(shí)例化流程來(lái)深入分析@Configuration注解在Spring源碼層面的執(zhí)行流程。

5.1 注冊(cè)ConfigurationClassPostProcessor流程源碼解析

@Configuration注解涉及到ConfigurationClassPostProcessor類的Bean定義信息的注冊(cè)流程的源碼執(zhí)行過(guò)程可結(jié)合圖1-1進(jìn)行分析。啟動(dòng)Spring IOC容器時(shí),@Configuration注解涉及到的ConfigurationClassPostProcessor核心類的注冊(cè)流程的源碼執(zhí)行過(guò)程如下所示。

(1)運(yùn)行案例程序啟動(dòng)類ConfigurationAnnotationTest的main()方法

源碼詳見(jiàn):io.binghe.spring.annotation.chapter01.configuration.ConfigurationAnnotationTest#main()。

public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConfigurationAnnotationConfig.class);
//#############省略其他代碼##################
}

可以看到,在main()方法中會(huì)調(diào)用AnnotationConfigApplicationContext類的構(gòu)造方法傳入配置類ConfigurationAnnotationConfig的Class對(duì)象來(lái)創(chuàng)建IOC容器。接下來(lái),會(huì)進(jìn)入AnnotationConfigApplicationContext類的構(gòu)造方法。

(2)解析AnnotationConfigApplicationContext類的AnnotationConfigApplicationContext(Class... componentClasses)構(gòu)造方法

源碼詳見(jiàn):org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class... componentClasses)。

public AnnotationConfigApplicationContext(Class... componentClasses) {
this();
register(componentClasses);
refresh();
}

可以看到,在上述構(gòu)造方法中,會(huì)通過(guò)this()調(diào)用AnnotationConfigApplicationContext類的無(wú)參構(gòu)造方法。

(3)解析AnnotationConfigApplicationContext類的AnnotationConfigApplicationContext()無(wú)參構(gòu)造方法

源碼詳見(jiàn):org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext()。

public AnnotationConfigApplicationContext() {
StartupStep createAnnotatedBeanDefReader = this.getApplicationStartup().start("spring.context.annotated-bean-reader.create");
this.reader = new AnnotatedBeanDefinitionReader(this);
createAnnotatedBeanDefReader.end();
this.scanner = new ClassPathBeanDefinitionScanner(this);
}

可以看到,在AnnotationConfigApplicationContext類的無(wú)參構(gòu)造方法中,主要的邏輯就是實(shí)例化了AnnotatedBeanDefinitionReader類型的reader成員變量和ClassPathBeanDefinitionScanner類型的scanner成員變量。

reader:表示注解類型的Bean定義信息讀取器,主要就是讀取通過(guò)注解方式進(jìn)行實(shí)例化的Bean的定義信息。

scanner:表示類路徑下的Bean定義掃描器,主要就是掃描類路徑下的Bean定義信息。

@Configuration注解涉及到的注冊(cè)流程源碼的執(zhí)行過(guò)程,會(huì)執(zhí)行實(shí)例化reader成員變量的代碼,也就是下面的代碼片段。

this.reader = new AnnotatedBeanDefinitionReader(this);

接下來(lái),會(huì)調(diào)用AnnotatedBeanDefinitionReader類中的AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry)構(gòu)造方法。

(4)解析AnnotatedBeanDefinitionReader類中的AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry)構(gòu)造方法

源碼詳見(jiàn):org.springframework.context.annotation.AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry)。

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
this(registry, getOrCreateEnvironment(registry));
}

可以看到,在上述構(gòu)造方法中,通過(guò)this調(diào)用了AnnotatedBeanDefinitionReader類的AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment)構(gòu)造方法。

(5)解析AnnotatedBeanDefinitionReader類的AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment)構(gòu)造方法

源碼詳見(jiàn):org.springframework.context.annotation.AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment)。

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);
}

可以看到,在上述構(gòu)造方法中,最核心的邏輯就是調(diào)用了AnnotationConfigUtils工具類的registerAnnotationConfigProcessors()方法,將BeanDefinitionRegistry類型的registry對(duì)象傳入方法中。其中,registry對(duì)象本質(zhì)上就是一個(gè)AnnotationConfigApplicationContext類對(duì)象的實(shí)例,這是因?yàn)锳nnotationConfigApplicationContext類繼承了GenericApplicationContext類,而GenericApplicationContext類實(shí)現(xiàn)了BeanDefinitionRegistry接口。

(6)解析AnnotationConfigUtils類的registerAnnotationConfigProcessors(BeanDefinitionRegistry registry)方法

源碼詳見(jiàn):org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry registry)。

public static void registerAnnotationConfigProcessors(BeanDefinitionRegistry registry) {
registerAnnotationConfigProcessors(registry, null);
}

可以看到,在AnnotationConfigUtils類的registerAnnotationConfigProcessors(BeanDefinitionRegistry registry)方法中調(diào)用了AnnotationConfigUtils類中的另外一個(gè)registerAnnotationConfigProcessors()方法。

(7)解析AnnotationConfigUtils類的registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, Object source)方法

源碼詳見(jiàn):org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, Object source)。

這里,只給出在AnnotationConfigUtils類的registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, Object source)方法中,將@Configuration注解涉及到的ConfigurationClassPostProcessor類的Bean定義信息注冊(cè)到IOC容器中的核心代碼,如下所示。

public static Set registerAnnotationConfigProcessors(
BeanDefinitionRegistry registry, @Nullable Object source) {
//################省略其他代碼########################
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));
}
//################省略其他代碼########################
}

可以看到,會(huì)調(diào)用registerPostProcessor()方法注冊(cè)后置處理器。

(8)解析registerPostProcessor(BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName)方法

源碼詳見(jiàn):org.springframework.context.annotation.AnnotationConfigUtils#registerPostProcessor(BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName)。

private static BeanDefinitionHolder registerPostProcessor(
BeanDefinitionRegistry registry, RootBeanDefinition definition, String beanName) {
definition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(beanName, definition);
return new BeanDefinitionHolder(definition, beanName);
}

可以看到,上述代碼中,調(diào)用了registry參數(shù)的registerBeanDefinition()方法來(lái)注冊(cè)ConfigurationClassPostProcessor類的Bean定義信息,definition參數(shù)本質(zhì)上就是一個(gè)AnnotationConfigApplicationContext類的實(shí)例對(duì)象。最終會(huì)調(diào)用DefaultListableBeanFactory類的registerBeanDefinition()方法來(lái)注冊(cè)ConfigurationClassPostProcessor類的Bean定義信息。

(9)解析DefaultListableBeanFactory類的registerBeanDefinition(String beanName, BeanDefinition beanDefinition)方法

源碼詳見(jiàn):org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition(String beanName, BeanDefinition beanDefinition)。

@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
//##################省略其他代碼###############
this.beanDefinitionMap.put(beanName, beanDefinition);
//##################省略其他代碼###############
}

通過(guò)上述代碼可知,向Spring的IOC容器中注冊(cè)類的Bean定義信息,其實(shí)就是向beanDefinitionMap對(duì)象中添加元素,beanDefinitionMap對(duì)象本質(zhì)上是一個(gè)ConcurrentHashMap對(duì)象。向beanDefinitionMap對(duì)象中添加的元素的Key為Bean的名稱,Value為Bean的定義信息。

beanDefinitionMap源碼詳見(jiàn):org.springframework.beans.factory.support.DefaultListableBeanFactory#beanDefinitionMap。

private final Map beanDefinitionMap = new ConcurrentHashMap<>(256);

至此,@Configuration注解涉及到的ConfigurationClassPostProcessor類的注冊(cè)過(guò)程分析完畢。

5.2 注冊(cè)ConfigurationAnnotationConfig流程源碼解析

使用@Configuration注解標(biāo)注的ConfigurationAnnotationConfig類的Bean定義信息的注冊(cè)流程的源碼執(zhí)行過(guò)程可結(jié)合圖1-2進(jìn)行分析,啟動(dòng)Spring IOC容器時(shí),向IOC容器中注冊(cè)ConfigurationAnnotationConfig類的Bean定義信息的源碼執(zhí)行過(guò)程如下所示。

(1)運(yùn)行案例程序啟動(dòng)類ConfigurationAnnotationTest的main()方法,并進(jìn)入AnnotationConfigApplicationContext類的AnnotationConfigApplicationContext(Class... componentClasses)構(gòu)造方法。

源碼詳見(jiàn):org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class... componentClasses)。

public AnnotationConfigApplicationContext(Class... componentClasses) {
this();
register(componentClasses);
refresh();
}

可以看到,在AnnotationConfigApplicationContext(Class... componentClasses)方法中調(diào)用了register()方法,傳入componentClasses參數(shù)進(jìn)行注冊(cè)。

(2)解析AnnotationConfigApplicationContext類的register(Class... componentClasses)方法

源碼詳見(jiàn):org.springframework.context.annotation.AnnotationConfigApplicationContext#register(Class... componentClasses)。

@Override
public void register(Class... componentClasses) {
//###########省略其他代碼##############
this.reader.register(componentClasses);
//###########省略其他代碼##############
}

可以看到,在register(Class... componentClasses)方法中調(diào)用了reader的register()方法。

(3)解析AnnotatedBeanDefinitionReader類的register(Class... componentClasses)方法

源碼詳見(jiàn):org.springframework.context.annotation.AnnotatedBeanDefinitionReader#register(Class... componentClasses)。

public void register(Class... componentClasses) {
for (Class componentClass : componentClasses) {
registerBean(componentClass);
}
}

可以看到,在register(Class... componentClasses)方法中,會(huì)循環(huán)遍歷傳入的可變參數(shù)componentClasses,每次循環(huán)時(shí),都會(huì)調(diào)用registerBean()方法。

(4)解析AnnotatedBeanDefinitionReader類的registerBean(Class beanClass)方法

源碼詳見(jiàn):org.springframework.context.annotation.AnnotatedBeanDefinitionReader#registerBean(Class beanClass)。

public void registerBean(Class beanClass) {
doRegisterBean(beanClass, null, null, null, null);
}

可以看到,在registerBean(Class beanClass)方法中調(diào)用了doRegisterBean()方法。

(5)解析AnnotatedBeanDefinitionReader類的doRegisterBean(ClassbeanClass, String name, Class[] qualifiers, Suppliersupplier, BeanDefinitionCustomizer[] customizers)方法。

源碼詳見(jiàn):org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean(ClassbeanClass, String name, Class[] qualifiers, Suppliersupplier, BeanDefinitionCustomizer[] customizers)。

private  void doRegisterBean(Class beanClass, @Nullable String name,@Nullable Class[] qualifiers, @Nullable Supplier supplier, @Nullable BeanDefinitionCustomizer[] customizers) {

AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
//###########################省略其他代碼#############################
String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
//###########################省略其他代碼#############################
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

可以看到,在doRegisterBean(ClassbeanClass, String name, Class[] qualifiers, Suppliersupplier, BeanDefinitionCustomizer[] customizers)方法中調(diào)用了BeanDefinitionReaderUtils類的registerBeanDefinition()方法。

(6)解析BeanDefinitionReaderUtils類的registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法

源碼詳見(jiàn):org.springframework.beans.factory.support.BeanDefinitionReaderUtils#registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)。

public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {

// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
//###########################省略其他代碼#############################
}

可以看到,在registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)方法中通過(guò)調(diào)用registry的registerBeanDefinition()方法來(lái)向IOC容器中注冊(cè)Bean定義信息。

注意:到目前為止,后續(xù)向IOC容器注冊(cè)Bean定義信息的源碼執(zhí)行流程與向IOC容器中注冊(cè)ConfigurationClassPostProcessor類的Bean定義信息的源碼執(zhí)行流程基本相同,這里不再贅述。

5.3 實(shí)例化流程源碼解析

Spring IOC容器在刷新時(shí),會(huì)實(shí)例化使用@Configuration注解標(biāo)注的類,可結(jié)合圖1-3-1和圖1-3-2理解,具體的源碼執(zhí)行流程如下所示。

(1)運(yùn)行案例程序啟動(dòng)類ConfigurationAnnotationTest的main()方法,并進(jìn)入AnnotationConfigApplicationContext類的AnnotationConfigApplicationContext(Class... componentClasses)構(gòu)造方法。

源碼詳見(jiàn):org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext(Class... componentClasses)。

public AnnotationConfigApplicationContext(Class... componentClasses) {
this();
register(componentClasses);
refresh();
}

可以看到,在AnnotationConfigApplicationContext(Class... componentClasses)構(gòu)造方法中會(huì)調(diào)用refresh()方法刷新IOC容器。

(2)解析AbstractApplicationContext類的refresh()方法

源碼詳見(jiàn):org.springframework.context.support.AbstractApplicationContext#refresh()。

@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//#############省略其他代碼#####################
try {
//#############省略其他代碼#####################
invokeBeanFactoryPostProcessors(beanFactory);
//#############省略其他代碼#####################
}
catch (BeansException ex) {
//#############省略其他代碼#####################
}
finally {
//#############省略其他代碼#####################
}
}
}

可以看到,在refresh()方法中調(diào)用了invokeBeanFactoryPostProcessors()方法。

(3)解析AbstractApplicationContext類的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)方法

源碼詳見(jiàn):org.springframework.context.support.AbstractApplicationContext#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)。

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
//################省略其他代碼####################
}

可以看到,在invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory)方法中調(diào)用了PostProcessorRegistrationDelegate類的invokeBeanFactoryPostProcessors()方法。

(4)解析PostProcessorRegistrationDelegate類的invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors)方法

源碼詳見(jiàn):org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, ListbeanFactoryPostProcessors)。

public static void invokeBeanFactoryPostProcessors(
ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) {
//#################省略其他代碼##################
invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
//#################省略其他代碼##################
}

在invokeBeanFactoryPostProcessors()方法中會(huì)解析標(biāo)注了@Configuration注解的類中標(biāo)注了@Bean等注解的方法,生成相應(yīng)的Bean定義信息注冊(cè)到IOC容器中。這里,主要關(guān)注的是標(biāo)注了@Configuration注解的類的實(shí)例化過(guò)程,所以,只需要關(guān)注invokeBeanFactoryPostProcessors()方法中的上述代碼片段即可。

可以看到,在invokeBeanFactoryPostProcessors()方法中又調(diào)用了PostProcessorRegistrationDelegate類中的另一個(gè)invokeBeanFactoryPostProcessors()方法。

(5)解析PostProcessorRegistrationDelegate類的invokeBeanFactoryPostProcessors(Collection postProcessors, ConfigurableListableBeanFactory beanFactory)方法

源碼詳見(jiàn):org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(Collection postProcessors, ConfigurableListableBeanFactory beanFactory)。

private static void invokeBeanFactoryPostProcessors(Collection postProcessors, ConfigurableListableBeanFactory beanFactory) {
for (BeanFactoryPostProcessor postProcessor : postProcessors) {
StartupStep postProcessBeanFactory = beanFactory.getApplicationStartup().start("spring.context.bean-factory.post-process")
.tag("postProcessor", postProcessor::toString);
postProcessor.postProcessBeanFactory(beanFactory);
postProcessBeanFactory.end();
}
}

可以看到,在invokeBeanFactoryPostProcessors()方法中,會(huì)循環(huán)遍歷傳遞進(jìn)來(lái)的所有postProcessors集合,每次循環(huán)時(shí),都會(huì)使用一個(gè)postProcessor對(duì)象來(lái)接收postProcessors集合中的每一個(gè)元素,調(diào)用postProcessor對(duì)象的postProcessBeanFactory()方法,并傳入beanFactory來(lái)實(shí)例化對(duì)象。

(6)解析ConfigurationClassPostProcessor類中的postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法

源碼詳見(jiàn):org.springframework.context.annotation.ConfigurationClassPostProcessor#postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//##############省略其他代碼###############
enhanceConfigurationClasses(beanFactory);
beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

可以看到,在postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)方法中調(diào)用了enhanceConfigurationClasses()方法。

(7)解析ConfigurationClassPostProcessor類的enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory)方法

源碼詳見(jiàn):org.springframework.context.annotation.ConfigurationClassPostProcessor#enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory)。

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
//################省略其他代碼########################
ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
for (Map.Entry entry : configBeanDefs.entrySet()) {
AbstractBeanDefinition beanDef = entry.getValue();
// If a @Configuration class gets proxied, always proxy the target class
beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
// Set enhanced subclass of the user-specified bean class
Class configClass = beanDef.getBeanClass();
Class enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
if (configClass != enhancedClass) {
//################省略其他代碼###################
beanDef.setBeanClass(enhancedClass);
}
}
enhanceConfigClasses.tag("classCount", () -> String.valueOf(configBeanDefs.keySet().size())).end();
}

可以看到,在enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory)方法中,主要是使用ConfigurationClassEnhancer對(duì)象的enhance()方法生成代理類,也就是使用CGLib
分享標(biāo)題:我竟然寫了三萬(wàn)字解析@Configuration注解
網(wǎng)站網(wǎng)址:http://m.5511xx.com/article/coeeidp.html