Dunne JARs met Spring Boot

1. Inleiding

In deze tutorial gaan we kijken naar hoe u een Spring Boot-project in een dun JAR-bestand bouwt met behulp van de spring-boot-thin-launcher project.

Spring Boot staat bekend om zijn "dikke" JAR-implementaties, waarbij een enkel uitvoerbaar artefact zowel de applicatiecode als al zijn afhankelijkheden bevat.

Boot wordt ook veel gebruikt om microservices te ontwikkelen. Dit kan soms in strijd zijn met de "dikke JAR" -benadering, omdat het steeds weer opnieuw opnemen van dezelfde afhankelijkheden in veel artefacten een belangrijke verspilling van middelen kan worden.

2. Vereisten

Allereerst hebben we natuurlijk een Spring Boot-project nodig. In dit artikel zullen we Maven-builds bekijken en Gradle-builds in hun meest voorkomende configuraties.

Het is onmogelijk om alle build-systemen en configuraties die er zijn te behandelen, maar hopelijk zullen we voldoende algemene principes bekijken dat je ze zou moeten kunnen toepassen op je specifieke setup.

2.1. Maven-projecten

In een Boot-project gebouwd met Maven, zouden we de Spring Boot Maven-plug-in moeten hebben geconfigureerd in ons project pom.xml bestand, zijn ouder of een van zijn voorouders:

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

De versie van Spring Boot-afhankelijkheden wordt meestal bepaald door een stuklijst te gebruiken of te erven van een bovenliggende POM, zoals in ons referentieproject:

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

2.2. Gradle-projecten

In een Boot-project gebouwd met Gradle, hebben we de Boot Gradle-plug-in:

buildscript {ext {springBootPlugin = 'org.springframework.boot: spring-boot-gradle-plugin' springBootVersion = '2.2.2.RELEASE'} opslagplaatsen {mavenCentral ()} afhankelijkheden {classpath ("$ {springBootPlugin}: $ {springBootVersion } ")}} // weggelaten plug-in toepassen: 'org.springframework.boot' plug-in toepassen: 'io.spring.dependency-management' springBoot {mainClassName = 'com.baeldung.DemoApplication'}

Merk op dat we in dit artikel alleen Boot 2.x en latere projecten zullen beschouwen. De Thin Launcher ondersteunt ook eerdere versies, maar het vereist een iets andere Gradle-configuratie die we voor de eenvoud weglaten. Kijk op de homepage van het project voor meer details.

3. Hoe maak je een dunne JAR?

De Spring Boot Thin Launcher is een kleine bibliotheek die de afhankelijkheden van een artefact leest uit een bestand dat in het archief zelf is gebundeld, deze downloadt uit een Maven-repository en uiteindelijk de hoofdklasse van de applicatie start.

Zo, wanneer we een project bouwen met de bibliotheek, krijgen we een JAR-bestand met onze code, een bestand dat de afhankelijkheden opsomt, en de hoofdklasse uit de bibliotheek die de bovenstaande taken uitvoert.

De zaken zijn natuurlijk wat genuanceerder dan onze vereenvoudigde uitleg; we zullen later in het artikel enkele onderwerpen dieper bespreken.

4. Basisgebruik

Laten we nu kijken hoe we een "dunne" JAR kunnen bouwen vanuit onze reguliere Spring Boot-applicatie.

We starten de applicatie met het gebruikelijke java -jar, met optionele aanvullende opdrachtregelargumenten die de Thin Launcher besturen. We zullen er een paar zien in de volgende secties; de homepage van het project bevat de volledige lijst.

4.1. Maven-projecten

In een Maven-project moeten we de declaratie van de Boot-plug-in aanpassen (zie sectie 2.1) om een ​​afhankelijkheid van de aangepaste "dunne" lay-out op te nemen:

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

Het opstartprogramma leest afhankelijkheden van het pom.xml -bestand dat Maven opslaat in de gegenereerde JAR in het META-INF / maven directory.

We zullen de build zoals gewoonlijk uitvoeren, bijvoorbeeld met mvn installeren.

Als we zowel dunne als dikke builds willen produceren (bijvoorbeeld in een project met meerdere modules), kunnen we de aangepaste lay-out declareren in een speciaal Maven-profiel.

4.2. Maven en afhankelijkheden: thin.eigenschappen

We kunnen Maven ook een thin.eigenschappen bestand naast pom.xml. In dat geval bevat het bestand de volledige lijst met afhankelijkheden, inclusief transitieve, en het opstartprogramma geeft er de voorkeur aan boven de pom.xml.

De mojo (plug-in) om dit te doen is spring-boot-thin-maven-plugin: eigenschappen, en standaard voert het het thin.eigenschappen bestand in src / main / resources / META-INF, maar we kunnen de locatie specificeren met de dun. uitgang eigendom:

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

Houd er rekening mee dat de uitvoermap moet bestaan ​​om het doel te laten slagen, zelfs als we de standaardmap hebben behouden.

4.3. Gradle-projecten

In een Gradle-project voegen we in plaats daarvan een speciale plug-in toe:

buildscript {ext {// ... thinPlugin = 'org.springframework.boot.experimental: spring-boot-thin-gradle-plugin' thinVersion = '1.0.11.RELEASE'} // ... afhankelijkheden {//. .. classpath ("$ {thinPlugin}: $ {thinVersion}")}} // weggelaten plug-in toepassen: 'maven' plug-in toepassen: 'org.springframework.boot.experimental.thin-launcher'

Om een ​​dunne build te krijgen, zullen we Gradle vertellen om het thinJar taak:

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

4.4. Gradle en afhankelijkheden: pom.xml

In het codevoorbeeld in de vorige sectie hebben we de Maven-plug-in verklaard naast de Thin Launcher (evenals de Boot- en Dependency Management-plug-ins die we al hadden gezien in de sectie Vereisten).

Dat komt omdat, net als in het geval van Maven dat we eerder hebben gezien, het artefact een pom.xml bestand met een opsomming van de afhankelijkheden van de toepassing. De pom.xml bestand wordt gegenereerd door een taak met de naam thinPom, wat een impliciete afhankelijkheid is van een jar-taak.

We kunnen het gegenereerde pom.xml bestand met een speciale taak. Hier repliceren we gewoon wat de dunne plug-in al automatisch doet:

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

Om onze gewoonte te gebruiken pom.xml bestand, voegen we de bovenstaande taak toe aan de afhankelijkheden van de jar-taak:

bootJar.dependsOn = [createPom]

4.5. Gradle en afhankelijkheden: thin.eigenschappen

We kunnen Gradle ook een thin.eigenschappen bestand in plaats van pom.xml, zoals we eerder deden met Maven.

De taak die het thin.eigenschappen bestand heet thinProperties, en het wordt niet standaard gebruikt. We kunnen het toevoegen als een afhankelijkheid van de jar-taak:

bootJar.dependsOn = [thinProperties]

5. Afhankelijkheden opslaan

Het hele punt van dunne potten is om te voorkomen dat de afhankelijkheden met de applicatie worden gebundeld. Afhankelijkheden verdwijnen echter niet op magische wijze, ze worden gewoon ergens anders opgeslagen.

In het bijzonder gebruikt de Thin Launcher de Maven-infrastructuur om afhankelijkheden op te lossen, dus:

  1. het controleert de lokale Maven-repository, die standaard in ~ / .m2 / repository maar kan elders worden verplaatst;
  2. vervolgens downloadt het ontbrekende afhankelijkheden van Maven Central (of een andere geconfigureerde repository);
  3. Ten slotte slaat het ze op in de lokale repository, zodat het de volgende keer dat we de applicatie starten niet opnieuw hoeft te downloaden.

Natuurlijk, de downloadfase is het langzame en foutgevoelige deel van het proces, omdat het toegang tot Maven Central via internet vereist, of toegang tot een lokale proxy, en we weten allemaal hoe die dingen over het algemeen onbetrouwbaar zijn.

Gelukkig zijn er verschillende manieren om de afhankelijkheden samen met de applicatie (s) in te zetten, bijvoorbeeld in een voorverpakte container voor cloudimplementatie.

5.1. De applicatie voor opwarming uitvoeren

De eenvoudigste manier om de afhankelijkheden in de cache te plaatsen, is door de toepassing in de doelomgeving op te warmen. Zoals we eerder hebben gezien, zorgt dit ervoor dat de afhankelijkheden worden gedownload en in de cache worden opgeslagen in de lokale Maven-repository. Als we meer dan één app uitvoeren, bevat de repository alle afhankelijkheden zonder duplicaten.

Aangezien het uitvoeren van een applicatie ongewenste bijwerkingen kan hebben, we kunnen ook een "testversie" uitvoeren die alleen de afhankelijkheden oplost en downloadt zonder gebruikerscode uit te voeren:

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

Merk op dat we, volgens de Spring Boot-conventies, de -Dthin.dryrun eigenschap ook met een -Thin.dryrun opdrachtregelargument voor de toepassing of met een THIN_DRYRUN systeemeigenschap. Elke waarde behalve false zal de Thin Launcher instrueren om een ​​test uit te voeren.

5.2. De afhankelijkheden verpakken tijdens de build

Een andere optie is om de afhankelijkheden tijdens het bouwen te verzamelen, zonder ze in de JAR te bundelen. Vervolgens kunnen we ze naar de doelomgeving kopiëren als onderdeel van de implementatieprocedure.

Dit is over het algemeen eenvoudiger omdat het niet nodig is om de applicatie in de doelomgeving uit te voeren. Als we echter meerdere applicaties implementeren, moeten we hun afhankelijkheden handmatig of met een script samenvoegen.

Het formaat waarin de Thin Plugin voor Maven en Gradle de afhankelijkheden verpakt tijdens een build is hetzelfde als een lokale Maven-repository:

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

In feite kunnen we een applicatie die de Thin Launcher gebruikt tijdens runtime naar een dergelijke map (inclusief een lokale Maven-repository) verwijzen met de dun. wortel eigendom:

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

We kunnen ook veilig meerdere van dergelijke mappen samenvoegen door ze over elkaar te kopiëren, waardoor we een Maven-repository krijgen met alle noodzakelijke afhankelijkheden.

5.3. De afhankelijkheden verpakken met Maven

Om Maven de afhankelijkheden voor ons te laten verpakken, gebruiken we de oplossen doel van de spring-boot-thin-maven-plugin. We kunnen het handmatig of automatisch oproepen in ons pom.xml:

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

Nadat we het project hebben gebouwd, vinden we een map doelwit / dun / wortel / met de structuur die we in de vorige sectie hebben besproken.

5.4. De afhankelijkheden verpakken met Gradle

Als we Gradle gebruiken met de dunne draagraket plug-in, in plaats daarvan hebben we een ThinResolve taak beschikbaar. De taak slaat de applicatie en zijn afhankelijkheden op in het build / thin / root / directory, vergelijkbaar met de Maven-plug-in van de vorige sectie:

$ gradlew dun Oplossen

Houd er rekening mee dat op het moment van schrijven het dunne draagraket plug-in heeft een bug die verhindert dat de afhankelijkheden worden opgeslagen als thin.eigenschappen wordt gebruikt: //github.com/dsyer/spring-boot-thin-launcher/issues/53.

6. Conclusies en verder lezen

In dit artikel hebben we bekeken hoe we onze dunne pot kunnen maken. We hebben ook gezien hoe we de Maven-infrastructuur kunnen gebruiken om hun afhankelijkheden te downloaden en op te slaan.

De startpagina van de thin launcher heeft nog een paar HOW-TO-handleidingen voor scenario's zoals cloudimplementaties naar Heroku, evenals de volledige lijst met ondersteunde opdrachtregelargumenten.

De implementatie van alle Maven-voorbeelden en codefragmenten is te vinden in het GitHub-project - als een Maven-project, dus het moet gemakkelijk te importeren en uit te voeren zijn zoals het is.

Evenzo verwijzen alle Gradle-voorbeelden naar dit GitHub-project.