新聞中心
?一、學(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 SetregisterAnnotationConfigProcessors(
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 MapbeanDefinitionMap = 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 extends Annotation>[] qualifiers, Suppliersupplier, BeanDefinitionCustomizer[] customizers)方法。
源碼詳見(jiàn):org.springframework.context.annotation.AnnotatedBeanDefinitionReader#doRegisterBean(ClassbeanClass, String name, Class extends Annotation>[] qualifiers, Suppliersupplier, BeanDefinitionCustomizer[] customizers)。
privatevoid doRegisterBean(Class beanClass, @Nullable String name,@Nullable Class extends Annotation>[] 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 extends Annotation>[] 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, ListbeanFactoryPostProcessors) {
//#################省略其他代碼##################
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 extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory)方法
源碼詳見(jiàn):org.springframework.context.support.PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(Collection extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory)。
private static void invokeBeanFactoryPostProcessors(Collection extends BeanFactoryPostProcessor> 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
分享題目:我竟然寫了三萬(wàn)字解析@Configuration注解
文章地址:http://m.fisionsoft.com.cn/article/coeeidp.html


咨詢
建站咨詢
