四)初始Spring源碼以及常用組件
目錄
一, @Conditional 註解
- 條件註冊bean,根據指定條件選擇性地註冊bean實例
4.1.1,什麼叫做條件註冊bean?
模擬以下場景: 我在當操作系統爲 windows 的時候 @Bean(lison)實例, 而在linux的時候 @Bean("james")
示例代碼 4.1.1:
@Conditional(WinCondition.class)
@Bean("lison")
public Person lison(){
System.out.println("給容器中添加lison.......");
return new Person("Lison",58);
}
@Conditional(LinCondition.class)
@Bean("james")//bean在容器中的ID爲james, IOC容器MAP, map.put("id",value)
public Person james(){
System.out.println("給容器中添加james.......");
return new Person("james",20);
}
@Conditional(WinCondition.class) WinCondition.class 是我自己定義的一個過濾類, 這個需要實現 Condition 接口,
在Spring 源碼中 拿到bean 的信息, 都是 通過 BeanFactory() , 而 註冊 都是通過 FactoryBean() ,獲得操作 系統 可以通過 Spring 上下文, 也可以通過 System.getProperty("os.name") , 裏面 的 參數 os.name 是定義好的
public class WinCondition implements Condition{
/*
*ConditionContext: 判斷條件可以使用的上下文(環境)
*AnnotatedTypeMetadata: 註解的信息
*
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO 是否爲WINDOW系統
//能獲取到IOC容器正在使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//獲取當前環境變量(包括我們操作系統是WIN還是LINUX??)
Environment environment = context.getEnvironment();
String os_name = environment.getProperty("os.name");
if(os_name.contains("Windows")){
return true;
}
return false;
}
}
@Conditional(LinCondition.class)
public class LinCondition implements Condition{
/*
*ConditionContext: 判斷條件可以使用的上下文(環境)
*AnnotatedTypeMetadata: 註解的信息
*
*/
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO 是否爲WINDOW系統
//能獲取到IOC容器正在使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//獲取當前環境變量(包括我們操作系統是WIN還是LINUX??)
Environment environment = context.getEnvironment();
String os_name = environment.getProperty("os.name");
if(os_name.contains("linux")){
return true;
}
return false;
}
進行測試,得到我們想要的結果:
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap7MainConfig.class);
System.out.println("IOC容器創建完成.........");
//app.getBean("person");//執行獲取的時候才創建並初始化bean
String[] beanDefinitionNames = app.getBeanDefinitionNames();
for(String name:beanDefinitionNames){
System.out.println(name);
}
}
lison 已經根據我們的條件注入bean
二, @Import註解 註冊組件
- 手動添加組件到Ioc容器;
- 使用 ImportSelector 自定義返回組件
- 使用 ImportBeanDefinitionRegistrar返回自定義組件
4.2.1,給容器中註冊組件的方式:
- @Bean 導入第三方的類或包的組件,比如 Person 爲第三方類,需要在我們的 IOC 容器 中使用,包掃描+組件的標註註解(@ComponentScan : @Contorller @Service @Reponsitory @Componet ,一般是針對我們自己寫的類,使用這個)
- @Import : 快速給容器導入一個組件,注意: @Bean 有點簡單,
- @Import (要導入到容器中的組件) 容器會自動註冊這個組件,bean的 id爲全類名,
- ImportSelector : 是一個接口,返回需要導入到容器的組件的全類名數組
- ImportBeanDefinitionRegistrar : 可以手動添加組件到IOC容器,所有Bean的註冊可以使用BeanDefinitionRegeistry寫JamesImportBeanDefinitionRegistrar實現ImportBeanDefinitionRegistrar接口即可
- 使用Spring提供的FactoryBean(工廠bean)進行註冊
4.2.1 示例源碼: 定義 6 個類, 裏面沒有任何操作:見 項目源碼---》 cap6
我們使用FactoryBean(工廠bean)進行註冊: 將上面我們新建的 Monkey 類 註冊到 工廠bean
public class JamesFactoryBean implements FactoryBean<Monkey>{
@Override
public Monkey getObject() throws Exception {
// TODO Auto-generated method stub
return new Monkey();
}
@Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return Monkey.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
我們使用 ImportBeanDefinitionRegistrar 添加組件, 實現 ImportBeanDefinitionRegistrar 接口, 重寫 registerBeanDefinitions
方法 , AnnotationMetadata 參數 是當前類的註解信息, BeanDefinitionRegistry 即 BeanDefinition註冊類。 將 dog, cat 兩個類註冊到 容器中去
public class JamesImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/*
*AnnotationMetadata:當前類的註解信息
*BeanDefinitionRegistry:BeanDefinition註冊類
* 把所有需要添加到容器中的bean加入;
* @Scope
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean bean1 = registry.containsBeanDefinition("com.enjoy.cap6.bean.Dog");
boolean bean2 = registry.containsBeanDefinition("com.enjoy.cap6.bean.Cat");
//如果Dog和Cat同時存在於我們IOC容器中,那麼創建Pig類, 加入到容器
//對於我們要註冊的bean, 給bean進行封裝,
if(bean1 && bean2){
RootBeanDefinition beanDefinition = new RootBeanDefinition(Pig.class);
registry.registerBeanDefinition("pig", beanDefinition);
}
}
@ImportSelectot 接口 返回的是一個數組, 裏面可以添加多個 類, 一起註冊到 容器中去,
public class JamesImportSelector implements ImportSelector{
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata){
//返回全類名的bean
return new String[]{"com.enjoy.cap6.bean.Fish","com.enjoy.cap6.bean.Tiger"};
}
}
測試: -----》 項目源碼: cap6Test
@Test
public void test01(){
AnnotationConfigApplicationContext app = new AnnotationConfigApplicationContext(Cap6MainConfig.class);
System.out.println("IOC容器創建完成........");
//如果返回true,說明是單例,返回false,說明是多例。。
Object bean1 = app.getBean("&jamesFactoryBean");
Object bean2 = app.getBean("jamesFactoryBean");//取Money
System.out.println("bean的類型="+bean1.getClass());
System.out.println(bean1 == bean2);
String[] beanDefinitionNames = app.getBeanDefinitionNames();
for(String name:beanDefinitionNames){
System.out.println(name);
}
三,項目 Demo地址
Spring源碼深度解析,(附代碼示例 碼雲地址: https://gitee.com/Crazycw/SpringCode.git)
參考資料: https://docs.spring.io/spring/docs/4.3.18.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/
請看下篇: Spring源碼深度解析,初始Spring源碼----Bean 生命週期(五)(附代碼示例:)