- Spring Boot進(jìn)階:原理、實(shí)戰(zhàn)與面試題分析
- 鄭天民
- 886字
- 2022-07-05 09:41:42
2.2.1 Bean的注冊(cè)
在使用Spring時(shí),我們可以通過(guò)獲取一個(gè)應(yīng)用上下文(ApplicationContext)對(duì)象來(lái)操作各種Bean,示例代碼如代碼清單2-9所示,相信你對(duì)這段代碼不會(huì)陌生。
代碼清單2-9 獲取應(yīng)用上下文對(duì)象的示例代碼
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
這里的ApplicationContext接口代表的就是一個(gè)Spring IoC容器,而在Spring中存在一大批ApplicationContext接口的實(shí)現(xiàn)類。如果使用基于注解的配置方式,就可以使用上述代碼中的AnnotationConfigApplicationContext來(lái)初始化容器上下文對(duì)象。
在剛開(kāi)始閱讀Spring源碼時(shí),我建議你直接從AnnotationConfigApplicationContext的啟動(dòng)流程切入,這一流程位于它的構(gòu)造函數(shù)中,如代碼清單2-10所示。
代碼清單2-10 AnnotationConfigApplicationContext構(gòu)造函數(shù)代碼
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) { this(); //根據(jù)注解配置類注冊(cè)Bean register(annotatedClasses); //刷新容器 refresh(); } public AnnotationConfigApplicationContext(String... basePackages) { this(); //根據(jù)包路徑配置掃描Bean scan(basePackages); //刷新容器 refresh(); }
這兩個(gè)構(gòu)造函數(shù)的作用很明確,一個(gè)是根據(jù)注解配置類注冊(cè)Bean,另一個(gè)則是根據(jù)包路徑配置掃描Bean。這里我們以register()方法為例,來(lái)討論Bean的注冊(cè)過(guò)程,該方法如代碼清單2-11所示。
代碼清單2-11 AnnotationConfigApplicationContext的register()方法代碼
public void register(Class<?>... annotatedClasses) { Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified"); this.reader.register(annotatedClasses); }
這里依賴AnnotatedBeanDefinitionReader工具類來(lái)完成Bean的注冊(cè)。AnnotatedBean-DefinitionReader會(huì)遍歷所有傳入的annotatedClasses注解類,然后通過(guò)代碼清單2-12所示的doRegisterBean()方法完成注冊(cè)。(由于該方法的代碼較長(zhǎng),我們對(duì)重要邏輯添加了注釋,對(duì)不重要的代碼做了省略。)
代碼清單2-12 AnnotationConfigApplicationContext的doRegisterBean()方法代碼
<T> void doRegisterBean(...) { //將注解配置類信息轉(zhuǎn)換成一種BeanDefinition AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass); … //獲取Bean的作用域元數(shù)據(jù),解析Bean作用域 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd); //將Bean的作用域?qū)懟谺eanDefinition abd.setScope(scopeMetadata.getScopeName()); //生成beanName String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry)); //解析AnnotatedBeanDefinitionReader中的@Lazy、@Primary等注解 AnnotationConfigUtils.processCommonDefinitionAnnotations(abd); //處理@Qualifier注解 if (qualifiers != null) { for (Class<? extends Annotation> qualifier : qualifiers) { if (Primary.class == qualifier) { // 如果設(shè)置了@Primary注解,則設(shè)置當(dāng)前Bean為首選Bean abd.setPrimary(true); } else if (Lazy.class == qualifier) { //如果設(shè)置了@Lazy注解,則設(shè)置當(dāng)前Bean為延遲加載模式 abd.setLazyInit(true); } else { //其他注解,則添加到BeanDefinition中 abd.addQualifier(new AutowireCandidateQualifier(qualifier)); } } } … //注冊(cè)Bean對(duì)象 BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry); }
這段代碼包含Bean注冊(cè)過(guò)程中的三個(gè)核心步驟,如圖2-1所示。

圖2-1 AnnotatedBeanDefinitionReader中Bean注冊(cè)的核心步驟示意圖
首先,我們構(gòu)建用來(lái)描述Bean實(shí)例信息的BeanDefinition對(duì)象,這需要將注解配置類信息轉(zhuǎn)成AnnotatedGenericBeanDefinition數(shù)據(jù)結(jié)構(gòu),而AnnotatedGenericBeanDefinition就是一種BeanDefinition,包含了Bean的構(gòu)造函數(shù)參數(shù)、各種屬性值以及所添加的注解信息。
然后,我們?cè)O(shè)置BeanDefinition屬性,這一步驟完成了對(duì)@Scope、@Primary、@Lazy等注解的處理。
最后,通過(guò)registerBeanDefinition()方法完成Bean的注冊(cè),該方法內(nèi)部通過(guò)Listable-BeanFactory的實(shí)現(xiàn)類DefaultListableBeanFactory將Bean定義信息注冊(cè)到Spring IoC容器中。ListableBeanFactory是Spring中常用的一個(gè)BeanFactory,通過(guò)這個(gè)接口,我們可以一次獲取多個(gè)Bean。
- 數(shù)據(jù)庫(kù)系統(tǒng)教程(第2版)
- HTML5+CSS3基礎(chǔ)開(kāi)發(fā)教程(第2版)
- Instant 960 Grid System
- C/C++常用算法手冊(cè)(第3版)
- Hands-On JavaScript High Performance
- Linux操作系統(tǒng)基礎(chǔ)案例教程
- Mastering JBoss Enterprise Application Platform 7
- Node.js Design Patterns
- 區(qū)塊鏈技術(shù)與應(yīng)用
- Java Web應(yīng)用開(kāi)發(fā)項(xiàng)目教程
- OpenCV Android開(kāi)發(fā)實(shí)戰(zhàn)
- AutoCAD基礎(chǔ)教程
- Visual C++ 開(kāi)發(fā)從入門(mén)到精通
- Office VBA開(kāi)發(fā)經(jīng)典:中級(jí)進(jìn)階卷
- jMonkeyEngine 3.0 Cookbook