JARs minces avec bottes à ressort

JARs minces avec botte à ressort

1. introduction

Dans ce tutoriel, nous allons examinerhow to build a Spring Boot project into a thin JAR file, using the spring-boot-thin-launcher project.

Spring Boot est connu pour ses déploiements JAR «lourds», dans lesquels un seul artefact exécutable contient à la fois le code de l'application et toutes ses dépendances.

Boot est également largement utilisé pour développer des microservices. Cela peut parfois aller à l’encontre de l’approche «gros JAR» car inclure de nombreuses dépendances dans de nombreux artefacts peut devenir un gaspillage important de ressources.

2. Conditions préalables

Tout d’abord, nous avons évidemment besoin d’un projet Spring Boot. Dans cet article, nous examinerons les versions Maven et Gradle dans leurs configurations les plus courantes.

Il est impossible de couvrir tous les systèmes de construction et de créer des configurations, mais nous espérons que nous verrons suffisamment de principes généraux pour que vous puissiez les appliquer à votre configuration spécifique.

2.1. Projets Maven

Dans un projet Boot construit avec Maven, nous devrions avoir le plugin Spring Boot Maven configuré dans le fichierpom.xml de notre projet, son parent ou l'un de ses ancêtres:


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

Ici, nous faisons référence à la version2.0.2.RELEASE du plugin, la dernière au moment de la rédaction. La version des dépendances Spring Boot est généralement définie à l'aide d'une nomenclature ou en héritant d'un POM parent, comme dans notre projet de référence:


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

2.2. Projets Gradle

Dans un projet Boot construit avec Gradle, nous aurons le plugin Boot Gradle:

buildscript {
    ext {
        springBootPlugin = 'org.springframework.boot:spring-boot-gradle-plugin'
        springBootVersion = '2.0.1.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("${springBootPlugin}:${springBootVersion}")
    }
}

// elided

apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

springBoot {
    mainClassName = 'org.example.DemoApplication'
}

Notez que, dans cet article, nous ne considérerons que les projets Boot 2.x et ultérieurs. Le Thin Launcher prend également en charge les versions antérieures, mais il nécessite une configuration Gradle légèrement différente que nous omettons pour plus de simplicité. Veuillez consulter la page d’accueil du projet pour plus de détails.

3. Comment créer un JAR fin?

Le Spring Boot Thin Launcher est une petite bibliothèque qui lit les dépendances d'un artefact à partir d'un fichier regroupé dans l'archive elle-même, les télécharge à partir d'un référentiel Maven et lance enfin la classe principale de l'application.

Donc,when we build a project with the library, we get a JAR file with our code, a file enumerating its dependencies, and the main class from the library that performs the above tasks.

Bien sûr, les choses sont un peu plus nuancées que notre explication simplifiée; nous aborderons certains sujets en profondeur plus loin dans l'article.

4. Utilisation de base

Voyons maintenant comment créer un JAR «fin» à partir de notre application Spring Boot standard.

Nous allons lancer l'application avec lesjava -jar <my-app-1.0.jar>, habituels avec des arguments de ligne de commande supplémentaires facultatifs qui contrôlent le Thin Launcher. Nous en verrons quelques-uns dans les sections suivantes; la page d’accueil du projet contient la liste complète.

4.1. Projets Maven

Dans un projet Maven, nous devons modifier la déclaration du plug-in Boot (voir la section 2.1) pour inclure une dépendance à la disposition «légère» personnalisée:


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

Leslauncher liront les dépendances du fichierpom.xml que Maven stocke dans le JAR généré dans le répertoireMETA-INF/maven.

Nous allons effectuer la compilation comme d'habitude, par exemple avecmvn install.

Si nous voulons être en mesure de produire des versions à la fois minces et épaisses (par exemple dans un projet avec plusieurs modules), nous pouvons déclarer la mise en page personnalisée dans un profil Maven dédié.

4.2. Maven et dépendances:thin.properties

We can also have Maven generate a thin.properties file in addition to pom.xml. Dans ce cas, le fichier contiendra la liste complète des dépendances, y compris les dépendances transitives, et le lanceur la préférera auxpom.xml.

Le mojo (plugin) pour ce faire estspring-boot-thin-maven-plugin:properties, et par défaut, il sort le fichierthin.properties danssrc/main/resources/META-INF, mais nous pouvons spécifier son emplacement avec la propriététhin.output:

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

Veuillez noter que le répertoire de sortie doit exister pour que l'objectif réussisse, même si nous avons conservé celui par défaut.

4.3. Projets Gradle

Dans un projet Gradle, nous ajoutons un plugin dédié:

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'

Pour obtenir un build léger, nous allons dire à Gradle d'exécuter la tâchethinJar:

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

4.4. Gradle et dépendances:pom.xml

Dans l'exemple de code de la section précédente, nous avons déclaré le plugin Maven en plus du Thin Launcher (ainsi que les plugins Boot and Dependency Management que nous avions déjà vus dans la section Prerequisites).

C'est parce que, tout comme dans le cas Maven que nous avons vu précédemment, l'artefact contiendra et utilisera un fichierpom.xml énumérant les dépendances de l'application. Le fichierpom.xml est généré par une tâche appeléethinPom, qui est une dépendance implicite de toute tâche jar.

We can customize the generated pom.xml file with a dedicated task. Ici, nous allons simplement répliquer ce que le plugin léger fait déjà automatiquement:

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

Pour utiliser notre fichier personnalisépom.xml, nous ajoutons la tâche ci-dessus aux dépendances de la tâche jar:

bootJar.dependsOn = [createPom]

4.5. Gradle et dépendances:thin.properties

We can also have Gradle generate a thin.properties file rather than pom.xml, comme nous l'avons fait précédemment avec Maven.

La tâche qui génère le fichierthin.properties s'appellethinProperties, et n'est pas utilisée par défaut. Nous pouvons l'ajouter en tant que dépendance de la tâche jar:

bootJar.dependsOn = [thinProperties]

5. Stockage des dépendances

Le but des jar Thin est d'éviter de regrouper les dépendances avec l'application. Cependant, les dépendances ne disparaissent pas comme par magie, elles sont simplement stockées ailleurs.

En particulier, Thin Launcher utilise l'infrastructure Maven pour résoudre les dépendances. Ainsi:

  1. il vérifie le référentiel Maven local, qui par défaut se trouve dans~/.m2/repository mais peut être déplacé ailleurs;

  2. ensuite, il télécharge les dépendances manquantes depuis Maven Central (ou tout autre référentiel configuré);

  3. enfin, il les met en cache dans le référentiel local, afin de ne pas avoir à les télécharger à nouveau la prochaine fois que nous exécuterons l'application.

Bien sûr,the download phase is the slow and error-prone part of the process, parce qu'il nécessite un accès à Maven Central via Internet, ou un accès à un proxy local, et nous savons tous à quel point ces choses ne sont généralement pas fiables.

Heureusement, il existe différentes manières de déployer les dépendances avec la ou les applications, par exemple dans un conteneur préemballé pour le déploiement dans le cloud.

5.1. Lancer l'application pour l'échauffement

Le moyen le plus simple de mettre en cache les dépendances consiste à exécuter une exécution de préchauffage de l'application dans l'environnement cible. Comme nous l'avons vu précédemment, cela entraînera le téléchargement et la mise en cache des dépendances dans le référentiel Maven local. Si nous exécutons plus d'une application, le référentiel finira par contenir toutes les dépendances sans doublons.

Étant donné que l'exécution d'une application peut avoir des effets secondaires indésirables,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

Notez que, conformément aux conventions de Spring Boot, nous pouvons définir la propriété-Dthin.dryrun également avec un argument de ligne de commande–thin.dryrun pour l'application ou avec une propriété systèmeTHIN_DRYRUN. Toute valeur à l'exception defalse indiquera au Thin Launcher d'effectuer un essai à sec.

5.2. Empaquetage des dépendances pendant la construction

Une autre option consiste à collecter les dépendances lors de la génération, sans les regrouper dans le fichier JAR. Ensuite, nous pouvons les copier dans l'environnement cible dans le cadre de la procédure de déploiement.

Ceci est généralement plus simple car il n’est pas nécessaire d’exécuter l’application dans l’environnement cible. Cependant, si nous déployons plusieurs applications, nous devrons fusionner leurs dépendances, manuellement ou avec un script.

Le format dans lequel Thin Plugin pour Maven et Gradle empaquette les dépendances lors de la construction est identique à celui d'un référentiel local Maven:

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

En fait, nous pouvons pointer une application utilisant le Thin Launcher vers un tel répertoire (y compris un référentiel Maven local) au moment de l'exécution avec la propriététhin.root:

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

Nous pouvons également fusionner en toute sécurité plusieurs de ces répertoires en les copiant les uns sur les autres, obtenant ainsi un référentiel Maven avec toutes les dépendances nécessaires.

5.3. Empaqueter les dépendances avec Maven

Pour que Maven conditionne les dépendances pour nous, nous utilisons l'objectifresolve duspring-boot-thin-maven-plugin. Nous pouvons l'invoquer manuellement ou automatiquement dans nospom.xml:


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

Après avoir construit le projet, nous trouverons un répertoiretarget/thin/root/ avec la structure dont nous avons parlé dans la section précédente.

5.4. Empaquetage des dépendances avec Gradle

Si nous utilisons Gradle avec le pluginthin-launcher, nous avons à la place une tâchethinResolve disponible. La tâche enregistrera l'application et ses dépendances dans le répertoirebuild/thin/root/, de la même manière que le plugin Maven de la section précédente:

$ gradlew thinResolve

Veuillez noter qu'au moment de l'écriture, le pluginthin-launcher a un bogue qui empêche la sauvegarde des dépendances sithin.properties est utilisé:https://github.com/dsyer/spring-boot-thin-launcher/issues/53.

6. Conclusions et lectures complémentaires

Dans cet article, nous avons examiné comment fabriquer notre pot fin. Nous avons également vu comment utiliser l'infrastructure Maven pour télécharger et stocker leurs dépendances.

Lehomepage du lanceur léger contient quelques autres guides pratiques pour des scénarios tels que les déploiements cloud sur Heroku, ainsi que la liste complète des arguments de ligne de commande pris en charge.

L'implémentation de tous les exemples et extraits de code Maven peut être trouvée dansthe GitHub project - en tant que projet Maven, il devrait donc être facile à importer et à exécuter tel quel.

De même, tous les exemples Gradle font référence àthis GitHub project.