今天深入学习了Spring的@Conditional注解。@Conditional注解可以根据代码中设置的条件装载不同的bean,在设置条件注解之前,先要把装载的bean类去实现Condition接口,然后对该实现接口的类设置是否装载的条件。
具体的使用方法网上很多,在此就不再赘述。今天主要想记录和分享一下,在单纯Spring的环境下,实现类似于Springboot中@ConditionalOnExpression的功能,该注解是通过判断表达式的值来进行条件检查的。我现在只是简单实现了通过表达式${"……"}来获取配置文件里的值,来实现条件判断
准备工作:
- 1、准备一个主配置文件和两个子配置文件
application.properties、appliction-dev.properties、appliction-test.properties
当配置文件中配置的Spring.profile.active.test = true时,只加载appliction-test.properties中的才参数,装配TestConfig中定义的bean
application.properties
Spring.profile.active = TEST
Spring.profile.active.test = true
appliction-test.properties
school.name = 中关村小学
school.address = 北京海淀区中关村
appliction-dev.properties
school.name = 朝阳小学
school.address = 北京朝阳区
-
2、创建了一个主配置类和两个子配置类
MainConfig、TestConfig、DevConfig
@PropertySource({"classpath:/properties/application.properties"})
@Configuration
@ComponentScan(value = {"com.***.core"})
public class MainConfig {
}
@Configuration //告诉容器该类是个配置类
@PropertySource({"classpath:/properties/application-test.properties"})
@Conditional({ProfileConditionTest.class}) //最开始使用的方式
public class ConfigTest {
@Bean
public School school(@Value("${school.name}") String name,@Value("${school.address}") String address) {
return new School(name,address);
}
}
@Configuration //告诉容器该类是个配置类
@PropertySource({"classpath:/properties/application-dev.properties"})
@Conditional({ProfileConditionDev.class}) //最开始使用的方式
public class ConfigDev {
@Bean
public School school(@Value("${school.name}") String name,@Value("${school.address}") String address) {
return new School(name,address);
}
3、开始的实现是定义了两个类分别实现了condition接口,配置见上边ConfigDev和ConfigTest。两个自定义类如下:
public class ProfileConditionDev implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if("DEV".equals(context.getEnvironment().getProperty("Spring.profile.active").toUpperCase()))
return true;
return false;
}
}
public class ProfileConditionTest implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if("TEST".equals(context.getEnvironment().getProperty("Spring.profile.active").toUpperCase()))
return true;
return false;
}
}
后边考虑使用一个注解来实现该条件判断方式,就自定义了一个@ProfileConditional,如下:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ProfileCondition.class)
public @interface ProfileConditional {
String value() default "false";
}
ProfileCondition实现如下:
@Component
public class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) throws IllegalArgumentException {
String expression = (String) metadata
.getAnnotationAttributes(ProfileConditional.class.getName())
.get("value"); //获取注解中的表达式,即Value的值
if (StringUtils.isBlank(expression)) {
return Boolean.FALSE;
}
// 解析表达式
expression = expression.replace("${","").replace("}","");
String[] expressions = expression.split(":");
if (expressions.length > 2) {
throw new IllegalArgumentException(String.format("Profile %s formatter is wrong",expression));
}
Boolean result = Boolean.FALSE;
if (StringUtils.isNotBlank(context.getEnvironment().getProperty(expressions[0]))) {
result = Boolean.valueOf(context.getEnvironment().getProperty(expressions[0]));
} else if(expressions.length == 2){ //如果配置文件没有配置,则取表达式上的默认值即:后边的值
result = Boolean.valueOf(expressions[1]);
}
return (result != null && (boolean) result);
}
}
CongfigDev和CongfigTest配置如下:
@Configuration //告诉容器该类是个配置类
@PropertySource({"classpath:/properties/application-test.properties"})
@Conditional({ProfileConditionTest.class}) //最开始使用的方式
public class ConfigTest {
@Bean
public School school(@Value("${school.name}") String name,@Value("${school.address}") String address) {
return new School(name,address);
}
}
@Configuration //告诉容器该类是个配置类
@PropertySource({"classpath:/properties/application-dev.properties"})
@Conditional({ProfileConditionDev.class}) //最开始使用的方式
public class ConfigDev {
@Bean
public School school(@Value("${school.name}") String name,@Value("${school.address}") String address) {
return new School(name,address);
}
测试类:
public class LuxyConfig_Test_profileTest {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
@Test
public void testConfig(){
School school = (School)context.getBean(School.class);
System.out.println(school.getName() + ":" +school.getAddress());
}
}
两种方式的测试结果都是:
中关村小学:北京海淀区中关村
以上只是自己学习过程中的一些想法,记录下来并分享给大家,如有不合适地方,大家多多交流