Art*_*tem 33 java spring spring-profiles
Spring Profile注释允许您选择配置文件.但是,如果您阅读文档,它只允许您使用OR操作选择多个配置文件.如果指定@Profile("A","B"),那么如果配置文件A或配置文件B处于活动状态,则bean将启动.
我们的用例不同,我们希望支持多种配置的TEST和PROD版本.因此,有时我们只想在配置文件TEST和CONFIG1都处于活动状态时自动装配bean.
有没有办法用Spring做到这一点?什么是最简单的方法?
Mit*_*hun 23
由于Spring不提供开箱即用的AND功能.我会建议以下策略:
当前@Profile注释具有条件注释@Conditional(ProfileCondition.class).在ProfileCondition.class其中迭代配置文件并检查配置文件是否处于活动状态.类似地,您可以创建自己的条件实现并限制注册bean.例如
public class MyProfileCondition implements Condition {
@Override
public boolean matches(final ConditionContext context,
final AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null) {
final MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (final Object value : attrs.get("value")) {
final String activeProfiles = context.getEnvironment().getProperty("spring.profiles.active");
for (final String profile : (String[]) value) {
if (!activeProfiles.contains(profile)) {
return false;
}
}
}
return true;
}
}
return true;
}
}
Run Code Online (Sandbox Code Playgroud)
在你的班上:
@Component
@Profile("dev")
@Conditional(value = { MyProfileCondition.class })
public class DevDatasourceConfig
Run Code Online (Sandbox Code Playgroud)
注意:我没有检查所有角落情况(如空值,长度检查等).但是,这个方向可能有所帮助.
f-C*_*-CJ 16
从Spring 5.1(包含在Spring Boot 2.1中)开始,可以在配置文件字符串注释中使用配置文件表达式.所以:
在Spring 5.1(Spring Boot 2.1)及以上版本中,它很简单:
@Component
@Profile("TEST & CONFIG1")
public class MyComponent {}
Run Code Online (Sandbox Code Playgroud)
Spring 4.x和5.0.x:
方法1:由@Mithun回答,它完全涵盖了你在配置文件注释中将OR转换为AND的情况,只要你用他的Condition类实现注释Spring Bean .但我想提供另一种方法,没有人提出它有它的专业和骗局.
方法2:只需使用@Conditional并创建Condition所需组合的实现.它有必须创建尽可能多的组合实现,但如果你没有很多组合,在我看来,它是一个更简洁的解决方案,它提供了更多的灵活性和实现更复杂的逻辑分辨率的机会.
方法2的实施如下.
你的春豆:
@Component
@Conditional(value = { TestAndConfig1Profiles.class })
public class MyComponent {}
Run Code Online (Sandbox Code Playgroud)
TestAndConfig1Profiles 执行:
public class TestAndConfig1Profiles implements Condition {
@Override
public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
return context.getEnvironment().acceptsProfiles("TEST")
&& context.getEnvironment().acceptsProfiles("CONFIG1");
}
}
Run Code Online (Sandbox Code Playgroud)
使用这种方法,您可以轻松覆盖更复杂的逻辑情况,例如:
(TEST&CONFIG1)| (TEST&CONFIG3)
只是想给出一个更新的问题答案,并补充其他答案.
一点点改进版的@Mithun回答:
public class AndProfilesCondition implements Condition {
public static final String VALUE = "value";
public static final String DEFAULT_PROFILE = "default";
@Override
public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() == null) {
return true;
}
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs == null) {
return true;
}
String[] activeProfiles = context.getEnvironment().getActiveProfiles();
String[] definedProfiles = (String[]) attrs.getFirst(VALUE);
Set<String> allowedProfiles = new HashSet<>(1);
Set<String> restrictedProfiles = new HashSet<>(1);
for (String nextDefinedProfile : definedProfiles) {
if (!nextDefinedProfile.isEmpty() && nextDefinedProfile.charAt(0) == '!') {
restrictedProfiles.add(nextDefinedProfile.substring(1, nextDefinedProfile.length()));
continue;
}
allowedProfiles.add(nextDefinedProfile);
}
int activeAllowedCount = 0;
for (String nextActiveProfile : activeProfiles) {
// quick exit when default profile is active and allowed profiles is empty
if (DEFAULT_PROFILE.equals(nextActiveProfile) && allowedProfiles.isEmpty()) {
continue;
}
// quick exit when one of active profiles is restricted
if (restrictedProfiles.contains(nextActiveProfile)) {
return false;
}
// just go ahead when there is no allowed profiles (just need to check that there is no active restricted profiles)
if (allowedProfiles.isEmpty()) {
continue;
}
if (allowedProfiles.contains(nextActiveProfile)) {
activeAllowedCount++;
}
}
return activeAllowedCount == allowedProfiles.size();
}
}
Run Code Online (Sandbox Code Playgroud)
无法在评论中发布.
另一种选择是播放@Profile注释所允许的类/方法级别.不像实施那样灵活,MyProfileCondition但如果适合您的情况则快速和干净.
例如,当FAST和DEV都处于活动状态时,这将不会启动,但如果只有DEV是:
@Configuration
@Profile("!" + SPRING_PROFILE_FAST)
public class TomcatLogbackAccessConfiguration {
@Bean
@Profile({SPRING_PROFILE_DEVELOPMENT, SPRING_PROFILE_STAGING})
public EmbeddedServletContainerCustomizer containerCustomizer() {
Run Code Online (Sandbox Code Playgroud)
我改进了@ rozhoc的答案,因为答案没有说明在使用@Profile时没有配置文件等同于'default'.另外,我想要的条件是!default && !a@ rozhoc的代码没有正确处理.最后我使用了一些Java8并仅显示matches了简洁的方法.
@Override
public boolean matches(final ConditionContext context, final AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() == null) {
return true;
}
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs == null) {
return true;
}
Set<String> activeProfilesSet = Arrays.stream(context.getEnvironment().getActiveProfiles()).collect(Collectors.toSet());
String[] definedProfiles = (String[]) attrs.getFirst(VALUE);
Set<String> allowedProfiles = new HashSet<>(1);
Set<String> restrictedProfiles = new HashSet<>(1);
if (activeProfilesSet.size() == 0) {
activeProfilesSet.add(DEFAULT_PROFILE); // no profile is equivalent in @Profile terms to "default"
}
for (String nextDefinedProfile : definedProfiles) {
if (!nextDefinedProfile.isEmpty() && nextDefinedProfile.charAt(0) == '!') {
restrictedProfiles.add(nextDefinedProfile.substring(1, nextDefinedProfile.length()));
continue;
}
allowedProfiles.add(nextDefinedProfile);
}
boolean allowed = true;
for (String allowedProfile : allowedProfiles) {
allowed = allowed && activeProfilesSet.contains(allowedProfile);
}
boolean restricted = true;
for (String restrictedProfile : restrictedProfiles) {
restricted = restricted && !activeProfilesSet.contains(restrictedProfile);
}
return allowed && restricted;
}
Run Code Online (Sandbox Code Playgroud)
以下是您实际使用它的方式,以防令人困惑:
@Profile({"!default", "!a"})
@Conditional(value={AndProfilesCondition.class})
Run Code Online (Sandbox Code Playgroud)
如果您已经使用@Profile注释标记了配置类或bean方法,则可以轻松检查其他配置文件(例如,对于AND条件) Environment.acceptsProfiles()
@Autowired Environment env;
@Profile("profile1")
@Bean
public MyBean myBean() {
if( env.acceptsProfiles("profile2") ) {
return new MyBean();
}
else {
return null;
}
}
Run Code Online (Sandbox Code Playgroud)
另一种技巧但可能在许多情况下都可以使用@Profile注释放在@Configuration上,另一个@Profile放在@Bean上 - 它在基于java的spring配置中的2个配置文件之间创建逻辑AND.
@Configuration
@Profile("Profile1")
public class TomcatLogbackAccessConfiguration {
@Bean
@Profile("Profile2")
public EmbeddedServletContainerCustomizer containerCustomizer() {
Run Code Online (Sandbox Code Playgroud)