後からMaven+SpringBootでマルチモジュールプロジェクト


記事の内容

Maven ProjectでSpring Bootのプロジェクトを後からマルチモジュール化しなくてはいけなくなった場合の対応方法を残す。
ここでのマルチモジュール化の目的は、後述するCommonプロジェクトのJava実装やResourceを各プロジェクトで使えるようにすることである。
事例が見当たらなかったので残すことで誰かの役に立つことを祈る。

制約

Parentプロジェクトが必要だが、Parentプロジェクトは、現在のプロジェクトの上位パスには用意できない(プロジェクトルートは変えない)。

初期の構成

Maven

Service1, Service2

Spring Boot Applicationである。それぞれが単独のWebアプリケーションになる。

Common

Spring Boot Applicationである。Test時のみ単独起動する。基本的には、Service1,2の共通実装。
application-common.ymlを持ち、Service1, Service2の共用プロパティを持つ。

この構成は、Eclipse上は、リソースファイルの共用が可能だが、Mavenによるパッケージングを行うとリソースファイルは取得できない。

まとめるとこんな感じ

buildイメージ

要件をまとめると

  • Parentプロジェクトを上位フォルダに用意したくない
  • Service1 + Common, Service2 + Commonのマルチモジュールを目指す

結論構成

Service1を例に構成を示す。
この構成で、要件を満たしながら以下すべて可能となる。

  • mvn package
  • mvn spring-boot:run
  • 個別のプロジェクトのSpring Boot Applicationの実行

※この構成は、常に-Dspring.profiles.active=commonオプションが必要となる
※環境別のapplication-*.yml(local,develop,staging,production)はServiceプロジェクト配下に作成し制御する。
 この場合、-Dspring.profiles.active=common,localといような指定で起動することになる。
※Jarの場合、java -Dspring.profiles.active=common,local -jar Service1-0.0.1-SNAPSHOT.jarの形。

フォルダ構成

Service1 ・・・この単位でソース管理
└ ParentPom
  └ pom.xml
└ pom.xml
Common ・・・この単位でソース管理
└ pom.xml

Service1とCommonは同じディレクトリに存在することが前提。
Service1,CommonはそれぞれMavenプロジェクトとして管理し、ソース管理するからこの前提は満たしやすい(というかそういう状況のはず)。

イメージ

Service1プロジェクトの中にParentプロジェクトを作ってしまう。
こうすることで、ソース管理上も実装上のJavaプロジェクトの管理上もパッケージングの単位も要件通りになる。

  • Mavenマルチモジュールプロジェクトとしての依存関係は、
    • Common --> Service1 --> Service1Parent
    • Common --> Service1Parent

実際の対応

Service1へ行った対応。

ParentPomディレクトリの作成

マルチモジュールのために、ParentPomを作成する必要があるが、Service1プロジェクト内に作成する。

Service1
└ ParentPom
  └ pom.xml

ParentPomの pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.2</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <packaging>pom</packaging>

    <modules>
        <!-- ../で上位ディレクトリを遡るのがミソ -->
        <module>../../Common</module>
        <module>../../Service1</module>
    </modules>

    <groupId>com.example</groupId>
    <artifactId>Service1Parent</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Service1</name>

    <properties>
        <java.version>15</java.version>
    </properties>

    <dependencies>
    </dependencies>
</project>

Service1 pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <!-- Parentへの参照 -->
    <parent>
        <groupId>com.example</groupId>
        <artifactId>Service1Parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <relativePath/>
    </parent>
    <!-- package成果物 -->
    <packaging>jar</packaging>

    <artifactId>Service1</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Service1</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>15</java.version>
        <spring.base.profile>common</spring.base.profile>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>2.4.2</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <!-- Mavenマルチモジュールの構成で必要 -->
    <dependencies>
        <dependency>
            <groupId>com.example</groupId>
            <artifactId>Common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- Spring-Boot用 -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>2.4.2</version>
                <configuration>
                    <mainClass>com.example.SampleApplication</mainClass>
                    <layout>JAR</layout>
                    <jvmArguments>-Dspring.profiles.active=${spring.base.profile}</jvmArguments>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
            <!-- Compile -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.7.0</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <compilerArgs>
                        <arg>-Xlint:all,-options,-path</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
            <!-- Test -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <configuration>
                    <argLine>-Dspring.profiles.active=${spring.base.profile}</argLine>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

Common pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- Parentへの参照 -->
    <parent>
        <groupId>com.example</groupId>
        <artifactId>Service1Parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>

    <!-- package成果物 -->
    <packaging>jar</packaging>
    <artifactId>Common</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>Common</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>15</java.version>
    </properties>

    <dependencies>
    </dependencies>

</project>