Menjalankan Aplikasi Spring Boot dengan Maven vs Perang yang Boleh Dieksekusi

1. Pengenalan

Dalam tutorial ini, kita akan meneroka perbezaan antara memulakan aplikasi web Spring Boot melalui perintah mvn spring-boot: run dan menjalankannya setelah disusun ke dalam paket jar / perang melalui perintah java -jar .

Mari kita anggap di sini anda sudah biasa dengan konfigurasi matlamat pengemasan semula Boot Boot . Untuk keterangan lebih lanjut mengenai topik ini, sila baca Buat Aplikasi Fat Jar dengan Spring Boot.

2. Plugin Spring Boot Maven

Semasa menulis aplikasi Spring Boot, plugin Spring Boot Maven adalah alat yang disyorkan untuk membina, menguji, dan mengemas kod kami.

Plugin ini dihantar dengan banyak ciri mudah, seperti:

  • ia menyelesaikan versi kebergantungan yang betul untuk kita
  • ia dapat mengemas semua kebergantungan kami (termasuk pelayan aplikasi tertanam jika diperlukan) dalam satu jar / perang lemak yang boleh dijalankan dan juga:
    • uruskan konfigurasi classpath untuk kami, jadi kami boleh melangkau pilihan -pp panjang dalam arahan java -jar kami
    • laksanakan ClassLoader khusus untuk mencari dan memuatkan semua perpustakaan balang luaran, kini bersarang di dalam pakej
    • temukan secara automatik kaedah utama () dan konfigurasikannya dalam manifes, jadi kami tidak perlu menentukan kelas utama dalam perintah java -jar kami

3. Menjalankan Kod dengan Maven dalam Bentuk yang Meletup

Semasa kami mengerjakan aplikasi web, kami dapat memanfaatkan satu lagi fitur yang sangat menarik dari plugin Spring Boot Maven: kemampuan untuk menyebarkan aplikasi web kami secara automatik dalam pelayan aplikasi yang disematkan.

Kami hanya memerlukan satu pergantungan untuk memberitahu pemalam bahawa kami ingin menggunakan Tomcat untuk menjalankan kod kami:

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

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

Menjalankan perintah mvn spring-boot: run memicu muat turun Apache Tomcat dan memulakan permulaan Tomcat.

Mari mencubanya:

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

Apabila log menunjukkan garis yang mengandungi 'Permulaan Permulaan', aplikasi web kami siap untuk ditanyakan melalui penyemak imbas di alamat // localhost: 8080 /

4. Menjalankan Kod sebagai Aplikasi Berkemas Sendiri

Sebaik sahaja kita melalui tahap pengembangan dan kita ingin maju ke arah membawa aplikasi kita ke produksi, kita perlu mengemas aplikasi kita.

Malangnya, jika kita bekerja dengan paket jar , tujuan pakej Maven asas tidak termasuk kebergantungan luaran.

Ini bermaksud bahawa kita dapat menggunakannya hanya sebagai perpustakaan dalam projek yang lebih besar.

Untuk mengelakkan batasan ini, kita perlu memanfaatkan matlamat pengemasan semula plugin Maven Spring Boot untuk menjalankan jar / perang kita sebagai aplikasi yang berdiri sendiri.

4.1. Konfigurasi

Biasanya, kita hanya perlu mengkonfigurasi plugin build:

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

Tetapi projek contoh kami mengandungi lebih dari satu kelas utama, jadi kami harus memberitahu Java kelas mana yang akan dijalankan, dengan mengkonfigurasi pemalam:

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

atau dengan menetapkan harta tanah kelas permulaan :

 com.baeldung.webjar.WebjarsdemoApplication 

4.2. Menjalankan Aplikasi

Sekarang, kita boleh menjalankan perang contoh kita dengan dua perintah mudah:

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

Maklumat lebih lanjut mengenai cara menjalankan fail jar boleh didapati di artikel kami Jalankan Aplikasi JAR Dengan Argumen Baris Perintah.

4.3. Di dalam Fail Perang

Untuk memahami dengan lebih baik bagaimana perintah yang disebutkan di atas dapat menjalankan aplikasi pelayan penuh, kita dapat melihat ke spring-boot-ops.war kami .

Sekiranya kita tidak mengeluarkannya dan mengintip ke dalam, kita dapati suspek yang biasa:

  • META-INF , dengan MANIFEST.MF yang dihasilkan secara automatik
  • WEB-INF/classes, containing our compiled classes
  • WEB-INF/lib, which holds our war dependencies and the embedded Tomcat jar files

That's not all though, as there are some folders specific to our fat package configuration:

  • WEB-INF/lib-provided, containing external libraries required when running embedded but not required when deploying
  • org/springframework/boot/loader, which holds the Spring Boot custom class loader — this library is responsible for loading our external dependencies and making them accessible in runtime

4.4. Inside the War Manifest

As mentioned before, the Maven Spring Boot plugin finds the main class and generates the configuration needed for running the java command.

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 meneroka perbezaan antara menjalankan kod kami melalui Maven dan melalui perintah java -jar . Kami juga menjalankan tinjauan ringkas mengenai beberapa senario kes praktikal.

Kod sumber yang digunakan dalam artikel ini terdapat di GitHub.