Executando um aplicativo Spring Boot com Maven vs um executável War/Jar

Executando um aplicativo Spring Boot com Maven vs um executável War / Jar

1. Introdução

Neste tutorial, vamos explorar as diferenças entre iniciar um aplicativo da web Spring Boot por meio do comandomvn spring-boot:run e executá-lo depois de compilado em um pacote jar / war por meio do comandojava -jar.

Vamos supor que você já esteja familiarizado com a configuração da metarepackage do Spring Boot. Para obter mais detalhes sobre este tópico, leiaCreate a Fat Jar App with Spring Boot.

2. O plug-in Maven para inicialização em primavera

Ao escrever um aplicativo Spring Boot,Spring Boot Maven plugin é a ferramenta recomendada para construir, testar e empacotar nosso código.

Este plugin é fornecido com muitos recursos convenientes, como:

  • resolve as versões de dependência corretas para nós

  • ele pode empacotar todas as nossas dependências (incluindo um servidor de aplicativos incorporado, se necessário) em um único jar / war fatecível e executável e também:

    • gerencie para nós a configuração do classpath, para que possamos pular aquela longa opção-cp em nosso comandojava -jar

    • implementar umClassLoader personalizado para localizar e carregar todas as bibliotecas jar externas, agora aninhadas dentro do pacote

    • encontre automaticamente o métodomain() e configure-o no manifesto, para que não tenhamos que especificar a classe principal em nosso comandojava -jar

3. Executando o código com o Maven em forma explodida

Quando estamos trabalhando em um aplicativo da web, podemos aproveitar outro recurso muito interessante doSpring Boot Maven plugin:, a capacidade de implantar automaticamente nosso aplicativo da web em um servidor de aplicativos incorporado.

Precisamos apenas de uma dependência para que o plugin saiba que queremos usar o Tomcat para executar nosso código:


    org.springframework.boot
    spring-boot-starter-web

Agora, ao executar o comandomvn spring-boot:run na pasta raiz do nosso projeto, o plugin lê a configuração do pom e entende que precisamos de um contêiner de aplicativo da web.

Executar o comandomvn spring-boot:run aciona o download do Apache Tomcat e inicializa a inicialização do Tomcat.

Vamos tentar:

$ mvn spring-boot:run
...
...
[INFO] --------------------< com.example:spring-boot-ops >--------------------
[INFO] Building spring-boot-ops 0.0.1-SNAPSHOT
[INFO] --------------------------------[ war ]---------------------------------
[INFO]
[INFO] >>> spring-boot-maven-plugin:2.1.3.RELEASE:run (default-cli) > test-compile @ spring-boot-ops >>>
Downloading from central: https://repo.maven.apache.org/maven2/org/apache/tomcat/embed/tomcat-embed-core/9.0.16/tomcat-embed-core-9.0.16.pom
Downloaded from central: https://repo.maven.apache.org/maven2/org/apache/tomcat/embed/tomcat-embed-core/9.0.16/tomcat-embed-core-9.0.16.pom (1.8 kB at 2.8 kB/s)
...
...
[INFO] --- spring-boot-maven-plugin:2.1.3.RELEASE:run (default-cli) @ spring-boot-ops ---
...
...
11:33:36.648 [main] INFO  o.a.catalina.core.StandardService - Starting service [Tomcat]
11:33:36.649 [main] INFO  o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/9.0.16]
...
...
11:33:36.952 [main] INFO  o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
...
...
11:33:48.223 [main] INFO  o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
11:33:48.289 [main] INFO  o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port(s): 8080 (http) with context path ''
11:33:48.292 [main] INFO  org.example.boot.Application - Started Application in 22.454 seconds (JVM running for 37.692)

Quando o log mostra a linha contendo ‘Aplicativo iniciado’, nosso aplicativo da web está pronto para ser consultado por meio do navegador no endereçohttp://localhost:8080/

4. Executando o código como um aplicativo empacotado autônomo

Assim que passarmos da fase de desenvolvimento e quisermos progredir no sentido de trazer nosso aplicativo para produção, precisamos empacotar nosso aplicativo.

Infelizmente, se estivermos trabalhando com um pacotejar, o objetivo básico Mavenpackage não inclui nenhuma das dependências externas.

Isso significa que podemos usá-lo apenas como uma biblioteca em um projeto maior.

Para contornar essa limitação, precisamos aproveitar a metarepackage do plugin Maven Spring Boot para executar nosso jar / war como um aplicativo independente.

4.1. Configuração

Normalmente, precisamos apenas configurar o plug-in de compilação:


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

Mas nosso exemplo de projeto contém mais de uma classe principal, portanto, precisamos dizer ao Java qual classe executar, configurando o plugin:


    org.springframework.boot
    spring-boot-maven-plugin
    
        
            
                com.example.webjar.WebjarsdemoApplication
            
        
    

ou definindo a propriedadestart-class:


    com.example.webjar.WebjarsdemoApplication

4.2. Executando o aplicativo

Agora, podemos executar nosso exemplo de guerra com dois comandos simples:

$ mvn clean package spring-boot:repackage
$ java -jar target/spring-boot-ops.war

Mais detalhes sobre como executar um arquivo jar podem ser encontrados em nosso artigoRun JAR Application With Command Line Arguments.

4.3. Dentro do arquivo de guerra

Para entender melhor como o comando mencionado acima pode executar um aplicativo de servidor completo, podemos dar uma olhada em nossospring-boot-ops.war.

Se descompactá-lo e espiar por dentro, encontramos os suspeitos de sempre:

  • META-INF, com oMANIFEST.MF gerado automaticamente

  • WEB-INF/classes, contendo nossas classes compiladas

  • WEB-INF/lib, que contém nossas dependências de guerra e os arquivos jar Tomcat incorporados

Porém, isso não é tudo, pois existem algumas pastas específicas para nossa configuração de pacote gordo:

  • WEB-INF/lib-provided, contendo bibliotecas externas necessárias ao executar incorporado, mas não necessárias ao implantar

  • org/springframework/boot/loader, que contém o carregador de classe personalizado Spring Boot - esta biblioteca é responsável por carregar nossas dependências externas e torná-las acessíveis em tempo de execução

4.4. Por dentro do manifesto de guerra

Como mencionado antes, o plugin Maven Spring Boot encontra a classe principal e gera a configuração necessária para executar o comandojava.

OMANIFEST.MF resultante tem algumas linhas adicionais:

Start-Class: com.example.webjar.WebjarsdemoApplication
Main-Class: org.springframework.boot.loader.WarLauncher

Em particular, podemos observar que o último especifica o iniciador do carregador de classes Spring Boot a ser usado.

4.5. Dentro de um arquivo jar

Devido à estratégia de empacotamento padrão, nosso cenáriowar packaging não difere muito, se usamos oSpring Boot Maven Plugin ou não.

Para apreciar melhor as vantagens do plugin, podemos tentar alterar a configuração de pompackaging parajare executarmvn clean package novamente.

Agora podemos observar que nosso frasco de gordura está organizado de maneira um pouco diferente do nosso arquivo de guerra anterior:

  • Todas as nossas classes e pastas de recursos agora estão localizadas emBOOT-INF/classes

  • BOOT-INF/lib contém todas as bibliotecas externas

Sem o plugin, a pastalib não existiria e todo o conteúdo deBOOT-INF/classes estaria localizado na raiz do pacote.

4.6. Inside the Jar Manifest

Além disso, oMANIFEST.MF mudou, apresentando estas linhas adicionais:

Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Version: 2.1.3.RELEASE
Main-Class: org.springframework.boot.loader.JarLauncher

Spring-Boot-ClasseseSpring-Boot-Lib são particularmente interessantes, pois nos dizem onde o carregador de classes encontrará classes e bibliotecas externas.

5. Como escolher

Ao analisar ferramentas,it’s imperative to take account of the purpose these tools are created for. Queremos facilitar o desenvolvimento ou garantir uma implantação e portabilidade sem problemas? Vamos dar uma olhada nas fases mais afetadas por esta escolha.

5.1. Desenvolvimento

Como desenvolvedores, geralmente passamos a maior parte do tempo codificando sem precisar gastar muito tempo configurando nosso ambiente para executar o código localmente. Em aplicativos simples, isso geralmente não é uma preocupação. Mas, para projetos mais complexos, podemos precisar definir variáveis ​​de ambiente, iniciar servidores e preencher bancos de dados.

Configuring the right environment every time we want to run the application would be very impractical, especialmente se mais de um serviço tiver que ser executado ao mesmo tempo.

É aí que executar o código com o Maven nos ajuda. Já temos toda a base de código verificada localmente, para que possamos aproveitar os arquivos de configuração e recursos do pom. Podemos definir variáveis ​​de ambiente, gerar um banco de dados na memória e até baixar a versão correta do servidor e implantar nosso aplicativo com um comando.

Mesmo em uma base de código com vários módulos, em que cada módulo precisa de diferentes variáveis ​​e versões de servidor, podemos executar facilmente o ambiente certo através de perfis Maven.

5.2. Produção

Quanto mais nos movemos para a produção, mais a conversa muda para a estabilidade e a segurança. É por isso que não podemos aplicar o processo usado para nossa máquina de desenvolvimento a um servidor com clientes ativos.

Executar o código por meio do Maven nesta fase é uma prática ruim por vários motivos:

  • Primeiro de tudo, precisaríamos instalar o Maven

  • Então, apenas porque precisamos compilar o código, precisamos do Java Development Kit (JDK) completo

  • Em seguida, precisamos copiar a base de código para o servidor, deixando todo o código proprietário em texto sem formatação

  • O comandomvn deve executar todas as fases do ciclo de vida (encontrar fontes, compilar e executar)

  • Graças ao ponto anterior, também desperdiçaríamos CPU e, no caso de um servidor em nuvem, dinheiro

  • O Maven gera vários processos Java, cada um usando memória (por padrão, cada um usa a mesma quantidade de memória que o processo pai)

  • Por fim, se tivermos vários servidores para implantar, tudo isso será repetido em cada um

Estas são apenas algumas razões pelas quaisshipping the application as a package is more practical for production.

6. Conclusão

Neste tutorial, exploramos as diferenças entre executar nosso código via Maven e via comandojava -jar. Também fizemos uma rápida visão geral de alguns casos práticos.

O código-fonte usado neste artigo está disponívelover on GitHub.