Hot questions for Spring Profiles

Top 10 Java Open Source / Spring / Spring Profiles

Question:

I have two Spring profiles: dev and test. I want to set the active profile in the server environment, I don't want to set it in my code so that wherever I deploy my application the profile gets loaded based on the profile in the server. How can I do that?


Answer:

You can simply set a system property on the server as follows...

-Dspring.profiles.active=test

Edit: To add this to tomcat in eclipse, select Run -> Run Configurations and choose your Tomcat run configuration. Click the Arguments tab and add -Dspring.profiles.active=test at the end of VM arguments. Another way would be to add the property to your catalina.properties in your Servers project, but if you add it there omit the -D

Edit: For use with Spring Boot, you have an additional choice. You can pass the property as a program argument if you prepend the property with two dashes.

Here are two examples using a Spring Boot executable jar file...

System Property

[user@host ~]$ java -jar -Dspring.profiles.active=test myproject.jar

Program Argument

[user@host ~]$ java -jar myproject.jar --spring.profiles.active=test

Question:

I have an application with maven as a build tool.

I am using maven profiles to set up different properties from different profiles.

What i would like to do is that all active profiles in maven will be ported to spring active profiles as well so i can reference them in bean signature (@profile). but i am not sure how to do it.

for example: consider the following maven setup

<profiles>
    <profile>
        <id>profile1</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
        </properties>
    </profile>
    <profile>
        <id>profile2</id>
        <properties>
        </properties>
    </profile>
    <profile>
        <id>development</id>
        <activation>
            <activeByDefault>true</activeByDefault>
        </activation>
        <properties>
        </properties>
    </profile>
    <profile>
        <id>production</id>
        <properties>    
        </properties>
    </profile>
</profiles>

assuming i run maven with out specifying any other profiles i would like for spring to have profile1 and development as active profiles.


Answer:

There is a more elegant way to switch between 2 maven+spring profiles simultaneously.

First, add profiles to POM (pay attention - maven+spring profile is activated by single system variable):

<profiles>
    <profile>
        <id>postgres</id>
        <activation>
            <activeByDefault>true</activeByDefault>
            <property>
                <name>spring.profiles.active</name>
                <value>postgres</value>
            </property>
        </activation>
        <dependencies>
            <dependency>
                <groupId>postgresql</groupId>
                <artifactId>postgresql</artifactId>
                <version>9.1-901.jdbc4</version>
            </dependency>
        </dependencies>
    </profile>
    <profile>
        <id>h2</id>
        <activation>
            <property>
                <name>spring.profiles.active</name>
                <value>h2</value>
            </property>
        </activation>           
        <dependencies>
            <dependency>
                <groupId>com.h2database</groupId>
                <artifactId>h2</artifactId>
                <version>1.4.191</version>
            </dependency>
        </dependencies>
    </profile>
</profiles>

Second, set default profile for spring (for maven it is already set in POM). For web application, I inserted following lines to web.xml:

<context-param>
   <param-name>spring.profiles.default</param-name>
   <param-value>postgres</param-value>
</context-param>

Third, add profile-dependent beans to your config. In my case (XML config), it is:

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="mainDataSource" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter" />
    </property>
    <property name="jpaProperties" ref="hibProps"/>
    <property name="packagesToScan">
        <list>
            <value>my.test.model</value>
        </list>
    </property>
</bean>
...
<beans profile="postgres">
    <bean name="mainDataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.postgresql.Driver" />
        <property name="url" value="jdbc:postgresql://127.0.0.1:5432/webchat" />
        <property name="username" value="postgres" />
        <property name="password" value="postgres" />
    </bean>
</beans>

<beans profile="h2">
    <bean name="mainDataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.h2.Driver" />
        <property name="url" value="jdbc:h2:file:./newsdb;INIT=RUNSCRIPT FROM 'classpath:init.sql';TRACE_LEVEL_FILE=0" />
        <property name="username" value="sa" />
        <property name="password" value="" />
    </bean>
</beans>

Now it is possible to:

  • Run my web-app on Postgres DB with mvn jetty:run or mvn jetty:run -Dspring.profiles.active=postgres commands
  • Run my web-app on H2 DB with mvn clean jetty:run -Dspring.profiles.active=h2

Question:

Now I have a spring-boot app which uses MsSQL server. And we use flyway for migrations.

I want to add an additional profile for tests. I want to generate tables from entity classes instead of using flyway.

I tried smth to write like this in application.yaml

spring:
  profiles: test
  jpa:
      generate-ddl: true
      hibernate:
  datasource:
    url: jdbc:h2:mem:test_db;MODE=MSSQLServer
    username: sa
    password:

but flyway starts anyway


Answer:

FYI, for anybody who comes here looking for this, the property name has changed for Spring Boot 2.0:

For application.properties format:

spring.flyway.enabled=false

For application.yml format:

spring:
    flyway:
        enabled: false

Update: To disable flyway in a specific profile, you can put that property in the properties file specific to that profile. For instance, if your profile is called "abc", you can put it in application-abc.properties. Check out Spring's documentation on Profile-specific properties for more clarity on how to name the files. Generally, the format is application-{profileName}.properties.

Question:

Spring Profile annotation allows you to select profiles. However if you read documentation it only allows you to select more than one profile with OR operation. If you specify @Profile("A", "B") then your bean will be up if either profile A or profile B is active.

Our use case is different we want to support TEST and PROD versions of multiple configurations. Therefore sometimes we want to autowire the bean only if both profiles TEST and CONFIG1 are active.

Is there any way to do it with Spring? What would be the simplest way?


Answer:

Since Spring does not provide the AND feature out of the box. I would suggest the following strategy:

Currently @Profile annotation has a conditional annotation @Conditional(ProfileCondition.class). In ProfileCondition.class it iterates through the profiles and checks if the profile is active. Similarly you could create your own conditional implementation and restrict registering the bean. e.g.

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;
    }

}

In your class:

@Component
@Profile("dev")
@Conditional(value = { MyProfileCondition.class })
public class DevDatasourceConfig

NOTE: I have not checked for all the corner cases (like null, length checks etc). But, this direction could help.

Question:

I've generated a Spring Boot web application using Spring Initializer, embedded Tomcat, Thymeleaf template engine.Technologies used: Spring Boot 1.4.2.RELEASE, Spring 4.3.4.RELEASE, Thymeleaf 2.1.5.RELEASE, Tomcat Embed 8.5.6, Maven 3, Java 8

I have an SpringBoot app. with these 2 classes:

@Profile("!war")
@SpringBootApplication
@Import({SecurityConfig.class ,PersistenceConfig.class, ServiceConfig.class})
public class BookApplication {

    public static void main(String[] args) {
        SpringApplication.run(BookApplication.class, args);
    }
}

@Profile("war")
@Import({SecurityConfig.class ,PersistenceConfig.class})
@SpringBootApplication
public class BookApplicationWar extends SpringBootServletInitializer {

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(BookApplicationWar.class);
    }

    public static void main(String[] args) throws Exception {
        SpringApplication.run(BookApplicationWar.class, args);
    }

}

I generate the war with this command

 mvn clean package -DskipTests -Dspring.profiles.active=pebloc,war -DAPP-KEY=pebloc

But I got this error:

[ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:1.5.2.RELEASE:repackage (default) on project book: Execution default of goal org.springframework.boot:spring-boot-maven-plugin:1.5.2.RELEASE:repackage failed: Unable to find a single main class from the following candidates [com.tdk.BookApplication, com.tdk.BookApplicationWar] -> [Help 1]

Answer:

If you have more than one main class, you need to explicitly configure the main class in each profile:

<profiles>
    <profile>
        <id>profile1</id>
        <properties>
          <spring.boot.mainclass>com.SomeClass</spring.boot.mainclass>
        </properties>
    </profile>
    <profile>
        <id>profile2</id>
        <properties>
          <spring.boot.mainclass>com.SomeOtherClass</spring.boot.mainclass>
        </properties>
    </profile>
</profiles>

...

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
    <version>1.5.2.RELEASE</version>
    <executions>
      <execution>
        <goals>
          <goal>repackage</goal>
        </goals>
        <configuration>
          <mainClass>${spring.boot.mainclass}</mainClass>
        </configuration>
      </execution>
    </executions>
    ...
</plugin>

See spring-boot:repackage

Question:

I am just wondering what the order of precedence is when multiple Spring active profiles have been specified.

Say I want the default profile to be active but the dev profile to override it when there are several identical elements (beans for instance) to choose from but with different profiles...

Say for instance I have two PropertySourcesPlaceholderConfigurer beans configured with "default" and "dev" values a environment profiles.

If I use the following profile activation: -Dspring.profiles.active="default,dev"

Will the dev profile override the default one?

If not how can the above behavior be achieved?


Answer:

The order of the profiles in the spring.profiles.active system property doesn't matter. "Precedence" is defined by the declaration order of the beans, including beans specific to a profile, and the last bean definition wins.

Using your example, if -Dspring.profiles.active="default,dev" is used, the props bean in the default profile would be used here, simply because it's the last active definition of that bean:

<beans profile="dev">
    <bean id="props" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="location" value="classpath:META-INF/dev.properties"/>
    </bean>
</beans>
<beans profile="default">
    <bean id="props" class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
        <property name="location" value="classpath:META-INF/default.properties"/>
    </bean>
</beans>

Invert the order of the beans, and then the dev version would be used, regardless of how the profiles are ordered in spring.profiles.active.

Notice that I did not use <context:property-placeholder/> because it does not allow you to explicitly specify a bean id, and so I'm not sure what behavior it would exhibit if more than one is used. I imagine that the properties would be merged, so that properties defined by both would use the last definition, but properties specific to each file would remain intact.

Otherwise, in my experience, you would typically define beans in this order:

  1. "Default" bean definitions, not specific to a profile
  2. Overriding bean definitions in an environment-specific profile
  3. Overriding bean definitions in a test-specific profile

This way, test profile beans would win if used in combination with other profiles; else you would either use environment-specific beans or default beans based on the profile.

Question:

I've a Spring Boot application with different Profile setup : dev, prod, qc, console etc.

The two configuration classes are setup as follows. MyConfigurationA should be registered for all profiles except console. MyConfigurationB should be registered except for console and dev.

When I run the application with profile console, the MyConfigurationA doesn't get registered - which is fine. But MyConfigurationB gets registered - which I do not want. I've setup the @Profile annotation as follows to not register the MyConfigurationB for profile console and dev .

But the MyConfigurationB is getting registered when I run the application with profile console.

@Profile({ "!" + Constants.PROFILE_CONSOLE ,  "!" + Constants.PROFILE_DEVELOPMENT })

The documentation ( http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/Profile.html) has an example of including one profile and excluding the other. In my example I'm excluding both as @Profile({"!p1", "!p2"}):

@Profile({"p1", "!p2"}), registration will occur if profile 'p1' is active OR if profile 'p2' is not active.

My question is : How can we skip registration of the configurations of both profiles? @Profile({"!p1", "!p2"}) is doing OR operation. We need AND operation here.


The code :

@Configuration
@Profile({ "!" + Constants.PROFILE_CONSOLE  })
public class MyConfigurationA {
    static{
        System.out.println("MyConfigurationA registering...");
    }
}

@Configuration
@Profile({ "!" + Constants.PROFILE_CONSOLE ,  "!" + Constants.PROFILE_DEVELOPMENT }) // doesn't exclude both, its OR condition
public class MyConfigurationB {
    static{
        System.out.println("MyConfigurationB registering...");
    }
}

public final class Constants {
    public static final String PROFILE_DEVELOPMENT = "dev";
    public static final String PROFILE_CONSOLE = "console";
    ...
}

Answer:

@Profile({"!console", "!dev"}) means (NOT console) OR (NOT dev) which is true if you run your app with the profile 'console'. To solve this you can create a custom Condition:

public class NotConsoleAndDevCondition implements Condition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        Environment environment = context.getEnvironment();
        return !environment.acceptsProfiles("console", "dev");
    }
}

And apply the condition via the @Conditional annotation to the Configuration:

@Conditional(NotConsoleAndDevCondition.class)
public class MyConfigurationB {

Question:

I have an interface define as below:

public interface MyService {
}

And two classes implementing it:

@Service
@Profile("dev")
public class DevImplementation implements MyService {
}

and

@Service
@Profile("prod")
public class ProdImplementation implements MyService {
}

And there's another service trying to use it:

@Service
public MyClass {
    @Autowired
    MyService myservice;
}

The problem is that I'm getting NoSuchBeanException when running the code. It's run using

mvn spring-boot:run -P dev

What am I doing wrong?


Answer:

With -P you enable a Maven profile. However Maven profiles are independent of Spring profiles. As long as you don't have the Maven profile configured to set the appropriate Spring property, you have to enable the Spring profile this way:

mvn spring-boot:run -Dspring.profiles.active=dev

Question:

My intention is to have two profiles in a spring boot application - development and production one. Development profile is meant just to override some variables of production profile (like in-memory database instead of database in the cloud). As I expect some changes to be done to production profile in the future, duplicating variables in development profile doesn't seem to be a solution.

So, in Spring Reference I read that spring.profiles.include is supposed to only add properties from referenced profile, but from what I've checked it rather overrides it. So, when having two profiles foo and bar, in separate yaml files:

application-foo.yaml:

myproperty: 44

application-bar.yaml:

spring:
  profiles:
    include: foo
    active: bar,foo
myproperty: 55

And setting -Dspring.profiles.active=bar variable in IDE, the runtime value of myproperty is 44. That means that bar, is overriden with foo which was supposed to only add properties, but not to override them. When starting the application, I get:

The following profiles are active: foo,bar

I added spring.profiles.active=bar to application-bar.yaml as suggested by this answer, in another question, but it has no effect - there is no difference when property is there or not (I also tried using dash listing instead of comma separated values).

My question is, is it how it is supposed to work (then Spring Reference is misleading)? If so, are there any solutions for that?

Adding a link to the application source code on a github.


Answer:

We implemented the Spring active profiles in a slightly different way. Let's say the default properties file, application.yml, contains all default values which is same in both production and development environments.

Create separate properties for production and development files named application-prd.yml and application-dev.yml respectively. These files may contain extra properties or override some of the default properties.

During application startup, we pass the spring.profiles.active as an environment variable. For example,

-Dspring.profiles.active=prd

will pick up application-prd.yml along with application.yml

or

-Dspring.profiles.active=dev

will pick up application-dev.yml along with application.yml

Question:

We have different config servers per environment. Each spring boot application should target its corresponding config server. I have tried to achieve this by setting profiles in the bootstrap.properties file, e.g.:

spring.application.name=app-name
spring.cloud.config.uri=http://default-config-server.com

---
spring.profiles=dev
spring.cloud.config.uri=http://dev-config-server.com

---
spring.profiles=stage
spring.cloud.config.uri=http://stage-config-server.com

---
spring.profiles=prod
spring.cloud.config.uri=http://prod-config-server.com

And then I set the cla -Dspring.profiles.active=dev but the loaded config server is always the last one set in the file (i.e. prod config server would be loaded in the above settings, and then if prod is removed, stage would be loaded).

Is it possible to set bootstrap profiles for the cloud config server? I followed this example but can't seem to get it working. For what it's worth, these profiles work great to load the correct config (i.e. app-name-dev.properties will load if the dev profile is active), but aren't being pulled from the proper config server.


Answer:

Specifying different profiles in a single file is only support for YAML files and doesn't apply to property files. For property files specify an environment specific bootstrap-[profile].properties to override properties from the default bootstrap.properties.

So in your case you would get 4 files bootstrap.properties, bootstrap-prod.properties, bootstrap-stage.properties and bootstrap-dev.properties.

However instead of that you could also only provide the default bootstrap.properties and when starting the application override the property by passing a -Dspring.cloud.config.uri=<desired-uri> to your application.

java -jar <your-app>.jar -Dspring.cloud.config.uri=<desired-url>

This will take precedence over the default configured values.