With spring beans and profiles, Spring (Boot) gives us a very powerful feature to configure our applications. Spring has the @profile annotation to control which beans get added to the application context. Only beans associated with the active profile are used.
This post is about how I usually use @beans and @profile annotation.
You can check out the sample code at this Github repository: https://github.com/jenad88/enad-spring-1
When a Spring Boot application is started with certain profile(s) activated, the application can react in a certain way based on the activated profile(s).
The way I use profiles, is per environment. So first, I identify the different environments my application will reside. In my case: local, dev, qa, staging, prod
Then for each profile, I create a application.properties file (or .yaml file if you prefer those). The convention is to append the profile name to the filename. For example: application-local.properties or application-prod.properties.
Each of the profile .properties file would then be filled with the configuration that would be used for that profile/environment. In my case, for local:
services.myservice=false
app.message1=Hello World - LOCAL
app.value1=THIS IS LOCAL
and for prod:
services.myservice=true
app.message1=Hello World - PROD
app.value1=THIS IS PROD
I do the same for the rest of the other .properties files...
I then create a Spring Bean which uses those configurations.
public class MyBeanImpl implements MyBean {
private String message1;
private String value1;
public MyBeanImpl(@Value("${app.message1}") String message1, @Value("${app.value1}") String value1) {
this.message1 = message1;
this.value1 = value1;
}
public String getMessage1() {
return message1;
}
public String getValue1() {
return value1;
}
@Override
public String doSomething() {
String result = getClass() + ".doSomething() - " + this.getMessage1() + "=" + this.getValue1();
return result;
}
}
Now when Spring Boot starts it will automatically grab the right configuration file based on the profile that is active. Then it loads the configuration properties into the spring beans from that file.
I then create a couple of services called NonProdServiceImpl and ProdServiceImpl which both implement the MyService interface.
In the constructor, it receives the MyBean spring bean. Each of the services is also marked with the @profile annotation. The ProdServiceImpl has @profile("prod"). That directs Spring Boot to use this if the active profile is prod. The NonProdServiceImpl has @profile("!prod"). This tells Spring Boot that if the active profile is not prod, then use this service.
package com.johnenad.enadspring1.service;
import com.johnenad.enadspring1.config.MyBean;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Profile("!prod")
@Service
public class NonProdMyServiceImpl implements MyService {
private MyBean myBean;
public NonProdMyServiceImpl(MyBean myBean) {
this.myBean = myBean;
}
@Override
public String doSomething() {
String result = String.format("NonProdMyServiceImpl: %s=%s", myBean.getMessage1(), myBean.getValue1());
// do non-production-specific stuff here
System.out.println(result);
return result;
}
}
For the prod version:
package com.johnenad.enadspring1.service;
import com.johnenad.enadspring1.config.MyBean;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Profile("prod")
@Service
public class ProdMyServiceImpl implements MyService {
private MyBean myBean;
public ProdMyServiceImpl(MyBean myBean) {
this.myBean = myBean;
}
@Override
public String doSomething() {
String result = String.format("ProdMyServiceImpl: %s=%s", myBean.getMessage1(), myBean.getValue1());
// do production-specific stuff here
System.out.println(result);
return result;
}
}
A MyServiceController controller is then created that has a MyService injected (@Autowired) in its constructor. The service that is injected depends on which profile is active.
package com.johnenad.enadspring1.controller.v1;
import com.johnenad.enadspring1.dto.MyServiceResponse;
import com.johnenad.enadspring1.service.MyService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static com.johnenad.enadspring1.util.DemoUtil.PATH_V1;
@RestController
@RequestMapping(PATH_V1 + "/myservice")
public class MyServiceController implements MyServiceApi {
private final MyService myService;
@Autowired
public MyServiceController(MyService myService) {
this.myService = myService;
}
@Override
public ResponseEntity<MyServiceResponse> doSomething() {
try {
String result = myService.doSomething();
if (result == null || result.isEmpty()) {
return new ResponseEntity<>(HttpStatus.NO_CONTENT);
}
return new ResponseEntity<>(MyServiceResponse.builder().data(result).build(), HttpStatus.OK);
} catch (Exception e) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
}
How do we tell Spring Boot which profile is the active profile? Just make sure that when you launch your application, we use the -D parameter with the active profile.
Example: -Dspring.profiles.active=prod
When running locally, using IntelliJ IDEA, here is how my configuration looks: (Note the Active profiles field)
If you have a jar file:
java -jar -Dspring.profiles.active=prod enad-spring-1-0.0.1-SNAPSHOT.jar
You can check out the sample code at this Github repository: https://github.com/jenad88/enad-spring-1
Top comments (0)