Transações não devem ser habilitadas por default

Atualmente estou trabalhando num projeto que obviamente usa a stack do Spring, facilitado pelo Spring Boot :). E queria contar uma coisinha sobre o uso do Spring Data JPA.

Por algum motivo, que eu realmente não sei explicar, os métodos declarados nos seus Repositories criados em cima do Spring Data JPA são transacionais por default. Ou seja, se você chamar dois métodos diferentes eles serão executados em transações diferentes e, quase sempre, não é isso que você quer.

As transações são intimamente ligadas com fluxos de negócios e tal comportamento não deveria ser decidido implicitamente. Isso atrapalha o entendimento do código e sua consequentemente manutenção.

Para resolver, de maneira fácil, minha sugestão é desabilitar esse comportamento. Toda transação precisa ser declarada. Para fazer isso, vá na sua classe de startup do projeto com Spring Boot e adicione a seguinte annotation:

@SpringBootApplication
@EnableJpaRepositories(enableDefaultTransactions = false)
public class Boot {
....
}

Pronto, agora se você não anotar o método com @Transactional vai receber uma exception informando que uma transação é necessária para aquela operação. Em geral, todo código de negócio que é explícito, tende a ser mais benéfico no médio e longo prazo. Lembrando que a annotation @Transactional pode ser tanto a do Spring quanto a da especificação JTA.

Esse post foi super rápido, mas espero que seja útil. E a verdade é que eu estava com saudade de escrever por aqui também :).

Será que o Struts 1 estava certo?

Só para você não estranhar. O blog é sobre Spring e os exemplos usados durante o post são baseados no Spring MVC. Só que esse realmente é um post de um tema mais geral.

Há muito tempo atrás tínhamos o Struts1 e o seu jeito pouco automático de fazer as coisas. Recebíamos request response como argumentos e, além disso, recebíamos também um outro parâmetro que, se não me engano, era do tipo ActionForm. Esse bendito/maldito desse ActionForm fazia o povo agonizar. Você basicamente tinha que copiar os atributos da sua classe para lá e, se tudo chegasse como deveria, você criava seu objeto de domínio e seguia com sua vida. Ele até “evoluiu” e permitiu que você tivesse atributos do mesmo tipo do seu domínio, para minimizar a duplicidade.

    public class AutorForm extends ActionForm {
      private Autor autor = new Autor();

      public void setAutor(Autor autor) {
        this.autor = autor;
      }

      public Autor getAutor(){
        return this.autor;
      }
    }

E talvez essa tenha sido a pior contribuição do Struts para os frameworks web do mundo Java. Todo mundo começou a achar que os formulários da aplicação deveriam mapear para objetos de classes do seu domínio. Claro temos os casos felizes, como o que segue abaixo.

    public String novo(@Valid Autor autor,BindingResult bindingResult){
      if (bindingResult.hasErrors()) {
        return form(autor);
      }   

      autorDao.save(autor);
      return "redirect:/autores";
    }

Só que, o meu sentimento, é que na maioria das vezes o caso não é feliz, como esse aqui.

    public String novo(@Valid Livro livro,BindingResult bindingResult){
      if (bindingResult.hasErrors()) {
        return form(livro);
      }   

      autorDao.save(livro);
      return "redirect:/livros";
    }

Você pode até parar e pensar: calma ê, isso daí é um cadastro de um livro com autor, então faz sentido criar um objeto Livro  direto. Só que é isso mesmo que o framework faz para você? O objeto do tipo Livro que foi criado, está em perfeito estado para uso? Vamos ver.

    if(livro.getAutor().getEstado().equals(Estado.BLA)) {
      ...
    }

O resultado desse código é um belo de um NPE. O estado do autor não foi preenchido automaticamente e o programador, olhando apenas para essa parte do código, não tem como saber disso. Dessa situação começam a proliferar as verificações de null no código. Ah, então vou fazer com que o método retorne Optional! Só que o estado é obrigatório, essa solução não deveria passar num código review.

Os exemplos não param aqui. Agora temos uma tela onde é necessário atualizar apenas o email do autor. E o que acontece muito é o código acabar com esse método no controller.

    public String atualizaEmail(Autor autor,BindingResult bindingResult){
      if (bindingResult.hasErrors()) {
        return form(livro);
      }   

      ...
    }

E agora? Como você vai validar que o email não veio em branco? O @Valid vai tentar aplicar a validação no objeto inteiro e ele não está completamente preenchido. Você pode até buscar uma saída e ter perfis de validação na Bean Validation. Uma outra solução que eu já vi, é a de retirar as anotações de validação do modelo e criar métodos que fazem validações específicas. Perceba, uma simples tela afetou a definição do seu modelo!

Para finalizar, quero que você note que eu nem falei do fato de você ter um monte de setter e getter no seu domínio que você não necessariamente quer. E uma parte importante, eu também era meio que a favor de fazer a maioria das coisas que eu estou criticando no post. Para mim parecia um tradeoff ok. Deixou de ser quando eu percebi o quanto de problema básico a gente tinha nos sistemas.

  1. NPE por conta de objetos inconsistentes
  2. Validações falhando por conta de ficar preso ao lance de usar a classe de domínio
  3. Métodos claramente de view explodindo nos domínios. Você tenta buscar pelo método na sua IDE e um monte só é achado na view.
  4. Dificuldade em montar um fluxo de uma tela porque você fica dependente de já ter o modelo escrito. Aí, já que vai escrever o modelo, já mapeia para o ORM e por aí vai.
  5. Uso quase nulo do construtor, potencializando a criação de objetos que não possuem a consistência exigida pelo negócio.

Talvez existam até mais motivos, mas esses foram o que vieram direto na minha mente.

Já faz um bom tempo que não associo mais formulários diretamente com domínio. E eu agradeço o Struts1 por isso! Os caras eram visionários, de verdade. Podemos agradecer também ao livro DDD, mas prefiro agradecer ao Struts mesmo :P. Está definido na minha vida que a parte web da aplicação é algo separado do domínio e faço de tudo para que as coisas não se misturem. Pegando o primeiro exemplo que eu dei aqui, que já o caso feliz. Eu faria assim:

    public String novo(@Valid NovoAutorForm form,BindingResult bindingResult){
      if (bindingResult.hasErrors()) {
        return novoForm(form);
      }   

      Autor autor = form.toAutor();
      ...
    }

Agora pegando o exemplo do cadastro de autor.

    public String novo(@Valid NovoLivroForm form,BindingResult bindingResult){
      if (bindingResult.hasErrors()) {
        return novoForm(form);
      }   

      Livro livro = form.toLivro(autorDao);
      ...
    }

Para fechar, o exemplo da atualização do cadastro do email.

    public String atualizaEmail(@Valid AtualizaEmailAutorForm form,BindingResult bindingResult){
      if (bindingResult.hasErrors()) {
        return novoForm(form);
      }   

      Autor autor = autorDao.findById(form.getId());
      autor.setEmail(form.getEmail());

      ...
    }

Para todos os exemplos eu crio objetos que representam o formulário, até para o caso super feliz. Tive uma diminuição absurda em erros bobos na aplicação, como validações simples que falhavam e NPEs. As classes de domínio ficaram bem mais enxutas, apenas com métodos que realmente precisam ser acessados. Depois de um tempo usando, posso dizer que o lance da duplicidade é coisa pequena perto dos ganhos. E para ser sincero, não é bem uma duplicação. É apenas uma incrível coincidência e, muitas vezes, uma “forçada de coincidência” por parte do programador(a) .

As classes que representam forms, em geral, tem os atributos que devem ser mapeados do formulário html(id do autor é criado como um Integer ou Long) e também possuem um método que pegam as informações e criam os objetos em questão. E aí, por exemplo, já temos um belo de um uso para o construtor das classes de domínio. Outro ponto possivelmente ruim é essa passagem do DAO como argumento do método toYYY.  Eu considero má prática, mas acho pior ainda criar uma outra classe de conversão para isso. Entre duas ruins, fui com a que eu achava menos deprimente.

Claro que agora temos alguma duplicidade e precisamos também escrever mais código. Só que os ganhos tem sido tão bons, que estou bem ok com isso. Você agora tem uma classe super coesa em relação ao formulário em questão e, se precisar, pode criar quantos métodos quiser específicos para aquele cenário.

Pelo que eu vejo no mercado, o padrão é atrelar com o domínio mesmo. Eu não tenho feito mais assim e voltei para os velhos ActionForms :).  E você, como faz? Fique a vontade, deixe o seu comentário e enriqueça a discussão.

Repository mais perto do Model

Há 11 anos atrás era escrito no blog da caelum um post sobre como seria legal que os nossos modelos tivessem acesso, de alguma forma, a um conjunto de objetos onde eles poderiam fazer queries. Essa ideia, também conhecida como Repository, já foi discutida zilhares de vezes por aí. Inclusive a discussão recai que, na maioria das vezes, a versão de Repository da aplicação na verdade é um simples Dao.

Independente da nomenclatura, eu acho legal dar mais poder para os models desde que não ferre o design/performance/xpto do projeto como um todo.

Para ficar mais fácil de visualizar, vamos pensar num exemplo de código. Preciso escrever uma funcionalidade que, dado um cliente, eu preciso agora buscar a lista de orçamentos dele desde um ano xpto. Algo assim:

    Cliente cliente = recuperaCliente();
    List<Orcamento> orcamentosCliente = orcamentos.buscaTodosAPartirDeDeterminadoAno(2000);

Esse é o fluxo normal, você tem um objeto cliente, mas para buscar informações sobre ele você precisa acessar o Dao. Caso clássico de comportamento separado de estado…  Eu queria que isso funcionasse da seguinte forma:

    Cliente cliente = recuperaCliente();
    cliente.orcamentosAPartirDeDeterminadoAno(2000);

O meu desejo veio a tona, mas como que eu vou conseguir navegar na lista de orçamentos? Um primeiro pensamento pode ser o seguinte:

    public class Cliente {
      ...
      @OneToMany(mappedBy="cliente")
      private List<Orcamento> orcamentos;

      public List<Orcamento> orcamentosAPartirDeDeterminadoAno(int ano){
        return orcamentos.stream()
          //o metodo que verifica o ano pode ficar dentro do orcamento...
          .filter(orcamento -> orcamento.getAno() >= ano)
          .collect(Collectors.toList());
      }
    }

O problema é que aqui estamos violando a regra de não ferrar o projeto. A depender do número de orçamentos, é muito mais rápido fazer uma query no banco e trazer o resultado. De fato então precisamos do Repository/Dao dentro do nosso modelo, já que uma query mais esperta é realmente necessária. Uma possível solução seria essa:

    public class Cliente {
      ...

      public List<Orcamento> orcamentosAPartirDeDeterminadoAno(int ano){
        return this.clienteRepository.buscaOrcamentosAPartirDoAno(this,ano);
      }
    }

Aqui vem o código que é ok para o DDD, mas que causa angustia nos nossos olhos. Não somos acostumados a isso, mesmo que o tema já tenha sido blogado, em português, faz mais de 10 anos :P. Uma vez que você supere isso e queira se aventurar, vem o segundo problema: como fazer para a instância do Repository aparecer dentro do meu model? Abaixo temos uma primeira abordagem.

    public class OrcamentoController{

      @GetMapping(...)
      @ResponseBody
      public List<Orcamento> orcamentosAPartirDeDeterminadoAno(Model model,int ano,@AuthenticationPrincipal Cliente cliente){
        cliente.setRepository(this.clienteRepository);
        return this.orcamentoRepository.buscaOrcamentosAPartirDoAno(this,ano);
      }
    }

Eu não acho ruim essa solução, só não gosto que ela fique exposta no meio do controller, service ou qualquer outra camada que não seja exatamente onde o objeto foi carregado. Como costumo usar o Spring Data JPA, e não queria escrever delegates para adicionar a lógica de setar o Repository, tentei uma outra solução.

    
    @Entity
    @EntityListeners(RepositoryAwareListener.class)
    public class Cliente {

      ...
    	
      private transient ClienteRepository repository;

      public List<Orcamento> orcamentosAPartirDeDeterminadoAno(int ano){
        return this.repository.buscaOrcamentosAPartirDoAno(this,ano);
      }

    }




    public class RepositoryAwareListener {

    	@PostLoad
    	public void postLoad(Object entity) throws Exception {

    		Repositories repositories = new Repositories(ApplicationContextHolder.getInstance());
    		Optional<Object> repository = repositories.getRepositoryFor(entity.getClass());

    		try {
    			Field repositoryField = entity.getClass().getDeclaredField("repository");
    			repositoryField.setAccessible(true);
    			repositoryField.set(entity, repository.get());
    		} catch (NoSuchFieldException noSuchFieldException) {
    			throw new RuntimeException(
    					"é necessário definir o atributo com nome **repository** na classe "
    							+ entity.getClass(),
    					noSuchFieldException);
    		}

    	}
    }

Caso eu precise do Repository disponível no meu modelo, eu anoto ele com um EntityListener da JPA e aí tem um listener que eu escrevi responsável por descobrir o Repository correto e setar na entidade via reflection. Esse não é um código fácil de ler, mas também ele deve ser pouco alterado durante o desenvolvimento e manutenção do sistema, por isso que não me dói muito. Uma parte legal dele é que o Spring Data já fornece uma abstração onde você pode buscar pelo Repository específico de uma entidade :).

    		Repositories repositories = new Repositories(ApplicationContextHolder.getInstance());
    		Optional<Object> repository = repositories.getRepositoryFor(entity.getClass());

Sempre vale a pena dar uma olhada/pesquisada por classes mais internas do Spring, geralmente tem algo que funciona bem para a sua necessidade do momento.

Para conseguir o ApplicationContext, eu usei a mesma tática já citada aqui no blog. Por sinal, eu só fiz isso porque não consegui achar um jeito do Spring gerenciar o Listener… Quem souber, me avise!!

Eu acho esse jeito legal e realmente não me incomoda acessar o Dao/Repository daquele ponto do código. Dado que achei um jeito razoável de fazer a injeção, consigo viver bem. E para mim o benefício claro é que você da mais contexto ao uso dos métodos do seu Repository, minimizando a chance de passar parâmetros inapropriados. A parte ruim, claramente, foi essa volta que eu tive de dar para jogar o objeto lá dentro, sem contar que é uma solução não padrão(pelo menos eu acho). Um outro ponto que exige observação é que múltiplas invocações do método vão gerar diversas queries, coisa que não aconteceria usando um lazy load do Hibernate por exemplo.

E você, em 2018, o que acha? 11 anos depois ela continua estranha? Será que vale a pena testar?

Remote Configuration com Spring Cloud Config

O Spring provê uma forma bem maneira de lidar com os configurações em nossos projetos, através do arquivo application.properties ou application.yml.

Com eles temos o conceito de profiles, que resolve o problema de termos configurações diferentes para ambientes diferentes.

Como, por exemplo se, precisarmos nos conectar à um banco de dados diferente dependendo do ambiente que estivermos utilizando (produção, desenvolvimento, homologação).

Podemos atender essa necessidade configurando uma conexão especifica para cada profile:

Utilizando application.properties

Quando trabalhamos com arquivos .properties temos que ter um arquivo para cada profile com o seguinte padrão para o nome:
application-“nome do profile”.properties.

Como nosso caso temos três ambientes (produção, desenvolvimento e homologação). Teremos três arquivos:

application-production.properties


spring.datasource.username=application-user
spring.datasource.password=x123XASD&1
spring.datasource.url=jdbc:mysql://servidor-em-algum-lugar-do-mundo/bancoDeDadosOficial

application-development.properties


spring.datasource.username=root
spring.datasource.password=
spring.datasource.url=jdbc:mysql://localhost/bancoDeDados

application-acceptence.properties


spring.datasource.username=another-user
spring.datasource.password=123accepteence
spring.datasource.url=jdbc:mysql://localhost/bancoDeDados

Utilizando application.yml

Quando estamos trabalhando com arquivos .yml podemos ter um unico arquivo e dentro dele separar os profiles utilizando a sintaxe do próprio formato YAML.


#isso indica um novo arquivo
--- 
spring:
    profiles: production
    datasource:
        username: application-user
        password: x123XASD&1
        url: jdbc:mysql://servidor-em-algum-lugar-do-mundo/bancoDeDadosOficial
---
spring:
    profiles: development
    datasource:
        username: root
        password: 
        url: jdbc:mysql://localhost/bancoDeDados
---
spring:
    profiles: acceptence
    datasource:
        username: another-user
        password: 123accepteence
        url: jdbc:mysql://localhost/bancoDeDados

Em ambos os casos podemos utilizar o parâmetro -Dspring.profiles.active=“nome do profile” para selecionar qual profile queremos utilizar.

Um ponto a ser levado em consideração é que todas as informações estão expostas para quem tiver acesso ao repositório do projeto.

Então para minimizar esse problema podemos externalizar essas configurações em variáveis de ambientes. Com isso poderíamos ter um único arquivo de configuração.

Exemplo:

application.properties


spring.datasource.username=${DATABASE_USER}
spring.datasource.password=${DATA_BASE_PASSWORD}
spring.datasource.url=${DATA_BASE_URL}

Ou

application.yml


#isso indica um novo arquivo
--- 
spring:
    datasource:
        username: ${DATABASE_USER}
        password: ${DATA_BASE_PASSWORD}
        url: ${DATA_BASE_URL}

Para deixar mais flexível ainda podemos definir um valor default caso não exista a variável de ambiente. Para isso usamos a sintaxe ${VARIAVEL_DE_AMBIENTE:VALOR_DEFAULT}.

Dessa forma podemos deixar o valor default para o ambiente de desenvolvimento, e definir manualmente os valores para os ambientes de produção e homologação.

Exemplo:

application.properties


spring.datasource.username=${DATABASE_USER:root}
spring.datasource.password=${DATA_BASE_PASSWORD:}
spring.datasource.url=${DATA_BASE_URL:jdbc:mysql://localhost/bancoDeDados}

Ou

application.yml


#isso indica um novo arquivo
--- 
spring:
    datasource:
        username: ${DATABASE_USER:root}
        password: ${DATA_BASE_PASSWORD:}
        url: ${DATA_BASE_URL:jdbc:mysql://localhost/bancoDeDados}

Mas essa implementação nos leva a outro ponto: Alterar a maquina física para definir a variável de ambiente.

Quando pensamos em uma única aplicação, não parece doer tanto ter que alterar as variáveis de ambiente para que a aplicação funcione nos ambientes de produção e homologação.

Mas e quando estamos em uma arquitetura de microserviços, quantas variáveis de ambiente teremos que lembrar de preencher?

Nesse cenário é bem comum termos dezenas/centenas de aplicações rodando simultaneamente. E a quantidade de configurações que irá demandar para que nossa aplicação funcione, pode se tornar um problema quando temos que gerenciar tudo isso junto.

Para facilitar a gestão dessas configurações, podemos centralizar essas configurações e fazer com que nossa aplicação busque essas configurações quando for necessário ou ao inicia-la.

Essa técnica é chamada de Remote Configuration.

O Spring já tem um projeto específico para essa finalidade chamado Spring Cloud Config.

O Spring Cloud Config é um projeto dentro do guarda-chuva de projetos Spring Cloud. A ideia desse guarda-chuva de projetos é facilitar a utilização de Computação na nuvem e Arquitetura de Micro Serviços.


Entendendo como funciona o configuração remota

A ideia base desse projeto é centralizar todos os arquivos de configurações em um repositório GIT (para que seja possível versionar e gerenciar credenciais).

E teremos arquitetura cliente e servidor, onde o servidor irá se conectar ao repositório e irá servir as configurações.

O cliente, por sua vez, ao iniciar a aplicação irá solicitar ao servidor uma configuração. E com isso o cliente se auto-configura com base nos dados que o servidor configuração entregou.

Dessa forma, o cliente só precisa conhecer o servidor de configuração e nada mais.


Configurando o Servidor

Nosso servidor será uma aplicação com Spring Boot, só adicionaremos a dependência para o Spring Cloud Config.

Nosso pom.xml ficará assim:

<?xml version="1.0" encoding="UTF­8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0 (http://maven.apach
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.domineospring.cloud</groupId>
    <artifactId>config­server</artifactId>
    <version>1.0­SNAPSHOT</version>
    <packaging>pom</packaging>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF­8</project.build.sourceEnco
    </properties>
    <dependencyManagement>
        <dependencies>
            <!­­--Spring Boot ­­-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring­boot­dependencies</artifactId>
                <version>1.5.6.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!­­--Spring Cloud­­-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring­cloud­dependencies</artifactId>
                <version>Edgware.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring­cloud­config­server</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring­boot­starter­web</artifactId>
        </dependency>
    </dependencies>
</project>

Nossa classe que irá iniciar o Spring Boot precisa ter a anotação @EnableConfigServer

@EnableConfigServer
@SpringBootApplication
public class Boot {

    public static void main(String[] args) {
        SpringApplication.run(Boot.class, args);
    }
}

Em nosso arquivo de configuração application.properties devemos definir onde está o repositório GIT. (No meu caso defini também a porta que ele irá subir, pois estou rodando tudo na mesma máquina.)

server.port=8001
spring.cloud.config.server.git.uri=https://github.com/feh-wilinando/spring-cloud-config-files

Pronto, terminamos de configurar o nosso Config Server.
Vamos dar uma olhada nesse repositório.


Repositório GIT para arquivos de configuração

Nesse repositório podemos ter vários arquivos de configurações (.properties ou .yml) com profiles e tudo que já conhecemos quando utilizamos Spring Boot.

O nome do arquivo deve ser o nome da aplicação que iremos configurar no cliente.

Caso seja um arquivo com a extensão .properties devemos seguir a mesma convenção de nome para o profile (nome da aplicaçãoprofile.properties).

Caso seja um arquivo com a extensão .yml podemos definir o(s) profile(s) dentro do próprio arquivo.(vide acima)

No repositório que configuramos temos dois arquivos:

cliente-development.properties


spring.datasource.username=root
spring.datasource.password=
spring.datasource.url=jdbc:mysql://localhost/bancoDeDados

cliente-prod.properties


spring.datasource.username=application-user
spring.datasource.password=x123XASD&1
spring.datasource.url=jdbc:mysql://localhost/bancoDeDadosOficial

Agora que entendemos o que deve ter no repositório, vamos configurar o nosso cliente.


Configurando o Cliente

As configurações do lado do cliente são bem semelhantes à do servidor. Teremos uma aplicação Spring Boot e adicionaremos a dependência do Spring Cloud Config

Nosso arquivo pom.xml ficará assim:

<?xml version="1.0" encoding="UTF­8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0 (http://maven.apach
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.domineospring.cloud</groupId>
    <artifactId>config­client</artifactId>
    <version>1.0­SNAPSHOT</version>
    <packaging>pom</packaging>
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <project.build.sourceEncoding>UTF­8</project.build.sourceEnco
    </properties>
    <dependencyManagement>
        <dependencies>
            <!--­­Spring Boot ­-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring­boot­dependencies</artifactId>
                <version>1.5.6.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!­­--Spring Cloud­­-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring­cloud­dependencies</artifactId>
                <version>Edgware.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring­boot­starter­web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring­cloud­config­client</artifactId>
        </dependency>
    </dependencies>
</project>

Nossa classe que iniciará a aplicação não precisa ter nada de especial:

package com.domineospring.cloud.client;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Boot {

    public static void main(String[] args) {
        SpringApplication.run(Boot.class, args);
    }
}

Teremos um único controller que irá receber injetado os valores referente à configuração do datasource (que foi definida nos arquivos de configurações) e irá retornar-los no próprio body da response.

Para injetar os valores referente ao datasource vamos usar a anotação @Value.

package com.domineospring.cloud.client.controllers;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
public class HomeController {

    @Value("${spring.datasource.username}")
    private String username;

    @Value("${spring.datasource.password}")
    private String password;

    @Value("${spring.datasource.url}")
    private String url;

    @GetMapping("/datasource")
    public String configurations(){

        Map<String, String> response = new HashMap<>();

        response.put("username", username);
        response.put("password", password);
        response.put("url", url);

        return response.toString();
    }

}

No arquivo application.properties vamos definir o nome da aplicação:


spring.application.name=client

Agora precisamos conectar a nossa aplicação cliente ao servidor de configuração.

Para isso, no nosso projeto teremos um arquivo bootstrap.properties ou bootstrap.yml. Esse arquivo é lido durante o processo de inicialização da aplicação, nele iremos indicar o endereço do servidor de configuração.

bootstrap.properties


spring.cloud.config.uri=http://localhost:8001

Podemos definir o profile da nossa aplicação para filtrar qual arquivo de configuração deve ser utilizado.

Uma prática comum é deixar essa configuração de profile ativo no arquivo bootstrap.properties.

Pois nele fica a configuração do endereço do Config Server. E como o servidor de configuração utiliza o profile para selecionar o arquivo corretamente, nada mais justo que essas configurações fiquem próximas.

Vamos definir o profile development também no nosso arquivo bootstrap.properteis.

bootstrap.properties


spring.cloud.config.uri=http://localhost:8001
spring.profiles.active=development

Pronto, nossas aplicações servidora e cliente estão pronta para serem usadas.
Basta iniciar a Config Server e depois inicializar nossa aplicação cliente.

Podemos utilizar o utilitário curl para fazer a requisição para a aplicação cliente e ver como resultado para cada ambiente.

curl http://localhost:8080/datasource

Com definimos o profile development veremos o seguinte resultado: {password=, url=jdbc:mysql://localhost/bancoDeDados, username=root} que são justamente os valores que definimos arquivo client-development.properties

Se alteramos para o profile production e utilizarmos novamente o curl para efetuar o request, veremos o seguinte resultado.

{password=x123XASD&1, url=jdbc:mysql://localhost/bancoDeDadosOficial, username=application-user}

E se não definirmos nenhum profile?

Nesse caso o Config Server vai procurar um arquivo que não tenha uma definição de profile. No nosso caso ele irá procurar um arquivo client.properties (se estivéssemos usando arquivo no formato yml ele iria procurar dentro do arquivo uma configuração que não estivesse atrelada a nenhum profile).

Como não temos esse arquivo isso causará um erro. As alternativas aqui são criar o arquivo sem profile no repositório com GIT ou deixar a configuração sem profile no próprio arquivo application.properties ou application.yml.

Agora nossas configurações ficam todas centralizadas no nosso repositório GIT.


Para saber mais:

Autenticação

Não precisamos de nenhum tipo de autenticação pois o repositório que estou utilizando no exemplo é público.

Porém se fosse um repositório privado precisaríamos informar ou usuário/senha ou chave ssh

Para usuário e senha precisamos definir também as seguintes configurações:

spring.cloud.config.server.git.username=seu-usuario-aqui
spring.cloud.config.server.git.password=sua-senha-aqui

No caso de usar chaves ssh temos duas opções a primeira (que é a padrão) é utilizar as configurações de ssh da maquina (que irá utilizar as configurações contidas em ~/.ssh e /etc/ssh).

Dessa forma não precisamos fazer nada via código ou configuração no projeto.

A segunda opção é manter as configuração no arquivo application.properties.

Para isso temos definir as seguintes configurações:

spring.cloud.config.server.git.ignoreLocalSshSettings=true #para não considerar configurações de ssh locais
spring.cloud.config.server.git.hostKey=seu-host-aqui
spring.cloud.config.server.git.hostKeyAlgorithm=ssh-rsa #algoritmo utilizado para gerar as chaves (ssh-dss, ssh-rsa, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384 ,ecdsa-sha2-nistp521)
spring.cloud.config.server.git.privateKey= |
-----BEGIN RSA PRIVATE KEY-----                        
MIIEpgIBAAKCAQEAx4UbaDzY5xjW6hc9jwN0mX33XpTDVW9WqHp5AKaRbtAC3DqX                         
IXFMPgw3K45jxRb93f8tv9vL3rD9CUG1Gv4FM+o7ds7FRES5RTjv2RT/JVNJCoqF                         
ol8+ngLqRZCyBtQN7zYByWMRirPGoDUqdPYrj2yq+ObBBNhg5N+hOwKjjpzdj2Ud                         
1l7R+wxIqmJo1IYyy16xS8WsjyQuyC0lL456qkd5BDZ0Ag8j2X9H9D5220Ln7s9i                         
oezTipXipS7p7Jekf3Ywx6abJwOmB0rX79dV4qiNcGgzATnG1PkXxqt76VhcGa0W                         
DDVHEEYGbSQ6hIGSh0I7BQun0aLRZojfE3gqHQIDAQABAoIBAQCZmGrk8BK6tXCd                         
fY6yTiKxFzwb38IQP0ojIUWNrq0+9Xt+NsypviLHkXfXXCKKU4zUHeIGVRq5MN9b                         
BO56/RrcQHHOoJdUWuOV2qMqJvPUtC0CpGkD+valhfD75MxoXU7s3FK7yjxy3rsG                         
EmfA6tHV8/4a5umo5TqSd2YTm5B19AhRqiuUVI1wTB41DjULUGiMYrnYrhzQlVvj                         
5MjnKTlYu3V8PoYDfv1GmxPPh6vlpafXEeEYN8VB97e5x3DGHjZ5UrurAmTLTdO8                         
+AahyoKsIY612TkkQthJlt7FJAwnCGMgY6podzzvzICLFmmTXYiZ/28I4BX/mOSe                         
pZVnfRixAoGBAO6Uiwt40/PKs53mCEWngslSCsh9oGAaLTf/XdvMns5VmuyyAyKG                         
ti8Ol5wqBMi4GIUzjbgUvSUt+IowIrG3f5tN85wpjQ1UGVcpTnl5Qo9xaS1PFScQ                         
xrtWZ9eNj2TsIAMp/svJsyGG3OibxfnuAIpSXNQiJPwRlW3irzpGgVx/AoGBANYW                         
dnhshUcEHMJi3aXwR12OTDnaLoanVGLwLnkqLSYUZA7ZegpKq90UAuBdcEfgdpyi                         
PhKpeaeIiAaNnFo8m9aoTKr+7I6/uMTlwrVnfrsVTZv3orxjwQV20YIBCVRKD1uX                         
VhE0ozPZxwwKSPAFocpyWpGHGreGF1AIYBE9UBtjAoGBAI8bfPgJpyFyMiGBjO6z                         
FwlJc/xlFqDusrcHL7abW5qq0L4v3R+FrJw3ZYufzLTVcKfdj6GelwJJO+8wBm+R                         
gTKYJItEhT48duLIfTDyIpHGVm9+I1MGhh5zKuCqIhxIYr9jHloBB7kRm0rPvYY4                         
VAykcNgyDvtAVODP+4m6JvhjAoGBALbtTqErKN47V0+JJpapLnF0KxGrqeGIjIRV                         
cYA6V4WYGr7NeIfesecfOC356PyhgPfpcVyEztwlvwTKb3RzIT1TZN8fH4YBr6Ee                         
KTbTjefRFhVUjQqnucAvfGi29f+9oE3Ei9f7wA+H35ocF6JvTYUsHNMIO/3gZ38N                         
CPjyCMa9AoGBAMhsITNe3QcbsXAbdUR00dDsIFVROzyFJ2m40i4KCRM35bC/BIBs                         
q0TY3we+ERB40U8Z2BvU61QuwaunJ2+uGadHo58VSVdggqAo0BSkH58innKKt96J                         
69pcVH/4rmLbXdcmNYGm6iu+MlPQk4BUZknHSmVHIFdJ0EPupVaQ8RHT
-----END RSA PRIVATE KEY-----

Utilizando Labels

Além do nome da aplicação e profile, podemos utilizar labels para indicar ao servidor de configuração qual arquivo usar.

Com a label podemos definir qual a branch se encontra o nosso arquivo de configuração.

Em nosso repositório temos além da branch master a branch aws, com os mesmos arquivos:

cliente-development.properties na branch AWS


spring.datasource.username=aws-development
spring.datasource.password=aws-simple-password
spring.datasource.url=jdbc:mysql://aws-host/bancoDeDados

cliente-production.properties na branch AWS


spring.datasource.username=application-user
spring.datasource.password=x123XASD&1
spring.datasource.url=jdbc:mysql://localhost/bancoDeDadosOficial

Podemos definir a label também no arquivo bootstrap.properteis:

bootstrap.properties


spring.cloud.config.uri=http://localhost:8001
spring.profiles.active=production
spring.cloud.config.label=aws

Com isso, ao efetuar novamente a requisição via curl:

curl http://localhost:8080/datasource

Veremos no como resultado o texto {password=aws-x123XASD&1, url=jdbc:mysql://aws-host/bancoDeDadosOficial, username=aws-user}, pois está definido o profile production e a label aws.

Se definirmos o profile development e repetirmos o processo veremos como resultado o texto {password=aws-simple-password, url=jdbc:mysql://aws-host/bancoDeDados, username=aws-development}.

Ganhamos muita flexibilidade para gerenciar qual arquivo deve ser selecionado.

E aí o que achou do Spring Cloud Config?

Spring Boot via DependencyManagement

Geralmente quando usamos Spring Boot com Maven temos uma declaração muito parecida com essa em nosso arquivo pom.xml:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.6.RELEASE</version>
</parent>

Estamos herdando de outro arquivo pom.xml onde tem todas as dependências do Spring Boot declaradas com todas suas versões e etc.

Muitos projetos utilizam essa técnica de ter um projeto maven somente com o arquivo pom.xml  e nele todas as dependências declaradas para ser herdado em outros projetos, assim garantimos que não teremos dependências duplicadas ou em versões diferentes.

O que aconteceria se um projeto tem um pom.xml para ser herdado e queremos usar o Spring Boot? Aí deu ruim, pois não podemos ter duas declarações  no mesmo arquivo pom.xml.

Para resolver esse problema podemos usar o a tag dependencyManagement. Ela tem basicamente a mesma funcionalidade que a tag parent, ou seja ter um projeto com todas as dependências declaradas nele para serem usadas em outros projetos.

Nossa declaração no pom.xml ficaria assim:

<dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>1.5.6.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
</dependencyManagement>

Esse projeto com as dependências pode ser projeto maven com pom.xml ou war entre outros. Por isso precisamos especificar o tipo.

A vantagem do dependencyManagement é que podemos ter mais de uma dependência dentro da tag dependencies.

Dessa forma podemos usar o Spring Boot em e ainda assim ter um parent no nosso projeto.

Essa é só uma outra forma de utilizar o Spring Boot no nosso projeto.

Você já conhecia essa forma de utilizar? O que vocês acharam?

Como seu sistema envia email?

Mandar email é uma tarefa bem rotineira em nossos projetos. Mandamos email de boas vindas quando um novo usuário se cadastra, email para recuperar senha etc. Uma coisa que geralmente todas essas features tem em comum, é que o email deve ser enviado em HTML.

Existem alguns projetos para facilitar a escrita do template do email e carregá-lo dentro do projeto, o Freemarker é um desses. Mais antigamente, o Velocity também era uma opção muito usada no mercado. O objetivo do post é justamente mostrar que podemos implementar essas features com algo que já está presente no seu projeto, sem a necessidade de aprender uma nova tecnologia ou adicionar uma dependência nova no seu projeto.

Pensando no código que você escreveria com o Freemarker, seria algo mais ou menos assim:

		@Controller
		public class HomeController {
		
			@PostMapping("/usuario")
			public String novoUsuario(Usuario usuario) {
		
				usuarioDao.salva(usuario);
  
				HtmlEmail email = new HtmlEmail();
				Freemarker template = new Freemarker();
				String body = template.resolve(HomeController.class.getResourceAsStream("/boasvidas.ftl"),usuario);
				email.setBody(body);
				...
			}
		}

A ideia é que agora a gente tem que carregar o HTML de uma outra forma. Pensando que nosso projeto é web, meio que já temos isso pronto. Dê uma olhada no controller que temos abaixo:

		@Controller
		public class TemplateController {
		
			@GetMapping("/template/boas-vindas")
			public String boasVindas(Integer idUsuario,Model model) {
				Usuario usuario = usuarioDao.carrega(idUsuario);
				model.addAttribute("usuario", usuario);
				return "templates/boasvindas";
			}
		}

Este é um controller que serve como gerador de HTML para emails que precisam ser enviados pelo sistema. Agora só precisamos acessar o endereço no momento de envio dos emails.

		@Controller
		public class HomeController {
		
			@GetMapping("/")
			public String novoUsuario(Usuario usuario) {
				RestTemplate restTemplate = new RestTemplate();
				ResponseEntity<String> entity = restTemplate.getForEntity("http://localhost:8080/template/boas-vindas/"+usuario.getId(), String.class);
				String body = entity.getBody();
				
				HtmlEmail email = new HtmlEmail();
				email.setBody(body);
				
				return "home/index";
			}
		}

Dessa forma resolvemos o problema de geração do conteúdo do email. Como o acesso é feito via localhost, não interfere quase nada no desempenho. Além disso você pode continuar escrevendo seu HTML da mesma maneira que sempre escreveu, o que também é um bom ganho.

O post em si não tem muito haver com o universo do Spring, mas achei legal compartilhar porque é uma solução que tenho usado bastante nos projetos e acho que tem funcionado bem. E aí, o que achou? Muita gambiarra?

Projections com Spring Data JPA

No primeiro post aqui do blog foi explicado o objetivo e como usar o projeto Spring Data JPA. Desde então já usei ele em diversos projetos e não tenho como falar o tanto de tempo que foi economizado :). Falando especificamente em relação ao post de hoje, a ideia é mostrar uma característica interessante do projeto, que é a possibilidade de fazer queries trazendo apenas uma parte dos seus objetos.

Para tentar deixar mais claro, imagine a seguinte situação: existe um projeto que eu mantenho chamado setupmyproject. Nele eu gero vários projetos dos mais variados tipos e todos eles tem um tipo, data de criação, tecnologias escolhidas, tempo que levaria para fazer na mão etc. Agora eu quero fazer um relatório só mostrando o tipo e a data de criação dos projetos. Normalmente faríamos uma JPQL da seguinte maneira:

  select p from Project p;

O código acima traz todas as informações dos objetos, mas só exibimos uma parte deles. Pensando no melhor cenário, a ideia é trazer apenas as informações necessárias, algo parecido com o que segue.

  select p.type,p.creationDate from Project p;

O problema é que isso não vai voltar em forma de List<Project> e sim em forma de List<Object[]>, já que a implementação da JPA não sabe no que transformar esse pedaço de informação. Basicamente o problema é que a JPA não tem suporte a queries parciais. Uma solução, já presente na especificação é a utilização das constructor expressions. Uma alternativa é a utliza das projections do Spring Data JPA.

A primeira coisa é modificar o método na sua interface que representa o DAO genérico no Spring Data JPA. Vamos fazer ele retornar uma List<TipoDataCriacao>.

   public interface ProjectDao extends Repository<Project,Integer>{
      @Query("select p.type,p.creationDate from Project p")
      public List<TypeCreationDate> all();
   }

Perceba que não modificamos a nossa query! Agora só precisamos criar uma interface com os getters relativos aos atributos que especificamos e o Spring Data JPA se encarrega de já criar uma implementação em execução.

  interface TypeCreationDate {
     public Type getType();
     public DateTime getCreationDate();
  }

A parte legal dessa abordagem é que você só deixa acessível o que realmente precisa e não precisa deixar a query mais complexa adicionando o new na query.

E aí, já utilizou esse recurso com o Spring Data JPA? Fique a vontade para comentar aqui no post :).

Feign uma forma simples para consumir serviços

Dificilmente uma aplicação “vive sozinha” e em algum momento pode surgir a necessidade de integrá-la com outras aplicações.

As necessidades podem ser diversas:

  • Consultar dados de um sistema legado
  • Retornar endereço a partir de um CEP
  • Efetuar pagamento online
  • Consultar o custo de envio de determinada mercadoria
  • Fazer autenticação através de alguma rede social ou qualquer

Hoje em dia muitas das integrações utilizam o modelo arquitetural REST, e não é uma tarefa muito difícil criar um cliente para consumir um serviço web.

Vamos usar como exemplo via cep, que é uma alternativa para pesquisar um endereço a partir de um CEP.

Para consumir o serviço este basta efetuar uma requisição GET no seguinte formato https://viacep.com.br/ws/NUMERO-DO-CEP/FORMATO. O formato pode ser: json,xml,piped,querty.

Se por exemplo efetuarmos uma requisição GET para https://viacep.com.br/ws/04101300/json, temos o seguinte retorno:

{
"cep": "04101-300",
"logradouro": "Rua Vergueiro",
"complemento": "de 2771 a 5049 – lado ímpar",
"bairro": "Vila Mariana",
"localidade": "São Paulo",
"uf": "SP",
"unidade": "",
"ibge": "3550308",
"gia": "1004"
}

Consumindo Serviço com RestTemplate

Para consumir um Web Service  no Spring podemos usar a classe RestTemplate que tem uma interface de uso bem simples.

Vamos criar uma aplicação com Spring Boot para consumir o serviço do via cep.

Vamos começar adicionando o parent do Spring Boot ao pom.xml:

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.3.RELEASE</version>
    </parent>

E também o plugin para gerar o JAR auto-contido:

<build>
    <plugins>
       <plugin>
           <groupId>org.springframework.boot</groupId>
           <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Para representar o retorno do Web Service vamos criar a classe Endereco

public class Endereco {

    private String cep;
    private String logradouro;
    private String complemento;
    private String bairro;
    private String localidade;
    private String uf;

    //getters 

    @Override
    public String toString() {
        return "Endereco{" +
                "cep='" + cep + '\'' +
                ", logradouro='" + logradouro + '\'' +
                ", complemento='" + complemento + '\'' +
                ", bairro='" + bairro + '\'' +
                ", localidade='" + localidade + '\'' +
                ", uf='" + uf + '\'' +
                '}';
    }
}

Agora vamos criar a classe que inicializa o contexto do Spring:

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Vamos encapsular o código para consumir o serviço em uma classe ViaCepCleient


@Component
public class ViaCEPClient {

    public Endereco buscaEnderecoPor(String cep){
        RestTemplate template = new RestRemplate();
        return template.getForObject("https://viacep.com.br/ws/{cep}/json",Endereco.class, cep);
    }
}

Como a ideia do projeto é bem simples vamos rodar tudo a partir da classe Application. A ideia é pegar o cep na variável args e através do cliente (ViaCepClient) retornar o endereço e logar no console.

O Spring Boot disponibiliza uma interface chamada CommandLineRunner que tem a declaração do método void run(String… args) throws Exception;.

Quando temos no nosso classpath algum Bean que produza CommandLineRunner, o Spring Boot após a inicialização pega o que foi recebido no método main e repassa para esse Bean.

Dessa forma conseguimos separar o código de inicialização e o código que deve ser executado baseado nos argumentos que foi passado para o Spring Boot.

Vamos alterar nossa classe Application para que ela tenha um Bean para CommandLineRunner.

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public CommandLineRunner run(ViaCEPClient client){
        return args -> {
            if (args.length > 0) {
                String cep = args[0];

                Endereco endereco = client
                                     .buscaEnderecoPor(cep);

                System.out.println(endereco);
            }
        };
    }

}

Ao executarmos nossa classe Application com algum argumento veremos no console algo similar a isso:

Endereco{cep=’04101-300′, logradouro=’Rua Vergueiro’, complemento=’de 2771 a 5049 – lado ímpar’, bairro=’Vila Mariana’, localidade=’São Paulo’, uf=’SP’}

Consumindo serviço com Feign

Agora imagine em uma aplicação onde temos que consumir diversos serviços, ou em um arquitetura de micro serviços onde temos que nos integrar com diversos serviços para que a aplicação como um todo funcione. Pense em quantas vezes vamos ter que escrever um cliente usando o RestTemplate?

E geralmente se o programador teve que repetir uma tarefa mais de 3 vezes, ele automatiza essa tarefa de alguma forma.

Pensando nisso alguém teve a brilhante ideia de deixar ainda mais fácil a criação de clientes para consumir serviços. E então nasceu o projeto Feign.

O projeto Feign foi inspirado em: Retrofit, JAXRS-2.0, entre outras. Com ele podemos criar clientes de uma forma declarativa.

Então a galera do Spring incorporou o Feign dentro da sua própria stack. Mais especificamente abaixo do projeto Spring Cloud. Que é um projeto voltado para arquitetura de micro serviços, cloud e etc.

Vamos então migrar nosso cliente de RestTemplate para usar Feign. Primeiramente vamos adicionar as dependências do projeto Spring Cloud e do Feign.

O Spring Cloud segue a mesma linha do Spring Boot com as ideias dos Starters. Só que ao invés de usar um projeto base (parent) ele usa o conceito de dependências gerenciadas.

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Dalston.SR1</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

Agora que temos o Spring Cloud no nosso projeto vamos adicionar a dependência do Feign

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-feign</artifactId>
</dependency>

Para habilitar o Feign no projeto vamos anotar a classe Application com @EnableFeignClients

@SpringBootApplication
@EnableFeignClients
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Bean
    public CommandLineRunner run(ViaCEPClient client){
        return args -> {
            if (args.length > 0) {
                String cep = args[0];

                Endereco endereco = client.buscaEnderecoPor(cep);

                System.out.println(endereco);
            }
        };
    }
}

Como falei anteriormente com o Feign podemos criar clientes de uma forma declarativa. Ou seja só iremos declarar o cliente sem colocar nenhuma implementação.

Vamos começar convertendo nossa classe ViaCEPClient para uma interface (pois não queremos ter nenhuma implementação nela).

@Component
public interface ViaCEPClient {
    Endereco buscaEnderecoPor(String cep);
}

Vamos trocar a anotação @Component para @FeignClient e nela adicionar a URL do serviço.

@FeignClient(url="https://viacep.com.br/ws/")
public interface ViaCEPClient {
    Endereco buscaEnderecoPor(String cep);
}

Precisamos dizer qual o Endpoint o método Endereco buscaEnderecoPor(String cep); deve acessar.

Para isso vamos usar as anotações de mapeamento do Spring MVC:

  • @RequestMapping
  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping

Da mesma forma precisamos mapear os parâmetros:

    • @PathVariable
    • @RequestParam

.

@FeignClient(url="https://viacep.com.br/ws/")
public interface ViaCEPClient {

    @GetMapping("{cep}/json")
    Endereco buscaEnderecoPor(@PathVariable("cep") String cep);
}

Por ultimo o Feign pede que além da URL o nosso cliente de serviço tenha um nome (que nada mais é do que um identificador). Vamos adicionar o parâmetro name na anotação @FeignClient.

@FeignClient(url="https://viacep.com.br/ws/", name = "viacep")
public interface ViaCEPClient {

    @GetMapping("{cep}/json")
    Endereco buscaEnderecoPor(@PathVariable("cep") String cep);
}

Se rodarmos o código novamente teremos o mesmo resultado utilizando o RestTemplate. Com a diferença que o código real para consumir o serviço será gerado em tempo de execução. Ou seja nunca mais precisamos usar RestTemplate para gerar clientes.

E aí o que achou do Feign?

Spring Data Rest é vida

Fala aí galera, tudo bem? Meu nome é Fernando e sou instrutor e desenvolvedor na caelumalura.

Não é de hoje que o Spring vem facilitando nossa vida, e vamos combinar que os caras mandam muito bem!

Agora imagina só um mundo perfeito, onde declaramos nossas classes de modelo, repositórios e magicamente temos enpoints para fazermos todas as operações que declaramos nos nossos repositórios (por exemplo incluir, listar, alterar e remover).

Esse mundo perfeito existe e nele essa mágica se chama Spring data rest. Sim, ao utilizarmos ele só de declararmos nossos repositórios temos endpoints disponíveis.

Tá, legal saber que existe o Spring Data Rest.  Mas e como uso ele? (Show me the code!!!)

Bom galera eu montei um exemplo bem básico com Spring Boot Maven e está no meu Github. Nele você vai ver no arquivo pom.xml as seguintes declarações:

Spring Boot

<parent>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-parent</artifactId>
	<version>1.4.2.RELEASE</version>
</parent>

E os staters do Spring Data e do Spring data Rest

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Além dos Starters tenho também o h2 como banco de dados em memória e o lombok.

<dependency>
	<groupId>com.h2database</groupId>
	<artifactId>h2</artifactId>
</dependency>

<dependency>
	<groupId>org.projectlombok</groupId>
	<artifactId>lombok</artifactId>
</dependency>

Para quem não conheçe o lombok, ele nos ajuda a escrever menos código repetitivo por exemplo: getter, setter, construtor, toString, equals/hashcode e etc...

Com ele anotamos nossos atributos com @Getter e/ou @Setter, anotamos nossa classe com @NoArgsConstructor ou @RequiredArgsConstructor, @AllArgsConstructor e ao compilarmos nosso código ele gera os byte-codes equivalentes.Vale a pena dar uma conferida!

No meu caso estou usando a anotação @Data (que engloba um monte dessas anotações) e @NoArgsConstructor (para ter um construtor sem parâmetros, por conta dos frameworks).

(OBS: Para rodar o projeto por dentro do IDE você precisa instalar o agent lombok, do contrário seu IDE não vai encontrar o getters, setters e afins.)

Em java temos duas classes de modelo Book e Author.

@Entity
@Data
@NoArgsConstructor
public class Author {

	@Id
	@GeneratedValue
	private Long id;

	@NotBlank
	private String name;

}
@Entity
@Data
@NoArgsConstructor
public class Book {

	@Id
	@GeneratedValue
	private Long id;

	@NotBlank
	private String title;

	@NotBlank
	private String description;

	@Min(50)
	private Integer numOfPages;

	@Valid
	@ManyToMany(fetch=FetchType.EAGER)
	private Set<Author> authors = new HashSet<>();

	public void add(Author author) {
		authors.add(author);
	}

}

Além do modelo tenho também um repositórios para cada, BookRepository e AuthorRepository

public interface BookRepository extends CrudRepository<Book, Long> {
}
public interface AuthorRepository extends CrudRepository<Author, Long>{
}

Para subir a aplicação tenho a classe Application que inicia o Spring Boot e salva um livro e um autor no banco de dados;

@SpringBootApplication
public class Application {

	public static void main(String[] args) throws Exception {
		SpringApplication.run(Application.class, args);
	}

	@Autowired
	private BookRepository bookRepository;

	@Autowired
	private AuthorRepository authorRepository;

	@PostConstruct
	@Transactional
	public void onLoad(){

		Author alberto = new Author();
		alberto.setName("Alberto");
		authorRepository.save(alberto);

		Book book = new Book();
		book.setTitle("Spring MVC");
		book.setDescription("Domine o principal framework web Java");
		book.setNumOfPages(237);

		book.add(alberto);

		bookRepository.save(book);

	}

}

E pronto! Temos uma endpoints para efetuarmos o CRUD de livros e autores. (Spring Data Rest é ou não é vida?)

Ao acessarmos http://localhost:8080, temos o seguinte retorno

{
"_links" : {
"authors" : {
"href" : "http://localhost:8080/authors&quot;
},
"books" : {
"href" : "http://localhost:8080/books&quot;
},
"profile" : {
"href" : "http://localhost:8080/profile&quot;
}
}
}

O Spring Data Rest usa fortemente os conceitos de HATEOAS que a grosso modo diz que através de um recurso disponível deve ser possível navegar pelo estado desse recurso através dos links. Por padrão o spring usa um modelo de apresentação chamado HAL para representação da resposta.

Bom os endpoints /authors e /books me parecem bem ok, o que deve ter neles, mas e o endpoint /profile?

Esse enpoint tem as meta-informações do seus serviços, por exemplo ao acessarmos http://localhost:8080/profile/authors temos o seguinte resultado

{
"alps":{
"version":"1.0",
"descriptors":[
{
"id":"author-representation",
"href":"http://localhost:8080/profile/authors&quot;,
"descriptors":[
{
"name":"name",
"type":"SEMANTIC"
}
]
},
{
"id":"create-authors",
"name":"authors",
"type":"UNSAFE",
"rt":"#author-representation"
},
{
"id":"get-authors",
"name":"authors",
"type":"SAFE",
"rt":"#author-representation"
},
{
"id":"patch-author",
"name":"author",
"type":"UNSAFE",
"rt":"#author-representation"
},
{
"id":"update-author",
"name":"author",
"type":"IDEMPOTENT",
"rt":"#author-representation"
},
{
"id":"get-author",
"name":"author",
"type":"SAFE",
"rt":"#author-representation"
},
{
"id":"delete-author",
"name":"author",
"type":"IDEMPOTENT",
"rt":"#author-representation"
}
]
}
}

Essas meta-informações estão associadas a qual representação temos no enpoint (no nosso caso)  authors e toda a definição dessa representação (os atributos e métodos). O endpoint profile é definido na RFC 6906.

Dentro da representação do author temos um objeto com id author-representation e nele temos um descritor com nome name e o tipo dele é SEMANTIC.
Esse tipo SEMANTIC indica um elemento que guarda um estado (atributo).

Além desse objeto temos um objeto com id create-authors, get-authors ou update-author ambos com nome author porém com tipos diferentes (nesse caso temos UNSAFE, SAFE e IDEMPOTENT). Esse objetos são as operações que podemos fazer sobre essa representação (methods).

Cada tipo tem um comportamento diferente:

SAFE – operações desse tipo são idempotentes e não alteram estado no servidor (GET, HEAD).

UNSAFE – operações desse tipo não são idempotentes e alteram estado no servidor (POST).

IDEMPOTENT – operações desse tipo são idempotentes e alteram estado no servidor (PUT, DELETE).

Uma operação é considerada idempotente se o resultado dele for bem sucedida indiferente do numero de vezes que ela for executada.

Agora sim, vamos para os endpoints mais “óbvieis“. Irei utilizar o programa curl para efetuar as chamadas para nosso endpoints.

Para listar nosso autores podemos efetuar a seguinte requisição:

curl -X GET http://localhost:8080/authors

(Deixei explicito o verbo GET somente por questões didáticas).

Nesse caso temos o seguinte resultado:

{
"_embedded" : {
"authors" : [ {
"name" : "Alberto",
"_links" : {
"self" : {
"href" : "http://localhost:8080/authors/1&quot;
},
"author" : {
"href" : "http://localhost:8080/authors/1&quot;
}
}]
},
"_links" : {
"self" : {
"href" : "http://localhost:8080/authors&quot;
},
"profile" : {
"href" : "http://localhost:8080/profile/authors&quot;
}
}
}

Nesse caso temos um autor com o nome Alberto e os links do autor levam ao recurso dele mesmo. E os links da representação como um todo levam a lista de autores ou ao profile de autores.

Vamos seguirmos o link para listar somente o autor Alberto.

curl -X GET http://localhost:8080/authors/1

E nesse caso temos o seguinte resultado:

{
"name" : "Alberto",
"_links" : {
"self" : {
"href" : "http://localhost:8080/authors/1&quot;
},
"author" : {
"href" : "http://localhost:8080/authors/1&quot;
}
}
}

E para criar um novo autor? bom para isso temos que usar o verbo POST.

curl -X POST -H "Content-type: application/json" -d '{"name": "Fernando"}' http://localhost:8080/authors

Agora temos dois autores, e para alterar? Fácil né, só usar outro verbo. Nesse caso o PATCH

 curl -X PATCH -H "Content-type: application/json" -d '{"name": "Fernando Willian"}' http://localhost:8080/authors/2

E para excluir vamos usar o verbo DELETE

curl -X DELETE http://localhost:8080/authors/2

Podemos usar as mesmas operações para os livros.

Para deixar um pouco mais interessante, vamos adicionar um pouco de segurança ao nossos endpoints.
Para isso vamos adicionar o starter do Spring Security no nosso arquivo pom.xml:

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-security</artifactId>
</dependency>

E vamos configurar como será nossa autenticação e quais os endereços que queremos proteger:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter{

	@Override
	protected void configure(AuthenticationManagerBuilder auth) throws Exception {
		auth.inMemoryAuthentication()
		.withUser("common").password("123").roles("USER").and()
		.withUser("admin").password("@dm1n").roles("ADMIN");
		
	}
	
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http
			.httpBasic()
			.and()
				.authorizeRequests()
					.antMatchers(HttpMethod.POST, "/**").hasRole("ADMIN")
					.antMatchers(HttpMethod.PATCH, "/**").hasRole("ADMIN")
					.antMatchers(HttpMethod.DELETE, "/**").hasRole("ADMIN")
				.and()
					.csrf()
						.disable();
	}
	
}

Nessa configuração temos que nos autenticar com uma conta com permissão de ADMIN para efetuar qualquer requisição com os verbos POST, PATCH e DELETE.

Agora para incluir um novo autor devemos nos autenticar:

curl -X POST -H "Content-type: application/json" -d '{"name": "Fernando"}' http://localhost:8080/authors -u admin:@dm1n

O mesmo se aplica para alteração ou exclusão.

E aí o que achou do Spring Data Rest? Já o conhecia?

Entrevista com Arjen Poutsma, commiter do Spring!

Eu estava esperando ansiosamente para escrever este post, ele contém uma rápida entrevista com um dos principais commiters do Spring, Arjen! Ele respondeu algumas perguntas que fiz sobre o mundo que nos espera com a nova versão do framework, com o suporte integral ao conceito de Reactive Programming. Já vou deixar aqui a versão em inglês, depois posto a tradução.

Alberto: Just to introduce to our readers, could you talk a little bit about you and your role in the Spring Framework?

Arjen:  My name is Arjen Poutsma. I have been working on Spring for over ten years. I started out with Spring Web Services, then worked on Spring MVC – specifically the REST features, then Spring Scala, and for the last two years I have been working on Spring Reactive, together with Brian Clozel, Sebastien Deleuze, Stephane Maldini, abd Rossen Stoyanchev.

In this project, our team has been creating a new reactive foundation for Spring. We have essentially looked at the Spring MVC module, evaluated the code therein, and rewrote those pieces we need over to a non-blocking, event-driven style. We used Reactor as a foundation for this. The resulting code lives in the new spring-web-reactive module, next to the existing spring-webmvc module. The two modules share many concepts, but do not share any code. This is because Spring Web Reactive runs on a Reactive Streams HTTP adapter layer that’s fully non-blocking and reactive all the way down to the HTTP runtime. So while Spring MVC is built for and runs on Servlet containers, Spring Web Reactive runs also on non-Servlet runtimes such as Netty.

On top of the Spring Web Reactive module, we have two programming models. One is the existing annotation-based model, with @Controller, @RequestMapping and so on. These are the exact same annotations you use in Spring Web MVC. The other model is a recently introduced functional model, with RouterFunctions and HandlerFunctions.

I’ve attached a picture which hopefully makes this clearer.

architecture

Alberto: So, we are all excited about the new version of Spring, with the Reactive support. Could you tell us how this update will affect our way of programming?

Arjen: Just to be clear: upgrading to Spring 5 alone will not require any changes any changes to your code. As with previous major version upgrades, we have made sure that Spring 5 is backward compatible with Spring 4. So if you have a Spring 4 MVC application running on a Servlet engine, you will not have to change anything.

However, if you choose to use the new Reactive features, you will have to reconsider your architecture, as there any many differences. For instance, the guarantee that a HTTP request will be handled in a single thread is no longer the case: request will be handled by many threads, each doing a piece of the work when there is need for it (i.e. backpressure). Essentially, you are switching from a model a big thread pool, where most threads are waiting (either for incoming HTTP request, or waiting on a blocking datastore) to a model with a smaller thread pool, where each thread switches between requests many times and is effectively . Fortunately, you will not have to deal with these threads yourself, as this is where Reactor comes in. Reactor allows you to define your request handling pipeline in a functional, high-level abstraction through Flux and Mono. So rather than returning a List<Person> from your controller, for instance, you would return a Flux<Person>. However, this does also mean that relying on ThreadLocals will no longer work, and there are quite a number of frameworks and libraries (including Spring) that rely on these for some of their functionality.

Alberto: A lot of frameworks and libraries do not support this style of programming, Hibernate is an example. How can we combine our most used frameworks and Reactive Programming? The stack will evolve to support? I really like Spring Data JPA and I was thinking about that.

Arjen: Indeed, another difference between a traditional architecture and a reactive one is data stores. Obviously, in Reactive environments, there is a strong preference for non-blocking, asynchronous data stores. For some data stores, this is a great fit; for others it is not. There are a couple of NoSQL data stores that do reactive support: Couchbase, and MongoDB come to mind. For other, more traditional data access APIs, such as JDBC and JPA, there is no reactive support yet, so if you rely on them, there is no other choice than to wrap these synchronous, blocking APIs, and give them access to their own thread pool. Oracle is actually working on a non-blocking version of JDBC, but details are very sparse at this point. For JPA, the question is whether a session-based model actually makes sense in a reactive world.

Alberto: Could you explain how the Reactive Programming will help our applications to scale? Is there this relation between Reactive Programming and scalibility?

Arjen: I like to compare the two programming styles with an analogy. Imagine you are eating in a restaurant, and want to order food. The moment you make your order with the waiter, he goes off to the kitchen and prepare it. When the food is done, he will come back from the kitchen to serve it to you. After that, he will look around to see if there are any other customers in need of his attention. Now imagine the owner of the restaurant wants to have more tables, i.e. he wants to scale up. If he wants to make sure every table does not have to wait for too long, he would have to hire as many waiters as he added tables, adding a lot of cost. This is the traditional, one-thread-per-request Servlet model, where the customers are HTTP clients, and the waiters represent threads.

A reactive model would operate more like a real-life restaurant, where the waiters just relay the orders to kitchen staff, and many people (i.e. threads) are working on your order before it is served to you. Looking at it this way, it becomes quite obvious why the reactive model scales better: you are using the resources available more efficiently.

That said, the reactive model does come with a certain cost. It is harder to write asynchronous code, and it is harder to debug. So Reactive Programming will definitely help *certain* application to scale; for others I would simply say that the effort is simply not worth it. There will always be many cases where imperative is just fine for the task at hand and others where reactive and non-blocking are a must. And with Spring 5, you can do both.

Espero que você tenha gostado da entrevista! Arjen foi muito solícito e acho que nos forneceu boas respostas.