跳到主要内容

DSL 基础知识

QWen Plus 中英对照 DSL Basics

org.springframework.integration.dsl 包包含前面提到的 IntegrationFlowBuilder API 以及多个 IntegrationComponentSpec 实现,这些实现也是构建器,并提供流畅的 API 来配置具体的端点。IntegrationFlowBuilder 基础设施为基于消息的应用程序提供了常见的 企业集成模式(EIP),例如通道、端点、轮询器和通道拦截器。

重要

IntegrationComponentSpecFactoryBean 的实现,因此其 getObject() 方法不能从 bean 定义中调用。IntegrationComponentSpec 实现必须保持原样用于 bean 定义,框架将管理其生命周期。对于目标 IntegrationComponentSpec 类型(一个 FactoryBean 值)的 bean 方法参数注入,必须用于 IntegrationFlow bean 定义,而不是 bean 方法引用。

端点在 DSL 中以动词形式表示,以提高可读性。以下列表包括常见的 DSL 方法名称和相关联的 EIP 端点:

  • 转换 → Transformer

  • 过滤 → Filter

  • 处理 → ServiceActivator

  • 分割 → Splitter

  • 聚合 → Aggregator

  • 路由 → Router

  • 桥接 → Bridge

概念上,集成过程是通过将这些端点组合成一个或多个消息流来构建的。注意,EIP 并没有正式定义“消息流”这个术语,但将其视为使用知名消息模式的工作单元是有用的。DSL 提供了一个 IntegrationFlow 组件来定义渠道和它们之间的端点的组合,但现在 IntegrationFlow 只在配置中起作用,用于填充应用程序上下文中的实际 bean,并且在运行时并不使用。然而,IntegrationFlow 的 bean 可以作为 Lifecycle 自动装配,以控制整个流程的 start()stop(),这些操作会委托给与此 IntegrationFlow 关联的所有 Spring Integration 组件。以下示例使用 IntegrationFlow 流式 API 通过 IntegrationFlowBuilder 中的 EIP 方法定义一个 IntegrationFlow bean:

@Bean
public IntegrationFlow integerFlow() {
return IntegrationFlow.from("input")
.<String, Integer>transform(Integer::parseInt)
.get();
}
java

transform 方法接受一个 lambda 作为终点参数来操作消息有效负载。该方法的实际参数是一个 GenericTransformer<S, T> 实例。因此,这里可以使用任何提供的转换器(如 ObjectToJsonTransformerFileToStringTransformer 等)。

在内部,IntegrationFlowBuilder 识别 MessageHandler 和其端点,分别是 MessageTransformingHandlerConsumerEndpointFactoryBean。考虑另一个例子:

@Bean
public IntegrationFlow myFlow() {
return IntegrationFlow.from("input")
.filter("World"::equals)
.transform("Hello "::concat)
.handle(System.out::println)
.get();
}
java

前面的例子组成了一串 Filter → Transformer → Service Activator 的序列。这个流程是“单向的”。也就是说,它不会提供回复消息,而只是将有效负载打印到 STDOUT。端点通过使用直接通道自动连接在一起。

important

Lambdas 和 Message<?> 参数

在 EIP 方法中使用 lambdas 时,"输入" 参数通常是消息的有效负载。如果你想访问整个消息,请使用其中一个重载方法,该方法将 Class<?> 作为第一个参数。例如,这行不通:

.<Message<?>, Foo>transform(m -> newFooFromMessage(m))
java

这将在运行时因 ClassCastException 失败,因为 lambda 不保留参数类型,框架会尝试将有效负载转换为 Message<?>

相反,使用:

.(Message.class, m -> newFooFromMessage(m))
java
important

Bean 定义覆盖

Java DSL 可以为在流定义中内联定义的对象注册 bean,也可以重用现有的、注入的 bean。如果为内联对象和现有 bean 定义了相同的 bean 名称,则会抛出 BeanDefinitionOverrideException,指出这种配置是错误的。然而,当你处理 prototype bean 时,无法从集成流处理器检测到现有的 bean 定义,因为每次我们从 BeanFactory 调用 prototype bean 时,我们都会得到一个新实例。这样提供的实例就会在 IntegrationFlow 中直接使用,而不会进行任何 bean 注册或对现有 prototype bean 定义的任何可能检查。但是,如果该对象具有显式的 id 并且以 prototype 作用域定义了名为此 id 的 bean,则会为此对象调用 BeanFactory.initializeBean()