javaspringspring-bootmaven

How to Use External Configuration Files to Provide Environment-Specific Configurations in a Spring Boot Application?


Is it possible to maintain a parameterized application configurations file with configurations that are most likely to change depending on the development environment maintenance in a different file as indicated below in a Spring boot application using the maven built tool?

Sample application.properties

spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?createDatabaseIfNotExist=true
spring.datasource.username=${MYSQL_USERNAME}
spring.datasource.password=${MYSQL_PASSWORD}
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.show-sql=false

# Flyway Configuration
spring.flyway.enabled=true
spring.flyway.baseline-on-migrate=true
spring.flyway.user=${MYSQL_USERNAME}
spring.flyway.password=${MYSQL_PASSWORD}
spring.flyway.schemas=${MYSQL_DATABASE}
spring.flyway.url=jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?createDatabaseIfNotExist=true
spring.flyway.locations=classpath:db/migration

frontend.web.host=${FRONTEND_WEB_HOST_URL}

#links
login.URL = ${LOGIN_URL}/signin

prod.properties (new configuration maintained at the root level of the program)

SPRING_PROFILES_ACTIVE=prod
MYSQL_HOST=178.18.10.16
MYSQL_PORT=3306
MYSQL_DATABASE=superdb
MYSQL_USERNAME=root
MYSQL_PASSWORD=123456
LOGIN_URL=178.18.10.16

FRONTEND_WEB_HOST_URL=http://localhost:3000
REACT_APP_HOST_URL=http://localhost:8080
REACT_APP_API=/api

If this process is possible in Spring Boot, how should this application be executed via the command line and Eclipse IDE?

When attempting to execute it via console using the below-mentioned command I am getting this error.

The command used mvn spring-boot:run -Dspring-boot.run.profiles=prod

Error Indicated

java.lang.IllegalStateException: Error processing condition on org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration
    at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:60) ~[spring-boot-autoconfigure-3.0.5.jar:3.0.5]
    at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:108) ~[spring-context-6.0.7.jar:6.0.7]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader$TrackedConditionEvaluator.shouldSkip(ConfigurationClassBeanDefinitionReader.java:466) ~[spring-context-6.0.7.jar:6.0.7]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader$TrackedConditionEvaluator.shouldSkip(ConfigurationClassBeanDefinitionReader.java:455) ~[spring-context-6.0.7.jar:6.0.7]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:131) ~[spring-context-6.0.7.jar:6.0.7]
    at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:120) ~[spring-context-6.0.7.jar:6.0.7]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:410) ~[spring-context-6.0.7.jar:6.0.7]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:283) ~[spring-context-6.0.7.jar:6.0.7]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:344) ~[spring-context-6.0.7.jar:6.0.7]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:115) ~[spring-context-6.0.7.jar:6.0.7]
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:747) ~[spring-context-6.0.7.jar:6.0.7]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:565) ~[spring-context-6.0.7.jar:6.0.7]
    at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.0.5.jar:3.0.5]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:732) ~[spring-boot-3.0.5.jar:3.0.5]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:434) ~[spring-boot-3.0.5.jar:3.0.5]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:310) ~[spring-boot-3.0.5.jar:3.0.5]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1304) ~[spring-boot-3.0.5.jar:3.0.5]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1293) ~[spring-boot-3.0.5.jar:3.0.5]
    at com.antarctica21.backend.Antarctica21BackendApplication.main(Antarctica21BackendApplication.java:16) ~[classes/:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:568) ~[na:na]
    at org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) ~[spring-boot-devtools-3.0.5.jar:3.0.5]
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'MYSQL_HOST' in value "jdbc:mysql://${MYSQL_HOST}:${MYSQL_PORT}/${MYSQL_DATABASE}?createDatabaseIfNotExist=true"
    at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:180) ~[spring-core-6.0.7.jar:6.0.7]
    at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126) ~[spring-core-6.0.7.jar:6.0.7]
    at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:239) ~[spring-core-6.0.7.jar:6.0.7]
    at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210) ~[spring-core-6.0.7.jar:6.0.7]
    at org.springframework.core.env.AbstractPropertyResolver.resolveNestedPlaceholders(AbstractPropertyResolver.java:230) ~[spring-core-6.0.7.jar:6.0.7]
    at org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertyResolver.getProperty(ConfigurationPropertySourcesPropertyResolver.java:79) ~[spring-boot-3.0.5.jar:3.0.5]
    at org.springframework.boot.context.properties.source.ConfigurationPropertySourcesPropertyResolver.getProperty(ConfigurationPropertySourcesPropertyResolver.java:60) ~[spring-boot-3.0.5.jar:3.0.5]
    at org.springframework.core.env.AbstractEnvironment.getProperty(AbstractEnvironment.java:552) ~[spring-core-6.0.7.jar:6.0.7]
    at org.springframework.boot.autoconfigure.condition.OnPropertyCondition$Spec.collectProperties(OnPropertyCondition.java:121) ~[spring-boot-autoconfigure-3.0.5.jar:3.0.5]
    at org.springframework.boot.autoconfigure.condition.OnPropertyCondition.determineOutcome(OnPropertyCondition.java:71) ~[spring-boot-autoconfigure-3.0.5.jar:3.0.5]
    at org.springframework.boot.autoconfigure.condition.OnPropertyCondition.getMatchOutcome(OnPropertyCondition.java:58) ~[spring-boot-autoconfigure-3.0.5.jar:3.0.5]
    at org.springframework.boot.autoconfigure.condition.AbstractNestedCondition$MemberOutcomes.getConditionOutcome(AbstractNestedCondition.java:195) ~[spring-boot-autoconfigure-3.0.5.jar:3.0.5]
    at org.springframework.boot.autoconfigure.condition.AbstractNestedCondition$MemberOutcomes.<init>(AbstractNestedCondition.java:189) ~[spring-boot-autoconfigure-3.0.5.jar:3.0.5]
    at org.springframework.boot.autoconfigure.condition.AbstractNestedCondition$MemberConditions.lambda$getMatchOutcomes$0(AbstractNestedCondition.java:169) ~[spring-boot-autoconfigure-3.0.5.jar:3.0.5]
    at java.base/java.util.Map.forEach(Map.java:713) ~[na:na]
    at java.base/java.util.Collections$UnmodifiableMap.forEach(Collections.java:1553) ~[na:na]
    at org.springframework.boot.autoconfigure.condition.AbstractNestedCondition$MemberConditions.getMatchOutcomes(AbstractNestedCondition.java:169) ~[spring-boot-autoconfigure-3.0.5.jar:3.0.5]
    at org.springframework.boot.autoconfigure.condition.AbstractNestedCondition$MemberMatchOutcomes.<init>(AbstractNestedCondition.java:78) ~[spring-boot-autoconfigure-3.0.5.jar:3.0.5]
    at org.springframework.boot.autoconfigure.condition.AbstractNestedCondition.getMatchOutcome(AbstractNestedCondition.java:63) ~[spring-boot-autoconfigure-3.0.5.jar:3.0.5]
    at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:47) ~[spring-boot-autoconfigure-3.0.5.jar:3.0.5]

Solution

  • To execute a Spring Boot application with environment-specific configurations, such as those defined in prod.properties, you'll need to ensure that the correct profile (prod in this case) is active and the configuration file is correctly loaded.

    1. Command Line

    You can use the following command to run your Spring Boot application from the command line:

    SPRING_PROFILES_ACTIVE=prod MYSQL_HOST=178.18.10.16 MYSQL_PORT=3306 MYSQL_DATABASE=superdb MYSQL_USERNAME=root MYSQL_PASSWORD=123456 LOGIN_URL=178.18.10.16 mvn spring-boot:run
    

    Alternatively, you can load the environment variables from your prod.properties file by sourcing it before running the Spring Boot application:

    source backend-service/prod.properties
    mvn spring-boot:run
    

    Or, you can pass the prod.properties file as environment variables when running the application:

    env $(cat backend-service/prod.properties | xargs) mvn spring-boot:run -Dspring-boot.run.profiles=prod
    

    2. Eclipse IDE

    In Eclipse, you can follow these steps:

    Open Run Configurations: Right-click on your project in the Project Explorer and select Run As > Run Configurations.

    Set Environment Variables:

    Under the Environment tab, manually add each environment variable (e.g., SPRING_PROFILES_ACTIVE, MYSQL_HOST, MYSQL_PORT, etc.) by clicking on New... and entering the variable name and value.

    Set Active Profile: In the Arguments tab, under VM Arguments, add: -Dspring.profiles.active=prod

    Run the Application Click Apply and then Run.

    3. IntelliJ IDEA

    In IntelliJ IDEA, follow these steps:

    Edit Configurations: Go to Run > Edit Configurations....

    Set Environment Variables: In the Configuration section, under Environment variables, add each environment variable (e.g., SPRING_PROFILES_ACTIVE, MYSQL_HOST, etc.). You can type them in the text box in the format KEY=VALUE separated by semicolons or click the ... button to enter them individually.

    Set Active Profile: In the VM Options field, add: -Dspring.profiles.active=prod

    Run the Application