Spring-Profile和Conditional

简介

spring支持模块化配置,项目中也经常会有选择性注入的场景。spring提供的Profile和Conditional机制是这种选择性注入的具体实现手段。
本文通过JavaConfig方式进行演示。

Maven依赖

虽然这里说的是Spring, 但不妨引入spring-boot-autoconfig, 因为它丰富了条件化注解、简化了依赖配置, 同时又可以在纯Spring项目中起作用。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-autoconfigure</artifactId>
            <version>2.4.5</version>
        </dependency>

Profile

Spring提供了Profile注解, 可以作用于类和方法上, Spring容器刷新时, 会根据配置的activeProfile选择性注入。
先定义一个普通的配置类:

@Configuration
public class DefaultConfig implements Condition {
    @Bean
    @Primary
    public Model model() {
        Model model = new Model();
        model.setName("default");
        return model;
    }
}

再定义一个带有Profile注解的配置类:

@Profile("profile-a")
@Configuration
public class ProfileAConfig {
    @Bean
    public Model aModel() {
        Model model = new Model();
        model.setName("a");
        return model;
    }
}

同理, 再定义一个profile-b, 此处省略代码。

激活并刷新容器

		AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        ConfigurableEnvironment environment = context.getEnvironment();
        environment.setActiveProfiles("profile-a", "profile-b");
        // 要设置激活的Profile,必须放置在扫描前,否则不生效
        context.scan("com.yan");
        context.refresh();

此时调用方法context.getBean()可以得到一个nameaModel实例。说明我们的配置生效了。

激活Profile

除了上面的激活方式外, 还有如下几种方式来激活:

  1. 通过JVM参数注入:
    1. -Dspring.profiles.active=xxx,yyy
    2. JAVA_OPTS=$JAVA_OPTS:spring.profiles.actives=xxx,yyy
  2. Web环境下激活Profile
    1. web.xml设置context-param
      	<param-name>spring.profiles.actives</param-name>
      	<param-value>xxx,yyy</param-value>
      
    2. JavaConfig方式
      public class MyWebAppInitializer implements WebApplicationInitializer {
        @Override
        public void onStartup(ServletContext container) {
       	container.setInitParameter("spring.profiles.active", "xxx,yyy");
        }
       }
      

参考官方文档:Profile

Conditional

Profile机制虽然很好, 但它(整体来说)属于静态注入, 即你事先定好要开启哪些禁用哪些, 如果要实现动态条件化注入,就必须借助注解Conditional了, 它的定义如下:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {

	/**
	 * All {@link Condition} classes that must {@linkplain Condition#matches match}
	 * in order for the component to be registered.
	 */
	Class<? extends Condition>[] value();

}

可以看出, 它只有一个实现了Condition接口的数组类型入参, 多个条件必须同时满足才会注入。
比如, 当存在Dummy这个类时,再注入Model实例, 可以这样定义:

    @Bean
    @Conditional(ICondition.class)
    public Model modelCondition() {
        Model model = new Model();
        model.setName("conditionalByConditional");
        return model;
    }

    public static class ICondition implements Condition {
        @Override
        public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
            return org.springframework.util.ClassUtils.isPresent(Dummy.class.getName(), DefaultConfig.class.getClassLoader());
        }
    }

springboot中提供了更为方便且更为丰富的注解, 比如同样的场景, 可以这样写:

    @Bean
    @ConditionalOnClass(Dummy.class)
    public Model modelConditionalOnClass() {
        Model model = new Model();
        model.setName("dummy");
        return model;
    }

除了ConditionalOnClass, 还有很多其他内置注解, 比如ConditionalOnBean等, 这里不展开,感兴趣可以自行探索。

小结

  1. Profile提供了类似角色或这组一样的规模化注入, 颗粒度较大且整体来说属于静态的
  2. Conditional提供了动态注入的实现,更为精细,且有springboot-autoconfig的加持,使用起来丰富且简约。

参考

  1. Spring.profiles多环境配置最佳实践

往期

  1. Spring Xml和JavaConfig

本文源码已上传至Gitee

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值