Lecci贸n abierta "Crear clientes REST en Spring"

Y de nuevo, buen d铆a! Muy pronto, comenzaremos a entrenar para el pr贸ximo grupo, "Desarrollador en el Marco de Primavera" , en relaci贸n con el cual llevamos a cabo una lecci贸n abierta, que ya se ha convertido en una tradici贸n en previsi贸n del lanzamiento. En este seminario web, hablamos sobre el desarrollo de clientes REST usando Spring, y tambi茅n aprendimos en detalle sobre tecnolog铆as como Spring Cache, Spring Retry e Hystrix.

Profesor: Yuri Dvorzhetsky - entrenador en el Centro de formaci贸n de Luxoft, desarrollador principal, candidato de ciencias f铆sicas y matem谩ticas.

Al seminario web asisti贸 una audiencia completamente diferente, evaluando su conocimiento de Spring dentro de 0-6 puntos en una escala de 10 puntos, sin embargo, a juzgar por las revisiones, la lecci贸n abierta parec铆a 煤til incluso para usuarios experimentados.



Algunas palabras sobre la primavera 5

Como saben, Spring Framework es un marco universal y bastante popular para la plataforma Java. Spring consiste en una masa de subproyectos o m贸dulos, lo que le permite resolver muchos problemas. De hecho, esta es una gran colecci贸n de "marcos en un marco", por ejemplo, solo algunos de ellos:

  • Spring IoC + AOP = Contexto,
  • Spring JDBC,
  • Spring ORM,
  • Spring Data (este es un conjunto completo de subproyectos),
  • Spring MVC, Spring WebFlux,
  • Seguridad de primavera
  • Spring Cloud (este es un conjunto a煤n m谩s grande de subproyectos)
  • Lote de primavera,
  • Bota de primavera.


Spring reemplaza la programaci贸n con algunas tareas para la configuraci贸n, pero la configuraci贸n a veces se convierte en una pesadilla. Para crear r谩pidamente aplicaciones de grado de producci贸n, utilizan Spring Boot . Este es un marco especial que contiene un conjunto de iniciadores ('iniciador'), que simplifican la configuraci贸n de los marcos Spring y otras tecnolog铆as.

Para mostrar algunas de las caracter铆sticas de Spring, el tema del bloqueo de sitios es perfecto, ya que ahora est谩 de moda)). Si desea participar activamente en la lecci贸n y la pr谩ctica, se recomienda descargar el repositorio con el c贸digo del servidor que sugiri贸 el profesor. Usamos el siguiente comando:

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

A continuaci贸n, simplemente ejecute, por ejemplo, as铆:

mvnw spring-boot:run

El mayor logro de Spring Boot es la capacidad de iniciar el servidor simplemente ejecutando la clase Main en IntelliJ IDEA.

El archivo BlockedSite.java contiene nuestro c贸digo fuente:

 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; 


Y aqu铆 est谩 el contenido del 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(); } } 



Tambi茅n preste atenci贸n a la base de datos anidada en 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> 

Ahora, de una manera simple y sin pretensiones, guardamos dos sitios bloqueados (DemoServerApplication.java) en nuestra base de datos a trav茅s del repositorio:

 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/")); } } 

Queda por iniciar el servidor usando Spring Boot y abrir la URL apropiada en el host local (localhost:8080/blocked-sites) . Al mismo tiempo, nuestro servidor nos devolver谩 una lista de los sitios que bloqueamos, es decir, aquellos sitios que agregamos a trav茅s de la base de datos.

Bueno, es hora de escribir un cliente en este servidor. Pero antes de continuar con esto, debe recordar algo.

Retiro te贸rico

Enumeremos algunos m茅todos HTTP (verbos):

  • GET: obtener una entidad o lista;
  • POST - creaci贸n de entidad;
  • PUT - entidad de cambio;
  • PARCHE - cambio de entidad (RFC -...);
  • BORRAR - borrar entidad;
  • HEAD, OPTIONS: m茅todos "dif铆ciles" para admitir el protocolo HTTP y los servicios REST en general;
  • TRACE es un m茅todo obsoleto que no se utiliza.

Uno no puede dejar de recordar una propiedad tan importante como la idempotencia . En t茅rminos simples, no importa cu谩ntas veces aplique una operaci贸n, su resultado ser谩 el mismo que si la aplicara solo una vez. Por ejemplo, saludaste a un hombre por la ma帽ana, diciendo "隆Hola!" Como resultado, tu amigo entra en un estado de "hola" :-). Y si le dices "隆Hola!" Varias veces durante el d铆a, nada cambiar谩, permanecer谩 en el mismo estado.

Ahora, pensemos cu谩l de los m茅todos HTTP anteriores es idempotente. Por supuesto, se entiende que observa la sem谩ntica. Si no lo sabe, el profesor le contar谩 m谩s sobre esto, a partir del minuto 26 del video.

DESCANSO

Para escribir un controlador REST, debe recordar qu茅 es REST:

  • RESTO - Transferencia de estado representativa;
  • Es un estilo arquitect贸nico, no un est谩ndar;
  • es, de hecho, un conjunto de principios-restricciones;
  • REST fue hace mucho tiempo, pero el t茅rmino apareci贸 relativamente recientemente;
  • La aplicaci贸n web de estilo REST se llama RESTful, su API en este caso es la API RESTful (el ant贸nimo es Stateful);
  • REST ahora se llama lo que quieran ...

En primer lugar, si hablamos de interacci贸n en forma de un cliente-servidor, entonces debe construirse en forma de una solicitud-respuesta. S铆, la interacci贸n no siempre se construye de esta manera, pero ahora esta interacci贸n es extremadamente com煤n, y para las aplicaciones web, algo m谩s parece muy extra帽o. Pero, por ejemplo, los sockets web, esto simplemente no es REST.

En segundo lugar, la limitaci贸n m谩s importante en REST es la falta de estado del cliente en el servidor. Se supone que el cliente siempre pasa el estado necesario al servidor con cada solicitud, es decir, el estado se guarda en el lado del cliente y no hay sesiones en el servidor.

C贸mo escribir un cliente en primavera

Para continuar, considere y ejecute el cliente (use el enlace al repositorio):

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

 mvnw spring-boot:run 

Esta es una aplicaci贸n de consola y cliente ya escrita, no un servidor web.

Nos fijamos en las dependencias:

 <?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> 

El cliente tiene una configuraci贸n:

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"); } } 

Y aqu铆 est谩 el contenido del archivo 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/"); }}); } } 

Resumir un poco:

  1. Las solicitudes se realizan a trav茅s de RestTemplate.
  2. RestTemplate se puede personalizar, y este es un bean normal.
  3. Jackson se usa para mapear JSON en objetos.
  4. A continuaci贸n, solo su vuelo de fantas铆a (los detalles sobre el lanzamiento del cliente est谩n en el video).

Colegas, el seminario web result贸 ser muy informativo, por lo tanto, para no perderse nada, es mejor verlo en su totalidad. @Cacheable la API real "en combate", agregar谩 @Cacheable al servicio, trabajar谩 con Spring Retry, aprender谩 sobre Hystrix y mucho m谩s. Tambi茅n te invitamos a la Jornada de Puertas Abiertas de Primavera, que se realizar谩 muy pronto.

Y, como siempre, 隆esperamos sus comentarios sobre la lecci贸n abierta del pasado!

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


All Articles