Spring原理学习--扩展@Conditional注解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wzl19870309/article/details/84940673

今天深入学习了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());
    }
}

两种方式的测试结果都是:

中关村小学:北京海淀区中关村

以上只是自己学习过程中的一些想法,记录下来并分享给大家,如有不合适地方,大家多多交流

猜你喜欢

转载自blog.csdn.net/wzl19870309/article/details/84940673