简体   繁体   中英

Spring - Multiple Profiles active

I basically have a bean in Spring that I wanted to activate only when 2 profiles are active. 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 .

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? :)

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? I wanted to understand this to ensure I'm not activating a wrong 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. 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.

Update (method level profile expressions): Actually I've tested some @Bean method level profile expressions and everything works like a charm:

/**
 * 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.

Unfortunately, @Profile activates if any listed profile is active. 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).
  • If Spring Boot is acceptable as a dependency, use @AllNestedConditions to create an annotation that's the AND instead of the OR.

It looks like what you're trying to do would be clean to write if you were using Spring Boot autoconfiguration classes; 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. Look up the @ConditionalOnExpression with your IDE then 'find usages' to see relevant examples within the source code. 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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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