Ausführen einer Spring Boot App mit Maven gegen einen ausführbaren Krieg / Jar

1. Einleitung

In diesem Tutorial werden die Unterschiede zwischen dem Starten einer Spring Boot-Webanwendung über den Befehl mvn spring-boot: run und dem Ausführen nach dem Kompilieren in ein jar / war-Paket über den Befehl java -jar untersucht .

Nehmen wir an, Sie sind hier bereits mit der Konfiguration des Spring Boot- Repackage- Ziels vertraut . Weitere Informationen zu diesem Thema finden Sie unter Erstellen einer Fat Jar-App mit Spring Boot.

2. Das Spring Boot Maven Plugin

Beim Schreiben einer Spring Boot-Anwendung ist das Spring Boot Maven-Plugin das empfohlene Tool zum Erstellen, Testen und Verpacken unseres Codes.

Dieses Plugin bietet viele praktische Funktionen wie:

  • Es löst die richtigen Abhängigkeitsversionen für uns auf
  • Es kann alle unsere Abhängigkeiten (einschließlich eines eingebetteten Anwendungsservers, falls erforderlich) in einem einzigen, ausführbaren Fat Jar / War verpacken und wird außerdem:
    • Verwalten Sie für uns die Klassenpfadkonfiguration, damit wir diese lange Option -cp in unserem Befehl java -jar überspringen können
    • Implementieren Sie einen benutzerdefinierten ClassLoader , um alle externen JAR-Bibliotheken zu suchen und zu laden, die jetzt im Paket verschachtelt sind
    • Suchen Sie automatisch die main () -Methode und konfigurieren Sie sie im Manifest, sodass wir die Hauptklasse nicht in unserem Befehl java -jar angeben müssen

3. Ausführen des Codes mit Maven in explodierter Form

Wenn wir an einer Webanwendung arbeiten, können wir eine weitere sehr interessante Funktion des Spring Boot Maven-Plugins nutzen: die Möglichkeit, unsere Webanwendung automatisch auf einem eingebetteten Anwendungsserver bereitzustellen.

Wir benötigen nur eine Abhängigkeit, um dem Plugin mitzuteilen, dass wir Tomcat zum Ausführen unseres Codes verwenden möchten:

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

Wenn Sie nun den Befehl mvn spring-boot: run in unserem Projektstammordner ausführen , liest das Plugin die pom-Konfiguration und versteht, dass wir einen Webanwendungscontainer benötigen.

Das Ausführen des Befehls mvn spring-boot: run löst den Download von Apache Tomcat aus und initialisiert den Start von Tomcat.

Lass es uns versuchen:

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

Wenn im Protokoll die Zeile "Gestartete Anwendung" angezeigt wird, kann unsere Webanwendung über den Browser unter der Adresse // localhost: 8080 / abgefragt werden.

4. Ausführen des Codes als eigenständige Paketanwendung

Sobald wir die Entwicklungsphase abgeschlossen haben und Fortschritte bei der Produktion unserer Anwendung erzielen möchten, müssen wir unsere Anwendung verpacken.

Wenn wir mit einem JAR- Paket arbeiten, enthält das grundlegende Maven- Paketziel leider keine der externen Abhängigkeiten.

Dies bedeutet, dass wir es nur als Bibliothek in einem größeren Projekt verwenden können.

Um diese Einschränkung zu umgehen, müssen wir das Ziel des Maven Spring Boot-Plugins zum Umpacken nutzen , um unser jar / war als eigenständige Anwendung auszuführen.

4.1. Aufbau

Normalerweise müssen wir nur das Build-Plugin konfigurieren:

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

Unser Beispielprojekt enthält jedoch mehr als eine Hauptklasse. Daher müssen wir Java mitteilen, welche Klasse ausgeführt werden soll, indem wir entweder das Plugin konfigurieren:

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

oder indem Sie die Eigenschaft start-class festlegen:

 com.baeldung.webjar.WebjarsdemoApplication 

4.2. Ausführen der Anwendung

Jetzt können wir unseren Beispielkrieg mit zwei einfachen Befehlen ausführen:

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

Weitere Informationen zum Ausführen einer JAR-Datei finden Sie in unserem Artikel JAR-Anwendung mit Befehlszeilenargumenten ausführen.

4.3. In der Kriegsakte

Um besser zu verstehen, wie der oben erwähnte Befehl eine vollständige Serveranwendung ausführen kann, können wir einen Blick in unsere spring-boot-ops.war werfen .

Wenn wir es dekomprimieren und hineinschauen, finden wir die üblichen Verdächtigen:

  • META-INF mit dem automatisch generierten MANIFEST.MF
  • WEB-INF / Klassen , die unsere kompilierten Klassen enthalten
  • WEB-INF / lib , das unsere Kriegsabhängigkeiten und die eingebetteten Tomcat-JAR-Dateien enthält

Das ist jedoch noch nicht alles, da es einige Ordner gibt, die für unsere Fat Package-Konfiguration spezifisch sind:

  • WEB-INF / lib-bereitgestellt , enthält externe Bibliotheken, die beim Ausführen von Embedded erforderlich sind, bei der Bereitstellung jedoch nicht
  • org / springframework / boot / loader , in dem sich der benutzerdefinierte Spring Boot-Klassenlader befindet. Diese Bibliothek ist dafür verantwortlich, unsere externen Abhängigkeiten zu laden und sie zur Laufzeit zugänglich zu machen

4.4. Im Kriegsmanifest

Wie bereits erwähnt, findet das Maven Spring Boot-Plugin die Hauptklasse und generiert die Konfiguration, die zum Ausführen des Java- Befehls erforderlich ist .

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

In diesem Tutorial haben wir die Unterschiede zwischen der Ausführung unseres Codes über Maven und über den Befehl java -jar untersucht . Wir haben auch einen kurzen Überblick über einige praktische Fallszenarien gegeben.

Der in diesem Artikel verwendete Quellcode ist über GitHub verfügbar.