Beginner Spring Boot Series - Part 3: @Profile and @Beans example

jenad88

John Enad

Posted on June 18, 2023

Beginner Spring Boot Series - Part 3: @Profile and @Beans example

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.

Image description

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

Image description

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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;
    }
}
Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

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)

Image description

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

💖 💪 🙅 🚩
jenad88
John Enad

Posted on June 18, 2023

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related