maven Tips : 簡単なpom.xmlでmavenを始める

Java

クラウド環境を勉強していると、実際に動くアプリで試したくなることがある。

久しぶりにJavaでサンプルを作ってみようかなと思ってみたのだが、どのサンプルを見てもspring bootやmavenやgradleが利用されており、どれも慣れていないので、まずはmavenでも覚えようかな。そのための手始めとして、簡単なpom.xmlを理解しつつ、簡単なjavaプログラムでコンパイル&配布パッケージの作成までやってみた。

以下の情報を参考にしつつ、私が理解できていない箇所は適宜調べて記載した。

Spring Boot Maven で Java プロジェクトの構築 - 公式サンプルコード
Spring Boot の概要から各機能の詳細までが網羅された公式リファレンスドキュメントです。開発者が最初に読むべきドキュメントです。

簡単なJavaプログラム

目的はmavenを理解することなので、Javaプログラムは小さいもので済ませる。

package hello;

public class HelloWorld {
	public static void main(String[] args)  {
		Greeter greeter = new Greeter();
		System.out.println(greeter.sayHello());
	}
}

package hello;

public class Greeter {
	public String sayHello() {
		return "Hello, World!";
	}
}

pom.xml

pom.xmlファイルとは、mavenのプロジェクト定義ファイルのことで、プログラムの名前、バージョン、依存関係などを定義するファイル。プロジェクトのルートフォルダーに保存する。

以下は簡単なpom.xmlファイルで、1つだけシェーディング用のプラグインを追加し、外部ライブラリは使用していないので依存関係は含まれていない。(外部ライブラリの定義やリポジトリについてはSpring Bootをやり始める時に改めて確認する)

調査しつつ、xmlではなくjsonにして欲しかったと思った。。

<?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>">
	
	<!-- POM自体のモデルバージョン(固定) -->
	<modelVersion>4.0.0</modelVersion>
	
	<!-- グループや組織を表す文字列、通常は逆ドメイン名を使用、パッケージ名とは異なる -->
	<groupId>com.juncleit</groupId>

	<!-- 生成ファイルのファイル名 -->
	<artifactId>simplemaven</artifactId>

	<!-- 生成ファイル拡張子 "jar" or "war" -->
	<packaging>jar</packaging>

	<!-- 生成ファイルのバージョン -->
	<version>0.1.0</version>

	<!-- javaコンパイラーに指定する言語仕様とランタイムのバージョン -->
	<properties>
		<!-- ソースコードで使用している言語バージョン -->
		<maven.compiler.source>1.8</maven.compiler.source>
		<!-- 生成ファイルの互換ランタイムバージョン -->
		<maven.compiler.target>1.8</maven.compiler.target>
	</properties>

	<!-- ビルド中に利用されるプラグインの定義領域 -->
	<build>
		<plugins>
			<plugin>

				<!-- 使用するプラグインのgroupId, artifactId, versionは必須要素 -->
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-shade-plugin</artifactId>
				<version>3.2.4</version>
				<executions>
					<execution>
						<!-- shadeプラグインを実行するフェーズの指定-->
						<phase>package</phase>
						<goals>
							<goal>shade</goal>
						</goals>
						<!-- shadeプラグインの設定 -->
						<configuration>
							<transformers>
								<!-- shadeプラグインの実行クラス=Manifestの書き換え -->
								<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
									<!-- ManifestファイルのMain-Classをhello.HelloWorldクラスに置き換え -->
									<mainClass>hello.HelloWorld</mainClass>
								</transformer>
							</transformers>
						</configuration>
					</execution>
				</executions>
			</plugin>
		</plugins>
	</build>
</project>

pluginとは

mavenのプラグインとは、ライフサイクルもしくはphaseで実行できる処理(=goal)が定義されたmojoのこと。1つのプラグインには複数の処理(goal)が定義されている。

以下、参考にした情報。

Maven – Guide to Configuring Plug-ins
Just a moment...

executions/executionタグとは

buildライフサイクルの特定フェーズにおいて、カスタム処理をmojoプラグインで実行するためのタグ。executionsには複数のexecutionを含めることができ、各executionには1つのmojoを含めることができる。executionには、実行対象のフェーズを示すphaseタグを指定できる。

1つのmojoは複数のexecutionで指定することができるので、複数のフェーズで異なるプロパティを指定したmojoを実行することができる。

サンプルpom.xmlの例では、ManifestResourceTranfomerというシェーディングを行うmojoが指定されており、packageフェーズでMANIFESTファイルのMain-Classを定義する。

mojoとは

Maven plain Old Java Objectの略。各mojoというのは実行可能なgoalを定義しており、mavenプラグインは1つ以上のmojoで構成されている。

buildライフサイクルとは

mavenのbuild ライフサイクルは、いくつかのフェーズで構成されており、以下の順番にフェーズを実行する。

  1. compile : ソースコードのコンパイル
  2. test-compile : テストソースコードのコンパイル
  3. test : テストの実行
  4. package : 配布ファイル(jar, war, ear, etc..)の作成
  5. integration-test : 結合テスト用の環境にデプロイ
  6. install : パッケージをローカルリポジトリへの登録
  7. deploy : パッケージをリモートリポジトリへ登録

mavenでbuildライフサイクルを実行する時に実行対象のフェーズを指定できる。例えば、packageフェーズを指定すると、compileからpackageまでのフェーズを順番に実行する。(通常デフォルトのフェーズはpackage)

以下、参考にした情報。

Just a moment...

phaseとは

buildライフサイクル、cleanライフサイクルを構成する一つのタスク。各phaseは一連のgoalで構成されている。

以下、参考にした情報。

Just a moment...

goalとは

各phaseを構成するプラグインの処理。1つのphaseを実行するとき、そのphaseにバインドされているプラグインのgoalが順番に実行される。

例えば、compile phaseにはデフォルトでmaven-compilerプラグインが指定されており、maven-compilerプラグインには「compile」という処理(=goal)が含まれている。

以下、compile phaseに含まれるプラグインとgoalの確認結果。

> mvn help:describe -Dcmd=compile
[INFO] Scanning for projects...
[INFO] 
[INFO] --------------------< org.springframework:gs-maven >--------------------
[INFO] Building gs-maven 0.1.0
[INFO]   from pom.xml
[INFO] --------------------------------[ jar ]---------------------------------
[INFO] 
[INFO] --- help:3.4.0:describe (default-cli) @ gs-maven ---
[INFO] **'compile' is a phase corresponding to this plugin:
org.apache.maven.plugins:maven-compiler-plugin:3.10.1:compile**

上記を要約すると、compileというphaseには、compilerというmavenプラグインのcompileというgoalが定義されており、compileフェーズの実行=mavenプラグインのcompileが実行されると解釈する。

以下、参考にした情報。

Just a moment...

u-ber jarとは

依存関係のある外部ライブラリを1つのjarファイルにまとめて、以下のように単純なコマンドで実行できるようにしたjarファイルのことをu-ber.jarと呼ぶ。

> java -jar target.jar

例えば、spring-bootでRESTfulなWEBサービス用のjarファイルを作成した場合、jarファイルにはSpring-Boot関連ライブラリ、Tomcat、Jackson、その他多くの外部ライブラリが含まれ、上記コマンドを実行することでTomcatが起動し、ブラウザからのリクエストに応答できるWEBアプリケーションが起動する。作成したjarファイルを配布する場合、利用者は依存関係のある全ての外部ライブラリを自分で準備することなく、簡単に利用することができるようになる。

以下、参考にした情報。

What is an uber JAR file?
I am reading Maven documentation and came across the name uber-jar. What does an uber-jar mean and what are its features...

shade plugin(シェーディング)とは

最もシンプルなmavenのpom.xmlファイルと言いつつも、上記で定義したファイルにはManifestファイルのShade pluginが使用されているが、これは先に説明したu-ber.jarを作成する場合にいくつかの考慮が必要な場合に便利なプラグイン。自分のプロジェクトファイルの他に多くの外部ライブラリを1つのjarファイルにマージすると多くの箇所でコンフリクトが発生する可能性がある。これを回避するための処理をshadeもしくはシェーディングという。

shade pluginにはいくつかのTransformerというクラスが定義されており、例えば、パッケージ名が衝突するようなライブラリを利用している場合はパッケージ名をリネームし、参照箇所も一緒に書き換える処理などが定義されている。

Shade pluginに含まれる各Transformerの用途が以下に定義されている。

Apache Maven Shade Plugin – Usage

最もシンプルなpom.xmlファイルで使用されているManifestResourceTransformerというクラスは、MANIFESTファイルのエントリーを設定するための処理が定義されている。

以下、参考にした情報。

Apache Maven Shade Plugin – Introduction
Apache Maven Shade Plugin – Resource Transformers

シェーディングされた結果を確認

mvnコマンドを実行した後、ManifestResourceTranformerによって置き換えられた(シェーディングされた)結果を確認してみる。

上記のpom.xmlでは、package phaseでシェーディング処理が実行される。シェーディングされると、targetディレクトリには”original”という接頭辞が含まれるファイルが生成されているが、これがシェーディングされる前のファイルで、”ogirinal”という接頭辞がないファイルがシェーディングされた後のファイル。

% ls
classes/                     maven-archiver/
generated-sources/           maven-status/
gs-maven-0.1.0.jar           original-gs-maven-0.1.0.jar

それぞれのjarファイルの内容を確認すると、MANIFEST.MFファイルだけサイズが異なっている。(表示されるファイルの順番が異なる理由はわからない)

% tar -tvf gs-maven-0.1.0.jar
-rw-rw-r--  0 0      0         111  1  1 11:52 META-INF/MANIFEST.MF
drwxrwxr-x  0 0      0           0  1  1 11:52 META-INF/
drwxrwxr-x  0 0      0           0  1  1 11:52 hello/
-rw-rw-r--  0 0      0         648  1  1 11:52 hello/HelloWorld.class
-rw-rw-r--  0 0      0         370  1  1 11:52 hello/Greeter.class
drwxrwxr-x  0 0      0           0 12 31 23:00 META-INF/maven/
drwxrwxr-x  0 0      0           0 12 31 23:00 META-INF/maven/org.springframework/
drwxrwxr-x  0 0      0           0 12 31 23:00 META-INF/maven/org.springframework/gs-maven/
-rw-rw-r--  0 0      0        1177 12 31 23:00 META-INF/maven/org.springframework/gs-maven/pom.xml
-rw-rw-r--  0 0      0          62  1  1 11:52 META-INF/maven/org.springframework/gs-maven/pom.properties

% tar -tvf original-gs-maven-0.1.0.jar
drwxr-xr-x  0 0      0           0  1  1 11:52 META-INF/
-rw-r--r--  0 0      0          81  1  1 11:52 META-INF/MANIFEST.MF
drwxr-xr-x  0 0      0           0  1  1 11:52 hello/
drwxr-xr-x  0 0      0           0  1  1 11:52 META-INF/maven/
drwxr-xr-x  0 0      0           0  1  1 11:52 META-INF/maven/org.springframework/
drwxr-xr-x  0 0      0           0  1  1 11:52 META-INF/maven/org.springframework/gs-maven/
-rw-r--r--  0 0      0         648  1  1 11:52 hello/HelloWorld.class
-rw-r--r--  0 0      0         370  1  1 11:52 hello/Greeter.class
-rw-r--r--  0 0      0        1177 12 31 23:00 META-INF/maven/org.springframework/gs-maven/pom.xml
-rw-r--r--  0 0      0          62  1  1 11:52 META-INF/maven/org.springframework/gs-maven/pom.properties

それぞれのMANIFEST.MFファイルを確認すると、シェーディングされたjarファイルのMANIFEST.MFには「Main-Class: hello.HelloWorld」というメインクラスが指定されている。

# gs-maven-0.1.0.jarのMANIFEST.MFファイルの内容
Manifest-Version: 1.0
Created-By: Maven JAR Plugin 3.3.0
Build-Jdk-Spec: 21
Main-Class: hello.HelloWorld
# original-gs-maven-0.1.0.jarのMANIFEST.MFファイルの内容
Manifest-Version: 1.0
Created-By: Maven JAR Plugin 3.3.0
Build-Jdk-Spec: 21

MANIFEST.MFにメインクラスの指定があると、javaコマンドでプログラムを実行する際に、メインクラスを指定する必要がなく、「java -jar *.jar」という単純なコマンドでプログラムを起動できる。

コメント

タイトルとURLをコピーしました