前文主要介紹了@bean @ComponentScan 掃包注入兩種方式,今天介紹一下@Conditional按照條件注入和@import導入組件的相關用法
1 首先是@Conditional的用法:按照一定的條件進行判斷,滿足條件給容器中註冊bean,之前是寫了@bean註解就會注入,但是在bean上加了@Conditional註解,並不一定就會注入了,而是進行條件判斷之後再說,該註解在springboot源碼中大量用到,該註解是在spring4.0引入的。
首先我們創建一個Config,並且在裏面註冊2個person的Bean其中一個爲windows另外一個爲linux,使用@Conditional註解來判斷當前操作系統是windows還是linux分別加載不同的bean,見下圖:
package com.study.chap4.conditional.config;
import com.study.chap4.conditional.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Conditional(WindowsCondition.class)
@Configuration
public class Config {
/**
* @Conditional({Condition}) : 按照一定的條件進行判斷,滿足條件給容器中註冊bean
*
* 如果系統是windows,給容器中註冊("windows")
* 如果是linux系統,給容器中註冊("linux")
*/
@Bean("windows")
public Person person01(){
return new Person("windows",18,"我是windows系統");
}
@Conditional(LinuxCondition.class)
@Bean("linux")
public Person person02(){
return new Person("linux", 48,"我是linux系統");
}
}
我們看下@Conditional源碼可知,該註解可以用在類上和用在方法上,我們分別創建判斷的條件類WindowsCondition,linuxCondition,這裏爲了節約空間,就只貼出一個類的代碼
package com.study.chap4.conditional.config;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* 判斷是否是windows系統
*
* */
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
return property.contains("Windows");
}
}
然後再測試類上面輸出所有注入的bean,由於系統運行是windows環境,所以windows的bean被注入,而linux沒有
import com.study.chap4.conditional.config.Config;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MainTest4 {
@Test
public void demoChap3(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);
String[] names = applicationContext.getBeanDefinitionNames();
for (String name : names) {
System.out.println(name);
}
}
}
2 使用@import註解快速給容器導入一個組件,觀察import源碼可知,該註解在spring3.0就有了,並且還可以link# ImportSelector:返回需要導入的組件的全類名數組;以及 ImportBeanDefinitionRegistrar:手動註冊bean到容器中
因此,我們首先直接用import的用法,直接導入組件名
package com.study.chap4.imports;
import com.study.chap4.imports.pojo.Blue;
import com.study.chap4.imports.pojo.Red;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
@Configuration
@Import({Red.class, Blue.class})
//@Import導入組件,默認是組件名是全類名
public class Config {
}
輸出可見
再用一種方式,ImportSelector:返回需要導入的組件的全類名數組,並且再springboot的啓動類中有用到這個特性,新建一個MyImportSelector去實現 ImportSelector 接口。
package com.study.chap4.imports;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportSelector implements ImportSelector {
/**
* @param importingClassMetadata 當前標註@Import註解的類的所有註解信息
* @return 返回值,就是到導入到容器中的組件全類名
*/
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{"com.study.chap4.imports.pojo.Yellow"};
}
}
再使用第三種方式 ImportBeanDefinitionRegistrar:手動註冊bean到容器中,ImportBeanDefinitionRegistrar在springboot啓動類中也有用到,稍後進行分析一下。
package com.study.chap4.imports;
import com.study.chap4.imports.pojo.RainBow;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:當前類的註解信息
* BeanDefinitionRegistry:BeanDefinition註冊類;裏面有很多方法,註冊一個bean,移除一個bean等等
* 把所有需要添加到容器中的bean;調用
* BeanDefinitionRegistry.registerBeanDefinition手工註冊進來
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean definition = registry.containsBeanDefinition("com.study.chap4.imports.pojo.Red");
boolean definition2 = registry.containsBeanDefinition("com.study.chap4.imports.pojo.Blue");
if(definition && definition2){
//指定Bean定義信息;(Bean的類型,Bean。。。)
RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
//註冊一個Bean,指定bean名
registry.registerBeanDefinition("rainBow", beanDefinition);
//移除一個bean
registry.removeBeanDefinition("config");
}
}
}
至此,最後我們輸出可知: config確實被移除掉了。
至此我們又新學了兩個註解的用法,一個是@Conditional 另外一個是@Import,其實spring以及springboot底層都是大量運用註解寫的相關代碼,充分利用了註解的特性,我們把這些註解會用並且弄懂,之後看源碼也不會覺得太吃力。
------------------------------------------------------------------------------------------------------------------------------------------------------------------
這裏順便分析一個springboot啓動用到的這個特性,我們首先查看springboot的啓動類註解@SpringBootApplication源碼,當然springboot源碼啓動有很多,這裏不會給大家全部分析完畢,只會對用到的@import註解特性的進行分析。
會看到裏面有3個註解,一個是@ComponentScan的自定義過濾條件的掃包注入容器,該註解在本人該系列第二篇博客中完整的詳細的解釋到了,這裏就不做過多的解釋。spring5學習系列之------2 給容器註冊組件二 @ComponentScan 自定義掃描規則,過濾組件,接下來看第二個註解 @SpringBootConfiguration接着點進去
這是對 @Configuration註解的進行封裝,在看一個註解是 @EnableAutoConfiguration ,打開這個註解的源碼可看到
重點來了,會看到一個@import註解,並且指定了一個導入的類,我們點進去,會看到用到了import的第二種方式