通过Spring集成进行消息处理

Spring Integration提供了Spring框架的扩展,以支持著名的企业集成模式。 它在基于Spring的应用程序中启用轻量级消息传递,并支持与外部系统的集成。 Spring Integration的最重要目标之一是为构建可维护且可测试的企业集成解决方案提供一个简单的模型。

主要成分

消息:它是任何Java对象的通用包装器,结合了框架在处理该对象时使用的元数据。 它由有效负载和标头组成。 消息有效负载可以是任何Java对象,消息头是字符串/对象映射,其中包含头名称和值。 MessageBuilder用于创建覆盖有效载荷和标头的消息,如下所示:

import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;Message message = MessageBuilder.withPayload("Message Payload").setHeader("Message_Header1", "Message_Header1_Value").setHeader("Message_Header2", "Message_Header2_Value").build();

消息通道:消息通道是通过其移动消息的组件,因此可以将其视为消息生产者和使用者之间的管道。 生产者将消息发送到渠道,而消费者从渠道接收消息。 消息通道可以遵循点对点或发布/订阅语义。 使用点对点通道,最多一个消费者可以接收发送到该通道的每条消息。 使用发布/订阅频道,多个订阅者可以接收发送到该频道的每个消息。 Spring Integration支持这两种方式。

在此示例项目中,使用直接通道和空通道。 直接通道是Spring Integration中的默认通道类型,也是最简单的点对点通道选项。 空通道是一个虚拟消息通道,主要用于测试和调试。 它不会将消息从发送方发送到接收方,但其send方法始终返回true,而receive方法返回null值。 除了DirectChannel和NullChannel,Spring Integration还提供了不同的消息通道实现,例如PublishSubscribeChannel,QueueChannel,PriorityChannel,RendezvousChannel,ExecutorChannel和ScopedChannel。

消息端点:消息端点将应用程序代码与基础结构隔离。 换句话说,它是应用程序代码和消息传递框架之间的抽象层。

主要消息端点

转换程序:消息转换程序负责转换消息的内容或结构并返回修改后的消息。 例如:它可用于将消息有效负载从一种格式转换为另一种格式或修改消息头值。

过滤器:消息过滤器确定是否将消息传递到消息通道。

路由器:消息路由器决定哪个信道(如果可用)接下来应接收消息。

拆分器:拆分器将传入的消息分解为多个消息,并将它们发送到适当的通道。

聚合器:聚合器将多个消息合并为单个消息。

服务激活器:服务激活器是用于将服务实例连接到消息传递系统的通用端点。

通道适配器:通道适配器是将消息通道连接到外部系统的端点。 通道适配器可以是入站的或出站的。 入站通道适配器端点将外部系统连接到MessageChannel。 出站通道适配器端点将MessageChannel连接到外部系统。

消息传递网关:网关是消息传递系统的入口,它对外部系统隐藏消息传递API。 通过覆盖请求和回复通道,它是双向的。

Spring Integration还提供了各种通道适配器和消息传递网关(用于AMQP,文件,Redis,Gemfire,Http,Jdbc,JPA,JMS,RMI,Stream等),以支持与外部系统的基于消息的通信。 请访问Spring Integration Reference文档以获取详细信息。

以下示例货物消息传递实现显示了易于理解的基本消息端点的行为。 货运信息系统通过使用CargoGateway接口监听来自外部系统的货运信息。 使用CargoSplitter,CargoFilter,CargoRouter,CargoTransformer MessageEndpoints处理收到的货物消息。 之后,已处理成功的国内和国际货运消息将发送到CargoServiceActivator。

货物信息系统的Spring整合流程如下:

otv_si

让我们看一下示例货物消息传递实现。

二手技术

  • JDK 1.8.0_25
  • 春天4.1.2
  • Spring Integration 4.1.0
  • Maven 3.2.2
  • Ubuntu 14.04

项目层次结构如下:

otv_si3

步骤1:依存关系

依赖关系已添加到Maven pom.xml。

<properties><spring.version>4.1.2.RELEASE</spring.version><spring.integration.version>4.1.0.RELEASE</spring.integration.version></properties><dependencies><!-- Spring 4 dependencies --><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring.version}</version></dependency><!-- Spring Integration dependencies --><dependency><groupId>org.springframework.integration</groupId><artifactId>spring-integration-core</artifactId><version>${spring.integration.version}</version></dependency></dependencies>

第2步:货柜机

创建CargoBuilder来构建货运请求。

public class Cargo {public enum ShippingType {DOMESTIC, INTERNATIONAL}private final long trackingId;private final String receiverName;private final String deliveryAddress;private final double weight;private final String description;private final ShippingType shippingType;private final int deliveryDayCommitment;private final int region;private Cargo(CargoBuilder cargoBuilder) {this.trackingId = cargoBuilder.trackingId;this.receiverName = cargoBuilder.receiverName;this.deliveryAddress = cargoBuilder.deliveryAddress;this.weight = cargoBuilder.weight;this.description = cargoBuilder.description;this.shippingType = cargoBuilder.shippingType;this.deliveryDayCommitment = cargoBuilder.deliveryDayCommitment;this.region = cargoBuilder.region;}// Getter methods...@Overridepublic String toString() {return "Cargo [trackingId=" + trackingId + ", receiverName="+ receiverName + ", deliveryAddress=" + deliveryAddress+ ", weight=" + weight + ", description=" + description+ ", shippingType=" + shippingType + ", deliveryDayCommitment="+ deliveryDayCommitment + ", region=" + region + "]";}public static class CargoBuilder {private final long trackingId;private final String receiverName;private final String deliveryAddress;private final double weight;private final ShippingType shippingType;private int deliveryDayCommitment;private int region;private String description;public CargoBuilder(long trackingId, String receiverName,String deliveryAddress, double weight, ShippingType shippingType) {this.trackingId = trackingId;this.receiverName = receiverName;this.deliveryAddress = deliveryAddress;this.weight = weight;this.shippingType = shippingType;}public CargoBuilder setDeliveryDayCommitment(int deliveryDayCommitment) {this.deliveryDayCommitment = deliveryDayCommitment;return this;}public CargoBuilder setDescription(String description) {this.description = description;return this;}public CargoBuilder setRegion(int region) {this.region = region;return this;}public Cargo build() {Cargo cargo = new Cargo(this);if ((ShippingType.DOMESTIC == cargo.getShippingType()) && (cargo.getRegion() <= 0 || cargo.getRegion() > 4)) {throw new IllegalStateException("Region is invalid! Cargo Tracking Id : " + cargo.getTrackingId());}return cargo;}}

步骤3:货运讯息

CargoMessage是“国内和国际货运消息”的父类。

public class CargoMessage {private final Cargo cargo;public CargoMessage(Cargo cargo) {this.cargo = cargo;}public Cargo getCargo() {return cargo;}@Overridepublic String toString() {return cargo.toString();}
}

步骤4:国内货运讯息

DomesticCargoMessage类模拟国内货物消息。

public class DomesticCargoMessage extends CargoMessage {public enum Region {NORTH(1), SOUTH(2), EAST(3), WEST(4);private int value;private Region(int value) {this.value = value;}public static Region fromValue(int value) {return Arrays.stream(Region.values()).filter(region -> region.value == value).findFirst().get();}}private final Region region; public DomesticCargoMessage(Cargo cargo, Region region) {super(cargo);this.region = region;}public Region getRegion() {return region;}@Overridepublic String toString() {return "DomesticCargoMessage [cargo=" + super.toString() + ", region=" + region + "]";}}

步骤5:国际货运讯息

InternationalCargoMessage类模拟国际货运消息。

public class InternationalCargoMessage extends CargoMessage {public enum DeliveryOption {NEXT_FLIGHT, PRIORITY, ECONOMY, STANDART}private final DeliveryOption deliveryOption;public InternationalCargoMessage(Cargo cargo, DeliveryOption deliveryOption) {super(cargo);this.deliveryOption = deliveryOption;}public DeliveryOption getDeliveryOption() {return deliveryOption;}@Overridepublic String toString() {return "InternationalCargoMessage [cargo=" + super.toString() + ", deliveryOption=" + deliveryOption + "]";}}

步骤6:应用程序配置

AppConfiguration是Spring容器的配置提供程序类。 它创建消息通道并注册到Spring BeanFactory。 此外, @EnableIntegration启用导入的Spring集成配置,而@IntegrationComponentScan扫描特定于Spring Integration的组件。 它们都带有Spring Integration 4.0。

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.IntegrationComponentScan;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.messaging.MessageChannel;@Configuration
@ComponentScan("com.onlinetechvision.integration")
@EnableIntegration
@IntegrationComponentScan("com.onlinetechvision.integration")
public class AppConfiguration {/*** Creates a new cargoGWDefaultRequest Channel and registers to BeanFactory.** @return direct channel*/@Beanpublic MessageChannel cargoGWDefaultRequestChannel() {return new DirectChannel();}/*** Creates a new cargoSplitterOutput Channel and registers to BeanFactory.** @return direct channel*/@Beanpublic MessageChannel cargoSplitterOutputChannel() {return new DirectChannel();}/*** Creates a new cargoFilterOutput Channel and registers to BeanFactory.** @return direct channel*/@Beanpublic MessageChannel cargoFilterOutputChannel() {return new DirectChannel();}/*** Creates a new cargoRouterDomesticOutput Channel and registers to BeanFactory.** @return direct channel*/@Beanpublic MessageChannel cargoRouterDomesticOutputChannel() {return new DirectChannel();}/*** Creates a new cargoRouterInternationalOutput Channel and registers to BeanFactory.** @return direct channel*/@Beanpublic MessageChannel cargoRouterInternationalOutputChannel() {return new DirectChannel();}/*** Creates a new cargoTransformerOutput Channel and registers to BeanFactory.** @return direct channel*/@Beanpublic MessageChannel cargoTransformerOutputChannel() {return new DirectChannel();}}

STEP 7:消息传递网关

CargoGateway接口向应用程序公开特定于域的方法。 换句话说,它提供了应用程序对消息传递系统的访问。 @MessagingGateway也是Spring Integration 4.0附带的,它简化了消息传递系统中的网关创建。 它的默认请求通道是cargoGWDefaultRequestChannel

import java.util.List;import org.springframework.integration.annotation.Gateway;
import org.springframework.integration.annotation.MessagingGateway;
import org.springframework.messaging.Message;import com.onlinetechvision.model.Cargo;@MessagingGateway(name = "cargoGateway", defaultRequestChannel = "cargoGWDefaultRequestChannel")
public interface ICargoGateway {/*** Processes Cargo Request** @param message SI Message covering Cargo List payload and Batch Cargo Id header.* @return operation result*/@Gatewayvoid processCargoRequest(Message<List<Cargo>> message);
}

步骤8:邮件分割器

CargoSplitter侦听cargoGWDefaultRequestChannel通道并将传入的“货物清单”分解为“货物”消息。 货物消息发送到cargoSplitterOutputChannel。

import java.util.List;import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.Splitter;
import org.springframework.messaging.Message;import com.onlinetechvision.model.Cargo;@MessageEndpoint
public class CargoSplitter {/*** Splits Cargo List to Cargo message(s)** @param message SI Message covering Cargo List payload and Batch Cargo Id header.* @return cargo list*/@Splitter(inputChannel = "cargoGWDefaultRequestChannel", outputChannel = "cargoSplitterOutputChannel")public List<Cargo> splitCargoList(Message<List<Cargo>> message) {return message.getPayload();}
}

步骤9:消息过滤器

CargoFilter确定是否将消息传递到消息通道。 它侦听cargoSplitterOutputChannel通道并过滤超过重量限制的货物消息。 如果货运消息低于重量限制,则将其发送到cargoFilterOutputChannel通道。 如果Cargo消息高于重量限制,则将其发送到cargoFilterDiscardChannel通道。

import org.springframework.integration.annotation.Filter;
import org.springframework.integration.annotation.MessageEndpoint;import com.onlinetechvision.model.Cargo;@MessageEndpoint
public class CargoFilter {private static final long CARGO_WEIGHT_LIMIT = 1_000;/*** Checks weight of cargo and filters if it exceeds limit.** @param Cargo message* @return check result*/@Filter(inputChannel="cargoSplitterOutputChannel", outputChannel="cargoFilterOutputChannel", discardChannel="cargoFilterDiscardChannel")public boolean filterIfCargoWeightExceedsLimit(Cargo cargo) {return cargo.getWeight() <= CARGO_WEIGHT_LIMIT;}
}

步骤10:丢弃货物消息监听器

DiscardedCargoMessageListener侦听cargoFilterDiscard通道并处理由CargoFilter丢弃的Cargo消息。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.messaging.handler.annotation.Header;import com.onlinetechvision.model.Cargo;@MessageEndpoint
public class DiscardedCargoMessageListener {private final Logger logger = LoggerFactory.getLogger(DiscardedCargoMessageListener.class);/*** Handles discarded domestic and international cargo request(s) and logs.** @param cargo domestic/international cargo message* @param batchId message header shows cargo batch id*/@ServiceActivator(inputChannel = "cargoFilterDiscardChannel")public void handleDiscardedCargo(Cargo cargo, @Header("CARGO_BATCH_ID") long batchId) {logger.debug("Message in Batch[" + batchId + "] is received with Discarded payload : " + cargo);}}

步骤11:消息传送路由器

CargoRouter确定哪些频道(如果有)接下来应接收该消息。 它侦听cargoFilterOutputChannel通道并根据货物运输类型返回相关的通道名称。 换句话说,它将进入的货物消息路由到国内( cargoRouterDomesticOutputChannel )或国际( cargoRouterInternationalOutputChannel )货物通道。 另外,如果未设置运送类型,则返回nullChannelnullChannel是一个虚拟消息通道,主要用于测试和调试。 它不会将消息从发送方发送到接收方,但其send方法始终返回true,而receive方法返回null值。

import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.Router;import com.onlinetechvision.model.Cargo;
import com.onlinetechvision.model.Cargo.ShippingType;@MessageEndpoint
public class CargoRouter {/*** Determines cargo request' s channel in the light of shipping type.** @param Cargo message* @return channel name*/@Router(inputChannel="cargoFilterOutputChannel")public String route(Cargo cargo) {if(cargo.getShippingType() == ShippingType.DOMESTIC) {return "cargoRouterDomesticOutputChannel";} else if(cargo.getShippingType() == ShippingType.INTERNATIONAL) {return "cargoRouterInternationalOutputChannel";} return "nullChannel"; }}

步骤12:讯息变压器

CargoTransformer侦听cargoRouterDomesticOutputChannelcargoRouterInternationalOutputChannel并将传入的货运请求转换为国内和国际货运消息。 之后,它将它们发送到cargoTransformerOutputChannel通道。

import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.Transformer;import com.onlinetechvision.model.Cargo;
import com.onlinetechvision.model.DomesticCargoMessage;
import com.onlinetechvision.model.DomesticCargoMessage.Region;
import com.onlinetechvision.model.InternationalCargoMessage;
import com.onlinetechvision.model.InternationalCargoMessage.DeliveryOption;@MessageEndpoint
public class CargoTransformer {/*** Transforms Cargo request to Domestic Cargo obj.** @param cargo*            request* @return Domestic Cargo obj*/@Transformer(inputChannel = "cargoRouterDomesticOutputChannel", outputChannel = "cargoTransformerOutputChannel")public DomesticCargoMessage transformDomesticCargo(Cargo cargo) {return new DomesticCargoMessage(cargo, Region.fromValue(cargo.getRegion()));}/*** Transforms Cargo request to International Cargo obj.** @param cargo*            request* @return International Cargo obj*/@Transformer(inputChannel = "cargoRouterInternationalOutputChannel", outputChannel = "cargoTransformerOutputChannel")public InternationalCargoMessage transformInternationalCargo(Cargo cargo) {return new InternationalCargoMessage(cargo, getDeliveryOption(cargo.getDeliveryDayCommitment()));}/*** Get delivery option by delivery day commitment.** @param deliveryDayCommitment delivery day commitment* @return delivery option*/private DeliveryOption getDeliveryOption(int deliveryDayCommitment) {if (deliveryDayCommitment == 1) {return DeliveryOption.NEXT_FLIGHT;} else if (deliveryDayCommitment == 2) {return DeliveryOption.PRIORITY;} else if (deliveryDayCommitment > 2 && deliveryDayCommitment < 5) {return DeliveryOption.ECONOMY;} else {return DeliveryOption.STANDART;}}}

步骤13:消息服务激活器

CargoServiceActivator是用于将服务实例连接到消息传递系统的通用端点。 它侦听cargoTransformerOutputChannel通道并获取已处理的国内和国际货物消息和日志。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.integration.annotation.MessageEndpoint;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.messaging.handler.annotation.Header;import com.onlinetechvision.model.CargoMessage;@MessageEndpoint
public class CargoServiceActivator {private final Logger logger = LoggerFactory.getLogger(CargoServiceActivator.class);/*** Gets processed domestic and international cargo request(s) and logs.** @param cargoMessage domestic/international cargo message* @param batchId message header shows cargo batch id*/@ServiceActivator(inputChannel = "cargoTransformerOutputChannel")public void getCargo(CargoMessage cargoMessage, @Header("CARGO_BATCH_ID") long batchId) {logger.debug("Message in Batch[" + batchId + "] is received with payload : " + cargoMessage);}}

步骤14:申请

创建应用程序类以运行应用程序。 它初始化应用程序上下文并将货运请求发送到消息传递系统。

import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.messaging.support.MessageBuilder;import com.onlinetechvision.integration.ICargoGateway;
import com.onlinetechvision.model.Cargo;
import com.onlinetechvision.model.Cargo.ShippingType;public class Application {public static void main(String[] args) {ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfiguration.class);ICargoGateway orderGateway = ctx.getBean(ICargoGateway.class);getCargoBatchMap().forEach((batchId, cargoList) -> orderGateway.processCargoRequest(MessageBuilder.withPayload(cargoList).setHeader("CARGO_BATCH_ID", batchId).build()));}/*** Creates a sample cargo batch map covering multiple batches and returns.** @return cargo batch map*/private static Map<Integer, List<Cargo>> getCargoBatchMap() {Map<Integer, List<Cargo>> cargoBatchMap = new HashMap<>();cargoBatchMap.put(1, Arrays.asList(new Cargo.CargoBuilder(1, "Receiver_Name1", "Address1", 0.5, ShippingType.DOMESTIC).setRegion(1).setDescription("Radio").build(),//Second cargo is filtered due to weight limit          new Cargo.CargoBuilder(2, "Receiver_Name2", "Address2", 2_000, ShippingType.INTERNATIONAL).setDeliveryDayCommitment(3).setDescription("Furniture").build(),new Cargo.CargoBuilder(3, "Receiver_Name3", "Address3", 5, ShippingType.INTERNATIONAL).setDeliveryDayCommitment(2).setDescription("TV").build(),//Fourth cargo is not processed due to no shipping type found           new Cargo.CargoBuilder(4, "Receiver_Name4", "Address4", 8, null).setDeliveryDayCommitment(2).setDescription("Chair").build()));cargoBatchMap.put(2, Arrays.asList(//Fifth cargo is filtered due to weight limitnew Cargo.CargoBuilder(5, "Receiver_Name5", "Address5", 1_200, ShippingType.DOMESTIC).setRegion(2).setDescription("Refrigerator").build(),new Cargo.CargoBuilder(6, "Receiver_Name6", "Address6", 20, ShippingType.DOMESTIC).setRegion(3).setDescription("Table").build(),//Seventh cargo is not processed due to no shipping type foundnew Cargo.CargoBuilder(7, "Receiver_Name7", "Address7", 5, null).setDeliveryDayCommitment(1).setDescription("TV").build()));cargoBatchMap.put(3, Arrays.asList(new Cargo.CargoBuilder(8, "Receiver_Name8", "Address8", 200, ShippingType.DOMESTIC).setRegion(2).setDescription("Washing Machine").build(),new Cargo.CargoBuilder(9, "Receiver_Name9", "Address9", 4.75, ShippingType.INTERNATIONAL).setDeliveryDayCommitment(1).setDescription("Document").build()));return Collections.unmodifiableMap(cargoBatchMap);}}

步骤15:建立专案

货运请求的运营结果如下:

货物1:已成功发送到服务激活器。
货物2:由于重量限制而被过滤。
货物3:已成功发送到服务激活器。 货物4:由于没有运输类型,因此未处理。 货物5:由于重量限制而被过滤。 货物6:已成功发送到服务激活器。 货物7:由于没有运输类型,因此未处理。 货物8:已成功发送到服务激活器。 货物9:已成功发送到服务激活器。

在构建并运行项目之后,将看到以下控制台输出日志:

2014-12-09 23:43:51 [main] DEBUG c.o.i.CargoServiceActivator - Message in Batch[1] is received with payload : DomesticCargoMessage [cargo=Cargo [trackingId=1, receiverName=Receiver_Name1, deliveryAddress=Address1, weight=0.5, description=Radio, shippingType=DOMESTIC, deliveryDayCommitment=0, region=1], region=NORTH]
2014-12-09 23:43:51 [main] DEBUG c.o.i.DiscardedCargoMessageListener - Message in Batch[1] is received with Discarded payload : Cargo [trackingId=2, receiverName=Receiver_Name2, deliveryAddress=Address2, weight=2000.0, description=Furniture, shippingType=INTERNATIONAL, deliveryDayCommitment=3, region=0]
2014-12-09 23:43:51 [main] DEBUG c.o.i.CargoServiceActivator - Message in Batch[1] is received with payload : InternationalCargoMessage [cargo=Cargo [trackingId=3, receiverName=Receiver_Name3, deliveryAddress=Address3, weight=5.0, description=TV, shippingType=INTERNATIONAL, deliveryDayCommitment=2, region=0], deliveryOption=PRIORITY]
2014-12-09 23:43:51 [main] DEBUG c.o.i.DiscardedCargoMessageListener - Message in Batch[2] is received with Discarded payload : Cargo [trackingId=5, receiverName=Receiver_Name5, deliveryAddress=Address5, weight=1200.0, description=Refrigerator, shippingType=DOMESTIC, deliveryDayCommitment=0, region=2]
2014-12-09 23:43:51 [main] DEBUG c.o.i.CargoServiceActivator - Message in Batch[2] is received with payload : DomesticCargoMessage [cargo=Cargo [trackingId=6, receiverName=Receiver_Name6, deliveryAddress=Address6, weight=20.0, description=Table, shippingType=DOMESTIC, deliveryDayCommitment=0, region=3], region=EAST]
2014-12-09 23:43:51 [main] DEBUG c.o.i.CargoServiceActivator - Message in Batch[3] is received with payload : DomesticCargoMessage [cargo=Cargo [trackingId=8, receiverName=Receiver_Name8, deliveryAddress=Address8, weight=200.0, description=Washing Machine, shippingType=DOMESTIC, deliveryDayCommitment=0, region=2], region=SOUTH]
2014-12-09 23:43:51 [main] DEBUG c.o.i.CargoServiceActivator - Message in Batch[3] is received with payload : InternationalCargoMessage [cargo=Cargo [trackingId=9, receiverName=Receiver_Name9, deliveryAddress=Address9, weight=4.75, description=Document, shippingType=INTERNATIONAL, deliveryDayCommitment=1, region=0], deliveryOption=NEXT_FLIGHT]

源代码

源代码在Github上可用

参考资料

  • 企业整合模式
  • Spring集成参考手册
  • Spring Integration 4.1.0.RELEASE API
  • Pro Spring整合
  • Spring Integration 3.0.2和4.0 Milestone 4发布

翻译自: https://www.javacodegeeks.com/2014/12/message-processing-with-spring-integration.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/338576.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

鸿蒙系统多会发布,华为官宣鸿蒙系统将发布,还将发布多款新品

华为今日官宣&#xff0c;6月2日20&#xff1a;00&#xff0c;将举行鸿蒙操作系统及华为全场景新品发布会。【1、鸿蒙OS2.0】本次发布会的重点将是推出华为今年的重点战略产品——鸿蒙系统。届时鸿蒙OS2.0正式版将陆续推送给手机用户。5月中旬时华为就先行推送了一波鸿蒙OS开发…

C语言结构体字节对齐

默认字节对齐C语言结构体字节对齐是老生常谈的问题了&#xff0c;也是高频面试题&#xff0c;现在我们来深入研究这个问题&#xff0c;彻底弄懂到底是怎么回事&#xff0c;给你一个结构体定义和平台机器位数就能手动计算出结构体占用字节数&#xff0c;现在我们不使用宏#pragma…

shell shocked伴奏版_Shell Shocked

【游戏简介】Shell Shocked是一款全新卡牌对战题材的策略类手机游戏。游戏有着丰富的卡牌种类&#xff0c;极具策略性的玩法。游戏中玩家可以将进行真人游戏对战&#xff0c;随机抽取卡牌对战。玩家必须采取适合的策略&#xff0c;才有可能赢取游戏。 游戏操作简单&#xff0c;…

meetup_使用RxNetty访问Meetup的流API

meetup本文将涉及多个主题&#xff1a;响应式编程&#xff0c;HTTP&#xff0c;解析JSON以及与社交API集成。 完全在一个用例中&#xff1a;我们将通过非夸张的RxNetty库实时加载和处理新的metup.com事件&#xff0c;结合Netty框架的强大功能和RxJava库的灵活性。 Meetup提供了…

html约束验证的例子,HTML5利用约束验证API来检查表单的输入数据的代码实例

HTML5对于表单有着极大程度的优化&#xff0c;无论是语义&#xff0c;小部件&#xff0c;还是数据格式的验证。我猜你肯定会以浏览器兼容作为借口不愿意使用这些“新功能”&#xff0c;但这绝不应该成为使你停滞不前的原因&#xff0c;况且还有像Modernizr和ployfill这样的工具…

C语言经典题(1)

输入某年某月某日&#xff0c;判断这一天是这一年的第几天&#xff1f;程序分析&#xff1a;以3月5日为例&#xff0c;应该先把前两个月的加起来&#xff0c;然后再加上5天即本年的第几天&#xff0c;特殊情况&#xff0c;闰年且输入月份大于3时需考虑多加一天#include int mai…

jpg图片使用pil的resize后_如何使用PIL调整图像大小并保持其纵横比?

是否有一种显而易见的方法可以解决这个问题&#xff1f; 我只是想制作缩略图。#1楼PIL已经可以选择裁剪图像img ImageOps.fit(img, size, Image.ANTIALIAS)#2楼我丑陋的例子。函数获取文件如&#xff1a;“pic [0-9a-z]。[extension]”&#xff0c;将它们调整为120x120&#x…

openshift_OpenShift上的Java EE工作流(技术提示#64)

openshift该网络研讨会展示了如何使用WildFly &#xff0c; JBoss Tools &#xff0c; Forge &#xff0c; Arquillian和OpenShift在OpenShift上创建Java EE工作流。 具体来说&#xff0c;它谈论&#xff1a; 如何使用JBoss Developer Studio轻松开发Java EE应用程序并将其直接…

C语言面试-指针和引用的使用场景?

先解决两个疑问◆ 指针和引用的不同之处是什么&#xff1f;◆ 何时用用指针&#xff1f;何时用引用&#xff1f;指针和引用的不同之处看如下代码&#xff1a;指针是用来表示内存地址的&#xff0c;而指针这个整数正是被指向的变量地址。而引用就是给变量重新起了一个名字&#…

排序算法html,排序算法总结.html

排序算法总结 | borens blog排序算法总结borens blog首页所有文章关于作者排序算法总结Apr 6, 2018| 技术人生| 阅读排序,顾名思义,将数据按照某种规则排列起来.这种规则可以是根据基本的数值大小排序,也可以是通过字符串长度比较来排序,又或者是优先根据两个字符串的第一个不…

win10商店下载位置_win10应用商店下载的东西在哪

win10应用商店下载的东西在哪&#xff1f;我们都知道&#xff0c;微软从win8开始&#xff0c;就推出了应用商店&#xff0c;到现在的win10&#xff0c;还是一样。现在的win10应用商店里的东西也越来越丰富了。但是有些win10新用户朋友们在win10应用商店下载了东西&#xff0c;准…

异步http 超时_具有CompletableFuture的异步超时

异步http 超时有一天&#xff0c;我重写了执行不佳的多线程代码&#xff0c;该代码在Future.get()某个时刻被阻塞&#xff1a; public void serve() throws InterruptedException, ExecutionException, TimeoutException {final Future<Response> responseFuture async…

C语言指针变量与一维数组

数组元素之间的地址是相连的&#xff1b;变量地址绝对不是相连的&#xff0c;如果找到规律那也只是一个偶然的&#xff0c;不是必然的&#xff1b;1. 指针变量和一位数组下面对指针数组进行分析&#xff0c;index(小标是从0开始)&#xff0c;array数组是int类型&#xff0c;每一…

天干地支计算公式_高大上:天干地支计算方法

天干地支是我国古代计算年月日的重要依据&#xff0c;作为现代人的我们&#xff0c;有必要去了解一下&#xff0c;他们&#xff0c;以备不时之需。首先介绍一下&#xff0c;天干和地支。共有十天干和十二地支。十天干&#xff1a;甲(jiǎ)、乙(yǐ)、丙(bǐng)、丁(dīng)、戊(…

input发送a.jax_JAX-RS 2.0:服务器端处理管道

input发送a.jax这篇文章的灵感来自JAX-RS 2.0规范文档 &#xff08;附录C&#xff09;中的Processing Pipeline部分。 我喜欢它的原因在于它提供了JAX-RS中所有模块的漂亮快照-以准备好吞咽的胶囊形式&#xff01; 礼貌– JAX-RS 2.0规范文档 因此&#xff0c;我想到了使用此…

html 登录失败,qq音乐登录失败 QQ音乐总是显示登录失败是怎么回事

urlproc.exe是什么进程?没见过&#xff0c;请前辈们指点&#xff1f;造成QQ音乐登录不上现象的原因有如下三种可能&#xff1a; 一、木马病毒对QQ音乐的必要组件或文件进行破坏&#xff0c;导致QQ音乐登录失败&#xff0c;登陆不上的情况发生。 二、Windows系统防火墙(或其他安…

C 常对象成员

C 常对象成员在C 中&#xff0c;可以将对象的成员声明为const&#xff0c;包括常数据成员和常成员函数C 常数据成员 常数据成员的作用与一般常变量相似&#xff0c;用关键字const来声明常数据成员。常数据成员的值是不能改变的&#xff0c;只能通过构造函数的参数初始化表对常数…

python gitlab_Python Gitlab Api 使用方法

简述公司使用gitlab 来托管代码,日常代码merge request 以及其他管理是交给测试&#xff0c;鉴于操作需经常打开网页,重复且繁琐,所以交给Python 管理。安装pip install python-gitlab环境: py3DEMO# -*- coding: utf-8 -*-__Author__ "xiewm"__Date__ 2017/12/26 …

tomee_Apache TomEE + JMS。 从未如此简单。

tomee我记得J2EE &#xff08;1.3和1.4&#xff09;的过去&#xff0c;使用JMS启动项目非常困难。 您需要安装JMS 代理 &#xff0c;创建主题或队列 &#xff0c;最后使用服务器配置文件和JNDI开始自己的战斗。 感谢JavaEE 6及其它&#xff0c;使用JMS确实非常简单。 但是&…

娄底二中高考2021成绩查询,2021年娄底高考状元名单公布,娄底高考状元学校资料及最高分...

2019年高考已经落下帷幕&#xff0c;高考放榜时刻就要到来&#xff0c;每年的高考状元都会被各界高度关注&#xff0c;那么今年娄底高考状元花落谁家呢&#xff1f;娄底高考状元会给人带来惊喜吗&#xff0c;让我们一起期待2019年娄底高考状元的诞生。下面小编为给为梳理下历年…