In a typical software development lifecycle, applications need to adapt to various environments like development, testing, and production. Each environment has its own specific configurations, such as database connections, messaging services, or even simple greeting messages. Instead of hardcoding these settings, Spring Boot offers a powerful feature called Profiles, which allows you to define and load environment-specific configurations dynamically. This blog post will show you how to create a simple Spring Boot application that prints different messages depending on whether it’s running in a local or production environment.
Scenario: Switching Between Local and Production Services
We will create two services, each printing a different message. The service used will depend on whether the application is running in a local environment or in production. This will demonstrate the flexibility of Spring profiles for environment-specific behavior.
Step 1: Define a Simple Service Interface
First, we’ll create an interface that both the local and production implementations will adhere to:
public interface GreetingService {
void printMessage();
}
JavaStep 2: Add Environment-Specific Configurations in application.properties
We’ll use separate configuration files for local and production environments, with a default application.properties
file to specify the active profile.
application.properties
:
# Default profile
spring.profiles.active=local
application-local.properties
:
spring.profiles.active=local
application-prod.properties
:
spring.profiles.active=prod
Step 3: Implement the Local Greeting Service
Now, implement the GreetingService
for the local environment:
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Service
@Profile("prod")
public class ProdGreetingService implements GreetingService {
@Override
public void printMessage() {
System.out.println("Welcome to the Production Environment");
}
}
JavaStep 4: Implement the Production Greeting Service
Similarly, implement the GreetingService
for the production environment:
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Service;
@Service
@Profile("prod")
public class ProdGreetingService implements GreetingService {
@Override
public void printMessage() {
System.out.println("Welcome to the Production Environment");
}
}
JavaStep 5: Use the Greeting Service in Your Application
Next, create a service that uses the GreetingService
to print the appropriate message based on the active profile:
import org.springframework.stereotype.Service;
@Service
public class MessageService {
private final GreetingService greetingService;
public MessageService(GreetingService greetingService) {
this.greetingService = greetingService;
}
public void printEnvironmentMessage() {
greetingService.printMessage();
}
}
JavaStep 6: Running the Application
- Local Environment: To run your application with the local profile, the default configuration will work since we’ve set
spring.profiles.active=local
inapplication.properties
. - Production Environment: To switch to the production profile, you can override the active profile by setting the following in your
application-prod.properties
or through command-line arguments when starting the application:
spring.profiles.active=prod
PlaintextTesting the Greeting Service
When writing unit tests, you can mock the GreetingService
to test how your MessageService
behaves without relying on the actual environment:
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;
import static org.mockito.Mockito.*;
class MessageServiceTest {
@Test
void testPrintEnvironmentMessage() {
// Arrange
GreetingService mockGreetingService = mock(GreetingService.class);
MessageService messageService = new MessageService(mockGreetingService);
// Act
messageService.printEnvironmentMessage();
// Assert
verify(mockGreetingService).printMessage();
}
}
JavaConclusion
Using Spring profiles and environment-specific configuration files, you can easily switch between different services or settings depending on the environment your application is running in. This approach ensures that your application remains flexible and adaptable, minimizing the need for code changes when moving between development and production environments.