公开课“在Spring上创建REST客户端”

再次,美好的一天! 很快,我们将开始为下一组“ Spring框架开发人员”进行培训,与此相关的是我们进行了公开课程,这已经成为人们期望启动的传统。 在这次网络研讨会上,我们讨论了使用Spring开发REST客户端的问题,还详细了解了诸如Spring Cache,Spring Retry和Hystrix等技术。

讲师: Yuri Dvorzhetsky -Luxoft培训中心的培训师,首席开发人员,物理和数学科学的候选人。

完全不同的观众访问了网络研讨会,以10分制在0-6分之间评估了他们对Spring的了解,但是从评论的角度来看,公开课程对于经验丰富的用户似乎也很有用。



关于Spring 5的几句话

如您所知,Spring框架是Java平台的通用且相当流行的框架。 Spring由大量子项目或模块组成,这使您可以解决许多问题。 实际上,这是“框架中的框架”的大集合,例如,其中一些:

  • Spring IoC + AOP =上下文,
  • Spring JDBC,
  • 春季ORM,
  • Spring Data(这是一整套子项目),
  • Spring MVC,Spring WebFlux,
  • 春季安全
  • Spring Cloud(这是更大的子项目集)
  • 春季批,
  • 春季引导。


Spring用一些用于配置的任务代替了编程,但是配置有时变成了一场噩梦。 为了快速创建生产级应用程序,他们使用Spring Boot 。 这是一个特殊的框架,其中包含一组启动程序(“启动程序”),可简化Spring框架和其他技术的配置。

为了显示Spring的某些功能,阻止站点的主题非常完美,因为它现在很流行))。 如果您想积极参与课程和练习,建议下载包含教师建议的服务器代码的存储库 。 我们使用以下命令:

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

接下来,例如运行,例如:

mvnw spring-boot:run

Spring Boot的最大成就是能够通过简单地在IntelliJ IDEA中运行Main类来启动服务器。

BlockedSite.java文件包含我们的源代码:

 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; 


这是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(); } } 



还请注意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> 

现在,我们以一种简单,朴实的方式通过存储库将两个被阻止的站点(DemoServerApplication.java)保存到我们的数据库中:

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

仍然可以使用Spring Boot启动服务器并在本地主机上打开相应的URL (localhost:8080/blocked-sites) 。 同时,我们的服务器将向我们返回被阻止站点的列表,即我们通过数据库添加的站点。

好了,是时候将客户端写入此服务器了。 但是在继续之前,您需要记住一些事情。

理论撤退

让我们列出一些HTTP方法(动词):

  • GET-获取实体或列表;
  • POST-建立实体;
  • PUT-变更实体;
  • PATCH-实体变更(RFC -...);
  • DELETE-删除实体;
  • HEAD,OPTIONS-通常支持HTTP协议和REST服务的“棘手”方法;
  • TRACE是未使用的过时方法。

人们只能回想起等性这一重要属性。 简而言之,无论您应用操作多少次,其结果都将与仅应用一次的结果相同。 例如,您早上打招呼,说“你好!” 结果,您的朋友进入“ hello”状态:-)。 如果您在白天多次对他说“你好!”,他什么也不会改变,他将保持原状。

现在,让我们考虑一下以上哪种HTTP方法是幂等的? 当然,可以理解您会观察语义。 如果您不知道,那么老师将从视频的第26分钟开始详细讨论。

休息

为了编写REST控制器,您需要记住REST是什么:

  • REST-代表性状态转移;
  • 这是一种建筑风格,而不是一种标准。
  • 实际上,这是一组原则限制;
  • REST是很久以前的,但是这个词相对较新出现。
  • REST风格的Web应用程序称为RESTful,在这种情况下,其API为RESTful API(反义词为Stateful)。
  • REST现在被称为他们想要的...

首先,如果我们以客户端-服务器的形式讨论交互,则需要以请求-响应的形式构建交互。 是的,交互并非总是以这种方式构建的,但是现在这种交互非常普遍,对于Web应用程序,其他东西看起来很奇怪。 但是,例如,Web套接字-这不是REST。

其次,REST中最重要的限制是服务器上缺少客户端状态。 假定客户端始终随每个请求将必要的状态传递给服务器,即状态保存在客户端,并且服务器上没有会话。

如何在Spring中编写客户端

要继续,请考虑并运行客户端(使用存储库的链接):

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

 mvnw spring-boot:run 

这是一个已经编写的客户端和控制台应用程序,而不是Web服务器。

我们看一下依赖关系:

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

客户端有一个配置:

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

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

简要总结一下:

  1. 通过RestTemplate发出请求。
  2. RestTemplate可以自定义,这是一个常规bean。
  3. Jackson用于将JSON映射到对象中。
  4. 下一步-仅看中您的想法(视频中介绍了启动客户端的详细信息)。

同事们,这次网络研讨会非常有用,因此,为了不遗漏任何内容,最好完整地观看。 您将“在战斗中”尝试真正的API,将@Cacheable添加到服务中,使用Spring Retry,了解有关Hystrix的更多信息。 我们还邀请您参加即将举行的春季开放日

而且,像往常一样,我们正在等待您对上一堂公开课的评论!

Source: https://habr.com/ru/post/zh-CN440046/


All Articles