Spring Configuration and Profiles

Spring supports profile specific application properties via a file naming convention:

  • Profile specific application properties outside your jar (application-{profile}.properties or application-{profile}.yml)
  • Profile specific application properties packaged in your jar (application-{profile}.properties or application-{profile}.yml)

If you’re using YAML, then you can also use profile documents inside the YAML file. Let’s take a closer look using the CommandLineRunner from last week’s post Externalized Configuration in Spring

@SpringBootApplication
public class Application implements CommandLineRunner {

    @Value("${myApp.name}")
    private String name;
    @Value("${myApp.primary}")
    private String primary = "foo";
    @Value("${myApp.secondary}")
    private String secondary = "foo";

    private static final Logger log = LoggerFactory.getLogger(Application.class);

    public static void main(String[] args) throws Exception {
        final SpringApplication app = new SpringApplicationBuilder(Application.class)
            .properties("spring.config.name=myApp")
            .build();
        app.run();
    }

    public void run(String ... strings)
        throws Exception
    {
        log.warn("name\t {}", name);
        log.warn("primary\t {}", primary);
        log.warn("secondary\t {}", secondary);
    }
}

A YAML file is actually a sequence of documents separated by --- lines, and Spring parses each document separately to a flattened map. If a YAML document contains a spring.profiles key, then that value controls whether that document is included in the final merge.

Let’s take a look at an example. Here is the file packaged in my jar:

myApp:
  name:         Package Default
  primary:      Package Default
  secondary:    Package Default

---
spring.profiles: one

myApp:
  name:         Package 1

---
spring.profiles: two

myApp:
  name:         Package 22
  primary:      Package 22

---
spring.profiles: three

myApp:
  primary:      Package 333
  secondary:    Package 333

With no active profile, we get the values from the first document.

$ java -jar target/application-config-0.1.0.jar
WARN  [main]  name       Package Default
WARN  [main]  primary    Package Default
WARN  [main]  secondary  Package Default

Set one as the active profile, to merge the first document with the document containing spring.profiles: one:

$ (export SPRING_PROFILES_ACTIVE=one ; java -jar target/application-config-0.1.0.jar)
WARN  [main]  name       Package 1
WARN  [main]  primary    Package Default
WARN  [main]  secondary  Package Default

Set two,three as the active profiles, to merge the first document with the documents containing spring.profiles: two, spring.profiles: three:

$ (export SPRING_PROFILES_ACTIVE=two,three ; java -jar target/application-config-0.1.0.jar)
WARN  [main]  name       Package 22
WARN  [main]  primary    Package 333
WARN  [main]  secondary  Package 333

And finally, add a local myApp.yml that sets the active profile. The merge order is:

  1. packaged default document
  2. local default document
  3. active profile document
$ cat myApp.yml
spring.profiles.active: one

myApp:
    name:       Local
    secondary:  Local
$ java -jar target/application-config-0.1.0.jar
WARN  [main]  name       Package 1
WARN  [main]  primary    Package Default
WARN  [main]  secondary  Local