简体   繁体   English

Spring - 多个配置文件处于活动状态

[英]Spring - Multiple Profiles active

I basically have a bean in Spring that I wanted to activate only when 2 profiles are active.我基本上在 Spring 中有一个 bean,我只想在 2 个配置文件处于活动状态时激活它。 Basically, it would be like:基本上,它会是这样的:

@Profile({"Tomcat", "Linux"})
public class AppConfigMongodbLinux{...}

@Profile({"Tomcat", "WindowsLocal"})
public class AppConfigMongodbWindowsLocal{...}

So I'd like that when I use -Dspring.profiles.active=Tomcat,WindowsLocal , it would try to use only the AppConfigMongodbWindowsLocal , but it still tries to register the AppConfigMongodbLinux .所以我希望当我使用-Dspring.profiles.active=Tomcat,WindowsLocal ,它会尝试只使用AppConfigMongodbWindowsLocal ,但它仍然尝试注册AppConfigMongodbLinux

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'appConfigMongodbLinux': Injection of autowired dependencies failed

Is it possible to make the bean be registerd only when both profiles are active or am I using it incorrectly?是否可以仅在两个配置文件都处于活动状态时才注册 bean,或者我是否错误地使用了它? :) :)

Thanks!!谢谢!!


Edit: Posting the full stack.编辑:发布完整堆栈。

The error is actually on a property that is missing on the properties, but will this bean get activated?错误实际上是在属性中缺少的属性上,但是这个 bean 会被激活吗? I wanted to understand this to ensure I'm not activating a wrong bean..我想了解这一点以确保我没有激活错误的 bean..

org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[]]
    ...
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'appConfigMongodbLinux': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.lang.Integer mycompany.config.AppConfigMongodbLinux.mongoPort; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'mongo.port' in string value "${mongo.port}"
    ... 40 more
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private java.lang.Integer mycompany.config.AppConfigMongodbLinux.mongoPort; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'mongo.port' in string value "${mongo.port}"
    ...
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'mongo.port' in string value "${mongo.port}"

Spring version 5.1 and above offers additional functionality for specifying more complex profile string expressions. Spring 5.1及更高版本提供了用于指定更复杂的配置文件字符串表达式的附加功能。 In your case desired functionality can be achieved in the following way:在您的情况下,可以通过以下方式实现所需的功能:

@Profile({"Tomcat & Linux"})
@Configuration
public class AppConfigMongodbLinux {...}

Please read Using @Profile chapter from Spring reference documentation for more info.请阅读 Spring 参考文档中的Using @Profile章节以获取更多信息。

Update (method level profile expressions): Actually I've tested some @Bean method level profile expressions and everything works like a charm:更新(方法级配置文件表达式):实际上我已经测试了一些 @Bean 方法级配置文件表达式,一切都像魅力一样:

/**
 * Shows basic usage of {@link Profile} annotations applied on method level.
 */
@Configuration
public class MethodLevelProfileConfiguration {

    /**
     * Point in time related to application startup.
     */
    @Profile("qa")
    @Bean
    public Instant startupInstant() {
        return Instant.now();
    }

    /**
     * Point in time related to scheduled shutdown of the application.
     */
    @Bean
    public Instant shutdownInstant() {
        return Instant.MAX;
    }

    /**
     * Point in time of 1970 year.
     */
    @Profile("develop & production")
    @Bean
    public Instant epochInstant() {
        return Instant.EPOCH;
    }
}

Integration tests:集成测试:

/**
 * Verifies {@link Profile} annotation functionality applied on method-level.
 */
public class MethodLevelProfileConfigurationTest {

    @RunWith(SpringRunner.class)
    @ContextConfiguration(classes = MethodLevelProfileConfiguration.class)
    @ActiveProfiles(profiles = "qa")
    public static class QaActiveProfileTest {

        @Autowired
        private ApplicationContext context;

        @Test
        public void shouldRegisterStartupAndShutdownInstants() {
            context.getBean("startupInstant", Instant.class);
            context.getBean("shutdownInstant", Instant.class);

            try {
                context.getBean("epochInstant", Instant.class);
                fail();
            } catch (NoSuchBeanDefinitionException ex) {
                // Legal to ignore.
            }
        }
    }

    @RunWith(SpringRunner.class)
    @ContextConfiguration(classes = MethodLevelProfileConfiguration.class)
    @ActiveProfiles(profiles = {"develop", "production"})
    public static class MethodProfileExpressionTest {

        @Autowired
        private ApplicationContext context;

        @Test
        public void shouldRegisterShutdownAndEpochInstants() {
            context.getBean("epochInstant", Instant.class);
            context.getBean("shutdownInstant", Instant.class);

            try {
                context.getBean("startupInstant", Instant.class);
                fail();
            } catch (NoSuchBeanDefinitionException ex) {
                // Legal to ignore.
            }
        }
    }
}

Spring 5.1.2 version was tested. Spring 5.1.2 版本已测试。

Unfortunately, @Profile activates if any listed profile is active.不幸的是, @Profile激活如有上市配置被激活。 There are a couple of ways around this.有几种方法可以解决这个问题。

  • Apply the common @Profile("Tomcat") annotation to a top-level configuration class, and then apply @Profile("Windows") to a nested configuration class (or @Bean method).将常见的@Profile("Tomcat")注释应用于顶级配置类,然后将@Profile("Windows")应用于嵌套的配置类(或@Bean方法)。
  • If Spring Boot is acceptable as a dependency, use @AllNestedConditions to create an annotation that's the AND instead of the OR.如果 Spring Boot 可接受作为依赖项,请使用@AllNestedConditions创建一个注释,即 AND 而不是 OR。

It looks like what you're trying to do would be clean to write if you were using Spring Boot autoconfiguration classes;如果您使用 Spring Boot 自动配置类,看起来您尝试做的事情写起来很干净; if it's practical to introduce autoconfiguration at this stage of your application's lifecycle, I recommend considering it.如果在应用程序生命周期的这个阶段引入自动配置是可行的,我建议考虑它。

@ConditionalOnExpression("#{environment.acceptsProfiles('Tomcat') && environment.acceptsProfiles('Linux')}")

Credits: Spring Source Code.致谢:Spring 源代码。 Look up the @ConditionalOnExpression with your IDE then 'find usages' to see relevant examples within the source code.使用您的 IDE 查找 @ConditionalOnExpression,然后“查找用法”以查看源代码中的相关示例。 This will enable you to become a better developer.这将使您成为更好的开发人员。

The first profile is on the Top level.第一个配置文件位于顶级。 The second I checked like this:第二个我是这样检查的:

@Autowired
private Environment env;
...
final boolean isMyProfile = Arrays.stream(env.getActiveProfiles()).anyMatch("MyProfile"::equals);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM