Intercept作为一个极其强大的扩展机制,其理念几乎存在于所有知名框架中,诸如Spring,Mybatis,Tomcat等等都无一例外地提供了相应的支持,在保持自身框架本身整洁的同时,实现对各类业务场景的支持。而我们的Apache Camel也毫不意外地也提供了自己的Intercept实现,本文我们将尝试对Apache Camel的支持方式和实现逻辑进行一次探究,以期做到熟练运用。
1. 概述
Apache Camel中作为EIP实现者,诸如"当xxxx的时候,系统需要yyyy"的场景应该是属于常规需求了。Apache Camel提供了多种方式来完成对其的支持,本文将注意力集中到其中的一种实现方式 —— Intercept。
2. 源码解读
首先依然是本次的测试用例:
CamelTestUtil.defaultPrepareTest2(new RouteBuilder() {
@Override
public void configure() throws Exception {
intercept().process(new Processor() {
@Override
public void process(Exchange exchange) throws Exception {
Console.error("intercept - " + exchange.getIn().getBody());
}
}).id("interceptProcess");
// EmptyProcessor为一个空的Processor实现, 里面不作任何操作
from("stream:in?promptMessage=Enter something:")//
// 这里intercept输出 键入的内容
.setBody(constant("1")).id("setBody-1")//
// 这里intercept输出 上面的1
.process(EmptyProcessor.me).id("EmptyProcessor1")//
// 这里intercept输出 上面的1
.setBody(constant("2")).id("setBody-2")//
// 这里intercept输出 上面的2
.process(EmptyProcessor.me).id("EmptyProcessor2")//
// 这里intercept输出 上面的2
.to("stream:err") // 这里输出红色的2
// 这里输出 上面的2
.process(CamelLogRecordProcessor.me).id("logProcessor");
}
});
}
本次的源码解读按照之前的博客格式,依然是分为两部分:
2.1 启动时
首先让我们来看看在初始化阶段,Apache Camel是如何将Intercept功能组装进Camel执行链条中的。
以上用例启动之后,有以下两个比较重要的堆栈信息:
堆栈一(准备intercept实现者InterceptDefinition
):
- 以上截图自
RouteDefinitionHelper
工具类,笔者特意将注释部分进行了保留,从中我们可以得知Apache Camel通过将InterceptDefinition
实例作为output集合中的第一个,实现了在intercept优先于actual route进行处理。这样就可以实现拦截所有node的目标。 - 图中字段
intercept
字段指向的实际类型正是InterceptDefinition
。
堆栈二(装配Intercept功能):
以上截图中:
output
字段指向的是测试用例配置的intercept()
后配置的匿名Processor
接口实现类。interceptedTarget
字段指向的则是本次Route定义中的node,笔者本次截图是在第一次命中断点时候,因此这个node就是setBody()
。- 上图中断点命中的位置可以看到 —— 作为拦截逻辑的匿名
Processor
实现和Route定义节点setBody()
被封装为了一个Pipeline实例
(注意两者的先后顺序,这关系到之后的执行顺序)。 - 在本次测试用例中,以上截图中的断点我们将命中六次,这正好对应测试用例中定义的node数量。
- 跳转到以上堆栈图中的
DefaultChannel.initChannel()
中,我们就会发现intercept()
的实现是依赖于接口InterceptStrategy
提供的扩展功能的。这一点从InterceptDefinition
类间接继承自ProcessorDefinition
,而其对于createProcessor
方法的实现中就可见端倪。
2.2 执行时
在控制台敲入任意字符,我们将得到如下堆栈:
结合之前的系列博文,加之上一小节的初始化逻辑解析,我们可以很容易地解读出以上堆栈中表述的逻辑。
输入任意字符,我们将得到以下输出结果(结合上面的测试用例中的注释一起理解):
3. 扩展
其它诸如intercept()
后接when()
来进行条件拦截,或者interceptFrom()
,interceptSendToEndpoint()
,限于篇幅原因本次就不再详述了,读者感兴趣的可以自行阅读相关源码或者下方给出的官方链接。
4. 总结
- 对应intercept(),Apache Camel内部使用
InterceptDefinition
类来统筹整个配置组装初始化的工作。而具体的实现逻辑则依赖于接口InterceptStrategy
提供的扩展功能。这一点可以从InterceptDefinition
类间接继承自ProcessorDefinition
,而其对于createProcessor
方法的实现中就可见端倪。 - 对应intercept(),其执行时机位于匹配node执行之前。正如上面解析的,Apache Camel使用Pipeline来将intercept功能和用户自定义node组装在一起,确保每个node拦截逻辑的独立性。
5. Links
- Office Site - Intercept
- Apache Camel源码研究之InterceptStrategy
- Apache Camel源码研究之ProcessorDefinition
- 《Camel In Action》 P178, 288, P377