JARs finos com bota de mola
1. Introdução
Neste tutorial, vamos dar uma olhada emhow to build a Spring Boot project into a thin JAR file, using the spring-boot-thin-launcher project.
O Spring Boot é conhecido por suas implementações JAR “gordas”, em que um único artefato executável contém o código do aplicativo e todas as suas dependências.
A inicialização também é amplamente usada para desenvolver microsserviços. Às vezes, isso pode estar em desacordo com a abordagem "JAR Gordo", porque incluir as mesmas dependências repetidamente em muitos artefatos pode se tornar um importante desperdício de recursos.
2. Pré-requisitos
Primeiro de tudo, precisamos de um projeto Spring Boot, é claro. Neste artigo, veremos as compilações do Maven e as compilações do Gradle em suas configurações mais comuns.
É impossível cobrir todos os sistemas e configurações de compilação que existem, mas, com sorte, veremos o suficiente dos princípios gerais para que você possa aplicá-los à sua configuração específica.
2.1. Projetos Maven
Em um projeto de inicialização construído com Maven, devemos ter o plugin Spring Boot Maven configurado no arquivopom.xml do nosso projeto, seu pai ou um de seus ancestrais:
org.springframework.boot
spring-boot-maven-plugin
Aqui, estamos nos referindo à versão2.0.2.RELEASE do plugin, a mais recente no momento da escrita. A versão das dependências do Spring Boot geralmente é decidida usando uma BOM ou herdando de uma POM pai, como em nosso projeto de referência:
org.springframework.boot
spring-boot-starter-parent
2.0.1.RELEASE
2.2. Projetos Gradle
Em um projeto de inicialização criado com Gradle, teremos o plug-in 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'
}
Observe que, neste artigo, consideraremos apenas o Boot 2.xe projetos posteriores. O Thin Launcher também oferece suporte a versões anteriores, mas requer uma configuração Gradle ligeiramente diferente que omitimos para simplificar. Por favor, olhe a página inicial do projeto para mais detalhes.
3. Como criar um JAR fino?
O Spring Boot Thin Launcher é uma pequena biblioteca que lê as dependências de um artefato de um arquivo empacotado no próprio arquivo, baixa-as de um repositório Maven e, finalmente, inicia a classe principal do aplicativo.
Então,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.
Claro, as coisas têm um pouco mais de nuances do que nossa explicação simplificada; discutiremos alguns tópicos em profundidade posteriormente neste artigo.
4. Uso básico
Vamos agora ver como construir um JAR “fino” a partir de nosso aplicativo Spring Boot regular.
Vamos iniciar o aplicativo com osjava -jar <my-app-1.0.jar>, usuais com argumentos de linha de comando adicionais opcionais que controlam o Thin Launcher. Veremos alguns deles nas seções a seguir; a página inicial do projeto contém a lista completa.
4.1. Projetos Maven
Em um projeto Maven, precisamos modificar a declaração do plug-in de inicialização (consulte a seção 2.1) para incluir uma dependência no layout "thin" personalizado:
org.springframework.boot
spring-boot-maven-plugin
org.springframework.boot.experimental
spring-boot-thin-layout
1.0.11.RELEASE
Olauncher lerá as dependências do arquivopom.xml que o Maven armazena no JAR gerado no diretórioMETA-INF/maven.
Faremos a construção normalmente, por exemplo, commvn install.
Se quisermos produzir compilações finas e gordas (por exemplo, em um projeto com vários módulos), podemos declarar o layout personalizado em um perfil Maven dedicado.
4.2. Maven e dependências:thin.properties
We can also have Maven generate a thin.properties file in addition to pom.xml. Nesse caso, o arquivo conterá a lista completa de dependências, incluindo as transitivas, e o inicializador irá preferi-la apom.xml.
O mojo (plugin) para fazer isso éspring-boot-thin-maven-plugin:properties,e, por padrão, ele produz o arquivothin.properties emsrc/main/resources/META-INF, mas podemos especificar sua localização com a propriedadethin.output:
$ mvn org.springframework.boot.experimental:spring-boot-thin-maven-plugin:properties -Dthin.output=.
Observe que o diretório de saída deve existir para que a meta seja bem-sucedida, mesmo que tenhamos mantido o diretório padrão.
4.3. Projetos Gradle
Em um projeto Gradle, em vez disso, adicionamos um plug-in dedicado:
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'
Para obter uma compilação fina, diremos ao Gradle para executar a tarefathinJar:
~/projects/example/spring-boot-gradle $ ./gradlew thinJar
4.4. Gradle e dependências:pom.xml
No exemplo de código da seção anterior, declaramos o plug-in Maven além do Thin Launcher (bem como os plug-ins Boot and Dependency Management que já vimos na seção Pré-requisitos).
Isso porque, assim como no caso do Maven que vimos anteriormente, o artefato conterá e fará uso de um arquivopom.xml enumerando as dependências do aplicativo. O arquivopom.xml é gerado por uma tarefa chamadathinPom, que é uma dependência implícita de qualquer tarefa jar.
We can customize the generated pom.xml file with a dedicated task. Aqui, vamos apenas replicar o que o plugin thin já faz automaticamente:
task createPom {
def basePath = 'build/resources/main/META-INF/maven'
doLast {
pom {
withXml(dependencyManagement.pomConfigurer)
}.writeTo("${basePath}/${project.group}/${project.name}/pom.xml")
}
}
Para usar nosso arquivopom.xml personalizado, adicionamos a tarefa acima às dependências da tarefa jar:
bootJar.dependsOn = [createPom]
4.5. Gradle e dependências:thin.properties
We can also have Gradle generate a thin.properties file rather than pom.xml, como fizemos anteriormente com Maven.
A tarefa que gera o arquivothin.properties é chamadathinProperties, e não é usada por padrão. Podemos adicioná-lo como uma dependência da tarefa jar:
bootJar.dependsOn = [thinProperties]
5. Armazenando Dependências
O objetivo dos jars finos é evitar agrupar as dependências com o aplicativo. No entanto, as dependências não desaparecem magicamente, elas são simplesmente armazenadas em outro lugar.
Em particular, o Thin Launcher usa a infraestrutura do Maven para resolver dependências, portanto:
-
ele verifica o repositório Maven local, que por padrão está em~/.m2/repository, mas pode ser movido para outro lugar;
-
depois, ele baixa as dependências ausentes do Maven Central (ou de qualquer outro repositório configurado);
-
finalmente, ele os armazena em cache no repositório local, para que não precise baixá-los novamente na próxima vez que executarmos o aplicativo.
Claro,the download phase is the slow and error-prone part of the process, porque requer acesso ao Maven Central através da Internet, ou acesso a um proxy local, e todos nós sabemos como essas coisas geralmente não são confiáveis.
Felizmente, existem várias maneiras de implantar as dependências juntamente com os aplicativos, por exemplo, em um contêiner pré-empacotado para implantação na nuvem.
5.1. Executando o aplicativo para aquecimento
A maneira mais simples de armazenar em cache as dependências é fazer uma execução de aquecimento do aplicativo no ambiente de destino. Como vimos antes, isso fará com que as dependências sejam baixadas e armazenadas em cache no repositório Maven local. Se executarmos mais de um aplicativo, o repositório acabará contendo todas as dependências sem duplicatas.
Como executar um aplicativo pode ter efeitos colaterais indesejados,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
Observe que, de acordo com as convenções do Spring Boot, podemos definir a propriedade-Dthin.dryrun também com um argumento de linha de comando–thin.dryrun para o aplicativo ou com uma propriedade de sistemaTHIN_DRYRUN. Qualquer valor, excetofalse, instruirá o Thin Launcher a executar uma simulação.
5.2. Empacotando as dependências durante a compilação
Outra opção é coletar as dependências durante a construção, sem agrupá-las no JAR. Em seguida, podemos copiá-los para o ambiente de destino como parte do procedimento de implantação.
Isso geralmente é mais simples porque não é necessário executar o aplicativo no ambiente de destino. No entanto, se estivermos implantando vários aplicativos, teremos que mesclar suas dependências, manualmente ou com um script.
O formato no qual o Thin Plugin for Maven e Gradle empacota as dependências durante uma compilação é o mesmo que um repositório local do Maven:
root/
repository/
com/
net/
org/
...
Na verdade, podemos apontar um aplicativo usando o Thin Launcher para qualquer diretório (incluindo um repositório Maven local) em tempo de execução com a propriedadethin.root:
$ java -jar my-app-1.0.jar --thin.root=my-app/deps
Também podemos mesclar com segurança vários desses diretórios, copiando-os um sobre o outro, obtendo assim um repositório Maven com todas as dependências necessárias.
5.3. Empacotando as dependências com Maven
Para que o Maven empacote as dependências para nós, usamos o objetivoresolve dospring-boot-thin-maven-plugin.. Podemos invocá-lo manualmente ou automaticamente em nossopom.xml:
org.springframework.boot.experimental
spring-boot-thin-maven-plugin
${thin.version}
resolve
resolve
false
Depois de construir o projeto, encontraremos um diretóriotarget/thin/root/ com a estrutura que discutimos na seção anterior.
5.4. Empacotando as dependências com o Gradle
Se estivermos usando o Gradle com o plug-inthin-launcher, em vez disso, temos uma tarefathinResolve disponível. A tarefa salvará o aplicativo e suas dependências no diretóriobuild/thin/root/, de forma semelhante ao plugin Maven da seção anterior:
$ gradlew thinResolve
Observe que, no momento da escrita, o pluginthin-launcher tem um bug que impede que as dependências sejam salvas sethin.properties for usado:https://github.com/dsyer/spring-boot-thin-launcher/issues/53.
6. Conclusões e leituras adicionais
Neste artigo, vimos como fazer nosso frasco fino. Também vimos como usar a infraestrutura Maven para baixar e armazenar suas dependências.
Ohomepage do thin launcher tem mais alguns guias de COMO FAZER para cenários como implantações de nuvem para Heroku, bem como a lista completa de argumentos de linha de comando suportados.
A implementação de todos os exemplos e trechos de código do Maven pode ser encontrada emthe GitHub project - como um projeto Maven, portanto, deve ser fácil importar e executar como está.
Da mesma forma, todos os exemplos do Gradle se referem athis GitHub project.