はじめに
SpringBoot でバックエンドを開発しているときにある話。
昨今、Dockerが出てきてから結合から本番までワンモジュール、ワンソースで最後まで管理する傾向にある。
各環境の区別は、環境引数を外から渡すだけ、という設計方針にした際に、
では、中身のSpringBootで作ったアプリケーションのモジュールはどういう風にすればよいのかをまとめる
環境の数が少ないなら
SpringBootのProfileで切替すればよい。
下記に公式情報があるため、こちらで実現できます。
公式
起動引数に下記を含めれば、
下記ファイルが読み込まれる仕様である。
ビルドでプロパティファイルを切替
mavenのprofile切替が簡単。
モジュールを固める、jar やwar などにするときに、maven の機能で設定ファイルの切り替えを行う。
ビルド時にファイル移送を実施するため、設定ファイルの切替で最も簡単な方法。
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>
<!-- 警告エラーが発生するため追加--> <failOnMissingWebXml>false</failOnMissingWebXml>
<archive>
<manifest>
<!-- main クラス指定(必要に応じて)--> <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>
シェルコマンドを内部に組み込むことも可能なため、
maven に慣れている人にとっては、なじみ深い。
ワンモジュールで環境切替したい
今までの問題点
1つ目、springboot のprofile 切替方法は、プロパティファイルの形式が固定されており、複数のファイルの読み込みなどの設定が非常にやりづらい。特に環境が増えた場合に柔軟性に欠ける。
2つ目、maven のprofile 切替方法は、複数ファイルの読み込みは、特定の環境には特定のモジュールしか動かない設定のため、デプロイシェル等に誤りや手順ミスがあった場合に、障害が発生する可能性が高い。
spring bootのprofile 機能拡張を利用する。
ApplicationContextInitializerを利用して起動時にprofileを設定するようにする。
Javaの起動引数指定 → ApplicationContextInitializerでprofileに指定
この方法の場合、profile 指定するときに、グルーピングできるようになり、
dev01で実行すると、dev とdev01の二つの情報を含めることができる。
java -Dproject.stage=dev01 hoge.war
public class SpringActiveProfileInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
/** ロガー */
private static final Logger LOG = LoggerFactory.getLogger(SpringActiveProfileInitializer.class);
@Override
public void initialize(final ConfigurableApplicationContext applicationContext) {
// 起動引数を取得(dev01)
String projectStage = System.getProperty("project.stage");
// 起動グループを取得(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">
<!-- JVMシステムパラメータで設定したProjectStageをSpringActiveProfileへ設定 -->
<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 はxml でも利用可能
xml 内部でもprofile を記載でき、
環境ごとに定義ファイルの切り分けられる
サンプルで記載しているが、共通化や外部ファイルの読み込みなどの設定も可能。
<beans profile="dev">
<!-- データソース -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<property name="jdbcUrl" value=""/>
</bean>
</beans>
<beans profile="stg">
<!-- データソース -->
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
<property name="jdbcUrl" value=""/>
</bean>
</beans>
<!-- カンマ区切りで複数ステージの指定も可能 -->
<beans profile="dev,stg">
<bean id="redis" >
</bean>
</beans>
<beans profile="dev01">
<!-- xmlからプロパティファイルの読み込みも可能 -->
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="location" value="classpath:config/connect-config-dev01.properties" />
</bean>
</beans>
<!-- xmlそのものをインポートすることも可能 -->
<import resource="classpath:META-INF/common-context.xml" />
おわりに
以上、spring boot で環境切替を行う方法でした。
ワンモジュール、ワンソース!