该版本仍在开发中,尚未被视为稳定。对于最新的稳定版本,请使用 Spring Integration 7.0.0spring-doc.cadn.net.cn

信息发布

(面向切面编程)AOP 消息发布功能允许你构造并发送一条消息,作为方法调用的副产品。例如,假设你有一个组件,每当该组件的状态发生变化时,你都希望收到消息通知。发送此类通知最简单的方式是将消息发送到专用通道,但你如何将改变对象状态的方法调用连接到消息发送过程?通知消息应如何结构化?AOP 消息发布功能采用配置驱动的方法来处理这些职责。spring-doc.cadn.net.cn

消息发布配置

Spring 集成提供两种方式:XML 配置和注释驱动(Java)配置。spring-doc.cadn.net.cn

注释驱动配置,具有@Publisher注解

注释驱动的方法允许你用@Publisher注释以指定“通道”属性。从版本5.1开始,要启用此功能,必须使用@EnablePublisher部分注释@Configuration类。 看配置和@EnableIntegration了解更多信息。消息由方法调用的返回值构建,并发送到“channel”属性指定的通道。为了进一步管理消息结构,你也可以结合使用两者@Payload@Header附注。spring-doc.cadn.net.cn

在内部,Spring Integration 的消息发布功能通过定义出版商注释顾问以及Spring表达式语言(SpEL),为你提供了相当大的灵活性和对结构的控制消息它出版。spring-doc.cadn.net.cn

出版商注释顾问定义并绑定以下变量:spring-doc.cadn.net.cn

  • #return:绑定返回值,允许你引用返回值或其属性(例如,#return 什么的,其中“某物”是绑定到的对象的属性#return)spring-doc.cadn.net.cn

  • #exception:如果方法调用抛出了异常,则绑定异常spring-doc.cadn.net.cn

  • #args: 绑定方法参数,以便你可以按名称提取单个参数(例如,#args.fname)spring-doc.cadn.net.cn

请考虑以下例子:spring-doc.cadn.net.cn

@Publisher
public String defaultPayload(String fname, String lname) {
  return fname + " " + lname;
}

在上述示例中,消息的结构如下:spring-doc.cadn.net.cn

  • 消息有效载荷是方法的返回类型和值。这是默认值。spring-doc.cadn.net.cn

  • 新构建的消息会发送到默认发布信道,该通道配置了注释后处理机(本节后面将详细介绍)。spring-doc.cadn.net.cn

以下示例与前例相同,但不使用默认发布渠道:spring-doc.cadn.net.cn

@Publisher(channel="testChannel")
public String defaultPayload(String fname, @Header("last") String lname) {
  return fname + " " + lname;
}

我们不使用默认的发布通道,而是通过设置@Publisher注解。 我们还会添加一个@Header注释,使得名为“LAST”的消息头与“lname”方法参数的值相同。该头会添加到新构建的消息中。spring-doc.cadn.net.cn

以下示例与前述几乎相同:spring-doc.cadn.net.cn

@Publisher(channel="testChannel")
@Payload
public String defaultPayloadButExplicitAnnotation(String fname, @Header String lname) {
  return fname + " " + lname;
}

唯一的区别是我们使用@Payload在方法上添加注释,明确指定方法的返回值应作为消息的有效载荷。spring-doc.cadn.net.cn

以下示例通过使用 Spring 表达式语言在@Payload注释以进一步指导框架如何构建消息:spring-doc.cadn.net.cn

@Publisher(channel="testChannel")
@Payload("#return + #args.lname")
public String setName(String fname, String lname, @Header("x") int num) {
  return fname + " " + lname;
}

在前述示例中,消息是方法调用返回值和“lname”输入参数的串接。名为“x”的Message头由“num”输入参数决定其值。该头会添加到新构建的消息中。spring-doc.cadn.net.cn

@Publisher(channel="testChannel")
public String argumentAsPayload(@Payload String fname, @Header String lname) {
  return fname + " " + lname;
}

在前面的例子中,你会看到另一种用法@Payload注解。 这里,我们注释一个方法参数,使其成为新构造消息的有效载荷。spring-doc.cadn.net.cn

和 Spring 中大多数其他注释驱动功能一样,你需要注册一个后处理器(出版商注释豆后处理器). 以下示例展示了如何实现:spring-doc.cadn.net.cn

<bean class="org.springframework.integration.aop.PublisherAnnotationBeanPostProcessor"/>

为了更简洁的配置,你可以使用命名空间支持,如下示例所示:spring-doc.cadn.net.cn

<int:annotation-config>
    <int:enable-publisher default-publisher-channel="defaultChannel"/>
</int:annotation-config>

对于 Java 配置,你必须使用@EnablePublisher注释,如下示例所示:spring-doc.cadn.net.cn

@Configuration
@EnableIntegration
@EnablePublisher("defaultChannel")
public class IntegrationConfiguration {
    ...
}

从5.1.3版本开始,<in:enable-publisher(能出版者>组件,以及@EnablePublisher注释具有代理目标类次序调谐代理工厂配置。spring-doc.cadn.net.cn

与其他春季注释类似 (@Component,@Scheduled,依此类推),你也可以使用@Publisher作为元注释。这意味着你可以自定义自己的注释,这些注释的处理方式与@Publisher本身。 以下示例展示了如何实现:spring-doc.cadn.net.cn

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Publisher(channel="auditChannel")
public @interface Audit {
...
}

在前面的例子中,我们定义了@Audit注释本身被注释为@Publisher. 另外请注意,你可以定义一个渠道在元注释上用属性封装消息发送位置。现在你可以用@Audit注释,如下示例所示:spring-doc.cadn.net.cn

@Audit
public String test() {
    return "Hello";
}

在上述例子中,每次调用测试()方法会生成一条消息,其有效载荷由返回值生成。每个消息都发送到名为审计频道. 该技术的一个好处是可以避免同一通道名称在多个注释中重复。你还可以在你自己的、可能是领域特定的注释与框架提供的注释之间提供一定程度的间接连接。spring-doc.cadn.net.cn

你也可以对类进行注释,这样你可以将该注释的属性应用到该类的每个公共方法上,如下示例所示:spring-doc.cadn.net.cn

@Audit
static class BankingOperationsImpl implements BankingOperations {

  public String debit(String amount) {
     . . .

  }

  public String credit(String amount) {
     . . .
  }

}

基于XML的方法<出版拦截者>元素

基于XML的方法允许你配置与基于命名空间的配置相同的AOP消息发布功能MessagePublishingInterceptor. 它确实比注释驱动的方法有一些优势,因为它允许你使用AOP点切割表达式,从而可能同时拦截多个方法,或者拦截并发布没有源代码的方法。spring-doc.cadn.net.cn

要配置使用 XML 的消息发布,只需完成以下两件事:spring-doc.cadn.net.cn

以下示例展示了如何配置出版拦截者元素:spring-doc.cadn.net.cn

<aop:config>
  <aop:advisor advice-ref="interceptor" pointcut="bean(testBean)" />
</aop:config>
<publishing-interceptor id="interceptor" default-channel="defaultChannel">
  <method pattern="echo" payload="'Echoing: ' + #return" channel="echoChannel">
    <header name="things" value="something"/>
  </method>
  <method pattern="repl*" payload="'Echoing: ' + #return" channel="echoChannel">
    <header name="things" expression="'something'.toUpperCase()"/>
  </method>
  <method pattern="echoDef*" payload="#return"/>
</publishing-interceptor>

<出版拦截者>配置看起来与基于注释的方法相当相似,并且它也利用了 Spring 表达式语言的强大功能。spring-doc.cadn.net.cn

在上述例子中,执行回波方法测试豆渲染消息结构如下:spring-doc.cadn.net.cn

第二种方法与第一种非常相似。这里,每个以“repl”开头的方法都表示消息结构如下:spring-doc.cadn.net.cn

第二种方法,映射任意以 开头的方法的执行回声防御,产生消息结构如下:spring-doc.cadn.net.cn

对于简单的映射规则,你可以依赖发行人默认值,如下示例所示:spring-doc.cadn.net.cn

<publishing-interceptor id="anotherInterceptor"/>

前例将每个匹配点割表达式的方法的返回值映射到有效载荷,并发送给默认信道. 如果你没有具体说明默认频道(如前例所示),消息被发送到全局零通道(相当于/dev/null).spring-doc.cadn.net.cn

异步出版

发布发生在组件执行的同一线程中。 所以,默认情况下,它是同步的。 这意味着整个消息流必须等待发布者的流程完成。 然而,开发者往往想要完全相反的效果:利用消息发布功能启动异步流。 例如,你可能托管一个服务(HTTP、WS等),它会接收到一个远程请求。 你可能想把这个请求内部提交到一个可能需要一段时间的流程中。 不过,你也可以尽快回复用户。 因此,你可以用“output-channel”或“replyChannel”首部向呼叫者发送类似确认的简单回复,同时利用消息发布者功能启动复杂流程,而不必向输出通道发送处理请求。spring-doc.cadn.net.cn

以下示例中的服务接收到一个复杂的负载(需要进一步发送处理),但同时需要以简单的确认回复呼叫者:spring-doc.cadn.net.cn

public String echo(Object complexPayload) {
     return "ACK";
}

因此,我们不再将复杂的流程连接到输出通道,而是使用消息发布功能。 我们配置它创建新消息,方法是使用服务方法的输入参数(如前例所示),并将其发送到“localProcessChannel”。 为了确保该流是异步的,我们只需将其发送到任意类型的异步通道(执行者频道下一个例子)。 以下示例展示了如何实现异步出版拦截者:spring-doc.cadn.net.cn

<int:service-activator  input-channel="inputChannel" output-channel="outputChannel" ref="sampleservice"/>

<bean id="sampleService" class="test.SampleService"/>

<aop:config>
  <aop:advisor advice-ref="interceptor" pointcut="bean(sampleService)" />
</aop:config>

<int:publishing-interceptor id="interceptor" >
  <int:method pattern="echo" payload="#args[0]" channel="localProcessChannel">
    <int:header name="sample_header" expression="'some sample value'"/>
  </int:method>
</int:publishing-interceptor>

<int:channel id="localProcessChannel">
  <int:dispatcher task-executor="executor"/>
</int:channel>

<task:executor id="executor" pool-size="5"/>

处理这种情况的另一种方法是使用窃听。 参见《窃听》spring-doc.cadn.net.cn

基于计划触发器生成和发布消息

在前几节中,我们介绍了消息发布功能,它构建并发布消息作为方法调用的副产品。 然而,在这些情况下,你仍然需要自行调用该方法。 Spring Integration 2.0 新增了对定时消息生成器和发布者的支持表达属性位于“Inbound-Channel-Adapter”元素上。 你可以根据多个触发器进行调度,其中任何一个都可以在“poller”元素上配置。 目前,我们支持克朗,固定费率,固定延迟以及你实现并由“trigger”属性值引用的任何自定义触发器。spring-doc.cadn.net.cn

如前所述,针对预定制片人和出版商的支持通过<入站通道适配器>XML元素。 请考虑以下例子:spring-doc.cadn.net.cn

<int:inbound-channel-adapter id="fixedDelayProducer"
       expression="'fixedDelayTest'"
       channel="fixedDelayChannel">
    <int:poller fixed-delay="1000"/>
</int:inbound-channel-adapter>

前述示例创建了一个入站通道适配器,构建消息,其有效载荷是定义于表达属性。 这类消息每次由固定延迟属性发生。spring-doc.cadn.net.cn

以下示例与前例类似,但使用了固定费率属性:spring-doc.cadn.net.cn

<int:inbound-channel-adapter id="fixedRateProducer"
       expression="'fixedRateTest'"
       channel="fixedRateChannel">
    <int:poller fixed-rate="1000"/>
</int:inbound-channel-adapter>

固定费率属性允许你以固定速率发送消息(从每个任务开始时间开始计算)。spring-doc.cadn.net.cn

以下示例展示了如何应用一个由克朗属性:spring-doc.cadn.net.cn

<int:inbound-channel-adapter id="cronProducer"
       expression="'cronTest'"
       channel="cronChannel">
    <int:poller cron="7 6 5 4 3 ?"/>
</int:inbound-channel-adapter>

以下示例展示了如何在消息中插入额外头部:spring-doc.cadn.net.cn

<int:inbound-channel-adapter id="headerExpressionsProducer"
       expression="'headerExpressionsTest'"
       channel="headerExpressionsChannel"
       auto-startup="false">
    <int:poller fixed-delay="5000"/>
    <int:header name="foo" expression="6 * 7"/>
    <int:header name="bar" value="x"/>
</int:inbound-channel-adapter>

额外的消息头可以取标量值或Spring表达式的评估结果。spring-doc.cadn.net.cn

如果你需要实现自己的自定义触发器,可以使用触发属性提供指向任何实现org.springframework.scheduling.Trigger接口。 以下示例展示了如何实现:spring-doc.cadn.net.cn

<int:inbound-channel-adapter id="triggerRefProducer"
       expression="'triggerRefTest'" channel="triggerRefChannel">
    <int:poller trigger="customTrigger"/>
</int:inbound-channel-adapter>

<beans:bean id="customTrigger" class="o.s.scheduling.support.PeriodicTrigger">
    <beans:constructor-arg value="9999"/>
</beans:bean>