プログラムで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は、内部で多くの面倒なタスクを処理できるため、開発者は時間を解放して、新機能の作成やテストなど、より便利な作業に集中できます。
