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

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

新聞中心

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

?一、學習指引

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

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

大部讀者都知道@Configuration注解可以標注到類上,當標注到類上時,啟動Spring就會自動掃描@Configuration注解標注的類,將其注冊到IOC容器中,并被實例化成Bean對象。

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

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

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

二、注解說明?

@Configuration注解的一點點說明

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

2.1 注解源碼

源碼詳見: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注解中每個屬性的含義如下所示。

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

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

enforceUniqueMethods:從Spring 6.0開始加入到@Configuration注解,指定使用@Bean注解標注的方法是否需要具有唯一的方法名稱,取值為true或者false。當取值為true時,表示使用@Bean注解標注的方法具有唯一的方法名稱,并且這些方法名稱不會重疊。當取值為false時,表示使用@Bean注解標注的方法名稱不唯一,存在被重疊的風險。默認取值為true。

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

2.2 注解使用場景

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

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

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

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

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

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

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

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

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

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

三、使用案例?

不給案例學起來挺枯燥的。

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

3.1  驗證proxyBeanMethods屬性的作用

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

3.1.1 驗證proxyBeanMethods取值為true的情況

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

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

Person類主要是用來注冊到IOC容器中,并實例化對象。

源碼詳見: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類的作用就是充當程序啟動的配置類,會在ConfigurationAnnotationConfig類上標注@Configuration注解,說明ConfigurationAnnotationConfig類是Spring啟動時的配置類。

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

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

可以看到,在ConfigurationAnnotationConfig類上標注了@Configuration注解,由于@Configuration注解中的proxyBeanMethods屬性默認為true,所以在ConfigurationAnnotationConfig類上的@Configuration注解省略了proxyBeanMethods屬性。

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

ConfigurationAnnotationTest類的作用就是整個案例程序的啟動類,對整個案例程序進行測試。

源碼詳見: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常見了IOC容器context,從context中獲取了ConfigurationAnnotationConfig類的Bean實例對象config,接下來,調(diào)用兩次config的person()方法分別賦值給Person類型的局部變量person1和person2,最后打印person1是否等于person2的日志。

(4)測試案例

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

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

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

3.1.2 驗證proxyBeanMethods取值為false的情況

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

(1)修改proxyBeanMethods屬性的值

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

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

可以看到,此時在ConfigurationAnnotationConfig類上標注的@Configuration注解的proxyBeanMethods屬性為false。

(2)測試案例

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

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

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

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

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

(1)刪除@Configuration注解

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

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

(2)測試案例

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

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

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

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

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

(1)修改測試類

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

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)測試案例

運行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容器時,不能省略配置類上的@Configuration注解,否則會拋出NoSuchBeanDefinitionException。

(4)添加@Configuration注解

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

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

(5)再次測試案例

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

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

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

四、源碼時序圖?

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

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

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

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

4.1 注冊ConfigurationClassPostProcessor流程源碼時序圖

ConfigurationClassPostProcessor后置處理器是解析@Configuration注解的核心類,也是Spring中的一個非常重要的后置處理器類, Spring IOC容器啟動時,會向IOC容器中注冊ConfigurationClassPostProcessor類的Bean定義信息。向IOC容器中注冊ConfigurationClassPostProcessor類的Bean定義信息的時序圖如圖1-1所示。

圖1-1

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

4.2 注冊ConfigurationAnnotationConfig流程源碼時序圖

ConfigurationAnnotationConfig類是本章中案例程序的配置類,在ConfigurationAnnotationConfig類上標注了@Configuration注解,當Spring IOC容器啟動時,也會將ConfigurationAnnotationConfig類的Bean定義信息注冊到Spring IOC容器中,向Spring IOC容器中注冊ConfigurationAnnotationConfig類的Bean定義信息的時序圖如圖1-2所示。

圖1-2

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

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

4.3 實例化流程源碼時序圖

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

圖1-3-1

圖1-3-2

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

五、源碼解析?

重點來了,源碼解析,跟上節(jié)奏,別走神!

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

5.1 注冊ConfigurationClassPostProcessor流程源碼解析

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

(1)運行案例程序啟動類ConfigurationAnnotationTest的main()方法

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

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

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

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

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

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

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

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

源碼詳見: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類的無參構(gòu)造方法中,主要的邏輯就是實例化了AnnotatedBeanDefinitionReader類型的reader成員變量和ClassPathBeanDefinitionScanner類型的scanner成員變量。

reader:表示注解類型的Bean定義信息讀取器,主要就是讀取通過注解方式進行實例化的Bean的定義信息。

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

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

this.reader = new AnnotatedBeanDefinitionReader(this);

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

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

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

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

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

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

源碼詳見: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對象傳入方法中。其中,registry對象本質(zhì)上就是一個AnnotationConfigApplicationContext類對象的實例,這是因為AnnotationConfigApplicationContext類繼承了GenericApplicationContext類,而GenericApplicationContext類實現(xiàn)了BeanDefinitionRegistry接口。

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

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

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

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

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

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

這里,只給出在AnnotationConfigUtils類的registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, Object source)方法中,將@Configuration注解涉及到的ConfigurationClassPostProcessor類的Bean定義信息注冊到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));
}
//################省略其他代碼########################
}

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

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

源碼詳見: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()方法來注冊ConfigurationClassPostProcessor類的Bean定義信息,definition參數(shù)本質(zhì)上就是一個AnnotationConfigApplicationContext類的實例對象。最終會調(diào)用DefaultListableBeanFactory類的registerBeanDefinition()方法來注冊ConfigurationClassPostProcessor類的Bean定義信息。

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

源碼詳見: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);
//##################省略其他代碼###############
}

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

beanDefinitionMap源碼詳見:org.springframework.beans.factory.support.DefaultListableBeanFactory#beanDefinitionMap。

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

至此,@Configuration注解涉及到的ConfigurationClassPostProcessor類的注冊過程分析完畢。

5.2 注冊ConfigurationAnnotationConfig流程源碼解析

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

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

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

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

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

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

源碼詳見: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)方法

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

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

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

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

源碼詳見: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)方法。

源碼詳見: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)方法

源碼詳見: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)方法中通過調(diào)用registry的registerBeanDefinition()方法來向IOC容器中注冊Bean定義信息。

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

5.3 實例化流程源碼解析

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

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

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

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

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

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

源碼詳見: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)方法

源碼詳見: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)方法

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

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

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

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

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

源碼詳見: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án)遍歷傳遞進來的所有postProcessors集合,每次循環(huán)時,都會使用一個postProcessor對象來接收postProcessors集合中的每一個元素,調(diào)用postProcessor對象的postProcessBeanFactory()方法,并傳入beanFactory來實例化對象。

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

源碼詳見: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)方法

源碼詳見: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對象的enhance()方法生成代理類,也就是使用CGLib
分享名稱:我竟然寫了三萬字解析@Configuration注解
鏈接URL:http://www.5511xx.com/article/coeeidp.html