[JAVA] Switch environment by boot argument with SpringBoot

Introduction

A story when developing a backend with SpringBoot.

Nowadays, since Docker came out, there is a tendency to manage from combination to production with one module and one source to the end.

The distinction between each environment is based on the design policy of simply passing environment arguments from the outside. So, I will summarize what to do with the application module created with Spring Boot inside

If the number of environments is small

You can switch with the profile of Spring Boot.

The official information is below, so you can do it here. Official

If you include the following in the startup argument,

-Dspring.profiles.active=dev1


It is a specification that the following files are read.

src/main/resource/application-dev1.properties」


Switch property files in build

Easy profile switching of maven.

When solidifying a module, making it into a jar, war, etc., switch the configuration file with the maven function. The easiest way to switch configuration files is to transfer files at build time.

pom.xml



<plugin>
	<groupId>org.apache.maven.plugins</groupId>
	<artifactId>maven-war-plugin</artifactId>
	<configuration>
		<webappDirectory>${project.build.directory}/${project.build.finalName}</webappDirectory>
		<webResources>
			<resource>
				<directory>${basedir}/release/${project.stage}</directory>
			</resource>
		</webResources>
		<useCache>false</useCache>
<!--Added due to warning error-->					<failOnMissingWebXml>false</failOnMissingWebXml>
		<archive>
			<manifest>
<!--main class specification (if necessary)-->							<mainClass>jp.co.●●.●●.Main</mainClass>
			</manifest>
		</archive>
	</configuration>
</plugin>

<profiles>
	<profile>
		<id>dev1</id>
		<activation>
			<activeByDefault>true</activeByDefault>
		</activation>
		<properties>
			<project.stage>dev01</project.stage>
		</properties>
	</profile>

	<profile>
		<id>dev02</id>
		<activation>
			<activeByDefault>false</activeByDefault>
		</activation>
		<properties>
			<project.stage>dev02</project.stage>
		</properties>
	</profile>
</profiles>

Since it is possible to embed shell commands internally, For those who are used to maven, it is familiar.

I want to switch the environment with one module

Problems so far

First, the profile switching method of springboot has a fixed property file format, and it is very difficult to make settings such as reading multiple files. It lacks flexibility, especially when the environment increases.

Secondly, the profile switching method of maven is a setting that only a specific module works in a specific environment when reading multiple files, so if there is an error or procedure error in the deployment shell etc., a failure will occur. Probability is high.

Use the profile extension of spring boot.

Use ApplicationContextInitializer to set profile at startup.

Java startup argument specification → Specify in profile with ApplicationContextInitializer With this method, you can group when you specify the profile. When executed with dev01, it can contain two pieces of information, dev and dev01.

java -Dproject.stage=dev01 hoge.war

public class SpringActiveProfileInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
	/**Logger*/
	private static final Logger LOG = LoggerFactory.getLogger(SpringActiveProfileInitializer.class);

	@Override
	public void initialize(final ConfigurableApplicationContext applicationContext) {

		//Get startup argument (dev01)
		String projectStage = System.getProperty("project.stage");

		//Get boot group (dev)
		String prjectStageGroup = projectStage.replaceAll("[0-9]+", "");
		ConfigurableEnvironment env = applicationContext.getEnvironment();
		if (StringUtils.equals(projectStage, prjectStageGroup)) {
			env.setActiveProfiles(projectStage);
		} else {
			env.setActiveProfiles(prjectStageGroup);
			env.addActiveProfile(projectStage);
		}

		if (LOG.isDebugEnabled()) {
			//  setting spring.profiles.active = [dev, dev01]
			LOG.debug("setting spring.profiles.active = {}", Arrays.asList(env.getActiveProfiles()));
		}
	}
}

web-fragment.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-fragment xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xmlns:webfragment="http://xmlns.jcp.org/xml/ns/javaee/web-fragment_3_0.xsd" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-fragment_3_0.xsd"
	version="3.0">
	<!--Set Project Stage set in JVM system parameter to Spring Active Profile-->
	<context-param>
		<param-name>contextInitializerClasses</param-name>
		<param-value>jp.co.future.project.palette.common.context.SpringActiveProfileInitializer</param-value>
	</context-param>

</xml>

spring boot profile is also available in xml

Profile can be described even inside xml, Definition files can be separated for each environment

Although described in the sample, settings such as commonality and reading of external files are also possible.


	<beans profile="dev">
		<!--Data source-->
		<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
			<property name="jdbcUrl" value=""/>
		</bean>
	</beans>

	<beans profile="stg">
		<!--Data source-->
		<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
			<property name="jdbcUrl" value=""/>
		</bean>
	</beans>

	<!--Multiple stages can be specified by separating them with commas-->
	<beans profile="dev,stg">
		<bean id="redis" >
		</bean>
	</beans>


	<beans profile="dev01">
		<!--Property files can also be read from xml-->
		<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
		    <property name="location" value="classpath:config/connect-config-dev01.properties" />
		</bean>
	</beans>

	<!--It is also possible to import the xml itself-->
	<import resource="classpath:META-INF/common-context.xml" />

in conclusion

As mentioned above, it was the method of switching the environment with spring boot. One module, one source!

Recommended Posts

Switch environment by boot argument with SpringBoot
Build Spring Boot project by environment with Gradle
Switch environment with Spring Boot application.properties and @Profile annotation
How to boot by environment with Spring Boot of Maven
Spring Boot environment construction with Docker (January 2021 version)
Make SpringBoot1.5 + Gradle4.4 + Java8 + Docker environment compatible with Java11
Create Spring Boot environment with Windows + VS Code
Create a Spring Boot development environment with docker
SSO with GitHub OAuth in Spring Boot 1.5.x environment
Database environment construction with Docker in Spring boot (IntellJ)
Spring boot development-development environment-
Switch java with direnv
Supports multi-port with SpringBoot
Download with Spring Boot
Change the injection target for each environment with Spring Boot 2