Spring Boot Profiles

Spring Boot Profiles | Writing and Loading Properties File based on Environment, is called as Profiles. Profiles are used to load property files based on the environment. Define one properties file for one Environment.

Environment:- A system/place where we can deploy and use our application. Dev Env, QA Env, UAT Env, Production Env,..etc. From one Environment to another Environment, if we move our application, the code remains the same. But properties data (key=val) may get modified.

Before moving ahead let us first see what are Command Line Arguments and VM/System Args because we are going to use it for configuring the profiles.

Command Line Arguments and VM/System Args

Command Line Arguments:- To pass input to the application while running to the main() method.

  • > java MainClassName.class val1 val2 val3 …etc
  • > java -jar .jar --main-class MainClassName val1 val2 val3 …etc
  • For Spring Boot, Syntax: --key=val (option args)

VM/System Args: Creating one variable at JVM/System level is called as VM Args, this is input to every application running in same VM.

Syntax: -Dkey=val
Read : System.getProperty(key):val

We can give setup data to the Spring Boot application (Properties/Arguments data) using the below order:-

  • Command Line Arguments: --key=val
  • VM Arguments: -Dkey=val
  • Properties Arguments: prefix.variable=value
  • YAML Arguments:-
prefix:
    variable:<space>value

Above data, we can read using @Value (or) @ConfigurationProperties.

package com.knowprogram.demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class TestRunner implements CommandLineRunner {

    @Value("${my.app.export.data}")
    private String data;

    @Override
    public void run(String... args) throws Exception {
        System.out.println(data);
    }

}

To pass either Command Line Arguments or VM Arguments follow the below steps in Eclipse or STS:-

  • Right-click on the main class/starter class
  • Run as > Run Configuration
  • Click on the Arguments tab
  • Enter details under
  • Program Arguments (Command Line Arguments): –my.app.export.data=CLA
  • VM Arguments: -Dmy.app.export.data=VMA
  • Apply > Run
Program Args and VM args in Ecllipse and STS

Also create: application.yml and application.properties with the above key and check order.

application.yml

my:
 app:
  export:
   data: YML

application.properties

my.app.export.data=Properties

For normal data we use either properties or YML files whereas Command Line Arguments and VM arguments are used while profile management.

Spring Boot Profiles Example

We create multiple properties files in project, by following syntax:-

  • application-[profilename].properties
  • application-[profileName].yml

You can choose any profile name. Ex: dev, qa, uat, prod etc.

  • application-qa.properties [Profile name: qa]
  • application-uat.properties [Profile name: uat]
  • application.properties [Profile name: default]

It is recomanded to define one default profile and you may define multiple other profiles. While Running application we must activate one profile (else default is activated)

--spring.profiles.active=<profileName>

JAR based command:-

java -jar <app-name>.jar --spring.profiles.active=<profileName>

application.properties

my.app.driver=Oracle-DEF

application-qa.properties

my.app.driver=MySQL-QA

Runner Class:-

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class MessageRunner implements CommandLineRunner {
    @Value("${my.app.driver}")
    private String driver;

    public void run(String... args) throws Exception {
        System.out.println("Value is :"+ driver);
    }
}

Run the application. Goto main class > Right-click on code > Run As > Run Configuration > Click on Arguments. Enter under program arguments:-

--spring.profiles.active=qa

Now, apply and run.

Profiles Fallback

If any key-value is not present in the current profile then the same key data will be taken from default profile properties i.e. called as profiles fallback.

Considering the default profile has 100 keys in qa we need only 10 keys with different values, the remaining 90 are the same as default, then define qa properties with 10 keys only.

application.properties

my.app.title=KP-DEFAULT
my.app.loc=DEFAULT

application-qa.properties

my.app.title=KP-QA
my.app.loc=QA

application-prod.properties

my.app.title=KP-PROD
my.app.loc=PROD
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import lombok.ToString;
@Component
@ToString
public class ProfileDataRunner implements CommandLineRunner {
    @Value("${my.app.title}")
    private String title;

    @Value("${my.app.loc}")
    private String loc;

    @Override
    public void run(String... args) throws Exception {
        System.out.println(this);
    }
}

Child Profiles

We can divide one properties file into multiple files. Finally, combine them. If a properties file contains more number of keys then define multiple properties files and link finally as one profile. Child Profiles makes our application modularized. Maintenance is easy.

spring.profiles.include=<child-profile-name-1>,<child-profile-name-2>
java -jar <projectName>.jar --spring.profiles.active=prod --
spring.profiles.include=proddb,prodmail

To Activate Profile:-

--spring.profiles.active=prod

To Include Profile:-

--spring.profiles.include=proddb,prodmail

If the same key is present in all locations then the last loaded file is considered as first priority.

Profiles using YAML

We can define YAML files for every profile as like properties files.

  • application.yml [default profile]
  • application-qa.yml [qa profile]
  • application-prod.yml [prod profile]

Or else we can define a single YAML file using profile separator 3 dashes (—).

application.yml:-

key: val
#default profile
---
key: val
spring:
  config:
    activate:
      on-profile: profilename
---
key: val
spring:
  config:
    activate:
      on-profile: profilename

Example:-

package com.knowprogram.demo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

import lombok.ToString;

@Component
@ToString
public class Test implements CommandLineRunner {

    @Value("${my.app.title}")
    private String title;

    @Override
    public void run(String... args) throws Exception {
        System.out.println(this);
    }

}

application.yml:-

my:
  app:
    title: DEFAULT
---
my:
  app:
    title: QA
spring:
  config:
    activate:
      on-profile: qa
---
my:
  app:
    title: PROD
spring:
  config:
    activate:
      on-profile: prod

@Profile Annotation

@Profile annotation is used to executed logic (code) based on Environment provided.

import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@Component
//@Profile("prod")
@Profile({"prod","qa"})
public class EmailAlert implements CommandLineRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("FROM EMAIL ALERT");
    }
}

Activate using:-

--spring.profiles.active=prod (or)
--spring.profiles.active=qa

Do we need to have a properties file for the current profile? No Need. Considering your current profile is prod, then we can create application-prod.properties with keys. If we do not create application-prod.properties or else a file is created without keys then Fallback is implemnted i.e. read data from application.properties.

import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@Component
//@Profile("prod")
@Profile({"prod","qa"})
public class EmailAlert implements CommandLineRunner {
    @Value("${my.app.title}")
    private String title;

    @Override
    public void run(String... args) throws Exception {
        System.out.println("FROM EMAIL ALERT => " + title);
    }
}

application.properties

my.app.title=DEFAULT

application-prod.properties

my.app.title=PRD_TITLE

application-qa.properties

my.app.title=QA_TITLE

@Profile, Service Interface & Properties

public interface AlertService {
    public void showAlertMsg();
}
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import in.nareshit.raghu.service.AlertService;
@Component
@Profile("default")
//@Profile({"default","dev"})
public class SmsAlertService implements AlertService {
    @Value("${my.app.service.code}")
    private String scode;

    @Override
    public void showAlertMsg() {
        System.out.println("FROM MESSAGE SERVICE => " + scode);
    }
}
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
import in.nareshit.raghu.service.AlertService;
@Component
@Profile({"qa","prod"})
public class EmailAlertService implements AlertService {
    @Value("${my.app.service.code}")
    private String scode;

    @Override
    public void showAlertMsg() {
        System.out.println("FROM MAIL SERVICE => " + scode);
    }
}

application.properties

my.app.service.code=SMS-SERVICE

application-qa.properties

my.app.service.code=EMAIL-qa-SERVICE

application-prod.properties

my.app.service.code=EMAIL-prod-SERVICE

Runner Class

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import in.nareshit.raghu.service.AlertService;
@Component
public class ExecuteServiceRunner implements CommandLineRunner {
    /**
    * -> goto container,
    * -> read impl class object
    * -> inject object to this(service) variable
    */

    @Autowired
    private AlertService service;

    @Override
    public void run(String... args) throws Exception {
        service.showAlertMsg();
    }
}

Activate profile using –spring.profiles.active=name

Summary

  • Profiles are used for environment-based loading.
  • Profiles can be activated using --spring.profiles.active=name
  • If keys are not present in the current profile then fallback -> application.properties
  • One profile properties we can divide into small properties files and finally include them using --spring.profiles.include=prodemail, prodb, prodsecurity
  • @Profile annotation for code/logic execution based on environment
  • We can mix the @Profile + properties concept. Properties for the profile are optional (fallback is applied).
  • If no profile is active, then the default is active. [application.properties]
  • In the case of YAML, we can use a single file for multiple profiles using 3 dash symbols (not supported child profiles, if you want to write individual YAML files).
  • If both YML and properties files exist then the properties file will get priority over the YAML file.

If you enjoyed this post, share it with your friends. Do you want to share more information about the topic discussed above or do you find anything incorrect? Let us know in the comments. Thank you!

Leave a Comment

Your email address will not be published. Required fields are marked *