JAR nipis dengan Spring Boot

1. Pengenalan

Dalam tutorial ini, kita akan melihat bagaimana membina projek Spring Boot ke dalam fail JAR nipis, menggunakan projek spring-boot-thin-launcher .

Spring Boot terkenal dengan penggunaan JAR "gemuk", di mana satu artifak boleh dilaksanakan mengandungi kod aplikasi dan semua kebergantungannya.

Boot juga banyak digunakan untuk mengembangkan perkhidmatan mikro. Ini kadang kala bertentangan dengan pendekatan "JAR lemak" kerana memasukkan kebergantungan yang sama berulang-ulang dalam banyak artifak boleh menjadi pembaziran sumber yang penting.

2. Prasyarat

Pertama sekali, kita memerlukan projek Spring Boot. Dalam artikel ini, kita akan melihat build Maven, dan build Gradle dalam konfigurasi mereka yang paling biasa.

Mustahil untuk merangkumi semua sistem binaan dan konfigurasi binaan di luar sana, tetapi, mudah-mudahan, kami akan melihat cukup banyak prinsip umum yang seharusnya anda dapat menerapkannya pada persediaan khusus anda.

2.1. Projek Maven

Dalam projek Boot yang dibina dengan Maven, kita semestinya mengkonfigurasi plugin Spring Boot Maven dalam fail pom.xml projek kita , induknya, atau salah satu nenek moyangnya:

 org.springframework.boot spring-boot-maven-plugin 

Versi kebergantungan Spring Boot biasanya diputuskan dengan menggunakan BOM atau mewarisi dari POM induk seperti dalam projek rujukan kami:

 org.springframework.boot spring-boot-starter-parent 2.2.2.RELEASE  

2.2. Projek Gradle

Dalam projek Boot yang dibina dengan Gradle, kita akan mempunyai plugin Boot Gradle:

buildscript { ext { springBootPlugin = 'org.springframework.boot:spring-boot-gradle-plugin' springBootVersion = '2.2.2.RELEASE' } repositories { mavenCentral() } dependencies { classpath("${springBootPlugin}:${springBootVersion}") } } // elided apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' springBoot { mainClassName = 'com.baeldung.DemoApplication' }

Perhatikan bahawa, dalam artikel ini, kita hanya akan mempertimbangkan projek Boot 2.x dan yang lebih baru. The Thin Launcher juga menyokong versi sebelumnya, tetapi memerlukan konfigurasi Gradle yang sedikit berbeza yang kita hilangkan untuk kesederhanaan. Lihat halaman utama projek untuk maklumat lebih lanjut.

3. Bagaimana Membuat JAR Tipis?

Spring Boot Thin Launcher adalah perpustakaan kecil yang membaca pergantungan artifak dari fail yang digabungkan dalam arkib itu sendiri, memuat turunnya dari repositori Maven dan akhirnya melancarkan kelas utama aplikasi.

Oleh itu, apabila kita membina projek dengan perpustakaan, kita mendapat fail JAR dengan kod kita, fail yang menyebutkan kebergantungannya, dan kelas utama dari perpustakaan yang melakukan tugas di atas.

Sudah tentu, perkara-perkara sedikit lebih bernuansa daripada penjelasan ringkas kami; kita akan membincangkan beberapa topik secara mendalam kemudian dalam artikel.

4. Penggunaan Asas

Sekarang mari kita lihat bagaimana membina JAR "nipis" dari aplikasi Spring Boot biasa kami.

Kami akan melancarkan aplikasi dengan java -jar biasa , dengan argumen baris perintah tambahan pilihan yang mengawal Thin Launcher. Kami akan melihat beberapa dari mereka dalam bahagian berikut; laman utama projek mengandungi senarai penuh.

4.1. Projek Maven

Dalam projek Maven, kita harus mengubah deklarasi Boot plugin (lihat bahagian 2.1) untuk memasukkan ketergantungan pada tata letak "tipis" khusus:

 org.springframework.boot spring-boot-maven-plugin    org.springframework.boot.experimental spring-boot-thin-layout 1.0.11.RELEASE   

Pelancar akan membaca kebergantungan dari fail pom.xml yang disimpan Maven dalam JAR yang dihasilkan dalam direktori META-INF / maven .

Kami akan melakukan binaan seperti biasa, misalnya dengan pemasangan mvn .

Sekiranya kita ingin dapat menghasilkan binaan nipis dan gemuk (contohnya dalam projek dengan pelbagai modul) kita dapat menyatakan susun atur khusus dalam profil Maven khusus.

4.2. Maven dan Ketergantungan: sifat nipis

Kita juga boleh meminta Maven menghasilkan fail thin.properties selain pom.xml . Dalam kes itu, fail akan mengandungi senarai kebergantungan lengkap, termasuk yang transitif, dan pelancar akan lebih memilihnya daripada pom.xml .

The mojo (plugin) for doing so is spring-boot-thin-maven-plugin:properties, and by default, it outputs the thin.properties file in src/main/resources/META-INF, but we can specify its location with the thin.output property:

$ mvn org.springframework.boot.experimental:spring-boot-thin-maven-plugin:properties -Dthin.output=.

Please note that the output directory must exist for the goal to succeed, even if we've kept the default one.

4.3. Gradle Projects

In a Gradle project, instead, we add a dedicated plugin:

buildscript { ext { //... thinPlugin = 'org.springframework.boot.experimental:spring-boot-thin-gradle-plugin' thinVersion = '1.0.11.RELEASE' } //... dependencies { //... classpath("${thinPlugin}:${thinVersion}") } } //elided apply plugin: 'maven' apply plugin: 'org.springframework.boot.experimental.thin-launcher'

To obtain a thin build, we'll tell Gradle to execute the thinJar task:

~/projects/baeldung/spring-boot-gradle $ ./gradlew thinJar

4.4. Gradle and Dependencies: pom.xml

In the code example in the previous section, we've declared the Maven plugin in addition to the Thin Launcher (as well as the Boot and Dependency Management plugins that we'd already seen in the Prerequisites section).

That's because, just like in the Maven case that we've seen earlier, the artifact will contain and make use of a pom.xml file enumerating the application's dependencies. The pom.xml file is generated by a task called thinPom, which is an implicit dependency of any jar task.

We can customize the generated pom.xml file with a dedicated task. Here, we'll just replicate what the thin plugin already does automatically:

task createPom { def basePath = 'build/resources/main/META-INF/maven' doLast { pom { withXml(dependencyManagement.pomConfigurer) }.writeTo("${basePath}/${project.group}/${project.name}/pom.xml") } }

To use our custom pom.xml file, we add the above task to the jar task's dependencies:

bootJar.dependsOn = [createPom]

4.5. Gradle and Dependencies: thin.properties

We can also have Gradle generate a thin.properties file rather than pom.xml, as we did earlier with Maven.

The task that generates the thin.properties file is called thinProperties, and it's not used by default. We can add it as a dependency of the jar task:

bootJar.dependsOn = [thinProperties]

5. Storing Dependencies

The whole point of thin jars is to avoid bundling the dependencies with the application. However, dependencies don't magically disappear, they're simply stored elsewhere.

In particular, the Thin Launcher uses the Maven infrastructure to resolve dependencies, so:

  1. it checks the local Maven repository, which by default lies in ~/.m2/repository but can be moved elsewhere;
  2. then, it downloads missing dependencies from Maven Central (or any other configured repository);
  3. finally, it caches them in the local repository, so that it won't have to download them again the next time we run the application.

Of course, the download phase is the slow and error-prone part of the process, because it requires access to Maven Central through the Internet, or access to a local proxy, and we all know how those things are generally unreliable.

Fortunately, there are various ways of deploying the dependencies together with the application(s), for example in a prepackaged container for cloud deployment.

5.1. Running the Application for Warm-up

The simplest way to cache the dependencies is to do a warm-up run of the application in the target environment. As we've seen earlier, this will cause the dependencies to be downloaded and cached in the local Maven repository. If we run more than one app, the repository will end up containing all the dependencies without duplicates.

Since running an application can have unwanted side effects, we can also perform a “dry run” that only resolves and downloads the dependencies without running any user code:

$ java -Dthin.dryrun=true -jar my-app-1.0.jar

Note that, as per Spring Boot conventions, we can set the -Dthin.dryrun property also with a –thin.dryrun command line argument to the application or with a THIN_DRYRUN system property. Any value except false will instruct the Thin Launcher to perform a dry run.

5.2. Packaging the Dependencies During the Build

Another option is to collect the dependencies during the build, without bundling them in the JAR. Then, we can copy them to the target environment as part of the deployment procedure.

This is generally simpler because it's not necessary to run the application in the target environment. However, if we're deploying multiple applications, we'll have to merge their dependencies, either manually or with a script.

The format in which the Thin Plugin for Maven and Gradle packages the dependencies during a build is the same as a Maven local repository:

root/ repository/ com/ net/ org/ ...

In fact, we can point an application using the Thin Launcher to any such directory (including a local Maven repository) at runtime with the thin.root property:

$ java -jar my-app-1.0.jar --thin.root=my-app/deps

We can also safely merge multiple such directories by copying them one over another, thus obtaining a Maven repository with all the necessary dependencies.

5.3. Packaging the Dependencies With Maven

To have Maven package the dependencies for us, we use the resolve goal of the spring-boot-thin-maven-plugin. We can invoke it manually or automatically in our pom.xml:

 org.springframework.boot.experimental spring-boot-thin-maven-plugin ${thin.version}    resolve  resolve  false   

After building the project, we'll find a directory target/thin/root/ with the structure that we've discussed in the previous section.

5.4. Packaging the Dependencies With Gradle

If we're using Gradle with the thin-launcher plugin, instead, we have a thinResolve task available. The task will save the application and its dependencies in the build/thin/root/ directory, similarly to the Maven plugin of the previous section:

$ gradlew thinResolve

Please note that, at the time of writing, the thin-launcher plugin has a bug that prevents the dependencies to be saved if thin.properties is used: //github.com/dsyer/spring-boot-thin-launcher/issues/53.

6. Conclusions and Further Reading

Dalam artikel ini, kami telah melihat bagaimana membuat balang nipis kami. Kami juga telah melihat bagaimana menggunakan infrastruktur Maven untuk memuat turun dan menyimpan pergantungan mereka.

Halaman utama pelancar tipis mempunyai beberapa panduan CARA-CARA untuk senario seperti penyebaran awan ke Heroku, serta senarai lengkap argumen baris perintah yang disokong.

Pelaksanaan semua contoh Maven dan potongan kode boleh didapati di projek GitHub - sebagai projek Maven, jadi mudah untuk diimport dan dijalankan sebagaimana adanya.

Begitu juga, semua contoh Gradle merujuk kepada projek GitHub ini.