简介
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()
可以得到一个name
为a
的Model
实例。说明我们的配置生效了。
激活Profile
除了上面的激活方式外, 还有如下几种方式来激活:
- 通过
JVM
参数注入:-Dspring.profiles.active=xxx,yyy
JAVA_OPTS=$JAVA_OPTS:spring.profiles.actives=xxx,yyy
- Web环境下激活Profile
web.xml
设置context-param
<param-name>spring.profiles.actives</param-name> <param-value>xxx,yyy</param-value>
- 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
等, 这里不展开,感兴趣可以自行探索。
小结
- Profile提供了类似角色或这组一样的规模化注入, 颗粒度较大且整体来说属于静态的
- Conditional提供了动态注入的实现,更为精细,且有springboot-autoconfig的加持,使用起来丰富且简约。
参考
往期
本文源码已上传至Gitee