プログラムでSpringBootアプリケーションを起動する
公開: 2022-03-11この記事では、別のJavaプログラムからSpringBootアプリケーションを起動する方法について説明します。 Spring Bootアプリケーションは通常、単一の実行可能JARアーカイブに組み込まれています。 内部にすべての依存関係が含まれ、ネストされたJARとしてパッケージ化されています。
同様に、Spring Bootプロジェクトは通常、提供されたmavenプラグインによって実行可能なJARファイルとしてビルドされ、すべてのダーティな作業を実行します。 その結果、他のユーザーと共有したり、サーバーにデプロイしたりするのが簡単な、便利な単一のJARファイルが作成されます。
Spring Bootアプリケーションの起動は、 java -jar mySpringProg.jarと入力するのと同じくらい簡単で、アプリケーションはコンソールにいくつかの適切にフォーマットされた情報メッセージを出力します。
しかし、Spring Boot開発者が、人間の介入なしに別のJavaプログラムからアプリケーションを実行したい場合はどうでしょうか。
ネストされたJARのしくみ
すべての依存関係を持つJavaプログラムを単一の実行可能なJARファイルにパックするには、JARファイルでもある依存関係を提供し、最終的な実行可能なJARファイル内に何らかの方法で格納する必要があります。
「シェーディング」は1つのオプションです。 依存関係のシェーディングは、依存関係を含めて名前を変更し、クラスを再配置し、影響を受けるバイトコードとリソースを書き換えて、アプリケーション(プロジェクト)独自のコードと一緒にバンドルされるコピーを作成するプロセスです。
シェーディングを使用すると、ユーザーはすべてのクラスとリソースを依存関係から解凍し、実行可能なJARファイルにパックして戻すことができます。 これは単純なシナリオでは機能する可能性がありますが、2つの依存関係にまったく同じ名前とパスを持つ同じリソースファイルまたはクラスが含まれている場合、それらは重複し、プログラムが機能しない可能性があります。
Spring Bootは別のアプローチを取り、依存関係のJARをネストされたJARとして実行可能なJAR内にパックします。
example.jar | +-META-INF | +-MANIFEST.MF +-org | +-springframework | +-boot | +-loader | +-<spring boot loader classes> +-BOOT-INF +-classes | +-mycompany | +-project | +-YourClasses.class +-lib +-dependency1.jar +-dependency2.jar
JARアーカイブは、標準のJava実行可能JARファイルとして編成されています。 SpringBootローダークラスはorg/springframework/boot/loader
パスにあり、ユーザークラスと依存関係はBOOT-INF/classes
とBOOT-INF/lib
にあります。
注: Springを初めて使用する場合は、SpringFrameworkの最も一般的な間違いトップ10の記事も参照してください。
一般的なSpringBootJARファイルには、次の3種類のエントリが含まれています。
- プロジェクトクラス
- ネストされたJARライブラリ
- SpringBootローダークラス
Spring Boot Classloaderは、最初にクラスパスにJARライブラリを設定し、次にプロジェクトクラスを設定します。これにより、IDE(Eclipse、IntelliJ)からのSpringBootアプリケーションの実行とコンソールからのSpringBootアプリケーションの実行にわずかな違いが生じます。
クラスオーバーライドとクラスローダーの詳細については、この記事を参照してください。
SpringBootアプリケーションの起動
コマンドラインまたはシェルから手動でSpringBootアプリケーションを起動するには、次のように入力します。
java -jar example.jar
ただし、別のJavaプログラムからプログラムでSpring Bootアプリケーションを起動するには、より多くの労力が必要です。 org/springframework/boot/loader/*.class
コードをロードし、Javaリフレクションを少し使用してJarFileArchive
、 JarLauncher
をインスタンス化し、 launch(String[])
メソッドを呼び出す必要があります。
これがどのように達成されるかについては、次のセクションでさらに詳しく見ていきます。
SpringBootLoaderクラスのロード
すでに指摘したように、SpringBootJARファイルは他のJARアーカイブとまったく同じです。 org/springframework/boot/loader/*.class
をロードし、Classオブジェクトを作成し、それらを使用して後でSpringBootアプリケーションを起動することができます。
import java.net.URLClassLoader; import java.util.Enumeration; import java.util.HashMap; import java.util.Map; import java.util.jar.JarEntry; import java.util.jar.JarFile; . . . public static void loadJar(final String pathToJar) throws IOException . . . { // Class name to Class object mapping. final Map<String, Class<?>> classMap = new HashMap<>(); final JarFile jarFile = new JarFile(pathToJar); final Enumeration<JarEntry> jarEntryEnum = jarFile.entries(); final URL[] urls = { new URL("jar:file:" + pathToJar + "!/") }; final URLClassLoader urlClassLoader = URLClassLoader.newInstance(urls);
ここでは、 classMap
がそれぞれのパッケージ名にマップされたClassオブジェクトを保持していることがわかります。たとえば、文字列値org.springframework.boot.loader.JarLauncher
はJarLauncher.class
オブジェクトにマップされます。
while (jarEntryEnum.hasMoreElements()) { final JarEntry jarEntry = jarEntryEnum.nextElement(); if (jarEntry.getName().startsWith("org/springframework/boot") && jarEntry.getName().endsWith(".class") == true) { int endIndex = jarEntryName.lastIndexOf(".class"); className = jarEntryName.substring(0, endIndex).replace('/', '.'); try { final Class<?> loadedClass = urlClassLoader.loadClass(className); result.put(loadedClass.getName(), loadedClass); } catch (final ClassNotFoundException ex) { } } } jarFile.close();
whileループの最終結果は、SpringBootローダークラスオブジェクトが入力されたマップです。

実際の起動の自動化
読み込みが邪魔にならないようにすると、自動起動を完了し、それを使用して実際にアプリを起動できます。
Javaリフレクションを使用すると、ロードされたクラスからオブジェクトを作成できます。これは、チュートリアルのコンテキストで非常に役立ちます。
最初のステップは、 JarFileArchive
オブジェクトを作成することです。
// Create JarFileArchive(File) object, needed for JarLauncher. final Class<?> jarFileArchiveClass = result.get("org.springframework.boot.loader.archive.JarFileArchive"); final Constructor<?> jarFileArchiveConstructor = jarFileArchiveClass.getConstructor(File.class); final Object jarFileArchive = jarFileArchiveConstructor.newInstance(new File(pathToJar));
JarFileArchive
オブジェクトのコンストラクターは、引数としてFile(String)
オブジェクトを受け取るため、指定する必要があります。
次のステップは、 JarLauncher
オブジェクトを作成することです。これには、コンストラクターにArchive
が必要です。
final Class<?> archiveClass = result.get("org.springframework.boot.loader.archive.Archive"); // Create JarLauncher object using JarLauncher(Archive) constructor. final Constructor<?> jarLauncherConstructor = mainClass.getDeclaredConstructor(archiveClass); jarLauncherConstructor.setAccessible(true); final Object jarLauncher = jarLauncherConstructor.newInstance(jarFileArchive);
混乱を避けるために、 Archive
は実際にはインターフェースであり、 JarFileArchive
は実装の1つであることに注意してください。
プロセスの最後のステップは、新しく作成したjarLauncher
オブジェクトでlaunch(String[])
メソッドを呼び出すことです。 これは比較的簡単で、数行のコードが必要です。
// Invoke JarLauncher#launch(String[]) method. final Class<?> launcherClass = result.get("org.springframework.boot.loader.Launcher"); final Method launchMethod = launcherClass.getDeclaredMethod("launch", String[].class); launchMethod.setAccessible(true); launchMethod.invoke(jarLauncher, new Object[]{new String[0]});
invoke(jarLauncer, new Object[]{new String[0]})
メソッドは、最終的にSpringBootアプリケーションを起動します。 メインスレッドが停止し、SpringBootアプリケーションが終了するのをここで待機することに注意してください。
SpringBootクラスローダーについての一言
Spring Boot JARファイルを調べると、次の構造が明らかになります。
+--- mySpringApp1-0.0.1-SNAPSHOT.jar +--- META-INF +--- BOOT-INF | +--- classes # 1 - project classes | | | | | +--- com.example.mySpringApp1 | | \--- SpringBootLoaderApplication.class | | | +--- lib # 2 - nested jar libraries | +--- javax.annotation-api-1.3.1 | +--- spring-boot-2.0.0.M7.jar | \--- (...) | +--- org.springframework.boot.loader # 3 - Spring Boot loader classes +--- JarLauncher.class +--- LaunchedURLClassLoader.class \--- (...)
次の3種類のエントリに注意してください。
- プロジェクトクラス
- ネストされたJARライブラリ
- SpringBootローダークラス
プロジェクトクラス( BOOT-INF/classes
)とネストされたJAR( BOOT-INF/lib
)の両方が、同じクラスローダーLaunchedURLClassLoader
によって処理されます。 このローダーは、SpringBootJARアプリケーションのルートにあります。
LaunchedURLClassLoader
は、IDEとは異なるライブラリコンテンツ( BOOT-INF/lib
)の後にクラスコンテンツ( BOOT-INF/classes
)をロードします。 たとえば、Eclipseは最初にクラスコンテンツをクラスパスに配置し、次にライブラリ(依存関係)を配置します。
LaunchedURLClassLoader
は、クラスのロードに使用されるURLのセットを使用して作成されたjava.net.URLClassLoader
を拡張します。 URLは、JARアーカイブやクラスフォルダなどの場所を指している場合があります。 クラスの読み込みを実行する場合、URLで指定されたすべてのリソースは、URLが提供された順序でトラバースされ、検索されたクラスを含む最初のリソースが使用されます。
まとめ
従来のJavaアプリケーションでは、すべての依存関係をクラスパス引数に列挙する必要があるため、起動手順がやや面倒で複雑になります。
対照的に、Spring Bootアプリケーションは便利で、コマンドラインから簡単に起動できます。 それらはすべての依存関係を管理し、エンドユーザーは詳細について心配する必要はありません。
ただし、別のJavaプログラムからSpring Bootアプリケーションを起動すると、Spring Bootのローダークラスをロードし、 JarFileArchive
やJarLauncher
などの特殊なオブジェクトを作成し、Javaリフレクションを使用してlaunch
メソッドを呼び出す必要があるため、手順がより複雑になります。
結論:Spring Bootは、内部で多くの面倒なタスクを処理できるため、開発者は時間を解放して、新機能の作成やテストなど、より便利な作業に集中できます。