简介
在学习SpringBoot的时候我们会在各种自动配置类中看到大量使用了@Conditional注解,这篇文章主要简单说下@Conditional注解的使用方法。
定义:
按照《Spring Boot实战》书中所说的:“@Conditional根据满足某一特定条件创建一个特定的Bean。比方说,当某一个jar包在一个类路径下的时候,自动配置一个或多个Bean;或者只有某个Bean被创建才会才会创建另外一个Bean。总的来说,就是根据特定判定条件来控制Bean的创建行为,这样我们可以利用这个特性进行一些自动的配置。”
在使用该注解之前我们来看一下@Conditional的源码
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
}
我们关注下面的两点:
-
@Target({ElementType.TYPE, ElementType.METHOD}):表示该注解可以加在类上,表示该类下面的所有@Bean都会启用配置;也可以加在方法上,只对该方法有效
-
Class<? extends Condition>[] value():表示要传一个Class集合给该注解,即@Conditional({XXX.class,XXX.class}) ,而且XXX类要继承Condition接口。关于Condition下面会说。
示例
我们以自己电脑的操作系统为判定条件,若我们的操作系统为Linux则创建Linux之父的Person的Bean;若操作系统为Windows则创建Windows之父的Person的Bean。
一、首先我们来创建一个Person类:
package com.study.spring.annotation.bean;
/**
* Created on 2020/3/31
* Package com.study.spring.annotation.bean
*
* @author dsy
*/
public class Person {
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
二、再创建一个配置类MyConfig:
package com.study.spring.annotation.config;
import com.study.spring.annotation.bean.Person;
import com.study.spring.annotation.condition.LinuxCondition;
import com.study.spring.annotation.condition.WindowsCondition;
import org.springframework.context.annotation.*;
/**
* Created on 2020/3/31
* Package com.study.spring.annotation.config
*
* @author dsy
*/
@Configuration //标识该类是一个配置类,相当于之前的一个xml配置文件
public class MyConfig {
/**
* @Conditional({Condition}) 注解:按照一定的条件将bean添加到IOC容器中
*/
@Conditional({LinuxCondition.class})
@Bean("linux")
public Person person01(){
return new Person("linux之父",55);
}
@Bean("windows")
@Conditional({WindowsCondition.class})
public Person person02(){
return new Person("windows之父",48);
}
}
三、接下来创建两个Condition类:LinuxCondition和WindowsCondition:
package com.study.spring.annotation.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* Created on 2020/3/31
* Package com.study.spring.annotation.condition
*
* @author dsy
*/
//判断是否Linux系统 要进行判断必须实现Condition接口
public class LinuxCondition implements Condition {
/**
*
* @param conditionContext 判断条件能使用的上下文环境
* @param annotatedTypeMetadata 当前标注了@Conditional注解的注释信息
* @return Boolean
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
return property!=null&&property.contains("Linux");
}
}
package com.study.spring.annotation.condition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata;
/**
* Created on 2020/3/31
* Package com.study.spring.annotation.condition
*
* @author dsy
*/
//判断是否Windows系统 要进行判断必须实现Condition接口
public class WindowsCondition implements Condition {
/**
*
* @param conditionContext 判断条件能使用的上下文环境
* @param annotatedTypeMetadata 当前标注了@Conditional注解的注释信息
* @return Boolean
*/
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
Environment environment = conditionContext.getEnvironment();
String property = environment.getProperty("os.name");
return property != null && property.contains("Windows");
}
}
说明: 这两个类均实现了Condition接口,里面只有一个matches方法返回值为boolea类型,也就是说@Conditional({LinuxCondition.class})或者@Conditional({WindowsCondition.class})通过判断注解括号里面条件类的matches方法的返回值来判定要不要在IOC容器中创建由@Conditional注解标识的这个Bean;若matches返回值为true则创建该Bean,若返回false则不创建该Bean。
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
四、测试:
package com.study.spring;
import com.study.spring.annotation.bean.Person;
import com.study.spring.annotation.config.MyConfig;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import java.util.Map;
@SpringBootTest
class ApplicationTests {
/**
* @Conditional 注解来按照条件获取bean实例
*/
@Test
void testAnnotationConfig2(){
//创建IOC容器
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
//getBeanNamesForType方法按照类来获取该类的Bean的id
String[] names = applicationContext.getBeanNamesForType(Person.class);
//打印
for (String name:names){
System.out.println(name);
}
//获取具体的Bean
Map<String, Person> beans = applicationContext.getBeansOfType(Person.class);
System.out.println(beans);
}
}
打印输出:
windows
{windows=Person{name='windows之父', age=48}}
若我们改变运行环境,加上参数:-Dos.name=Linux
再次运行打印输出:
linux
{linux=Person{name='linux之父', age=55}}
总结
1、@Conditional注解的作用是按照一定的判定条件来控制Bean的创建
2、@Conditional可以加在一个配置类上,表示该类下面的全部的@Bean都会启用配置;也可以加在一个@Bean的方法上面,只对该方法有效
3、判定条件类XXXCondition类要实现Condition接口,并重写matches方法,在该方法里面写自己的判定逻辑
参考资料:《Spring Boot实战》