Spring 集成提供了许多配置选项。 选择哪个选项取决于您的特定需求以及您喜欢在哪个级别工作。 与一般的 Spring 框架一样,您可以混合和匹配各种技术以适应手头的问题。 例如,可以为大多数配置选择基于 XSD 的命名空间,并将其与使用批注配置的少数对象组合在一起。 两者尽可能提供一致的命名。 XSD 架构定义的 XML 元素与批注的名称匹配,这些 XML 元素的属性与批注属性的名称匹配。 您也可以直接使用 API,但我们希望大多数开发人员选择更高级别的选项之一,或者基于命名空间和注释驱动的配置的组合。
命名空间支持
您可以使用直接映射到企业集成的术语和概念的 XML 元素来配置 Spring 集成组件。 在许多情况下,元素名称与企业集成模式手册中的元素名称匹配。
要在 Spring 配置文件中启用 Spring 集成的核心命名空间支持,请在顶级 'bean' 元素中添加以下命名空间引用和模式映射:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd">(我们已经强调了Spring Integration特有的线条。
您可以选择“xmlns:”之后的任何名称。 为了清楚起见,我们使用(集成的缩写),但您可能更喜欢另一个缩写。 另一方面,如果使用 XML 编辑器或 IDE 支持,则自动完成的可用性可能会说服您保留较长的名称以保持清晰。 或者,您可以创建使用 Spring 集成模式作为主命名空间的配置文件,如以下示例所示:int
<beans:beans xmlns="http://www.springframework.org/schema/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd">(我们已经强调了Spring Integration特有的线条。
使用此替代方法时,Spring 集成元素不需要前缀。 另一方面,如果在同一配置文件中定义通用 Spring Bean,则 Bean 元素需要一个前缀 ()。 由于模块化配置文件本身(基于责任或体系结构层)通常是一个好主意,因此您可能会发现在以集成为中心的配置文件中使用后一种方法是合适的,因为这些文件中很少需要通用 bean。 出于本文档的目的,我们假设集成命名空间是主要的。<beans:bean …/>
Spring Integration 提供了许多其他命名空间。 事实上,提供命名空间支持的每种适配器类型(JMS、文件等)都在单独的架构中定义其元素。 为了使用这些元素,请添加带有条目和相应映射的必要命名空间。 例如,以下根元素显示了其中的几个命名空间声明:xmlnsschemaLocation
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration" xmlns:int-file="http://www.springframework.org/schema/integration/file" xmlns:int-jms="http://www.springframework.org/schema/integration/jms" xmlns:int-mail="http://www.springframework.org/schema/integration/mail" xmlns:int-ws="http://www.springframework.org/schema/integration/ws" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd http://www.springframework.org/schema/integration/file https://www.springframework.org/schema/integration/file/spring-integration-file.xsd http://www.springframework.org/schema/integration/jms https://www.springframework.org/schema/integration/jms/spring-integration-jms.xsd http://www.springframework.org/schema/integration/mail https://www.springframework.org/schema/integration/mail/spring-integration-mail.xsd http://www.springframework.org/schema/integration/ws https://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd"> ...</beans>本参考手册在其相应章节中提供了各种元素的具体示例。 在这里,要识别的主要内容是每个命名空间 URI 和架构位置的命名一致性。
配置任务计划程序
在 Spring 集成中,它扮演着消息总线的核心角色,你只需要考虑几个配置选项。 首先,您可能希望控制中央实例。 为此,您可以提供一个名为 的单个 Bean。 这也定义为常量,如下所示:ApplicationContextTaskSchedulertaskScheduler
IntegrationContextUtils.TASK_SCHEDULER_BEAN_NAME默认情况下,Spring 集成依赖于 的实例,如 Spring 框架参考手册的任务执行和调度部分所述。 该默认值使用包含 10 个线程的池自动启动,但请参阅全局属性。 如果您改为提供自己的实例,则可以将“autoStartup”属性设置为或提供自己的池大小值。ThreadPoolTaskSchedulerTaskSchedulerTaskSchedulerfalse
当轮询使用者在其配置中提供显式任务执行程序引用时,处理程序方法的调用发生在该执行程序的线程池中,而不是主调度程序池中。 但是,当没有为终结点的轮询器提供任务执行器时,它将由主计划程序的线程之一调用。
不要在轮询器线程上运行长时间运行的任务。 请改用任务执行程序。 如果有很多轮询终结点,则可能会导致线程不足,除非增加池大小。 此外,轮询使用者的默认值为一秒。 由于轮询器线程这次会阻塞,因此我们建议您在存在许多此类终结点时使用任务执行器,以避免饥饿。 或者,您可以减少 .receiveTimeoutreceiveTimeout
如果终结点的输入通道是基于队列(即可轮询)的通道之一,则该终结点是轮询使用者。 事件驱动的使用者是那些具有具有调度程序而不是队列的输入通道的使用者(换句话说,它们是可订阅的)。 此类终结点没有轮询器配置,因为它们的处理程序是直接调用的。
在 JEE 容器中运行时,您可能需要使用 Spring 的 ,如此处所述,而不是默认的 . 为此,请为您的环境定义具有适当 JNDI 名称的 Bean,如以下示例所示:TimerManagerTaskSchedulertaskScheduler
<bean id="taskScheduler" class="org.springframework.scheduling.concurrent.DefaultManagedTaskScheduler"> <property name="jndiName" value="tm/MyTimerManager" /> <property name="resourceRef" value="true" /></bean>当在应用程序上下文中配置自定义时(如上面提到的),建议为其提供 ( bean) 以便能够将异常作为框架提供的 bean 进行处理。TaskSchedulerDefaultManagedTaskSchedulerMessagePublishingErrorHandlerintegrationMessagePublishingErrorHandlerErrorMessage`s sent to the error channel, as is done with the default `TaskScheduler
另请参阅错误处理以获取更多信息。
全局属性
可以通过在类路径上提供属性文件来重写某些全局框架属性。
默认属性可以在类中找到。 以下清单显示了默认值:org.springframework.integration.context.IntegrationProperties
spring.integration.channels.autoCreate=true spring.integration.channels.maxUnicastSubscribers=0x7fffffff spring.integration.channels.maxBroadcastSubscribers=0x7fffffff spring.integration.taskScheduler.poolSize=10 spring.integration.messagingTemplate.throwExceptionOnLateReply=false spring.integration.readOnly.headers= spring.integration.endpoints.noAutoStartup= spring.integration.channels.error.requireSubscribers=true spring.integration.channels.error.ignoreFailures=true如果为 true,则当未在应用程序上下文中显式找到实例时,将自动声明为实例。input-channelDirectChannel
设置允许的默认订户数,例如 . 它可用于避免无意中将多个端点订阅到同一通道。 您可以通过设置属性在各个通道上覆盖它。DirectChannelmax-subscribers
此属性提供允许的默认订户数,例如 . 它可用于避免无意中为同一通道订阅超过预期数量的终结点。 您可以通过设置属性在各个通道上覆盖它。PublishSubscribeChannelmax-subscribers
缺省 Bean 中可用的线程数。 请参阅配置任务计划程序。taskScheduler
当 ,到达网关回复通道的消息在网关不需要回复时引发异常(因为发送线程已超时或已收到回复)。true
在标头复制操作期间不应填充到实例中的消息标头名称的逗号分隔列表。 该列表由 Bean 使用,并传播到用于通过 (请参阅 MessageBuilder Helper 类) 构建消息的实例(请参阅 MessageHeaderAccessor API)。 默认情况下,仅在消息构建期间复制 和 不复制。 从版本 4.3.2 开始。MessageDefaultMessageBuilderFactoryIntegrationMessageHeaderAccessorMessageBuilderMessageHeaders.IDMessageHeaders.TIMESTAMP
不应在应用程序启动期间自动启动的 Bean 名称模式(、、*xxx 或 )的逗号分隔列表。 您可以稍后通过 (请参阅控制总线)通过其 Bean 名称、通过其角色(请参阅端点角色)或通过 Bean 注入手动启动这些端点。 可以通过指定 XML 注释或注释属性或通过调用 Bean 定义来显式覆盖此全局属性的效果。 从版本 4.3.12 开始。AbstractEndpointxxx*xxxxxx*yyyControl BusSmartLifecycleRoleControllerLifecycleauto-startupautoStartupAbstractEndpoint.setAutoStartup()
一个布尔标志,指示必须使用该选项配置默认全局。 从版本 5.4.3 开始。 有关详细信息,请参阅错误处理。errorChannelrequireSubscribers
一个布尔标志,指示默认全局必须忽略调度错误并将消息传递给下一个处理程序。 从版本 5.5 开始。errorChannel
可以通过向类路径或实例的 Bean 添加文件来覆盖这些属性。 您不需要提供所有属性,只需提供要重写的属性。/META-INF/spring.integration.propertiesIntegrationContextUtils.INTEGRATION_GLOBAL_PROPERTIES_BEAN_NAMEorg.springframework.integration.context.IntegrationProperties
从版本 5.1 开始,当为类别打开逻辑级别时,所有合并的全局属性都将在应用程序上下文启动后打印在日志中。 输出如下所示:DEBUGorg.springframework.integration
Spring Integration global properties:spring.integration.endpoints.noAutoStartup=fooService*spring.integration.taskScheduler.poolSize=20spring.integration.channels.maxUnicastSubscribers=0x7fffffffspring.integration.channels.autoCreate=truespring.integration.channels.maxBroadcastSubscribers=0x7fffffffspring.integration.readOnly.headers=spring.integration.messagingTemplate.throwExceptionOnLateReply=true注释支持
除了用于配置消息端点的 XML 命名空间支持之外,您还可以使用注释。 首先,Spring Integration 将类级别作为刻板型注释提供,这意味着它本身是用 Spring 的注释注释的,因此被 Spring 的组件扫描自动识别为 bean 定义。@MessageEndpoint@Component
更重要的是各种方法级注释。 它们指示带批注的方法能够处理消息。 下面的示例演示了类级和方法级注释:
@MessageEndpointpublic class FooService { @ServiceActivator public void processMessage(Message message) { ... }}方法“处理”消息的确切含义取决于特定的注释。 Spring 集成中可用的注释包括:
- @Aggregator(请参阅聚合器)
- @Filter(请参阅过滤器)
- @Router(请参阅路由器)
- @ServiceActivator(请参阅服务激活器)
- @Splitter(请参阅拆分器)
- @Transformer(参见变压器)
- @InboundChannelAdapter(请参阅通道适配器)
- @BridgeFrom(请参阅使用 Java 配置配置网桥)
- @BridgeTo(请参阅使用 Java 配置配置网桥)
- @MessagingGateway(请参阅消息网关)
- @IntegrationComponentScan(请参阅配置和@EnableIntegration)
如果将 XML 配置与批注结合使用,则不需要批注。 如果要从元素的属性配置 POJO 引用,则只能提供方法级注释。 在这种情况下,即使元素上不存在方法级属性,批注也会防止歧义。@MessageEndpointref<service-activator/><service-activator/>
在大多数情况下,带批注的处理程序方法不应要求将类型作为其参数。 相反,方法参数类型可以与消息的有效负载类型匹配,如以下示例所示:Message
public class ThingService { @ServiceActivator public void bar(Thing thing) { ... }}当方法参数应从 中的值映射时,另一个选项是使用参数级注释。 通常,使用 Spring 集成注释注释的方法可以接受自身、消息有效负载或标头值(带)作为参数。 实际上,该方法可以接受组合,如以下示例所示:MessageHeaders@HeaderMessage@Header
public class ThingService { @ServiceActivator public void otherThing(String payload, @Header("x") int valueX, @Header("y") int valueY) { ... }}还可以使用注释将所有邮件头作为 提供,如以下示例所示:@HeadersMap
public class ThingService { @ServiceActivator public void otherThing(String payload, @Headers Map<String, Object> headerMap) { ... }}注释的值也可以是 SpEL 表达式(例如 ),当您希望在注入标头值之前对其进行操作时,这很有用。 它还提供了一个可选属性,该属性指定属性值是否必须在标头中可用。 该属性的缺省值为 。someHeader.toUpperCase()requiredrequiredtrue
对于其中几个批注,当消息处理方法返回非 null 值时,终结点会尝试发送回复。 这在两个配置选项(命名空间和注释)中是一致的,因为使用此类终结点的输出通道(如果可用),并且消息标头值用作回退。REPLY_CHANNEL
终结点上的输出通道和回复通道消息标头的组合支持管道方法,其中多个组件具有输出通道,最后一个组件允许将回复消息转发到回复通道(如原始请求消息中指定)。 换句话说,最终组件取决于原始发送方提供的信息,因此可以动态支持任意数量的客户端。 这是寄信人地址模式的一个示例。
除了此处显示的示例外,这些批注还支持 and 属性,如以下示例所示:inputChanneloutputChannel
@Servicepublic class ThingService { @ServiceActivator(inputChannel="input", outputChannel="output") public void otherThing(String payload, @Headers Map<String, Object> headerMap) { ... }}这些注释的处理创建与相应的 XML 组件相同的 bean——实例和实例(或入站通道适配器的实例)。 请参阅@Bean方法的注释。 Bean 名称由以下模式生成:。 在前面的示例中,Bean 名称用于 和 ,并带有 () Bean 的附加 () 后缀。 可以使用注释和这些消息传递注释来自定义此类名称。 实例(实例)也有资格通过消息历史记录进行跟踪。AbstractEndpointMessageHandlerMessageSource[componentName].[methodName].[decapitalizedAnnotationClassShortName]thingService.otherThing.serviceActivatorAbstractEndpoint.handler.sourceMessageHandlerMessageSource@EndpointIdMessageHandlerMessageSource
从版本 4.0 开始,所有消息传递注释都提供选项( 和 ),以允许在应用程序上下文初始化时进行端点生命周期控制。 它们分别默认为 和。 要更改端点的状态(如 或 ),可以使用 (或自动连线) 获取对端点 Bean 的引用并调用这些方法。 或者,您可以向 发送命令消息(请参阅控制总线)。 出于这些目的,您应该使用上一段前面提到的方法。SmartLifecycleautoStartupphasetrue0start()stop()BeanFactoryControl BusbeanName
解析上述注释后自动创建的通道(未配置特定通道 Bean 时)和相应的使用方端点在上下文初始化结束时声明为 bean。 这些 bean 可以在其他服务中自动连线,但必须使用注释进行标记,因为在正常的自动连线处理期间,这些定义通常尚不可用。@Lazy
@Autowired@Lazy@Qualifier("someChannel")MessageChannel someChannel;...@BeanThing1 dependsOnSPCA(@Qualifier("someInboundAdapter") @Lazy SourcePollingChannelAdapter someInboundAdapter) { ...}从版本 6.0 开始,现在所有消息传递注释都已生效,因此可以在同一服务方法上声明多个相同类型的注释,其含义是创建与重复这些注释一样多的终结点:@Repeatable
@Transformer(inputChannel = "inputChannel1", outputChannel = "outputChannel1")@Transformer(inputChannel = "inputChannel2", outputChannel = "outputChannel2")public String transform(String input) { return input.toUpperCase();}使用注释@Poller
在 Spring Integration 4.0 之前,消息传递注释要求 be 引用 . 例如,需要一个元素来配置 并使复合端点成为 . 版本 4.0 引入了注释,以允许直接在消息传递注释上配置属性,如以下示例所示:inputChannelSubscribableChannelPollableChannel<int:bridge/><int:poller/>PollingConsumer@Pollerpoller
public class AnnotationService { @Transformer(inputChannel = "input", outputChannel = "output", poller = @Poller(maxMessagesPerPoll = "${poller.maxMessagesPerPoll}", fixedDelay = "${poller.fixedDelay}")) public String handle(String payload) { ... }}注释仅提供简单的选项。 您可以使用属性占位符配置批注的属性(、、 和 )。 此外,从版本 5.1 开始,还提供了 s 的选项。 如果需要提供更多轮询选项(例如、、、等),则应将 配置为通用 Bean,并将其 Bean 名称用作 的属性。 在这种情况下,不允许使用其他属性(必须在 Bean 上指定它们)。 请注意,如果 is a 且配置了 no,则使用默认值(如果它存在于应用程序上下文中)。 若要使用批注声明默认轮询器,请使用类似于以下示例的代码:@PollerPollerMetadata@PollermaxMessagesPerPollfixedDelayfixedRatecronreceiveTimeoutPollingConsumertransactionadvice-chainerror-handlerPollerMetadata@PollervaluePollerMetadatainputChannelPollableChannel@PollerPollerMetadata@Configuration
@Bean(name = PollerMetadata.DEFAULT_POLLER)public PollerMetadata defaultPoller() { PollerMetadata pollerMetadata = new PollerMetadata(); pollerMetadata.setTrigger(new PeriodicTrigger(10)); return pollerMetadata;}以下示例演示如何使用默认轮询器:
public class AnnotationService { @Transformer(inputChannel = "aPollableChannel", outputChannel = "output") public String handle(String payload) { ... }}以下示例演示如何使用命名轮询器:
@Beanpublic PollerMetadata myPoller() { PollerMetadata pollerMetadata = new PollerMetadata(); pollerMetadata.setTrigger(new PeriodicTrigger(1000)); return pollerMetadata;}以下示例显示了使用默认轮询器的终结点:
public class AnnotationService { @Transformer(inputChannel = "aPollableChannel", outputChannel = "output" poller = @Poller("myPoller")) public String handle(String payload) { ... }}从版本 4.3.3 开始,注释具有便于配置基础的属性。 此属性扮演的角色与 XML 组件中的角色相同。 有关详细信息,请参阅终结点命名空间支持。@PollererrorChannelMessagePublishingErrorHandlererror-channel<poller>
消息批注上的属性与该属性互斥。 有关详细信息,请参阅下一节。poller()reactive()
使用批注@Reactive
自 5.0 版以来一直存在,但仅当端点的输入通道是(或任何实现)时才应用它。 从版本 5.3 开始,当目标消息处理程序独立于输入通道类型时,框架也会创建其实例。 从版本 5.5 开始,所有消息传递注释都引入了子注释(类似于上面提到的)。 它接受可选的 Bean 引用,并且独立于输入通道类型和消息处理程序,将目标端点转换为实例。 运算符使用该函数对来自输入通道的反应性流源应用一些自定义(、、等)。ReactiveStreamsConsumerFluxMessageChannelorg.reactivestreams.PublisherReactiveMessageHandler@Reactive@PollerFunction<? super Flux<Message<?>>, ? extends Publisher<Message<?>>>ReactiveStreamsConsumerFlux.transform()publishOn()doOnNext()log()retry()
下面的示例演示如何将独立于最终订阅者和生产者的输入通道的发布线程更改为该通道:DirectChannel
@Beanpublic Function<Flux<?>, Flux<?>> publishOnCustomizer() { return flux -> flux.publishOn(Schedulers.parallel());}@ServiceActivator(inputChannel = "directChannel", reactive = @Reactive("publishOnCustomizer"))public void handleReactive(String payload) { ...}消息批注上的属性与该属性互斥。 有关详细信息,请参阅使用 @Poller 注释和反应式流支持。reactive()poller()
使用注释@InboundChannelAdapter
版本 4.0 引入了方法级注释。 它生成一个基于 a 的集成组件,用于注释方法。 此注解是 XML 组件的类似物,具有相同的限制:该方法不能有参数,并且返回类型不能为 。 它有两个属性:(所需的 Bean 名称)和(可选注释,如前所述)。 如果需要提供一些 ,请使用返回类型并使用 来构建 . 使用 可以配置 . 以下示例演示如何使用批注:@InboundChannelAdapterSourcePollingChannelAdapterMethodInvokingMessageSource<int:inbound-channel-adapter>voidvalueMessageChannelpoller@PollerMessageHeadersMessage<?>MessageBuilderMessage<?>MessageBuilderMessageHeaders@InboundChannelAdapter
@InboundChannelAdapter("counterChannel")public Integer count() { return this.counter.incrementAndGet();}@InboundChannelAdapter(value = "fooChannel", poller = @Poller(fixed-rate = "5000"))public String foo() { return "foo";}版本 4.3 引入了注释属性的别名,以提供更好的源代码可读性。 此外,目标 Bean 在第一次调用时通过提供的名称(由选项设置)解析,而不是在初始化阶段。 它允许“后期绑定”逻辑:从消费者的角度来看,目标 Bean 的创建和注册比解析阶段晚一点。channelvalueMessageChannelSourcePollingChannelAdapteroutputChannelNamereceive()MessageChannel@InboundChannelAdapter
第一个示例要求默认轮询器已在应用程序上下文中的其他位置声明。
使用注释@MessagingGateway
请参阅@MessagingGateway注释。
使用注释@IntegrationComponentScan
标准的 Spring 框架注释不会扫描接口中的构造型注释。 为了克服这个限制并允许配置(参见@MessagingGateway注释),我们引入了该机制。 此注释必须与注释一起放置,并对其进行自定义以定义其扫描选项, 如和 。 在这种情况下,所有已发现的带注释的接口都将被解析并注册为实例。 所有其他基于类的组件都由标准 解析。@ComponentScan@Component@MessagingGateway@IntegrationComponentScan@ConfigurationbasePackagesbasePackageClasses@MessagingGatewayGatewayProxyFactoryBean@ComponentScan
消息传递元注释
从版本 4.0 开始,所有消息传递注释都可以配置为元注释,并且所有用户定义的消息传递注释都可以定义相同的属性以覆盖其默认值。 此外,元注释可以分层配置,如以下示例所示:
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@ServiceActivator(inputChannel = "annInput", outputChannel = "annOutput")public @interface MyServiceActivator { String[] adviceChain = { "annAdvice" };}@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@MyServiceActivatorpublic @interface MyServiceActivator1 { String inputChannel(); String outputChannel();}...@MyServiceActivator1(inputChannel = "inputChannel", outputChannel = "outputChannel")public Object service(Object payload) { ...}分层配置元注释允许用户为各种属性设置缺省值,并允许将框架 Java 依赖项与用户注释隔离,从而避免在用户类中使用元注释。 如果框架找到具有具有框架元注释的用户注释的方法,则将其视为直接使用框架注释对该方法进行注释。
方法注释@Bean
从 V4.0 开始,您可以对类中的方法定义配置消息传递注释,以基于 Bean 而不是方法生成消息端点。 当定义是“现成的”实例(、 和其他实例)、实例(、 和其他实例)和实例(、 和其他实例)时,它很有用。 下面的示例演示如何将消息传递批注与批注一起使用:@Bean@Configuration@BeanMessageHandlerAggregatingMessageHandlerDefaultMessageSplitterTransformerJsonToObjectTransformerClaimCheckOutTransformerMessageSourceFileReadingMessageSourceRedisStoreMessageSource@Bean
@Configuration@EnableIntegrationpublic class MyFlowConfiguration { @Bean @InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000")) public MessageSource<String> consoleSource() { return CharacterStreamReadingMessageSource.stdin(); } @Bean @Transformer(inputChannel = "inputChannel", outputChannel = "httpChannel") public ObjectToMapTransformer toMapTransformer() { return new ObjectToMapTransformer(); } @Bean @ServiceActivator(inputChannel = "httpChannel") public HttpRequestExecutingMessageHandler httpHandler() { HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler("https://foo/service"); handler.setExpectedResponseType(String.class); handler.setOutputChannelName("outputChannel"); return handler; } @Bean @ServiceActivator(inputChannel = "outputChannel") public LoggingHandler loggingHandler() { return new LoggingHandler("info"); }}5.0 版引入了对带有返回值注释的支持,它可以生成 POJO 或 . 下面的示例演示如何使用该组合:@Bean@InboundChannelAdapterjava.util.function.SupplierMessage
@Configuration@EnableIntegrationpublic class MyFlowConfiguration { @Bean @InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000")) public Supplier<String> pojoSupplier() { return () -> "foo"; } @Bean @InboundChannelAdapter(value = "inputChannel", poller = @Poller(fixedDelay = "1000")) public Supplier<Message<String>> messageSupplier() { return () -> new GenericMessage<>("foo"); }}元注释规则也适用于方法(前面描述的注释可以应用于定义)。@Bean@MyServiceActivator@Bean
在使用者定义上使用这些注释时,如果 Bean 定义返回适当的(取决于注释类型),则必须在定义本身上设置属性(如、、 等)。 仅使用以下注释属性:、 和 。 所有其他属性都适用于处理程序。@BeanMessageHandleroutputChannelrequiresReplyorderMessageHandler@BeanadviceChainautoStartupinputChannelphasepoller
Bean 名称使用以下算法生成:
- () 从 上的方法名称或属性获取自己的标准名称。 这就像该方法上没有消息批注一样。MessageHandlerMessageSource@Beanname@Bean@Bean
- Bean 名称使用以下模式生成:。 例如,前面所示定义的端点获取的 Bean 名称为 。 与 POJO 方法不同,端点 Bean 名称中不包含 Bean 方法名称。 另请参阅端点 Bean 名称。AbstractEndpoint[@Bean name].[decapitalizedAnnotationClassShortName]SourcePollingChannelAdapterconsoleSource()consoleSource.inboundChannelAdapter
- 如果不能直接在目标端点中使用(不是 、 或 的实例),则注册相应的 以委托给此 。 此包装器的 Bean 名称使用以下模式生成:。@BeanMessageSourceAbstractReplyProducingMessageHandlerAbstractMessageRouterAbstractStandardMessageHandlerFactoryBean@Bean[@Bean name].[decapitalizedAnnotationClassShortName].[handler (or source)]
在定义上使用这些注释时,必须引用已声明的 Bean。 通道会自动声明为 iif 在应用程序上下文中尚不存在。@BeaninputChannel
使用 Java 配置,您可以使用方法级别上的任何(例如,)定义来跳过 Bean 注册,这是出于某种条件原因。 以下示例演示如何执行此操作:@Conditional@Profile@Bean
@Bean@ServiceActivator(inputChannel = "skippedChannel")@Profile("thing")public MessageHandler skipped() { return System.out::println;}与现有的 Spring 容器逻辑一起,消息传递端点 bean(基于注释)也没有注册。@ServiceActivator
创建带有注释的桥梁
从 4.0 版开始,Java 配置提供了 and 方法注释来标记类中的 bean。 这些确实是为了完整性而存在的,提供了一种方便的机制来声明 及其消息端点配置:@BridgeFrom@BridgeTo@BeanMessageChannel@ConfigurationBridgeHandler
@Beanpublic PollableChannel bridgeFromInput() { return new QueueChannel();}@Bean@BridgeFrom(value = "bridgeFromInput", poller = @Poller(fixedDelay = "1000"))public MessageChannel bridgeFromOutput() { return new DirectChannel();}@Beanpublic QueueChannel bridgeToOutput() { return new QueueChannel();}@Bean@BridgeTo("bridgeToOutput")public MessageChannel bridgeToInput() { return new DirectChannel();}您也可以将这些注释用作元注释。
为带注释的端点提供建议
请参阅使用注释为端点提供建议。
消息映射规则和约定
Spring 集成通过依赖一些默认规则并定义某些约定,实现了一个灵活的工具,将消息映射到方法及其参数,而无需提供额外的配置。 以下各节中的示例阐明了这些规则。
示例方案
下面的示例演示单个未批注的参数(对象或基元),该参数不是具有非 void 返回类型的对象或对象:MapProperties
public String doSomething(Object o);输入参数是消息有效负载。 如果参数类型与消息有效负载不兼容,则尝试使用 Spring 3.0 提供的转换服务对其进行转换。 返回值作为返回消息的有效负载合并。
下面的示例演示单个未批注的参数(对象或基元),该参数不是返回类型的 a 或 a:MapPropertiesMessage
public Message doSomething(Object o);输入参数是消息有效负载。 如果参数类型与消息有效负载不兼容,则尝试使用 Spring 3.0 提供的转换服务对其进行转换。 返回值是发送到下一个目标的新构造消息。
下面的示例演示单个参数,该参数是具有任意对象或基元返回类型的消息(或其子类之一):
public int doSomething(Message msg);输入参数本身为 . 返回值成为发送到下一个目标的有效负载。MessageMessage
下面的示例演示一个参数,该参数是一个(或其子类之一),其中返回类型为 (或其子类之一):MessageMessage
public Message doSomething(Message msg);输入参数本身为 . 返回值是发送到下一个目标的新构造值。MessageMessage
下面的示例演示类型或返回类型为 a 的单个参数:MapPropertiesMessage
public Message doSomething(Map m);这个有点意思。 尽管乍一看,它似乎很容易直接映射到消息标头,但始终优先考虑有效负载。 这意味着,如果有效负载的类型为 ,则此输入参数表示有效负载。 但是,如果有效负载不是类型 ,则转换服务不会尝试转换有效负载,并且输入参数将映射到消息标头。MessageMessageMapMessageMessageMap
下面的示例演示两个参数,其中一个是不是 或 对象的任意类型(对象或基元),另一个是类型或类型(无论返回如何):MapPropertiesMapProperties
public Message doSomething(Map h, <T> t);此组合包含两个输入参数,其中一个类型为 。 非参数(无论顺序如何)映射到有效负载,或(无论顺序如何)映射到消息头,为您提供一种与结构交互的很好的 POJO 方式。MapMapMessageMapPropertiesMessage
以下示例不显示任何参数(无论返回结果如何):
public String doSomething();此消息处理程序方法基于发送到此处理程序连接到的输入通道的消息调用。 但是,不会映射任何数据,因此使 act 成为调用处理程序的事件或触发器。 输出根据前面描述的规则进行映射。MessageMessage
以下示例显示没有参数和 void 返回:
public void soSomething();此示例与上一个示例相同,但不生成任何输出。
基于注释的映射
基于注释的映射是将消息映射到方法的最安全且最不明确的方法。 下面的示例演示如何将方法显式映射到标头:
public String doSomething(@Payload String s, @Header("someheader") String b)正如您稍后看到的,如果没有注释,此签名将导致不明确的条件。 但是,通过将第一个参数显式映射到有效负载,将第二个参数显式映射到消息标头的值,我们可以避免任何歧义。Messagesomeheader
以下示例与前面的示例几乎相同:
public String doSomething(@Payload String s, @RequestParam("something") String b)@RequestMapping或任何其他非 Spring 集成映射注释无关紧要,因此被忽略,使第二个参数未映射。 尽管第二个参数可以很容易地映射到有效负载,但只能有一个有效负载。 因此,注释使此方法不明确。
以下示例显示了另一种类似的方法,如果不是注释阐明意图,该方法将不明确:
public String foo(String s, @Header("foo") String b)唯一的区别是第一个参数隐式映射到消息有效负载。
以下示例显示了另一个签名,如果没有注释,该签名肯定会被视为不明确,因为它有两个以上的参数:
public String soSomething(@Headers Map m, @Header("something") Map f, @Header("someotherthing") String bar)此示例尤其成问题,因为它的两个参数是实例。 但是,使用基于注释的映射,很容易避免歧义。 在此示例中,第一个参数映射到所有消息头,而第二个和第三个参数映射到名为“something”和“someotherthing”的消息标头的值。 有效负载未映射到任何参数。Map
复杂场景
以下示例使用多个参数:
多个参数可能会在确定适当的映射方面产生很多歧义。 一般建议是使用 、 和 注释方法参数。 本节中的示例显示了导致引发异常的不明确条件。@Payload@Header@Headers
public String doSomething(String s, int i)这两个参数的权重相等。 因此,无法确定哪一个是有效载荷。
以下示例显示了类似的问题,仅具有三个参数:
public String foo(String s, Map m, String b)尽管 Map 可以很容易地映射到邮件头,但无法确定如何处理这两个 String 参数。
下面的示例演示了另一种不明确的方法:
public String foo(Map m, Map f)尽管有人可能会争辩说,一个可以映射到消息有效负载,另一个可以映射到消息头,但我们不能依赖顺序。Map
任何具有多个方法参数(不是 , ) 且具有未批注参数的方法签名都会导致不明确的条件并触发异常。Map<T>
下一组示例分别显示了导致歧义的多种方法。
具有多个方法的消息处理程序基于前面描述的相同规则进行映射(在示例中)。 但是,某些方案可能看起来仍然令人困惑。
以下示例显示了具有合法(可映射和明确)签名的多种方法:
public class Something { public String doSomething(String str, Map m); public String doSomething(Map m);}(无论方法具有相同名称还是不同名称都没有区别)。 可以映射到任一方法。 当消息有效负载可以映射到并且消息头可以映射到时,将调用第一种方法。 第二种方法也可以是候选方法,方法是仅将邮件头映射到 。 更糟糕的是,这两种方法都有相同的名称。 起初,由于以下配置,这可能看起来模棱两可:Messagestrmm
<int:service-activator input-channel="input" output-channel="output" method="doSomething"> <bean class="org.things.Something"/></int:service-activator>它之所以有效,是因为映射首先基于有效负载,然后基于其他所有内容。 换句话说,其第一个参数可以映射到有效负载的方法优先于所有其他方法。
现在考虑一个替代示例,它会产生一个真正不明确的条件:
public class Something { public String doSomething(String str, Map m); public String doSomething(String str);}这两种方法都具有可映射到消息有效负载的签名。 它们也有相同的名称。 此类处理程序方法将触发异常。 但是,如果方法名称不同,则可以使用属性影响映射(如下例所示)。 以下示例显示了具有两个不同方法名称的同一示例:method
public class Something { public String doSomething(String str, Map m); public String doSomethingElse(String str);}下面的示例演示如何使用属性来指示映射:method
<int:service-activator input-channel="input" output-channel="output" method="doSomethingElse"> <bean class="org.bar.Foo"/></int:service-activator>由于配置显式映射了方法,因此我们消除了歧义。doSomethingElse
【文章转自 盐城网站开发公司 http://www.1234xp.com/yancheng.html 复制请保留原URL】