【问题标题】:Spring - Multiple Profiles activeSpring - 多个配置文件处于活动状态
【发布时间】:2023-03-26 17:59:01
【问题描述】:

我基本上在 Spring 中有一个 bean,我只想在 2 个配置文件处于活动状态时激活它。基本上,它会是这样的:

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

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

所以我希望当我使用-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

是否可以仅在两个配置文件都处于活动状态或我使用不正确时才注册 bean? :)

谢谢!!


编辑:发布完整堆栈。

错误实际上是在属性上缺少的属性上,但是这个 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}"

【问题讨论】:

  • 这看起来不像是配置文件的问题。检查 appConfigMongodbLinux 中的自动装配 bean 是否可用。发布完整的堆栈跟踪。
  • @ShankarPS 看起来配置文件确实有问题。他不想激活的配置文件正在寻找该配置中不可用的属性。

标签: java spring


【解决方案1】:

很遗憾,如果任何列出的配置文件处于活动状态,@Profile 就会激活。有几种方法可以解决这个问题。

  • 将常见的@Profile("Tomcat") 注解应用于顶级配置类,然后将@Profile("Windows") 应用于嵌套的配置类(或@Bean 方法)。
  • 如果可以接受 Spring Boot 作为依赖项,请使用 @AllNestedConditions 创建一个注释,它是 AND 而不是 OR。

如果您使用的是 Spring Boot 自动配置类,您正在尝试做的事情看起来会很干净;如果在应用程序生命周期的这个阶段引入自动配置是可行的,我建议考虑它。

【讨论】:

【解决方案2】:
@ConditionalOnExpression("#{environment.acceptsProfiles('Tomcat') && environment.acceptsProfiles('Linux')}")

致谢:Spring 源代码。使用您的 IDE 查找 @ConditionalOnExpression,然后“查找用法”以查看源代码中的相关示例。这将使您成为更好的开发人员。

【讨论】:

  • 它是否接受 !(NOT) 运算符?
【解决方案3】:

Spring 5.1 版 及更高版本提供了用于指定更复杂的配置文件字符串表达式的附加功能。在您的情况下,可以通过以下方式实现所需的功能:

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

请阅读 Spring 参考文档中的 Using @Profile 章节以获取更多信息。

更新(方法级配置文件表达式): 实际上我已经测试了一些@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;
    }
}

集成测试:

/**
 * 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 版本已测试。

【讨论】:

  • 它似乎只适用于配置(类)级别。我在方法级别使用此语法进行了一些测试,但它没有遵守 spring 5.1 的条件。
  • @agodinhost 你能看看更新部分吗?
  • 看来我使用了错误的语法 - 没有大括号 - 我不记得了。我实际上使用的是 spring boot 2.1.1.RELEASE - 它不是我所说的 spring 5.1。这些花括号很烦人,但它是有道理的——它是一个表达,有点短,但表达对吗?
  • @agodinhost 它是一个内联数组,遵循标准 Java 语法(例如,查看 link)。 BTW Spring Boot 2.1.1 RELEASE 使用 Spring 5.1.3
  • @PéterVeres @Profile({"Tomcat", "Linux"}) 表示其中之一(或)
【解决方案4】:

第一个配置文件位于顶层。 我第二次检查是这样的:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-15
    • 2022-10-24
    • 2013-10-03
    • 1970-01-01
    • 1970-01-01
    • 2018-08-31
    相关资源
    最近更新 更多