DSL 基础知识
org.springframework.integration.dsl
包包含前面提到的 IntegrationFlowBuilder
API 以及多个 IntegrationComponentSpec
实现,这些实现也是构建器,并提供流畅的 API 来配置具体的端点。IntegrationFlowBuilder
基础设施为基于消息的应用程序提供了常见的 企业集成模式(EIP),例如通道、端点、轮询器和通道拦截器。
重要
IntegrationComponentSpec
是 FactoryBean
的实现,因此其 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();
}
transform
方法接受一个 lambda 作为终点参数来操作消息有效负载。该方法的实际参数是一个 GenericTransformer<S, T>
实例。因此,这里可以使用任何提供的转换器(如 ObjectToJsonTransformer
、FileToStringTransformer
等)。
在内部,IntegrationFlowBuilder
识别 MessageHandler
和其端点,分别是 MessageTransformingHandler
和 ConsumerEndpointFactoryBean
。考虑另一个例子:
@Bean
public IntegrationFlow myFlow() {
return IntegrationFlow.from("input")
.filter("World"::equals)
.transform("Hello "::concat)
.handle(System.out::println)
.get();
}
前面的例子组成了一串 Filter → Transformer → Service Activator
的序列。这个流程是“单向的”。也就是说,它不会提供回复消息,而只是将有效负载打印到 STDOUT。端点通过使用直接通道自动连接在一起。
Lambdas 和 Message<?>
参数
在 EIP 方法中使用 lambdas 时,"输入" 参数通常是消息的有效负载。如果你想访问整个消息,请使用其中一个重载方法,该方法将 Class<?>
作为第一个参数。例如,这行不通:
.<Message<?>, Foo>transform(m -> newFooFromMessage(m))
这将在运行时因 ClassCastException
失败,因为 lambda 不保留参数类型,框架会尝试将有效负载转换为 Message<?>
。
相反,使用:
.(Message.class, m -> newFooFromMessage(m))
Bean 定义覆盖
Java DSL 可以为在流定义中内联定义的对象注册 bean,也可以重用现有的、注入的 bean。如果为内联对象和现有 bean 定义了相同的 bean 名称,则会抛出 BeanDefinitionOverrideException
,指出这种配置是错误的。然而,当你处理 prototype
bean 时,无法从集成流处理器检测到现有的 bean 定义,因为每次我们从 BeanFactory
调用 prototype
bean 时,我们都会得到一个新实例。这样提供的实例就会在 IntegrationFlow
中直接使用,而不会进行任何 bean 注册或对现有 prototype
bean 定义的任何可能检查。但是,如果该对象具有显式的 id
并且以 prototype
作用域定义了名为此 id 的 bean,则会为此对象调用 BeanFactory.initializeBean()
。