JARs finos com bota de mola

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:

  1. ele verifica o repositório Maven local, que por padrão está em~/.m2/repository, mas pode ser movido para outro lugar;

  2. depois, ele baixa as dependências ausentes do Maven Central (ou de qualquer outro repositório configurado);

  3. 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.