BeanDefinition
容器中的每一個(gè) bean 都會(huì)有一個(gè)對(duì)應(yīng)的 BeanDefinition 實(shí)例,該實(shí)例負(fù)責(zé)保存bean對(duì)象的所有必要信息,包括 bean 對(duì)象的 class 類(lèi)型、是否是抽象類(lèi)、構(gòu)造方法和參數(shù)、其它屬性等等。
BeanDefinitionRegistry和 BeanFactory就是菜譜(記錄這道菜怎么做出來(lái)的)
BeanDefinitionRegistry 抽象出 bean 的注冊(cè)邏輯,而 BeanFactory 則抽象出了 bean 的管理邏輯,而各個(gè) BeanFactory 的實(shí)現(xiàn)類(lèi)就具體承擔(dān)了 bean 的注冊(cè)以及管理工作
DefaultListableBeanFactory
實(shí)現(xiàn)BeanFactory接口 ,它同時(shí)也實(shí)現(xiàn)了 BeanDefinitionRegistry 接口
IOC容器的工作流程
- 容器啟動(dòng)階段
容器啟動(dòng)時(shí),會(huì)通過(guò)某種途徑加載 ConfigurationMetaData(xml的配置文件)。除了代碼方式比較直接外,在大部分情況下,容器需要依賴(lài)某些工具類(lèi),比如: BeanDefinitionReader,BeanDefinitionReader 會(huì)對(duì)加載的 ConfigurationMetaData(xml的配置文件)進(jìn)行解析和分析,并將分析后的信息組裝為相應(yīng)的 BeanDefinition,最后把這些保存了 bean 定義的 BeanDefinition,注冊(cè)到相應(yīng)的 BeanDefinitionRegistry,這樣容器的啟動(dòng)工作就完成了。 - Bean的實(shí)例化階段
經(jīng)過(guò)第一階段,所有 bean 定義都通過(guò) BeanDefinition 的方式注冊(cè)到 BeanDefinitionRegistry 中,當(dāng)某個(gè)請(qǐng)求通過(guò)容器的 getBean 方法請(qǐng)求某個(gè)對(duì)象,或者因?yàn)橐蕾?lài)關(guān)系容器需要隱式的調(diào)用 getBean 時(shí),就會(huì)觸發(fā)第二階段的活動(dòng):容器會(huì)首先檢查所請(qǐng)求的對(duì)象之前是否已經(jīng)實(shí)例化完成。如果沒(méi)有,則會(huì)根據(jù)注冊(cè)的 BeanDefinition 所提供的信息實(shí)例化被請(qǐng)求對(duì)象,并為其注入依賴(lài)。當(dāng)該對(duì)象裝配完畢后,容器會(huì)立即將其返回給請(qǐng)求方法使用。
BeanFactory 只是 Spring IoC 容器的一種實(shí)現(xiàn),如果沒(méi)有特殊指定,它采用采用延遲初始化策略:只有當(dāng)訪問(wèn)容器中的某個(gè)對(duì)象時(shí),才對(duì)該對(duì)象進(jìn)行初始化和依賴(lài)注入操作。而在實(shí)際場(chǎng)景下,我們更多的使用另外一種類(lèi)型的容器: ApplicationContext,它構(gòu)建在 BeanFactory 之上,屬于更高級(jí)的容器,除了具有 BeanFactory 的所有能力之外,還提供對(duì)事件監(jiān)聽(tīng)機(jī)制以及國(guó)際化的支持等。它管理的 bean,在容器啟動(dòng)時(shí)全部完成初始化和依賴(lài)注入操作。(?? BeanFactory在被請(qǐng)求getBean要獲取bean時(shí)才實(shí)例化,而如果說(shuō)BeanFactory是Spring的心臟,那么ApplicationContext就是完整的軀體了,ApplicationContext由BeanFactory派生而來(lái),比它強(qiáng)大)
JavaConfig (@Configuration @Bean)
上面我們提到bean的定義和相互間是用xml描述的,因?yàn)楫a(chǎn)生了許多的xml文件因此人們對(duì)它不滿(mǎn),所以完善了用Annotation來(lái)注冊(cè)bean就是我們所見(jiàn)的@注釋
@ComponentScan
注解用于導(dǎo)入配置類(lèi),比如配置類(lèi)中的bean依賴(lài)于另一個(gè)別的類(lèi)的bean,那么就可以借助@import把依賴(lài)的那個(gè)bean引進(jìn)配置類(lèi)
通過(guò) basePackages等屬性來(lái)指定 @ComponentScan自動(dòng)掃描的范圍,如果不指定,默認(rèn)從聲明 @ComponentScan所在類(lèi)的 package進(jìn)行掃描。正因?yàn)槿绱?,SpringBoot的啟動(dòng)類(lèi)都默認(rèn)在 src/main/java下
@Import
用于導(dǎo)入配置類(lèi),比如配置類(lèi)中的bean依賴(lài)于另一個(gè)別的類(lèi)的bean,那么就可以借助@import把依賴(lài)的那個(gè)bean引進(jìn)配置類(lèi)
例子:
@Configuration
public class MoonBookConfiguration {
@Bean
public BookService bookService() {
return new BookServiceImpl();
}
}
現(xiàn)在有另外一個(gè)配置類(lèi),比如: MoonUserConfiguration,這個(gè)配置類(lèi)中有一個(gè)bean依賴(lài)于 MoonBookConfiguration中的bookService,如何將這兩個(gè)bean組合在一起?借助 @Import即可:
@Configuration
// 可以同時(shí)導(dǎo)入多個(gè)配置類(lèi),比如:@Import({A.class,B.class})
@Import(MoonBookConfiguration.class)
public class MoonUserConfiguration {
@Bean
public UserService userService(BookService bookService) {
return new BookServiceImpl(bookService);
}
}
在4.2之前, @Import注解只支持導(dǎo)入配置類(lèi),但是在4.2之后,它支持導(dǎo)入普通類(lèi),并將這個(gè)類(lèi)作為一個(gè)bean的定義注冊(cè)到IOC容器中
@Conditional
表示在滿(mǎn)足某種條件后才初始化一個(gè)bean或者啟用某些配置
@ConfigurationProperties與@EnableConfigurationProperties
當(dāng)某些屬性的值需要配置的時(shí)候,我們一般會(huì)在 application.properties文件中新建配置項(xiàng),然后在bean中使用 @Value注解來(lái)獲取配置的值。
使用 @Value注解注入的屬性通常都比較簡(jiǎn)單,如果同一個(gè)配置在多個(gè)地方使用,也存在不方便維護(hù)的問(wèn)題(考慮下,如果有幾十個(gè)地方在使用某個(gè)配置,而現(xiàn)在你想改下名字,你改怎么做?)。對(duì)于更為復(fù)雜的配置,Spring Boot提供了更優(yōu)雅的實(shí)現(xiàn)方式,那就是 @ConfigurationProperties注解。