使用Spring Integration进行消息处理

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通道。 如果货运消息高于重量限制,则将其发送到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/360954.shtml

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

相关文章

使用RxNetty访问Meetup的流API

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

js、react对象名和对象属性赋值

const resValue {}; resValue[standards${standardsNumber}] ""; Console.log(:test"&#xff0c;resValue )//

TIBCO产品的微服务和DevOps

如今&#xff0c;每个人都在谈论微服务。 您可以在数百篇文章和博客文章中读到很多有关微服务的信息。 马丁福勒 &#xff08; Martin Fowler &#xff09;的文章是一个很好的起点&#xff0c;该文章引发了有关这种新架构概念的大量讨论。 另一个很棒的资源是独立于供应商的分…

使用Degraph管理软件包依赖关系

软件开发领域的很大一部分是使系统的复杂性尽可能地低。 但是复杂性到底是什么&#xff1f; 虽然确切的语义有很大不同&#xff0c;但取决于您询问的人&#xff0c;大多数人可能都认为这与系统中部件的数量及其交互有很大关系。 考虑太空中的大理石&#xff0c;即行星&#xf…

[转载] 应急管理体系及其业务流程研究

转载于:https://www.cnblogs.com/6DAN_HUST/archive/2013/03/04/2942337.html

WP8手机上的图标

一直不清楚WP8手机上两个圆的标志是什么意思&#xff0c;今天看到下面的链接&#xff0c;终于搞明白了&#xff0c;原来是打开了GPS就有。 http://www.windowsphone.com/en-us/how-to/wp8/basics/what-do-the-icons-on-my-phone-mean 转载于:https://www.cnblogs.com/wonderow/…

ASIHTTPRequest类库简介和使用说明

官方网站&#xff1a; http://allseeing-i.com/ASIHTTPRequest/ 。可以从上面下载到最新源码&#xff0c;以及获取到相关的资料。 使用iOS SDK中的HTTP网络请求API&#xff0c;相当的复杂&#xff0c;调用很繁琐&#xff0c;ASIHTTPRequest就是一个对CFNetwork API进行了封装&a…

UltraESB的首选IDE – IntelliJ IDEA

在AdroitLogic&#xff0c;我们长期以来一直在使用IntelliJ IDEA进行开发。 它是Java和相关语言/技术的最佳IDE&#xff08;它可能也是许多其他语言的选择&#xff0c;但我的经验主要是Java和相关技术&#xff09;。 Groovy和IDEA的Grails的集成很棒。 通过自动发现JDBC驱动程…

跟我一步一步开发自己的Openfire插件

这篇是简单插件开发&#xff0c;下篇聊天记录插件。 开发环境&#xff1a; System&#xff1a;Windows WebBrowser&#xff1a;IE6、Firefox3 JavaEE Server&#xff1a;tomcat5.0.2.8、tomcat6 IDE&#xff1a;eclipse、MyEclipse 8开发依赖库&#xff1a; Jdk1.6、jasper-com…

Apache FOP与Eclipse和OSGi的集成

Apache FOP是由XSL格式化对象&#xff08; XSL-FO &#xff09;驱动的开源打印处理器。 例如&#xff0c;将数据对象转换为PDF可能非常有用。 但是&#xff0c;将其集成到PDE中并最终以OSGi Service的形式运行并最终显得有些麻烦。 因此&#xff0c;我提供了一个P2存储库&…

不删除侦听器–使用ListenerHandles

听一个可观察的实例并对它的变化做出反应很有趣。 做一些必要的事情来打断或结束这种聆听会变得很有趣。 让我们看看问题的根源和解决方法。 总览 这篇文章将首先讨论这种情况&#xff0c;然后再讨论常见的方法和问题所在。 然后&#xff0c;它将提供解决大多数问题的简单抽象…

使用Google Guava Cache进行本地缓存

很多时候&#xff0c;我们将不得不从数据库或另一个Web服务获取数据或从文件系统加载数据。 在涉及网络呼叫的情况下&#xff0c;将存在固有的网络等待时间&#xff0c;网络带宽限制。 解决此问题的方法之一是在应用程序本地拥有一个缓存。 如果您的应用程序跨越多个节点&…

JAX-RS 2.0:服务器端处理管道

这篇文章的灵感来自JAX-RS 2.0规范文档 &#xff08;附录C&#xff09;中的Processing Pipeline部分。 我喜欢它是因为它提供了JAX-RS中所有模块的漂亮快照-以准备好吞咽的胶囊形式&#xff01; 礼貌– JAX-RS 2.0规范文档 因此&#xff0c;我想到了使用此图简要概述不同的JA…

基于TCP/IP的文件服务器编程一例

来源&#xff0c;华清远见嵌入式学院实验手册&#xff0c;代码来源&#xff1a;华清远见曾宏安 实现的功能&#xff1a; 编写TCP文件服务器和客户端。客户端可以上传和下载文件 客户端支持功能如下&#xff1a; 1.支持一下命令 help 显示客户端所有命令和说明 list 显示服务器…

【Linux系统基础】(2)在Linux上部署MySQL、RabbitMQ、ElasticSearch、Zookeeper、Kafka、NoSQL等各类软件

实战章节&#xff1a;在Linux上部署各类软件 前言 为什么学习各类软件在Linux上的部署 在前面&#xff0c;我们学习了许多的Linux命令和高级技巧&#xff0c;这些知识点比较零散&#xff0c;同学们跟随着课程的内容进行练习虽然可以基础掌握这些命令和技巧的使用&#xff0c;…

JDK 7和JDK 8中大行读取速度较慢的原因

我之前发布了博客文章“使用JDK 7和JDK 8读取慢速行”&#xff0c;并且在该问题上有一些有用的评论来描述该问题。 这篇文章提供了更多解释&#xff0c;说明为何该文章中演示的文件读取&#xff08;并由Ant的LineContainsRegExp使用 &#xff09;在Java 7和Java 8中比在Java 6中…

Spring Stateless State Security第3部分:JWT +社会认证

我的Stateless Spring Security系列文章的第三部分也是最后一部分是关于将基于JWT令牌的身份验证与spring-social-security混合在一起的。 这篇文章直接建立在此基础上&#xff0c;并且主要集中在已更改的部分上。 想法是使用基于OAuth 2的“使用Facebook登录”功能来替换基于用…

nyoj239 月老的难题 二分图 匈牙利算法

月老的难题 时间限制&#xff1a;1000 ms | 内存限制&#xff1a;65535 KB难度&#xff1a;4描述月老准备给n个女孩与n个男孩牵红线&#xff0c;成就一对对美好的姻缘。 现在&#xff0c;由于一些原因&#xff0c;部分男孩与女孩可能结成幸福的一家&#xff0c;部分可能不会结…

Web应用程序体系结构– Spring MVC – AngularJs堆栈

Spring MVC和AngularJs共同为构建表单密集型Web应用程序提供了一个真正高效且吸引人的前端开发堆栈。在这篇博客文章中&#xff0c;我们将看到如何使用这些技术构建表单密集型Web应用程序&#xff0c;并将这种方法与其他方法进行比较可用选项。 可以在此github 存储库中找到功能…

antd Datepicker组件报错 ——date.clone is not a function或者date1.isAfter is not a function

问题描述&#xff1a; antd Datepicker组件报错 ——date.clone is not a function或者date1.isAfter is not a function 原因分析&#xff1a; 在From中渲染默认值&#xff0c;一般数据请求拿到返回值存在异步&#xff0c;会晚于渲染&#xff0c;因此日期转换不能放在DatePi…