使用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,一经查实,立即删除!

相关文章

mysql中的字段类型

Mysql中的字段类型 MySQL支持大量的列类型&#xff0c;它可以被分为3类&#xff1a;数字类型、日期和时间类型以及字符串(字符)类型。本节首先给出可用类型的一个概述&#xff0c;并且总结每个列类型的存储需求&#xff0c;然后提供每个类中的类型性质的更详细的描述。概述有意…

js 编码

编码 let url encodeURIComponent(this.url);

使用RxNetty访问Meetup的流API

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

SQL Server 2005/2008 导入导出数据常见报错

数据库导入导出时总失败&#xff0c;错误信息如下&#xff1a; 正在验证 (错误) 消息 错误 0xc0202049: 数据流任务 1: 无法在只读列“ID”中插入数据。 (SQL Server 导入和导出向导) 错误 0xc0202045: 数据流任务 1: 验证列元数据失败。 (SQL Server 导入和导出向导) 错…

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

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

TIBCO产品的微服务和DevOps

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

三层结构

三层结构&#xff1a; 1.UI层&#xff0c;表示层&#xff1b;负责界面&#xff0c;也就是我们看到的前台的东西&#xff0c;最直观的东西都在这里实现&#xff1b; 2.BLL层&#xff0c;业务逻辑层&#xff0c;负责软件的业务逻辑&#xff0c;逻辑代码在这里实现&#xff1b; 3.…

antd listView中onEndReached()失效

antd listView中useBodyScrolltrue会导致onEndReached&#xff08;&#xff09;失效

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

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

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

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

div中直接绑定富文本值

<div dangerouslySetInnerHTML{{ __html: ${currentGoods.ShortDescription} }}/>

[转载]创建数据库与完成数据添删改查--第一种写法

<?xml version"1.0" encoding"utf-8"?><manifest xmlns:android"http://schemas.android.com/apk/res/android"package"cn.itcast.db"android:versionCode"1"android:versionName"1.0" ><uses-s…

微信公众号网页开发:播放视频,在列表中滑动会脱离文档流

项目场景&#xff1a; antd移动端端开发微信公众号移动端网页开发 问题描述&#xff1a; video-react有问题&#xff0c;在跑马灯中第一次白屏&#xff0c;在列表中滑动会脱离文档流 原因分析&#xff1a; 未知 解决方案&#xff1a; 直接上代码 <videocontrols"c…

如何在没有Springockito的情况下模拟Spring bean

我在Spring工作了几年。 但是我总是对XML配置变得多么混乱感到沮丧。 随着各种注释和Java配置可能性的出现&#xff0c;我开始喜欢使用Spring进行编程。 这就是为什么我强烈使用Java配置的原因。 我认为&#xff0c;仅当您需要可视化Spring Integration或Spring Batch流时&…

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/…

JS保留两位小数

toFixed&#xff08;2&#xff09;

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

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

获取css样式

js模块代码 function getStyle(obj,attr){ if(obj.currentStyle){//兼容ie return obj.currentStyle[attr]; } else{ return getComputedStyle(obj,null)[attr]; } } window.οnlοadfunction(){ oDiv document.getElementsById(div); console.log((oDiv,width)); } HTML <d…

具有CompletableFuture的异步超时

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

js 中对于 css 的变量操作(React也可)

文章目录前言一、设置CSS变量&#xff1f;二、读取变量三、删除变量总结前言 主要讲js 中对于 css 的变量操作&#xff1b; 前端框架&#xff1a;antd框架 一、设置CSS变量&#xff1f; document.body.style.setProperty(--primary, #7F583F’);二、读取变量 document.body…