Meluncurkan Aplikasi Spring Boot Secara Terprogram

Diterbitkan: 2022-03-11

Artikel ini akan menunjukkan cara memulai aplikasi Spring Boot dari program Java lainnya. Aplikasi Spring Boot biasanya dibangun ke dalam satu arsip JAR yang dapat dieksekusi. Ini berisi semua dependensi di dalamnya, dikemas sebagai JAR bersarang.

Demikian juga, proyek Spring Boot biasanya dibuat sebagai file JAR yang dapat dieksekusi oleh plugin maven yang disediakan yang melakukan semua pekerjaan kotor. Hasilnya adalah file JAR tunggal yang nyaman yang mudah dibagikan dengan orang lain, disebarkan di server, dan sebagainya.

Memulai aplikasi Spring Boot semudah mengetik java -jar mySpringProg.jar , dan aplikasi akan mencetak di konsol beberapa pesan info yang diformat dengan baik.

Tetapi bagaimana jika pengembang Spring Boot ingin menjalankan aplikasi dari program Java lain, tanpa campur tangan manusia?

Cara Kerja JAR Bersarang

Untuk mengemas program Java dengan semua dependensi ke dalam satu file JAR yang dapat dijalankan, dependensi yang juga merupakan file JAR harus disediakan dan disimpan di dalam file JAR final yang dapat dijalankan.

"Shading" adalah salah satu pilihan. Ketergantungan bayangan adalah proses memasukkan dan mengganti nama dependensi, merelokasi kelas, dan menulis ulang bytecode dan sumber daya yang terpengaruh untuk membuat salinan yang dibundel bersama dengan kode aplikasi (proyek) sendiri.

Shading memungkinkan pengguna untuk membongkar semua kelas dan sumber daya dari dependensi dan mengemasnya kembali ke dalam file JAR yang dapat dijalankan. Ini mungkin bekerja untuk skenario sederhana, namun, jika dua dependensi berisi file atau kelas sumber daya yang sama dengan nama dan jalur yang sama persis, mereka akan tumpang tindih dan program mungkin tidak berfungsi.

Spring Boot mengambil pendekatan yang berbeda dan mengemas JAR ketergantungan di dalam JAR yang dapat dijalankan, sebagai JAR bersarang.

 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

Arsip JAR diatur sebagai file JAR standar yang dapat dijalankan Java. Kelas pemuat Boot Musim Semi terletak di jalur org/springframework/boot/loader , sedangkan kelas pengguna dan dependensi berada di BOOT-INF/classes dan BOOT-INF/lib .

Catatan: Jika Anda baru mengenal Spring, Anda mungkin juga ingin melihat artikel 10 Kesalahan Kerangka Kerja Musim Semi Paling Umum kami .

File Spring Boot JAR khas berisi tiga jenis entri:

  • Kelas proyek
  • Pustaka JAR bersarang
  • Kelas pemuat Boot Musim Semi

Spring Boot Classloader pertama-tama akan mengatur pustaka JAR di classpath dan kemudian kelas proyek, yang membuat sedikit perbedaan antara menjalankan aplikasi Spring Boot dari IDE (Eclipse, IntelliJ) dan dari konsol.

Untuk informasi tambahan tentang penggantian kelas dan pemuat kelas, Anda dapat membaca artikel ini.

Meluncurkan Aplikasi Boot Musim Semi

Meluncurkan aplikasi Spring Boot secara manual dari baris perintah atau shell semudah mengetik berikut ini:

 java -jar example.jar

Namun, memulai aplikasi Boot Musim Semi secara terprogram dari program Java lain membutuhkan lebih banyak usaha. Anda perlu memuat kode org/springframework/boot/loader/*.class , menggunakan sedikit refleksi Java untuk membuat instance JarFileArchive , JarLauncher , dan memanggil metode launch(String[]) .

Kami akan melihat lebih rinci tentang bagaimana hal ini dicapai di bagian berikut.

Memuat Kelas Spring Boot Loader

Seperti yang telah kami tunjukkan, file JAR Boot Musim Semi sama seperti arsip JAR lainnya. Dimungkinkan untuk memuat entri org/springframework/boot/loader/*.class , membuat objek Kelas, dan menggunakannya untuk meluncurkan aplikasi Boot Musim Semi nanti.

 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);

Di sini kita dapat melihat classMap akan menampung objek Kelas yang dipetakan ke nama paketnya masing-masing, misalnya, nilai String org.springframework.boot.loader.JarLauncher akan dipetakan ke objek 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();

Hasil akhir dari while loop adalah peta yang diisi dengan objek kelas pemuat Boot Musim Semi.

Mengotomatiskan Peluncuran Sebenarnya

Dengan menyingkir, kita dapat melanjutkan untuk menyelesaikan peluncuran otomatis dan menggunakannya untuk benar-benar memulai aplikasi kita.

Refleksi Java memungkinkan pembuatan objek dari kelas yang dimuat, yang cukup berguna dalam konteks tutorial kita.

Langkah pertama adalah membuat objek 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));

Konstruktor objek JarFileArchive mengambil objek File(String) sebagai argumen, jadi itu harus disediakan.

Langkah selanjutnya adalah membuat objek JarLauncher , yang membutuhkan Archive di konstruktornya.

 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);

Untuk menghindari kebingungan, harap perhatikan bahwa Archive sebenarnya adalah sebuah antarmuka, sedangkan JarFileArchive adalah salah satu implementasinya.

Langkah terakhir dalam proses ini adalah memanggil metode launch(String[]) pada objek jarLauncher yang baru kita buat. Ini relatif mudah dan hanya membutuhkan beberapa baris kode.

 // 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]});

Metode invoke(jarLauncer, new Object[]{new String[0]}) akhirnya akan memulai aplikasi Spring Boot. Perhatikan bahwa utas utama akan berhenti dan menunggu di sini hingga aplikasi Boot Musim Semi berakhir.

Sepatah Kata Tentang Pemuat Kelas Boot Musim Semi

Memeriksa file JAR Boot Musim Semi kami akan mengungkapkan struktur berikut:

 +--- 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 \--- (...)

Perhatikan tiga jenis entri:

  • Kelas proyek
  • Pustaka JAR bersarang
  • Kelas pemuat Boot Musim Semi

Kedua kelas proyek ( BOOT-INF/classes ) dan JAR bersarang ( BOOT-INF/lib ) ditangani oleh pemuat kelas yang sama LaunchedURLClassLoader . Loader ini berada di root aplikasi Spring Boot JAR.

LaunchedURLClassLoader akan memuat konten kelas ( BOOT-INF/classes ) setelah konten perpustakaan ( BOOT-INF/lib ), yang berbeda dari IDE. Misalnya, Eclipse pertama-tama akan menempatkan konten kelas di classpath dan kemudian perpustakaan (dependensi).

LaunchedURLClassLoader memperluas java.net.URLClassLoader , yang dibuat dengan sekumpulan URL yang akan digunakan untuk pemuatan kelas. URL mungkin mengarah ke lokasi seperti arsip JAR atau folder kelas. Saat melakukan pemuatan kelas, semua sumber daya yang ditentukan oleh URL akan dilalui dalam urutan URL yang disediakan, dan sumber daya pertama yang berisi kelas yang dicari akan digunakan.

Membungkus

Aplikasi Java klasik membutuhkan semua dependensi yang akan disebutkan dalam argumen classpath, membuat prosedur startup agak rumit dan rumit.

Sebaliknya, aplikasi Spring Boot berguna dan mudah untuk memulai dari baris perintah. Mereka mengelola semua dependensi, dan pengguna akhir tidak perlu khawatir tentang detailnya.

Namun, memulai aplikasi Boot Musim Semi dari program Java lain membuat prosedur menjadi lebih rumit, karena memerlukan pemuatan kelas pemuat Boot Musim Semi, membuat objek khusus seperti JarFileArchive dan JarLauncher , dan kemudian menggunakan refleksi Java untuk memanggil metode launch .

Intinya : Spring Boot dapat menangani banyak tugas kasar, memungkinkan pengembang untuk membebaskan waktu dan fokus pada pekerjaan yang lebih berguna seperti membuat fitur baru, pengujian, dan sebagainya.