Menjalankan Aplikasi Spring Boot dengan Maven vs sebuah Executable War / Jar

1. Perkenalan

Dalam tutorial ini, kita akan menjelajahi perbedaan antara memulai aplikasi web Spring Boot melalui perintah mvn spring-boot: run dan menjalankannya setelah dikompilasi ke dalam paket jar / war melalui perintah java -jar .

Anggap saja di sini Anda sudah terbiasa dengan konfigurasi tujuan pengemasan ulang Spring Boot . Untuk detail lebih lanjut tentang topik ini, baca Membuat Aplikasi Fat Jar dengan Spring Boot.

2. Plugin Spring Boot Maven

Saat menulis aplikasi Spring Boot, plugin Spring Boot Maven adalah alat yang disarankan untuk membuat, menguji, dan mengemas kode kami.

Plugin ini dikirimkan dengan banyak fitur praktis, seperti:

  • itu menyelesaikan versi ketergantungan yang benar untuk kita
  • itu dapat mengemas semua dependensi kita (termasuk server aplikasi tertanam jika diperlukan) dalam satu tabung / perang lemak yang dapat dijalankan dan juga akan:
    • kelola konfigurasi classpath untuk kami, sehingga kami dapat melewati opsi -cp panjang itu dalam perintah java -jar
    • mengimplementasikan ClassLoader kustom untuk mencari dan memuat semua library jar eksternal, sekarang bersarang di dalam paket
    • temukan secara otomatis metode main () dan konfigurasikan dalam manifes, jadi kita tidak perlu menentukan kelas utama dalam perintah java -jar kita

3. Menjalankan Kode dengan Maven dalam Bentuk Meledak

Saat kami mengerjakan aplikasi web, kami dapat memanfaatkan fitur lain yang sangat menarik dari plugin Spring Boot Maven: kemampuan untuk secara otomatis menyebarkan aplikasi web kami di server aplikasi tertanam.

Kami hanya membutuhkan satu ketergantungan untuk memberi tahu plugin bahwa kami ingin menggunakan Tomcat untuk menjalankan kode kami:

 org.springframework.boot spring-boot-starter-web 

Sekarang, saat menjalankan perintah mvn spring-boot: run di folder root proyek kami, plugin membaca konfigurasi pom dan memahami bahwa kami memerlukan wadah aplikasi web.

Menjalankan perintah mvn spring-boot: run memicu pengunduhan Apache Tomcat dan menginisialisasi startup Tomcat.

Ayo coba:

$ mvn spring-boot:run ... ... [INFO] ---------------------------------------- [INFO] Building spring-boot-ops 0.0.1-SNAPSHOT [INFO] --------------------------------[ war ]--------------------------------- [INFO] [INFO] >>> spring-boot-maven-plugin:2.1.3.RELEASE:run (default-cli) > test-compile @ spring-boot-ops >>> Downloading from central: //repo.maven.apache.org/maven2/org/apache/tomcat/embed/tomcat-embed-core/9.0.16/tomcat-embed-core-9.0.16.pom Downloaded from central: //repo.maven.apache.org/maven2/org/apache/tomcat/embed/tomcat-embed-core/9.0.16/tomcat-embed-core-9.0.16.pom (1.8 kB at 2.8 kB/s) ... ... [INFO] --- spring-boot-maven-plugin:2.1.3.RELEASE:run (default-cli) @ spring-boot-ops --- ... ... 11:33:36.648 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat] 11:33:36.649 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.16] ... ... 11:33:36.952 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext ... ... 11:33:48.223 [main] INFO o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"] 11:33:48.289 [main] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path '' 11:33:48.292 [main] INFO org.baeldung.boot.Application - Started Application in 22.454 seconds (JVM running for 37.692)

Ketika log menunjukkan baris yang berisi 'Aplikasi yang Dimulai', aplikasi web kita siap untuk ditanyai melalui browser di alamat // localhost: 8080 /

4. Menjalankan Kode sebagai Aplikasi Paket Stand-Alone

Setelah kami melewati fase pengembangan dan kami ingin melanjutkan untuk membawa aplikasi kami ke produksi, kami perlu mengemas aplikasi kami.

Sayangnya, jika kami bekerja dengan paket jar , tujuan paket Maven dasar tidak menyertakan dependensi eksternal apa pun.

Ini berarti kita hanya dapat menggunakannya sebagai perpustakaan dalam proyek yang lebih besar.

Untuk menghindari batasan ini, kita perlu memanfaatkan tujuan pengemasan ulang plugin Maven Spring Boot untuk menjalankan jar / war kita sebagai aplikasi yang berdiri sendiri.

4.1. Konfigurasi

Biasanya, kita hanya perlu mengkonfigurasi plugin build:

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

Tetapi proyek contoh kami berisi lebih dari satu kelas utama, jadi kami harus memberi tahu Java kelas mana yang harus dijalankan, dengan mengkonfigurasi plugin:

 org.springframework.boot spring-boot-maven-plugin    com.baeldung.webjar.WebjarsdemoApplication    

atau dengan menyetel properti kelas-mulai :

 com.baeldung.webjar.WebjarsdemoApplication 

4.2. Menjalankan Aplikasi

Sekarang, kita dapat menjalankan contoh perang kita dengan dua perintah sederhana:

$ mvn clean package spring-boot:repackage $ java -jar target/spring-boot-ops.war

Detail lebih lanjut tentang cara menjalankan file jar dapat ditemukan di artikel kami Menjalankan Aplikasi JAR Dengan Argumen Baris Perintah.

4.3. Di dalam File Perang

Untuk lebih memahami bagaimana perintah yang disebutkan di atas dapat menjalankan aplikasi server lengkap, kita dapat melihat ke dalam spring-boot-ops.war .

Jika kami membuka kompresnya dan mengintip ke dalam, kami menemukan tersangka yang biasa:

  • META-INF , dengan MANIFEST.MF yang dibuat secara otomatis
  • WEB-INF / class , berisi class yang kami kompilasi
  • WEB-INF / lib , yang menyimpan dependensi perang kami dan file jar Tomcat tertanam

Itu belum semuanya, karena ada beberapa folder khusus untuk konfigurasi paket lemak kami:

  • WEB-INF / lib-disediakan , berisi pustaka eksternal yang diperlukan saat menjalankan tertanam tetapi tidak diperlukan saat menerapkan
  • org / springframework / boot / loader , yang menampung loader kelas khusus Spring Boot - pustaka ini bertanggung jawab untuk memuat dependensi eksternal kami dan membuatnya dapat diakses saat runtime

4.4. Di dalam Manifes Perang

Seperti disebutkan sebelumnya, plugin Maven Spring Boot menemukan kelas utama dan menghasilkan konfigurasi yang diperlukan untuk menjalankan perintah java .

The resulting MANIFEST.MF has some additional lines:

Start-Class: com.baeldung.webjar.WebjarsdemoApplication Main-Class: org.springframework.boot.loader.WarLauncher

In particular, we can observe that the last one specifies the Spring Boot class loader launcher to use.

4.5. Inside a Jar File

Due to the default packaging strategy, our war packaging scenario doesn't differ much, whether we use the Spring Boot Maven Plugin or not.

To better appreciate the advantages of the plugin, we can try changing the pom packaging configuration to jar and run mvn clean package again.

We can now observe that our fat jar is organized a bit differently from our previous war file:

  • All our classes and resources folders are now located under BOOT-INF/classes
  • BOOT-INF/lib holds all the external libraries

Without the plugin, the lib folder would not exist, and all the content of BOOT-INF/classes would be located in the root of the package.

4.6. Inside the Jar Manifest

Also the MANIFEST.MF has changed, featuring these additional lines:

Spring-Boot-Classes: BOOT-INF/classes/ Spring-Boot-Lib: BOOT-INF/lib/ Spring-Boot-Version: 2.1.3.RELEASE Main-Class: org.springframework.boot.loader.JarLauncher

Spring-Boot-Classes and Spring-Boot-Lib are particularly interesting, as they tell us where the class loader is going to find classes and external libraries.

5. How to Choose

When analyzing tools, it's imperative to take account of the purpose these tools are created for. Do we want to ease the development or ensure smooth deployment and portability? Let's have a look at the phases most affected by this choice.

5.1. Development

As developers, we often spend most of our time coding without needing to spend a lot of time setting up our environment to run the code locally. In simple applications, that's usually not a concern. But, for more complex projects, we may need to set environment variables, start servers, and populate databases.

Configuring the right environment every time we want to run the application would be very impractical, especially if more than one service has to run at the same time.

That's where running the code with Maven helps us. We already have the entire codebase checked out locally, so we can leverage the pom configuration and resource files. We can set environment variables, spawn an in-memory database, and even download the correct server version and deploy our application with one command.

Even in a multi-module codebase, where each module needs different variables and server versions, we can easily run the right environment via Maven profiles.

5.2. Production

The more we move towards production, the more the conversation shifts towards stability and security. That is why we cannot apply the process used for our development machine to a server with live customers.

Running the code through Maven at this stage is bad practice for multiple reasons:

  • First of all, we would need to install Maven
  • Then, just because we need to compile the code, we need the full Java Development Kit (JDK)
  • Next, we have to copy the codebase to our server, leaving all our proprietary code in plain text
  • The mvn command has to execute all phases of the life cycle (find sources, compile, and run)
  • Thanks to the previous point, we would also waste CPU and, in the case of a cloud server, money
  • Maven spawns multiple Java processes, each using memory (by default, they each use the same memory amount as the parent process)
  • Finally, if we have multiple servers to deploy, all the above is repeated on each one

These are just a few reasons why shipping the application as a package is more practical for production.

6. Conclusion

Dalam tutorial ini, kami menjelajahi perbedaan antara menjalankan kode kami melalui Maven dan melalui perintah java -jar . Kami juga menjalankan ikhtisar singkat dari beberapa skenario kasus praktis.

Kode sumber yang digunakan dalam artikel ini tersedia di GitHub.