给容器注册bean的几种方式:
1.包扫描:ComponentScan+bean注解(@Controller,@Service,@Repository,@Component,Bean):
2.条件注入:@Conditional按条件选择注入
3.@Import快速给容器中导入一个组件
4.FactoryBean注册
@Conditional是基于条件注册bean的注解,也是springboot的实现原理。
当满足配置选择条件时,便会注册bean到容器中。
@Condotional注解源码如下:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
/**
* All {@link Condition}s that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
value 为实现Condition接口的实现类。由此我们可以猜测,@Condotional的实现就是根据value中的逻辑来判断是否需要加载bean的。
下面,我们写一个关于@Condotional按条件注册bean的例子。
首先定义两个实现Condition的接口实现类:LinuxConditional和WindowConditional
public class WindowConditional implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO是否linux系统
// 1、能获取到ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
System.out.println(beanFactory.getSingletonCount());
// 2、获取类加载器
ClassLoader classLoader = context.getClassLoader();
System.out.println(classLoader.toString());
// 3、获取当前环境信息
Environment environment = context.getEnvironment();
// 4、获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
String property = environment.getProperty("os.name");
// 可以判断容器中的bean注册情况,也可以给容器中注册bean
boolean definition = registry.containsBeanDefinition("person");
System.out.println(definition);
if (property.toUpperCase().contains("window".toUpperCase())) {
return true;
}
return false;
}
}
public class LinuxConditional implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// TODO是否linux系统
// 1、能获取到ioc使用的beanfactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
System.out.println(beanFactory.getSingletonCount());
// 2、获取类加载器
ClassLoader classLoader = context.getClassLoader();
System.out.println(classLoader.toString());
// 3、获取当前环境信息
Environment environment = context.getEnvironment();
// 4、获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
String property = environment.getProperty("os.name");
// 可以判断容器中的bean注册情况,也可以给容器中注册bean
boolean definition = registry.containsBeanDefinition("person");
System.out.println(definition);
if (property.toUpperCase().contains("linux".toUpperCase())) {
return true;
}
return false;
}
}
两个条件注入类实现好了,接着,写我们的配置类:ConditionalConfig
@Configuration
public class ConditionalConfig {
@Conditional({ WindowConditional.class })
@Bean
public Person person() {
System.out.println("给容器中添加Person....");
return new Person("张三", 25);
}
}
最后,我们来做一个测试:
@Test
public void testPerson() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConditionalConfig.class);
printBeans(context);
System.out.println("--------------------");
Person p = context.getBean(Person.class);
Person p2 = context.getBean(Person.class);
System.out.println(p == p2);
System.out.println(p);
}
private void printBeans(ApplicationContext context) {
String[] defBeans = context.getBeanDefinitionNames();
for (String name : defBeans) {
System.out.println(name);
}
}
以下为打印的内容:
现在,我们将注解注入条件更换为:@Conditional({ LinuxConditional.class })
查看打印结果:
可以发现,Person并没有注册进来。
总结:
1.条件选择器需要实现Condition接口
2.在配置中添加@Conditional注解并设置value为条件选择器
需要注意的是:@Conditional既可以配置在类上也可以配置在方法上。
1.当配置在类上时,类中组件统一设置。满足当前条件,这个类中配置的所有bean注册才能生效;
2.当配置在方法上时,只对当前方法生效。