前文主要介绍了@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");
}
}
}
至此我们又新学了两个注解的用法,一个是@Conditional 另外一个是@Import,其实spring以及springboot底层都是大量运用注解写的相关代码,充分利用了注解的特性,我们把这些注解会用并且弄懂,之后看源码也不会觉得太吃力。
------------------------------------------------------------------------------------------------------------------------------------------------------------------
这里顺便分析一个springboot启动用到的这个特性,我们首先查看springboot的启动类注解@SpringBootApplication源码,当然springboot源码启动有很多,这里不会给大家全部分析完毕,只会对用到的@import注解特性的进行分析。
会看到里面有3个注解,一个是@ComponentScan的自定义过滤条件的扫包注入容器,该注解在本人该系列第二篇博客中完整的详细的解释到了,这里就不做过多的解释。spring5学习系列之------2 给容器注册组件二 @ComponentScan 自定义扫描规则,过滤组件,接下来看第二个注解 @SpringBootConfiguration接着点进去
这是对 @Configuration注解的进行封装,在看一个注解是 @EnableAutoConfiguration ,打开这个注解的源码可看到
重点来了,会看到一个@import注解,并且指定了一个导入的类,我们点进去,会看到用到了import的第二种方式