SPRING IN ACTION:春天在行动-PART 2

#「笔耕不辍」-生命不止,写作不息#

简介

春天指南:PART 1 FOUNFATIONAL SPRING: 此处从第一个玉米饼云应用程序开始。

目录

6 Creating REST services

7 Consuming REST services

8 Sending messages asynchronously

9 Integrating Spring


 

6 Creating REST services

浏览器不再是访问互联网的主要方式。移动设备,平板电脑,智能手表和基于语音的设备很常见。甚至许多基于浏览器的应用程序实际上都在运行JS应用程序,而不是让浏览器成为服务器渲染内容的终端。

服务器公开了一个API,使得各种客户端都可以通过该API与后端进行交互。本章拓展了从第二章开始的关于Spring MVC的讨论,查看如何在Spring中编写REST API。

6.1-编写RESTful控制器:

在定义REST API时,相同注释仍然会派上用场。此外,Spring MVC为各种类型的HTTP支持一些其他请求:

6.2-从服务器检索数据:

 @RestController注解:

有两个目的,首先它是一个类似@Controller和@Service的原型注解,用于标记一个类以供组件发现扫描。但与REST的讨论相关的是,@RestController注解告诉Spring控制器中的所有处理程序方法都应该有它们的返回值。返回值直接写入响应的主题,而不是在模型到视图进行渲染。

@Controller注解:

使用@Controller注解就像任何Spring MVC控制器一样。但是还需要注释所有使用@ResponseBody的处理程序方法来实现相同的结果。然后返回一个ResponseEntity实体。

@RequestMapping注解:

与@GetMapping一起使用来注释指定方法以负责处理GET请求。@RequestMapping注解还设置了一个生产属性。这不仅将API限制为仅生成JSON结果,还允许另一个控制器处理具有相同路径的请求。 

@CrossOrigin注解:

可以轻松应用CORS(跨源资源共享)。允许来自任何域的客户端使用API。

6.3-向服务器发送数据:

在适当的情况下使用@ResponseStatus向客户端传达最具描述性和最标准的HTTP状态码是一个好主意。

正常情况下(不抛出异常时),所有响应都会有一个HTTP状态码200(OK),表示请求成功。尽管HTTP 200响应总是受欢迎的,但它并不总是具有足够的描述性。对POST请求。HTTP 201(CREATED)更具有描述性。它告诉客户端不仅请求成功,而且创建了资源。

6.4-更新服务器上的数据:

虽然PUT确实经常用于更新资源数据,但是它实际上与GET语义相反。GET请求用于数据从服务器传输到客户端,而PUT请求用于将数据从客户端发送到服务端。从这个意义上来说,PUT真正的目的是执行批量替换操作,而不是更新操作。相反,HTTP PATCH的目的是对资源数据进行补丁或部分更新。

Spring MVC的mapping注解,包括@PatchMapping和@PutMapping,只指定一个方法应该处理什么样的请求。这些注释不规定如何处理请求。尽管在PATCH语义上暗示部分更新,但可以在处理程序中编写代码来实际执行此类更新。

6.5-从服务器中删除数据:

有时根本不再需要数据。在这种情况下,客户端应该能够通过HTTP DELETE请求删除数据。

6.6-启动超媒体:

API URL进行硬编码并对使用的字符串操作会是客户端代码变的脆弱。超媒体作为应用程序状态引擎,或HATEOAS,是一种创建自描述API的方法。从APU返回的资源包含指向相关资源的链接。这使客户端能够在对API的URL了解很少的情况下导航API。

如果API使用了超媒体,API将描述其自己的URL,从而使客户端无需使用该知识进行硬编码。

6.7-添加超链接:

Spring HATEAOS提供了两种超链接资源的主要类型,Resource和Resources。Resource类型表示单个资源,而另一个是资源的集合。 

这两种类型都能携带指向其他资源的链接。当从Spring MVC REST控制器方法返回时,它携带的链接将包含在客户端收到的JSON(或XML)中。

这里不需要对主机进行硬编码,也不需要指定路径。指向一个对应的Controller的链接就好了,基本路径根据自己的定义来。

6.8-创建资源组装器:

创建一个类,添加指向列表中所调用的资源所携带的元素。

6.9-命名嵌入关系:

@Relation注解可以帮忙打破JSON字段名称和Java中定义的资源类型名称之间的耦合。通过使用此注释,可以指定Spring HATEOAS应如何命名结果JSON中的字段。

6.10-启用数据支持的服务:

在第三章可知,Spring Data通过根据在代码中定义的接口自动创建存储库实现来执行任务。Spring Data另一个功能可以帮助应用程序定义API。

Spring Data REST是Spring Data系列的另一个成员。它自动为Spring Data创建的存储库创建REST API。只需将Spring Data REST添加到构建中,就可以获得一个API,其中包含定义的每一个存储库的接口的操作。

开始使用Spring Data REST必须要添加依赖。将Spring Data用于自动存储库的项目公开REST API所需的全部内容:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>

6.11-调整资源路径的关系名称:

实际上,Spring Data REST确实提供了一个使用资源的端点。但是尽管Spring Data REST可能很聪明,但它在暴露资源端点的方式上表现的不那么出色。

事实上,Spring Data REST还公开了一个home资源,该资源具有所有公开端点的链接。e.g:只需向API基本路径发出GET请求即可获得商品。通过向实体类添加一个简单的注释,就可以调整关系名称和路径。@RestResource注释可以提供所需的任何关系名称和路径。

6.12-分页和排序:

可以通过在请求中指定的page和size参数来调整页面大小和显示的页面。sort参数允许按实体的任何属性对结果列表进行排序。

6.13-添加自定义端点:

Spring Data REST擅长创建端点以对Spring Data存储库执行CRUD操作。也可以脱离默认的CRUD API并创建一个能够触及问题核心的端点。因为没有什么能够阻止在带有@RestController注释的bean中实现想要的任何端点。

端点分离:如果控制器端点未映射到Spring Data REST的基本路径下,可以强制将它们映射到基本路径。如果基本路径更改,需要编辑控制器的映射以匹配。

@RepositoryRestController的命名与@restController相似,但它的语义与@RestController不同。它不能保证从处理程序方法返回的值自动写入响应正文。因此,需要使用@ResponseBody注释方法或返回包装响应数据的ResponseEntity。

6.14-向Spring Data端点添加自定义超链接:

Spring Data HATEOAS提供ResiurceProcessor,这是一个用于通过API返回资源之前对其进行操作的接口。

6.15-REST端点:

REST端点可以使用Spring MVC创建,其控制器遵循与面向浏览器的控制器相同的编程模式。控制器处理程序方法可以使用@ResponseBody注释或返回ResponseEntity对象以绕过模型并直接查看数据并写入响应正文。

@RestController注解简化了REST控制器,消除了在处理程序方法上使用@ResponseBody的需要。Spring HATEOAS支持从Spring MVC控制器返回的资源的超链接。Spring Data存储库可以使用Spring Data REST自动公开为REST API。

7 Consuming REST services

本章展示Spring应用程序如何使用REST API。Spring应用程序可以使用REST API Rest Template一个简单的同步REST客户端,由核心Spring框架。Tracerson Spring提供的超链接感知,同步REST客户端。

7.1-使用RestTemplate使用REST端点:

使用低级 HTTP库,客户端需要创建一个客户端示例和一个请求对象,执行请求,解释响应,将响应映射到域对象,并处理任何异常,也可能将响应内容仍在路上。

RestTemplate提供了41种与REST资源交互方法。表中大部分方法都被重载为三种方法形式:

        接受一个字符串URL规范,其URL参数在变量参数列表。

        接受一个字符串URL规范,参数在map<字符串,字符串>。

        接受java.net.URI作为URL规范,不支持参数化URL。

 7.2-获取资源:

这里URI对象是根据String规范定义的,它的占位符是从Map中的条目中填充的。ResponseEntity允许访问其他响应的详细信息。

 public Ingredient getIngredientById(String ingredientId) {
          ResponseEntity<Ingredient> responseEntity =
              rest.getForEntity("http://localhost:8080/ingredients/{id}",
                  Ingredient.class, ingredientId);
          log.info("Fetched time: " +
                  responseEntity.getHeaders().getDate());
          return responseEntity.getBody();
}

7.3-PUT资源:

为了发送HTTP PUT请求,RestTemplate提供了put()方法。此方法的所有三个重载变体都接受序列化并发送到给定URL的Object。至于URL本身,可以指定为URI对象,也可以指定为String。URL变量可以作为变量参数列表或Map提供。

7.4-DELETE资源:

从RestTemplate调用delete()方法。

7.5-POST资源:

RestTemplate有三种发送POST请求的方式,每种方式都用相同的重载变体来指定URL。

7.6-RestTemplate:

客户端可以使用RestTemplate针对REST API发出HTTP请求。Traverson是客户端能够使用嵌入在响应中。

8 Sending messages asynchronously

本章将研究使用异步通信来启用Spring应用程序,使用Java Message发送和接收消息服务(JMS),RabbitMQ和Kafka。异步消息传递是一种将消息从一个应用程序间接发送到另一个等待消息的端。这种间接的方式提供了松散的耦合和通信应用程序之间更大的扩展性。

异步消息传递在通信应用程序之间提供了一个间接层,这允许更松散的耦合和更大的可伸缩性。

        Spring支持使用JMS,RabbitMQ或Apache Kafka进行异步消息传递。应用程序可以使用基于模版的客户端(JmsTemplate,RabbitTemplate或KafkaTemplate)通过消息代理发送消息。

        接收应用程序可以使用基于pull model来使用消息相同的基于模版的客户端。消息也可以通过将消息监听器注释(@JmsListener,@RabbitListener,@KafkaListener)应用到bean方法来推送给消费者。

8.1-使用JMS发送消息:

它是一种Java标准,定义了用于处理消息代理的通用API。使用JMS,所有兼容的实现都可以通过一个通用的接口方式。此方式与JDBC提供关系数据库操作相似。

消息驱动POJO概念:以异步方式对到达队列或主题的消息作出反应。

8.2-设置JMS:

在构建中添加启动器依赖项。在此之前,需要决定是否使用Apache ActiveMQ或更新的Apache ActiveMQ Artemis代理。Artemis是Active MQ的下一代重新实现,有效的使ActiveMQ成为传统选项。

如果使用Apache ActiveMQ:

 <dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-activemq</artifactId>
 </dependency>

如果使用ActiveMQ Artemis:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-artemis</artifactId>
</dependency>

默认情况下,Spring假定Artemis代理正在监听localhost的61616端口。同时,可以设置一些属性来告诉Spring如何访问。下表为可能有用的属性:

8.3-使用Jms Template发送消息:

在构建中使用JMS启动器依赖项(Artemis或ActiveMQ),Spring Boot将自动配置一个Jms template,可以注入和用于发送和接受消息。

Jms Template是Spring的JMS集成支持的核心。很像Spring的其他面向模版的组件Jms Template消除了许多样板代码。

如以下代码所示,实际上只有两种方法, send() 和convertAndSend()。每个方法都被重写以支持不用的参数。三个send()方法需要MessageCreator来制造Message对象。三个convertAndSend()方法接受一个Object并在后台自动将该Object转换为Message。三个convertAndSend()方法自动将Object转换为Message,但也接受MessagePostProcessor以允许在发送Message之前对其进行自定义。

// Send raw messages
void send(MessageCreator messageCreator) throws JmsException;
void send(Destination destination, MessageCreator messageCreator)
                                                throws JmsException;
void send(String destinationName, MessageCreator messageCreator)
                                                throws JmsException;

// Send messages converted from objects
void convertAndSend(Object message) throws JmsException;
void convertAndSend(Destination destination, Object message)
                                                throws JmsException;
void convertAndSend(String destinationName, Object message)
                                                throws JmsException;

// Send messages converted from objects with post-processing
void convertAndSend(Object message,
            MessagePostProcessor postProcessor) throws JmsException;
void convertAndSend(Destination destination, Object message,
            MessagePostProcessor postProcessor) throws JmsException;
void convertAndSend(String destinationName, Object message,
            MessagePostProcessor postProcessor) throws JmsException;

8.4-发送前转换消息:

Jms Tsmplate的convertAndSend()方法通过消除提供MessageCreator的需要来简化消息发布。相似于send()方法,convertAndSend()方法将接受Destination或String值来指定目的地,或可以安全省略目的地以将消息发送到默认的目的地。

8.5-配置消息转换器:

MessageConverter是Spring定义的接口。通常不需要创建它,Spring已经提供了一些实现:

8.6-后处理消息:

使用特定的MessagePostProcessor来调用convertAndSend()。

8.7-接受JMS消息:

JmsTemplate提供了几种接受消息的方法,它们都是pull model。调用其中一个方法来请求消息,并且线程会阻塞,知道有消息可用。

也可以使用push model。定义在消息可用时调用消息的监听器。人们普遍认为push model是最佳选择,因为它不会阻塞线程。但在某些用例中,如果消息到达太快,监听器可能会负担过重。pull model使消费者能够声明好他们准备好处理的新消息。

8.8-JMS Template接受:

它提供了几种从代理中提取方法的方法。这六个方法反映了Jms Template中的send()和convertAndSend()方法。receive()方法接受原始消息,而receiverAndConvert()方法使用配置的消息转换器转换为域类型。对其中的每一个,可以指定Destination或包含目标名称的String,或者可以从默认目标中提取消息。

Message receive() throws JmsException;
Message receive(Destination destination) throws JmsException;
Message receive(String destinationName) throws JmsException;
Object receiveAndConvert() throws JmsException;
Object receiveAndConvert(Destination destination) throws JmsException;
Object receiveAndConvert(String destinationName) throws JmsException;

8.9-声明消息监听器:

与pull model不同,需要显式调用receive()或receiveAndConvert()来接收消息。消息监听器是一个被动件,在消息到达之前处于空闲状态。

要创建对JMS消息作出反应的消息监听器,需要使用@JmsListener注释组件中的方法。它类似于Spring MVC的请求映射注解之一,例如@GetMapping。在Spring MVC中,使用请求映射方法之一注释的方法对指定的请求做出反应。同样,使用@JmsListener注释的方法会对到达目的地的消息作出反应。

 消息监听适合快速处理多条消息。但是当消息处理程序需要能够根据自己的时间请求更多消息,JmsTemplate提供的pull model似乎更合适。因为JMS是标准Java规范定义的,并且被许多消息代理实现。它的主要缺点是仅限于Java应用程序。较新的消息传递选项,例如RabbitMQ和Kafka解决了这些缺点。

8.10-使用RabbitMQ和AMQP:

作为AMQP最突出的实现,RabbitMQ提供了更多的比JMS先进的消息路由策略。当消息到达RabbitMQ代理时,它会转到它为之交换得到解决。交换器负责将器路由到一个或多个队列。取决于交换的类型,交换和队列之间的绑定以及消息的路由键和值。

几种不同类型的交换。分别为默认(有broker自动创建。它将消息路由到名称与消息的路由键相同的队列。所有队列将自动绑定到默认交换);直接(将消息路由到绑定键与消息的路由键相同的队列);主题(将消息路由到一个或多个队列,其中消息的路由键匹配)... 

 要理解最重要的事情是消息被发送到带有路由键的交换器,并且它们是从队列中消耗的。它们如何从交换器到队列取决于绑定的定义以及最合适的用例方式。

8.11-将RabbitMQ添加到Spring:

使用Spring发送和接收RabbitMQ消息之前,需要将Spring Boot的AMQP启动器依赖项添加到构建中。然后触发自动配置,这将创建一个AMQP连接工厂和RabbitTemplate bean以及其他支持成分。

只需添加依赖即可开始发送和使用Spring从RabbitMQ代理接收消息。以下为部分可能需要了解的有用属性:

8.12-使用RabbitMQ发送消息:

Spring支持RabbitMQ消息传递的核心是RabbitTemplate。它是与JmaTemplate类似的,提供一组类似的方法。不同的是send()和convertAndSend()方法,根据交换和路由键发送消息。

8.13-配置消息转换器:

默认情况下,使用SimpleMessageConverter执行消息转换,它能够将简单类型(如String)和Serializable对象转换为Message对象。但是Spring为RabbitTemplate提供了几个消息转换器:

8.14-设置消息属性:

与JMS一样, 可能需要在发送的消息中设置一些header。创建自己的Message对象时,可以通过提供给消息转换器的MessageProperties实例来设置header。 

8.15-从RabbitMQ接收消息:

RabbitMQ队列接收消息与JMS接收消息没有太大区别。在这里有两种选择:

        使用RabbitTemplate从队列中提取消息

        将消息推送到@RabbitListener注释的方法

8.16-基于pull的RabbitTemplate.receiver()方法:

send()用于发送原始的Message对象,receive()从队列中接收原始的Message对象。同样,receiverAndConverter()接收消息并使用消息转换器将它们转换为域对象,然后再返回它们。对receive()的调用将立即返回,如果没有可用的信息,则可能返回null。

RabbiteMQListener,它是RabbitMQ与JmsLisener的对应物。要指定当消息到达RabbitMQ队列时应调用的方法。

8.17-使用Kafka进行消息传递:

它是一个消息代理,就像ActiveMQ,Artemis或Rabbit一样。它的设计在集群中运行,可提供出色的可拓展性。通过在集群中的所有实例上划分其主题,它是理货的。Rabbit MQ主要处理交换中的队列。而Kafka仅被用来使用主题来提供发布/订阅消息。

Kafka主题在集群中的所有代理之间复制。集群中的每个节点都充当一个或多个主题的leader,负责该主题的数据并将其复制到集群中的其他节点。

8.18-为Kafka消息传递设置Spring:

 在开始使用Kafka进行消息传递,需要将适当的依赖添加哦到构建中。然而与JMS和RabbitMQ不同,Kafka没有Spring Boot启动器。因此只需要一个依赖项。它的存在将出发Kafka的Spring Boot自动配置。除此之外,它将在Spring应用程序上下文中安排KafkaTemplate:

<dependency>
 <groupId>org.springframework.kafka</groupId>
 <artifactId>spring-kafka</artifactId>
</dependency>

KafkaTemplate默认使用本地机上的Kafka代理,监听端口9092.在开发应用程序的时候,在本地启动Kafka代理很好,但是需要投入生产时,需要配置不同的主机和端口。spring.kafka.bootstrap-server属性设置一个或多个Kafka服务器的位置,用于创建与Kafka集群的初始连接。

8.19-使用KafkaTemplate发送消息:

Kafka没有convertAndSend()方法。因为KafkaTemplate使用泛型输入并且能够处理域发送消息时直接输入。在某种程度上,所有的send()方法都在做convertAndSend()的工作。对于send()方法,可以选择发送ProducerRecord,它只不过是一种在单个对象中捕获所有前面参数的类型。也可以发送Message对象,但这样做需要将域对象转换为Message。通常,使用其他方法之一比创建和发送ProducerRecord或Message对象更容易。

8.20-编写Kafka监听器:

Kafka不提供任何接收消息的方法。这意味着使用Spring来Kafka主题的消息的唯一方法是编写消息监听器。

对于Kafka,消息监听器被定义为使用@KafkaListener注释的方法。此注释大致类似于@JmsListener和@RabbitListener,并使用方式大致相同。

9 Integrating Spring

本章讨论声明式使用Spring Integration项目进行应用程序的集成。处理数据,定义集成流程以及外部集成电子邮件和文件系统等。这里,列出Spring Integration中使用常见的集成模式。Spring Integration是Gregor Hohpe和Bpbby Woolf在Enterprise Integration Patterns中分类的许多集成模式的即用型实现。每个模式作为一个组件实现。消息通过该组件在管道中传送数据。使用Spring配置,可以将这些组件组装成数据流的pipeline。

9.1-声明一个简单的集成流程:

一般来说,Spring Integration支持创建集成流。应用程序可以通过这些集成流接收或发送数据到应用程序本身外部的某些资源。应用程序可以与之集成的一种资源是文件系统。

创建一个数据写入文件系统的集成流。将Spring Integration的依赖添加到构建中。第一个依赖是Spring Integration的Spring Boot Starter。第二个依赖是文件端点模块。该模块用于与外部系统集成的两端点模块之一。

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-integration</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.integration</groupId>
  <artifactId>spring-integration-file</artifactId>
</dependency>

@MessageGateway的defaultRequestChannel属性指示调用接口方法产生的任何消息都应该发送到给定的消息通道。

在有消息网关之后,需要配置集成流。尽管添加到构建中的Spring Integration启动器依赖项启用了Spring Integration的基本自动配置,但仍需要编写其他配置来定义满足应用程序需求的流。用于声明集成流的是那个配置选项为:

9.2-使用XML定义集成流:

配置一个名为textInChannel的通道。接收消息的转换器。它使用Spring表达式语言(SpEL)调用toUpperCase()在消息有效负载上。然后将大写操作结果发布到fileWriterChannel。 fileWriterChannel通道作为连接变压器与出站通道适配器的conduit。最后使用int文件名称空间配置出站通道适配器。 

9.3-使用Java配置集成流:

 使用Java配置,需要声明两个bean:一个转换器或一个文件写入消息处理程序。文件写入bean,使用@ServiceActivator注释表示。它将接收来自fileWriterChannel的消息并将这些消息交给FileWritingMessageHandler实例定义的服务。

9.4-使用Spring Integration的DSL配置:

使用此种方法,不需要为流程中的每一个组件声明一个独特的bean,而是声明一个定义整个流程的bean。

9.2-Spring Integration环境:

集成流由以下一个或多个组件组成。分别为通道,过滤器,转换器,路由器,拆分器,聚合器,服务激活器,通道适配器,网关。

9.3-消息通道:

是消息通过集成移动的方式管道。它将Spring集成管道的所有其他部分连接在一起。 

9.4-过滤器:

可以放置在集成管道中,以允许或禁止消息进入流程的下一步。

9.5-变压器:

Transformer对消息执行一些操作,通常会产生不同的消息,并且可能具有不用的有效负载类型。

9.6-路由器:

它基于某些路由标准,允许在集成流中进行分支,将消息引导到不同的通道。

9.7-拆分器:

在集成流程中,将消息拆分为多个消息以独立处理是可能有用的。它在许多情况下都很有用,但有两个基本用例可以使用拆分器:

        消息负载包含作为单个消费负载处理的相同类型的项目集合。

        消息有效负载携带的信息虽然相关,但可以分为两个或多个不同类型的消息。

9.8-服务激活器:

它从输入通道接收消息并将这些消息传到MessageHandler中实现。

 9.9-网关:

 它是应用程序可以将数据提交到集成中的方法流,并且接收作为流结果的响应。实施者Spring Integration,网关被实现为应用程序可以调用的接口向集成流发送消息。

9.10-通道适配器:

它代表集成流的入口和出口点。数据通过入站通道适配器进入集成流,并通关出站通道适配器退出集成流。入站通道适配器可以采用多种形式,具体取决于它们引入流的数据源。

9.11-端点模块:

 以下为Spring Integration提供的广泛的组件集,可以满足多种集成的需求。

 9.12-创建电子邮件集成流程:

相关阅读:

Walls, C., 2022. Spring in action. Simon and Schuster.

IService.java: IService.java example

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值