Тънки JARs с Spring Boot

1. Въведение

В този урок ще разгледаме как да изградим проект на Spring Boot в тънък JAR файл, използвайки проекта spring-boot-thin-launcher .

Spring Boot е известен със своите „дебели“ внедрявания на JAR, където един изпълним артефакт съдържа както кода на приложението, така и всички негови зависимости.

Boot също се използва широко за разработване на микроуслуги. Това понякога може да е в противоречие с подхода „дебел JAR“, защото включването на едни и същи зависимости отново и отново в много артефакти може да се превърне в важна загуба на ресурси.

2. Предпоставки

На първо място, имаме нужда от проект за пролетно зареждане, разбира се. В тази статия ще разгледаме компилациите на Maven и Gradle в техните най-често срещани конфигурации.

Невъзможно е да обхванете всички системи за изграждане и конфигурации за изграждане там, но, надяваме се, ще разгледаме достатъчно общите принципи, за да можете да ги приложите към вашата конкретна настройка.

2.1. Проекти на Maven

В проект за зареждане, изграден с Maven, трябва да имаме приставката Spring Boot Maven, конфигурирана във файла pom.xml на нашия проект , неговия родител или един от неговите предци:

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

Версията на зависимостите Spring Boot обикновено се решава чрез използване на спецификация или наследяване от родителски POM, както в нашия референтен проект:

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

2.2. Проекти на Gradle

В проект за стартиране, изграден с Gradle, ще имаме приставката Boot Gradle:

buildscript { ext { springBootPlugin = 'org.springframework.boot:spring-boot-gradle-plugin' springBootVersion = '2.2.2.RELEASE' } repositories { mavenCentral() } dependencies { classpath("${springBootPlugin}:${springBootVersion}") } } // elided apply plugin: 'org.springframework.boot' apply plugin: 'io.spring.dependency-management' springBoot { mainClassName = 'com.baeldung.DemoApplication' }

Имайте предвид, че в тази статия ще разглеждаме само проекти за Boot 2.x и по-нови версии. Тънкият стартер също поддържа по-ранни версии, но изисква малко по-различна конфигурация на Gradle, която пропускаме за простота. Моля, погледнете началната страница на проекта за повече подробности.

3. Как да създам тънък JAR?

Spring Boot Thin Launcher е малка библиотека, която чете зависимостите на артефакт от файл, сглобен в самия архив, изтегля ги от хранилището на Maven и накрая стартира основния клас на приложението.

И така, когато изграждаме проект с библиотеката, получаваме JAR файл с нашия код, файл, изброяващ зависимостите му, и основния клас от библиотеката, която изпълнява горните задачи.

Разбира се, нещата са малко по-нюансирани от нашето опростено обяснение; ще обсъдим някои теми задълбочено по-късно в статията.

4. Основна употреба

Нека сега видим как да изградим „тънък“ JAR от нашето редовно приложение Spring Boot.

Ще стартираме приложението с обичайния java -jar, с допълнителни аргументи от командния ред, които контролират Thin Launcher. Ще видим няколко от тях в следващите раздели; началната страница на проекта съдържа пълния списък.

4.1. Проекти на Maven

В проект на Maven трябва да модифицираме декларацията на приставката за стартиране (виж раздел 2.1), за да включим зависимост от персонализираното „тънко“ оформление:

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

Стартовият панел ще чете зависимости от файла pom.xml, който Maven съхранява в генерирания JAR в директорията META-INF / maven .

Ще изпълним компилацията както обикновено, например с mvn install .

Ако искаме да можем да произвеждаме както тънки, така и дебели компилации (например в проект с множество модули), можем да декларираме персонализираното оформление в специален профил на Maven.

4.2. Maven и зависимости: тънки.свойства

Можем също така да накараме Maven да генерира файл thin.properties в допълнение към pom.xml . В този случай файлът ще съдържа пълния списък със зависимости, включително преходни, и стартера ще го предпочете пред pom.xml .

The mojo (plugin) for doing so is spring-boot-thin-maven-plugin:properties, and by default, it outputs the thin.properties file in src/main/resources/META-INF, but we can specify its location with the thin.output property:

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

Please note that the output directory must exist for the goal to succeed, even if we've kept the default one.

4.3. Gradle Projects

In a Gradle project, instead, we add a dedicated plugin:

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'

To obtain a thin build, we'll tell Gradle to execute the thinJar task:

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

4.4. Gradle and Dependencies: pom.xml

In the code example in the previous section, we've declared the Maven plugin in addition to the Thin Launcher (as well as the Boot and Dependency Management plugins that we'd already seen in the Prerequisites section).

That's because, just like in the Maven case that we've seen earlier, the artifact will contain and make use of a pom.xml file enumerating the application's dependencies. The pom.xml file is generated by a task called thinPom, which is an implicit dependency of any jar task.

We can customize the generated pom.xml file with a dedicated task. Here, we'll just replicate what the thin plugin already does automatically:

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

To use our custom pom.xml file, we add the above task to the jar task's dependencies:

bootJar.dependsOn = [createPom]

4.5. Gradle and Dependencies: thin.properties

We can also have Gradle generate a thin.properties file rather than pom.xml, as we did earlier with Maven.

The task that generates the thin.properties file is called thinProperties, and it's not used by default. We can add it as a dependency of the jar task:

bootJar.dependsOn = [thinProperties]

5. Storing Dependencies

The whole point of thin jars is to avoid bundling the dependencies with the application. However, dependencies don't magically disappear, they're simply stored elsewhere.

In particular, the Thin Launcher uses the Maven infrastructure to resolve dependencies, so:

  1. it checks the local Maven repository, which by default lies in ~/.m2/repository but can be moved elsewhere;
  2. then, it downloads missing dependencies from Maven Central (or any other configured repository);
  3. finally, it caches them in the local repository, so that it won't have to download them again the next time we run the application.

Of course, the download phase is the slow and error-prone part of the process, because it requires access to Maven Central through the Internet, or access to a local proxy, and we all know how those things are generally unreliable.

Fortunately, there are various ways of deploying the dependencies together with the application(s), for example in a prepackaged container for cloud deployment.

5.1. Running the Application for Warm-up

The simplest way to cache the dependencies is to do a warm-up run of the application in the target environment. As we've seen earlier, this will cause the dependencies to be downloaded and cached in the local Maven repository. If we run more than one app, the repository will end up containing all the dependencies without duplicates.

Since running an application can have unwanted side effects, 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

Note that, as per Spring Boot conventions, we can set the -Dthin.dryrun property also with a –thin.dryrun command line argument to the application or with a THIN_DRYRUN system property. Any value except false will instruct the Thin Launcher to perform a dry run.

5.2. Packaging the Dependencies During the Build

Another option is to collect the dependencies during the build, without bundling them in the JAR. Then, we can copy them to the target environment as part of the deployment procedure.

This is generally simpler because it's not necessary to run the application in the target environment. However, if we're deploying multiple applications, we'll have to merge their dependencies, either manually or with a script.

The format in which the Thin Plugin for Maven and Gradle packages the dependencies during a build is the same as a Maven local repository:

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

In fact, we can point an application using the Thin Launcher to any such directory (including a local Maven repository) at runtime with the thin.root property:

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

We can also safely merge multiple such directories by copying them one over another, thus obtaining a Maven repository with all the necessary dependencies.

5.3. Packaging the Dependencies With Maven

To have Maven package the dependencies for us, we use the resolve goal of the spring-boot-thin-maven-plugin. We can invoke it manually or automatically in our pom.xml:

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

After building the project, we'll find a directory target/thin/root/ with the structure that we've discussed in the previous section.

5.4. Packaging the Dependencies With Gradle

If we're using Gradle with the thin-launcher plugin, instead, we have a thinResolve task available. The task will save the application and its dependencies in the build/thin/root/ directory, similarly to the Maven plugin of the previous section:

$ gradlew thinResolve

Please note that, at the time of writing, the thin-launcher plugin has a bug that prevents the dependencies to be saved if thin.properties is used: //github.com/dsyer/spring-boot-thin-launcher/issues/53.

6. Conclusions and Further Reading

В тази статия разгледахме как да си направим тънкия буркан. Видяхме също как да използваме инфраструктурата на Maven за изтегляне и съхраняване на техните зависимости.

Началната страница на тънкия стартер има още няколко Ръководства за HOW-TO за сценарии като разполагане на облак в Heroku, както и пълния списък на поддържаните аргументи на командния ред.

Изпълнението на всички примери на Maven и кодови фрагменти може да се намери в проекта GitHub - като проект на Maven, така че трябва да е лесно да се импортира и да се изпълнява както е.

По същия начин всички примери на Gradle се отнасят до този проект на GitHub.