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?
É muito bom saber que vcs não acabaram com o blog. Acompanho sempre as postagens de vcs, parabéns!!!
LikeLiked by 1 person
Oi João, a falta de postagem vem por conta de fatores da vida pessoal mesmo :), mas não acabamos não! Vamos tentar postar de vez em quando, o legal é que ele já tem muito conteúdo, então ainda vem ajudando muita gente.
LikeLiked by 1 person
Excelente conteudo amigo! Meu Parabéns
LikeLike
Reblogged this on Marcelo Nobre – Tecnologia Java and commented:
Excelente post sobre webservice.
LikeLiked by 1 person
até que enfim achei algo bom como o retrofit, vlw cara continue com as postagens, bjs de luz
LikeLiked by 1 person
Excelente artigo. Valeu pelas dicas e continuem com as postagens sobre Spring rs.
LikeLike
Feign + Ribbon + Zuul + Eureka = Vida
LikeLike
Feign + Ribbon + Zuul + Eureka = Netflix
LikeLike
Olá! Alguém saberia me dizer se o uso do Feign e do RestTemplate implicam em composição baseada em coreografia dos microsserviços?
LikeLike
Olá Elena tudo bem?
O Feign e o RestTemplate são somente clientes http, são só uma forma de você conseguir fazer requisições.
Para coreografia o spring tem um projeto para isso chamado Spring Hateoas.
LikeLike
Excelente post! Não conhecia o Feign, agora já estou louco para usar kkkkk..abraços!
LikeLike
Parabéns pelo post, vou usar nos próximos projeto.
só corrigido um ponto, acredito que na digitação (R => T)
RestTemplate template = new RestRemplate();
LikeLike
Os atributos da classe Endereço devem ter o mesmo nome do atributos que virão do json do ViaCep?
LikeLike
Olá Matheus,
Por padrão o Jackson vai usar os nomes dos atributos da classe para fazer o bind com os fields do json. Nesse caso precisam ser iguais.
Porém posso informar para o Jackson (via anotação) qual o nome do field do json que deve fazer o bind com a propriedade.
por exemplo:
Class Address {
@JsonProperty(“logradouro”)
private String street;
@JsonProperty(“cep”)
private String zipCode;
// getters e setters
}
LikeLike