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 5Como 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/> </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óricoVamos 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.
RESTPara 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/> </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> <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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <dependency> <groupId>org.springframework.retry</groupId> <artifactId>spring-retry</artifactId> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> </dependency> <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:- Os pedidos são feitos através do RestTemplate.
- O RestTemplate pode ser personalizado, e esse é um bean comum.
- Jackson é usado para mapear JSON em objetos.
- 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!