Lição aberta "Criando clientes REST no Spring"

E novamente, bom dia! Muito em breve, começaremos o treinamento para o próximo grupo, "Developer on the Spring Framework" , com o qual conduzimos uma lição aberta, que já se tornou uma tradição em antecipação ao lançamento. Neste seminário on-line, falamos sobre o desenvolvimento de clientes REST usando o Spring e também aprendemos em detalhes sobre tecnologias como Spring Cache, Spring Retry e Hystrix.

Palestrante: Yuri Dvorzhetsky - instrutor do Luxoft Training Center, desenvolvedor líder, candidato a ciências físicas e matemáticas.

O webinar foi assistido por um público completamente diferente, avaliando seu conhecimento sobre o Spring em 0 a 6 pontos em uma escala de 10 pontos; no entanto, a julgar pelas avaliações, a lição aberta parecia útil mesmo para usuários experientes.



Algumas palavras sobre a primavera 5

Como você sabe, o Spring Framework é um framework universal e bastante popular para a plataforma Java. O Spring consiste em uma massa de subprojetos ou módulos, o que permite resolver muitos problemas. De fato, esta é uma grande coleção de "estruturas em uma estrutura", por exemplo, apenas algumas delas:

  • Spring IoC + AOP = Contexto,
  • Spring JDBC,
  • Spring ORM,
  • Dados da Primavera (este é um conjunto completo de subprojetos),
  • Spring MVC, Spring WebFlux,
  • Segurança de primavera
  • Spring Cloud (este é um conjunto ainda maior de subprojetos)
  • Lote de primavera,
  • Bota de mola.


O Spring substitui a programação por algumas tarefas de configuração, mas a configuração às vezes se transforma apenas em um pesadelo. Para criar rapidamente aplicativos de nível de produção, eles usam o Spring Boot . Essa é uma estrutura especial que contém um conjunto de iniciantes ('iniciantes'), que simplifica a configuração das estruturas Spring e de outras tecnologias.

Para mostrar alguns dos recursos do Spring, o tema dos sites de bloqueio é perfeito, pois agora está na moda)). Se você quiser participar ativamente da lição e da prática, é recomendável fazer o download do repositório com o código do servidor sugerido pelo professor. Nós usamos o seguinte comando:

git clone git@github.com:ydvorzhetskiy/sb-server.git

Em seguida, basta executar, por exemplo, o seguinte:

mvnw spring-boot:run

A maior conquista do Spring Boot é a capacidade de iniciar o servidor simplesmente executando a classe Main no IntelliJ IDEA.

O arquivo BlockedSite.java contém nosso código-fonte:

 package ru.otus.demoserver.domain; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; @Entity public class BlockedSite { @Id @GeneratedValue private int id; private String url; 


E aqui está o conteúdo do controlador BlockedSitesController.java:

 package ru.otus.demoserver.rest; @RestController public class BlockedSitesController { private final Logger logger = LoggerFactory.getLogger(BlockedSitesController.class); private final BlockedSitesRepository repository; public BlockedSitesController(BlockedSitesRepository repository) { this.repository = repository; } @GetMapping("/blocked-sites") public List<BlockedSite> blockedSites() { logger.info("Request has been performed"); return repository.findAll(); } } 



Preste atenção também ao banco de dados aninhado em pom.xml:

  <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>ru.otus</groupId> <artifactId>demo-server</artifactId> <version>0.0.1-SNAPSHOT</version> <url>demo-server</url> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 

Agora, de uma maneira simples e despretensiosa, salvamos dois sites bloqueados (DemoServerApplication.java) em nosso banco de dados através do repositório:

 package ru.otus.demoserver; @SpringBootApplication public class DemoServerApplication { public static void main(String[] args) { ApplicationContext ctx = SpringApplication.run(DemoServerApplication.class, args); BlockedSitesRepository repository = ctx.getBean(BlockedSitesRepository.class); repository.save(new BlockedSite("https://telegram.org/")); repository.save(new BlockedSite("https://azure.microsoft.com/")); } } 

Resta iniciar o servidor usando o Spring Boot e abrir a URL apropriada no host local (localhost:8080/blocked-sites) . Ao mesmo tempo, nosso servidor retornará uma lista dos sites que bloqueamos, ou seja, aqueles que adicionamos ao banco de dados.

Bem, é hora de escrever um cliente neste servidor. Mas antes de prosseguir, você precisa se lembrar de algo.

Retiro teórico

Vamos listar alguns métodos HTTP (verbos):

  • GET - obtendo uma entidade ou lista;
  • POST - criação de entidade;
  • PUT - mudar de entidade;
  • PATCH - alteração de entidade (RFC -...);
  • DELETE - excluir entidade;
  • HEAD, OPTIONS - métodos “complicados” para suportar o protocolo HTTP e os serviços REST em geral;
  • TRACE é um método obsoleto que não é usado.

Não se pode deixar de recordar uma propriedade tão importante como a idempotência . Em termos simples, não importa quantas vezes você aplique uma operação, o resultado será o mesmo que se você a aplicasse apenas uma vez. Por exemplo, você cumprimentou um homem de manhã, dizendo "Olá!" Como resultado, seu amigo entra no estado "olá" :-). E se você disser "Olá!" Para ele várias vezes durante o dia, nada mudará, ele permanecerá no mesmo estado.

Agora, vamos pensar sobre quais dos métodos HTTP acima são idempotentes? Obviamente, entende-se que você observa a semântica. Se você não sabe, o professor fala sobre isso com mais detalhes a partir do 26º minuto do vídeo.

REST

Para escrever um controlador REST, você precisa se lembrar do que é REST:

  • REST - Transferência Representacional do Estado;
  • É um estilo arquitetônico, não um padrão;
  • é, de fato, um conjunto de princípios-restrições;
  • REST foi há muito tempo, mas o termo apareceu relativamente recentemente;
  • O aplicativo Web no estilo REST é chamado RESTful; nesse caso, sua API é a API RESTful (o antônimo é Stateful);
  • O REST agora é chamado como quiserem ...

Em primeiro lugar, se falamos de interação na forma de um cliente-servidor, ela precisa ser construída na forma de uma resposta de solicitação. Sim, a interação nem sempre é criada dessa maneira, mas agora essa interação é extremamente comum e, para aplicativos da Web, algo mais parece muito estranho. Mas, por exemplo, soquetes da Web - isso não é apenas REST.

Em segundo lugar, a limitação mais importante no REST é a falta de estado do cliente no servidor. Supõe-se que o cliente sempre transmita o estado necessário para o servidor a cada solicitação, ou seja, o estado é salvo no lado do cliente e não há sessões no servidor.

Como escrever um cliente no Spring

Para continuar, considere e execute o cliente (use o link para o repositório):

 git clone git@github.com:ydvorzhetskiy/sb-client.git 

 mvnw spring-boot:run 

Este é um aplicativo cliente e console já gravado, não um servidor web.

Examinamos as dependências:

 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>ru.otus</groupId> <artifactId>demo-client</artifactId> <version>0.0.1-SNAPSHOT</version> <url>demo-client</url> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <!--   RestTemplate,    - --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.1.4.RELEASE</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.8</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.8</version> </dependency> <!-- Cache --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!-- Retry --> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency> <!-- Hystrix --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> <version>2.0.2.RELEASE</version> </dependency> <dependency> <groupId>com.netflix.hystrix</groupId> <artifactId>hystrix-javanica</artifactId> <version>1.5.12</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project> 

O cliente tem uma configuração:

1. RestTemplateConfig.java

 package ru.otus.democlient.config; @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) { return restTemplateBuilder .setConnectTimeout(Duration.ofSeconds(2)) .setReadTimeout(Duration.ofSeconds(3)) .build(); } 

2. CacheConfig.java

 package ru.otus.democlient.config; @Configuration public class CacheConfig { @Bean public CacheManager cacheManager() { return new ConcurrentMapCacheManager("sites"); } } 

E aqui está o conteúdo do arquivo SiteServiceRest.java:

 package ru.otus.democlient.service; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.annotation.Cacheable; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.retry.annotation.Backoff; import org.springframework.retry.annotation.Retryable; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.Collections; import java.util.List; @Service public class SiteServiceRest implements SiteService { private final RestTemplate restTemplate; private final String serverUrl; public SiteServiceRest( RestTemplate restTemplate, @Value("${application.server.url}") String serverUrl ) { this.restTemplate = restTemplate; this.serverUrl = serverUrl; } @Override public List<SiteInfo> findAllBlockedSites() { return restTemplate.exchange( serverUrl + "/blocked-sites", HttpMethod.GET, null, new ParameterizedTypeReference<List<SiteInfo>>() { } ).getBody(); } public List<SiteInfo> getDefaultSites() { return Collections.singletonList(new SiteInfo() {{ setUrl("http://vk.com/"); }}); } } 

Resuma um pouco:

  1. Os pedidos são feitos através do RestTemplate.
  2. O RestTemplate pode ser personalizado, e esse é um bean comum.
  3. Jackson é usado para mapear JSON em objetos.
  4. Próximo - apenas o seu voo de fantasia (detalhes sobre o lançamento do cliente estão no vídeo).

Colegas, o webinar acabou sendo muito informativo; portanto, para não perder nada, é melhor assistir completamente. Você experimentará a API real "em combate", adicionará @Cacheable ao serviço, trabalhará com o Spring Retry, aprenderá sobre o Hystrix e muito mais. Também convidamos você para o Spring Open Day , que será realizado em breve.

E, como sempre, estamos aguardando seus comentários sobre a lição aberta do passado!

Source: https://habr.com/ru/post/pt440046/


All Articles