Deployando sua aplicação com o Spring Boot

Já escrevi aqui no blog, duas vezes, o quanto o Spring Boot pode nos ajudar no desenvolvimento das aplicações. Levou ao extremo o termo convenção sobre configuração e realmente deixou tudo muito mais simples. Um ponto que eu ainda não comentei foi sobre o processo de deploy, se você tiver escolhido o JSP como tecnologia de view. Caso tenha escolhido outras, como o ascendente Thymeleaf, o processo de deploy é o mais simples possível, já que é só gerar o jar e rodar. Em outro post eu comento porque eu escolhi não trocar de tecnologia de view.

O processo até é bem documentado no site, mas acho que alguns pontos merecem um reforço. O primeiro é justamente o fato de termos escolhido JSP como view. O Spring Boot configura o Tomcat para sempre procurar as páginas a partir do caminho src/main/webapp, relativo ao ponto de execução do seu jar. Só que esses arquivos não são copiados quando você executa um mvn clean install, ou seja, você precisa colocar no seu script de deploy um passo para copiar a estrutura e jogar no mesmo local do seu jar executável. Caso você seja curioso, dê uma olhada nesse commit e veja os valores fixos na classe.  A própria documentação sugere que você não deve usar o jar executável quando estiver com jsps.

Para contornar esse problema e deixar, pelo menos para mim, tudo um pouco mais simples, podemos recorrer a um outro plugin do maven que também roda um tomcat embedded, só que referenciando um war.  O webapp-runner gera para você um jar que sobe o tomcat e permite que você passe como argumento o caminho para um arquivo war.

java -jar dependency/webapp-runner.jar tudosobreesporte-1.0.0-SNAPSHOT.war

Para usá-lo, basta que você adicione a seguinte entrada no seu pom.xml. 

	<plugin>
		<groupId>org.apache.maven.plugins</groupId>
		<artifactId>maven-dependency-plugin</artifactId>
		<executions>
			<execution>
				<phase>package</phase>
				<goals>
					<goal>copy</goal>
				</goals>
				<configuration>
					<artifactItems>
						<artifactItem>
							<groupId>com.github.jsimone</groupId>
							<artifactId>webapp-runner</artifactId>
							<version>7.0.57.2</version>
							<destFileName>webapp-   runner.jar</destFileName>
						</artifactItem>
					</artifactItems>
				</configuration>
			</execution>
		</executions>
	</plugin>

Além disso você precisa fazer só mais algumas alterações no seu pom.xml. Quando o SetupMyProject gera o seu projeto para o Spring Boot, o tipo de projeto é jar, justamente porque é o que precisa para você subir uma aplicação com o Spring Boot em dev. Você precisa trocar o tipo para war.

  <packaging>war</packaging>

Outro detalhe importante é que precisamos apagar o plugin do Spring Boot que gera um fat jar quando executamos um mvn install. 

        <!-- apague esse trecho -->
	<plugin>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-maven-plugin</artifactId>
	</plugin>

Só que agora surgiu um possível problema. Quando gerávamos o jar com tudo dentro, era também incorporado uma classe específica do Spring Boot que usava a classe do nosso projeto que possuía o método main, geralmente aquela que chamamos de Boot,  para conseguir dar o start no nosso projeto. Isso é tão verdade que se você tiver mais de uma classe no projeto com o método main, dá erro de execução quando vamos rodar o mvn install com o plugin do bootAgora que temos um war, que vai ser carregado normalmente pelo Servlet Container, precisamos voltar a estratégia padrão do Spring MVC, ou seja, é necessário ter uma classe que implemente a interface WebApplicationInitializer. 

Para a nossa sorte o Spring Boot já possui uma implementação dessa interface justamente pensando nesse estilo de deploy :). Basta que a sua classe que tem o main e que, geralmente, também tem a annotation SpringBootApplication, herde de SpringBootServletInitializer.  Não teria problema alguma se você optasse por criar uma classe separada e herdasse também, fiz direto na classe Boot apenas pela facilidade.

  public class Boot extends SpringBootServletInitializer {
    ...
  }

Para fechar, precisamos indicar para a classe SpringBootServletInitializer quais as nossas classes responsáveis pela configuração do nosso projeto.

	@Override
	protected SpringApplicationBuilder configure(
			SpringApplicationBuilder application) {
		return application.sources(Boot.class);
	}

Repare que eu passei como argumento a própria classe anotada com SpringBootApplication, já que essa annotation já define que todas as classes, a partir do pacote onde ela está declarada, devem ser scaneadas.

Agora que já ajustamos o código precisamos só prestar atenção em último detalhe. O tempo inteiro numa aplicação com o Spring Boot usamos o arquivo application.properties. Só que quando vamos para produção, em geral vamos ter outras configurações. Alguns exemplos:

  1. Nome e dados de acesso do banco de dados
  2. Caminhos para escrita de arquivos

Essa ideia de environment já é bem suportada pelo Spring e o Spring Boot tira apenas proveito. Na hora que você passe o argumento -Dspring.profiles.active atribuindo o valor de ambiente que você quer.

  java -Dspring.profiles.active=production -jar webapp-runner.jar tudosobreesporte-1.0.0-SNAPSHOT.war& 

No exemplo acima você está ensinando ao Spring Boot a procurar por um arquivo chamado application-production.properties :).

É importante ressaltar que você pode continuar, em dev, executando a classe Boot. A única diferença é que em ambiente de produção você agora roda um war convencional.

O post de hoje teve mais o objetivo de centralizar algumas informações importantes sobre deploy de uma aplicação usando o Spring Boot. Como já comentei, o mundo do Spring é gigante e, se tiver algum assunto que você esteja mais interessado, não deixe de comentar ;).

 

18 thoughts on “Deployando sua aplicação com o Spring Boot

  1. Oi Alberto,

    Se eu entendi bem seu post o Spring-boot não funciona com JSPs quando executado a partir do método main() ou do JAR? Seria isso? Mesmo do Eclipse?

    Like

  2. Ele funciona com JSP em qualquer das duas situações. Caso você execute a classe com o main, diretamente do seu eclipse, vai funcionar porque você tem ali a pasta src/main/webapp. Caso execute direto do jar, vai ter que copiar essa estrutura para o local de execução do seu jar.

    Like

  3. Bom dia, você é o cara para me ajudar com um problema com spring,
    Comecei um MVP meu que falando de forma genérica tem uma tela de listagem, pesquisa, e uma ação que gera um insert, a página do about e só.

    Quando inicializo a aplicação pelo main, funciona tudo perfeitamente.
    Quando gero o war o select que ele deveria fazer na path “/” do controller não é executada, estou usando jsp para gerar as telas pois queria fazer o mais rápido possível.
    Por que isso pode estar acontecendo apenas quando gero o war?

    Like

    • Oi Jandrei,

      Não sei exatamente como você está fazendo o deploy. Seguiu as orientações do post? O war que você está colocando no seu tomcat, realmente está com todo o conteúdo que deveria?

      Like

      • A princípio está com tudo lá pois a mesma página que é chamada no “/” a index em um request GET é chamada após a ação que gera o insert porém esse é um POST,
        e após o POST ele mostra a listagem corretamente.

        Like

  4. Olá Alberto, recentemente participei de um projeto em que tive que usar o Spring Boot para fazer uma aplicação standalone e esse blog me ajudou bastante.
    Gostaria de repassar algumas informações adicionais, não sei se já foi coberto em outras postagens.
    Existe a possibilidade de usar jsps dentro do jar usando só as configurações básicas do Spring Boot, basta criar as pastas “/META-INF/resources” em “/src/main/resources/”, colocar os jsps lá, declarar o prefixo e o sufixo do dispatcherservlet no application.properties e adicionar a dependência do Tomcat embed e de servlet no pom.xml.
    Você pode criar a sua própria estrutura para conteúdo estático e os jsps, basta colocar a pasta no prefixo, igual a configuração para usar em um war normal, no projeto usei WEB-INF por costume, o conteúdo estático pode ficar direto em “/META-INF/resources/”, tudo nessa pasta vai para a raiz do jar e vai ser acessado normalmente.

    Diretório:
    src/main/resources/META-INF/resources

    application.properties:
    spring.mvc.view.prefix=/WEB-INF/
    spring.mvc.view.suffix=.jsp

    pom.xml:

    org.apache.tomcat.embed
    tomcat-embed-jasper

    javax.servlet
    javax.servlet-api
    3.1.0

    dependência opcional para usar JSTL:

    javax.servlet
    jstl
    1.2

    Like

  5. Olá Alberto! Tudo bem? Espero que sim. Neste artigo você comentou que “Caso tenha escolhido outras, como o ascendente Thymeleaf, o processo de deploy é o mais simples possível, já que é só gerar o jar e rodar”. Você tem algum exemplo de como eu coloco um JAR em produção? Desculpe-me pela pergunta mas estou começando agora com Spring e tenho apanhado bastante e de tudo que procurei na internet seus artigos são os mais bem estruturados, na minha opinião, pois vão direto ao ponto. Abraços!

    Like

    • Oi Bruno, vc está na melhor situação possível :). Se ta gerando o jar com tudo dentro, que é o padrão do spring boot, é só pegar esse jar e rodar com java -jar nomedojar.jar :). Pode ser que vc tenha que passar algum argumento, indicando por exemplo, o profile que vc quer habilitar.

      Like

      • Olá Alberto! No meu caso temos um ambiente de produção com Tomcat. Estou estudando Spring há pouco tempo. Estou tentando executar uma aplicação web de teste (só um CRUD pra testar o funcionamento do Spring) pra fazer um deploy com um war, mas não carrega e dá essa msg de erro abaixo (no meu localhost), já troquei pro tomcat 7, ja troquei pro java 1.7 e nada de sair do lugar 😦

        Log do Tomcat:
        Deploying on Apache Tomcat 8.0.27.0
        profile mode: false
        debug mode: false
        force redeploy: true
        Desimplantando…
        undeploy?path=/login
        OK – Undeployed application at context path /login
        Implantação local em C:\Users\XXXXXXXX\Documents\NetBeansProjects\XXXXXXXXXXXX\target\XXXXXXXXXXXXXX-0.0.1-SNAPSHOT
        Implantação em andamento…
        deploy?config=file%3A%2FC%3A%2FUsers%2FSTALLK%7E1%2FAppData%2FLocal%2FTemp%2Fcontext8922437635703955670.xml&path=/login
        FAIL – Deployed application at context path /login but context failed to start

        Já mexi nas dependências do pom.xml, já tirei, já coloquei e sempre dá falha ao implantar. Meu context.xml tá dentro de /webapp/META-INF/context.xml configurado só com duas linhas:

        Tem alguma ideia do que pode estar errado?

        Like

      • Oi Valmir, apenas por essas mensagens ta complicado para eu saber o problema. Acho que o context.xml está no lugar certo também… não tem nenhuma mensagem do spring em si?

        Like

  6. Olá Alberto!
    Estou com um problema em meu projeto, não totalmente relacionado ao post, mas como acredito que seja algo relacionado a configuração decidi postar.
    Tenho uma classe com a anotação @Component, e nela um repository anotado com @Autowired, só que por alguma razão esse atributo não está sendo injetado, fica sempre null. Anotei minha classe Main com @SpringBootApplication, extendi ela de SpringBootServletInitializer, tentei usar @ComponentScan, @EnableJpaRepositories, enfim, nada resolve. O que posso fazer para solucionar?

    Like

  7. Oi @Luiz, imagino que já tenha resolvido… de todo jeito, só olhando o comentário não tenho um chute.. apenas que a classe não estivesse na hierarquia de pacotes.. mas aí ele daria exception dizendo que não conseguiu resolver o ponto de injeção.

    Like

Leave a comment