Apache Camel源码研究之DataFormat

article/2025/1/17 3:10:42

在上一篇博客Apache Camel源码研究之TypeConverter中,我们介绍了Apache Camel实现数据格式转换的一种实现方式,本文中我们将介绍另外一种实现方式 —— DataFormat。

1. 概述

相较于前面博客介绍过的TypeConverter,DataFormat在平时应用中应该更容易被研发人员所感知,毕竟其所支持的json,yaml,xml等转换现在已经成为事实的数据传输格式。本文接下来的部分我们将就其实现原理以及如何进行自定义扩展作出一些笔者自己的理解。

2. 源码解读

首先是本次的测试用例:

@Test
public void marshal() throws Exception {// CamelTestUtil.defaultPrepareTest2(new RouteBuilder() {@Overridepublic void configure() throws Exception {from("stream:in?promptMessage=Enter something:")//.setBody(constant(Collections.singletonList("123")))//.marshal().json(JsonLibrary.Gson) // 将对象转换为JSON字符串//.setBody(constant("{'name':'LQ'}"))////.unmarshal().json(JsonLibrary.Gson)//	将JSON字符串转换为对象			.to("stream:out");}});
}

接下来,我们首先探究的是Camel是如何将这段转换逻辑插入到Route的执行流程中的:

跟随上述Route中定义的.marshal().json(JsonLibrary.Gson)可以看出 Apache Camel是将一个 MarshalDefinition实例添加到RouteDefinition中,注意该MarshalDefinition类型是间接继承自ProcessorDefinition<Type>类的,按照我们之前在博客Apache Camel源码研究之ProcessorDefinition中讨论的内容,则可以很轻松得找到如下内容:

// MarshalDefinition.createProcessor()
@Override
public Processor createProcessor(RouteContext routeContext) {// 专门的类和静态方法来根据用户配置创建相应的DataFormat实例// 这个过程牵扯到的细节较多,我们将在下面以专门的小节进行讨论DataFormat dataFormat = DataFormatDefinition.getDataFormat(routeContext, getDataFormatType(), ref);// 将DataFormat实例以Processor的形式介入到Apache Camel的执行逻辑链条中// === 这里多说一句的是, 与DataFormat类似的是 Camel中的 Expression 概念也是以类似的方式介入到Camel的执行逻辑链条中的,更具体的我们放到下一篇博客中return new MarshalProcessor(dataFormat);
}

接下来让我们看看 MarshalProcessor有哪些需要关注的:

// MarshalProcessor
@Override
protected void doStart() throws Exception {// inject CamelContext on data formatif (dataFormat instanceof CamelContextAware) {((CamelContextAware) dataFormat).setCamelContext(camelContext);}// add dataFormat as service which will also start the service// (false => we and handling the lifecycle of the dataFormat)getCamelContext().addService(dataFormat, false);
}@Override
protected void doStop() throws Exception {ServiceHelper.stopService(dataFormat);getCamelContext().removeService(dataFormat);
}

以上可以看到正是因为DataFormat被Processor封装,在获取并入到Apache Camel执行逻辑链条权利的同时,也通过MarshalProcessor覆写自基类的doStartdoStop方法享受到了共享生命周期的好处。DataFormat可以借此做一些自定义的初始化和销毁操作。

在了解完初始化时的逻辑之后,执行时候的逻辑也就一目了然了:DataFormat接口所定义的marshalunmarshal两个方法,在执行时序上依然是借助Apache Camel中的灵魂组件Processor 来实现的。通过在用户指定的时机采用同样的方式回调相应的Processor组件,来将原始数据格式转换为目标数据类型,这种组件化的实现方式隐藏了实现细节,增加了系统的弹性。感兴趣的读者可以参见 Apache Camel源码研究之启动 。

3. DataFormat扩展机制

在了解了DataFormat的基本载入和执行机制之后,我们来关注一点更加深入的内容 —— Apache Camel是如何实现DataFormat的可扩展性的。相比较之下,org.apache.camel.model.dataformat PACKAGE下DataFormatDefinition的子类,数量远远大于org.apache.camel.impl PACKAGE下DataFormat接口的实现类。而且Camel作为一个久负盛名,被多款商业级ESB平台作为核心组件的框架,将所有的实现类和相应依赖硬编码到自身的core JAR中肯定是不可能的。

经过一番探究,我们找到如下关键类和配置文件:

  1. DataFormatDefinition
    • 其构造函数约定了 dataFormatName,Camel将以此为标识符从容器中或者相应的配置目录下查找相应的DataFormat实现类。
    • createDataFormat(),作为一个protected修饰级别的方法,绝大部分子类正是借助这个方法的覆写来实现自身DataFormat实现类的构造的。
    • getDataFormat(RouteContext routeContext)。正是这个方法回掉了上面的createDataFormat()方法,在保证对外接口统一稳定的前提下,实现了强大的可扩展性。
  2. DefaultDataFormatResolver ,以DataFormatResolver接口形式直接在DefaultCamelContext中实例化。内部定义了查找目录路径"META-INF/services/org/apache/camel/dataformat/",Apache Camel将该路径加上前面提到的dataFormatName指示的文件名,从中读取到DataFormat接口实现类,
  3. 最终堆栈图如下:
    堆栈图
    camel-core下内置的DataFormat。
    camel-core下内置的DataFormat
    XStream扩展

4. 自定义扩展

下面让我们来尝试一个自定义DataFormat:

public static class EbcdicDataFormat implements DataFormat {// US EBCDIC 037 code pageprivate String codepage = "CP037";public EbcdicDataFormat() {}public EbcdicDataFormat(String codepage) {this.codepage = codepage;}@Overridepublic void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception {final String str = ExchangeHelper.convertToMandatoryType(exchange, String.class, graph);stream.write(str.getBytes(codepage));}@Overridepublic Object unmarshal(Exchange exchange, InputStream stream) throws Exception {final byte[] bytes = ExchangeHelper.convertToMandatoryType(exchange, byte[].class,stream);return new String(bytes, codepage);}
}

相应的测试用例:

@Test
public void custom() throws Exception {// CamelTestUtil.defaultPrepareTest2(new RouteBuilder() {@Overridepublic void configure() throws Exception {DataFormat df = new EbcdicDataFormat("CP037");from("stream:in?promptMessage=Enter something:")//// 相较于TypeConverter, DataFormat的使用就必须显式调用了.marshal(df) //.to("stream:err");}});
}

5. DataFormat实现

最后让我们看看DataFormat接口的定义,以构建一个全局的认识和理解:

public interface DataFormat {/*** Marshals the object to the given Stream.*/void marshal(Exchange exchange, Object graph, OutputStream stream) throws Exception;/*** Unmarshals the given stream into an object.*/Object unmarshal(Exchange exchange, InputStream stream) throws Exception;
}

正如上述定义,该接口只声明了两个契约marshalunmarshal(命名上也可以看出,这基本属于共识了,诸如Spring中的OXM, JAXB等等也是同样的),通过这对正/反向操作完成完整数据格式转换的闭环操作。

DataFormat实现类

6. Links

  1. 《Camel In Action》P77
  2. 《Apache Camel Developer’s CookBook》P100
  3. Office Site

http://chatgpt.dhexx.cn/article/yA24q3rv.shtml

相关文章

springboot引入ackson Dataformat XML后原本返回json的却返回xml

springboot引入ackson Dataformat XML后原本返回json的却返回xml springboot引入jackson Dataformat XML后原本返回json的却返回xml问题原因解决方式最后说明 springboot引入jackson Dataformat XML后原本返回json的却返回xml 今天项目需要生成xml文件&#xff0c;才引入了jac…

Error:java: 读取D:\apache-maven-3.6.1\req123\com\fasterxml\jackson\dataformat\jackson-dataformat-smile

Error:java: 读取D:\apache-maven-3.6.1\req123\com\fasterxml\jackson\dataformat\jackson-dataformat-smile\2.11.4\jackson-dataformat-smile-2.11.4.jar时出错; error in opening zip file 报错&#xff1a; 本地jar文件损坏 解决&#xff1a; 删除其jar包所在的文件夹…

@JsonFormat与@DataFormAT注解的区别

JsonFormat与DateTimeFormat注解的区别 JsonFormat主要是后台到前台的时间格式的转换DateTimeFormat主要是前后到后台的时间格式的转换 例子&#xff1a; pattern&#xff1a; 日期格式 timezone: 时区

Java常用类(Math类、Arrays类、Calendar类、Date类以及DataFormat类)

目录 一、Math类 1.主要用途 2.如何使用 &#xff08;1&#xff09;导包 &#xff08;2&#xff09;记住常用的方法名 &#xff08;3&#xff09;选择适当的场景&#xff0c;对其应用 3.常用方法整理 二、Arrays类 1.主要用途 2.使用方式同Math类相同 3.常用方法 三…

利用jackson-dataformat-csv读写csv文件

利用jackson-dataformat-csv读写csv文件 csv是comma-separated values的缩写&#xff0c;这类文件在日常项目中有时比较常见。sql工具一般具有将数据库数据导入、导出csv格式。 利用jackson-dataformat-csv读写csv文件&#xff0c;重点在与两个类&#xff1a;CsvMapper和CsvSc…

FormData详解

FormData 接口提供了一种表示表单数据的键值对 key/value 的构造方式&#xff0c;并且可以轻松的将数据通过XMLHttpRequest.send() 方法发送出去&#xff0c;本接口和此方法都相当简单直接。如果表单 enctype 属性设为 multipart/form-data &#xff0c;则会使用表单的 submit(…

源码解析-为什么引入了jackson-dataformat-xml 包我的接口全变成了xml格式?

本文从引入jackson-dataformat-xml 之后接口全变成xml 现象开始&#xff0c;一步步排查代码原因&#xff0c;并提出解决方案。希望能对遇到相关问题的人有所帮助 新调用上游一个接口&#xff0c;增加了对方的一个api包&#xff0c;没修改任何逻辑&#xff0c;接口却从json返回…

Java-常用API(StringBuffer,Math,Date,DataFormat,Calender,Runtime,System,包装类)

Java常用API StringBuffer 为了解决String字符串操作导致的内存冗余&#xff0c;提高效率&#xff0c;Java中提供了StringBuffer和StringBuilder来操作字符串&#xff0c;并且提供了很多方法&#xff0c;便于程序员开发 StringBuffer和StringBuilder中都有char类型可变长数组…

Java知识点--DataFormat,SimpleDateFormat和DateTimeFormatter

Java知识点–DataFormat&#xff0c;SimpleDateFormat和DateTimeFormatter 这个知识点没什么难懂的地方&#xff0c;就不一一赘述了。 import java.text.DateFormat; import java.util.Date; import java.util.Locale;public class test01 {public static void main(String[]…

04、添加 com.fasterxml.jackson.dataformat -- jackson-dataformat-xml 依赖报错

Correct the classpath of your application so that it contains a single, compatible version of com.fasterxml.jackson.dataformat.xml.XmlMapper 解决&#xff1a; 改用其他版本&#xff0c;我没写版本号&#xff0c;springboot自己默认的是 2.11.4 版本 成功启动项目…

Excel表格样式CellStyle的DataFormat可选值

Excel表格样式CellStyle的DataFormat可选值 干啥子遇到的问题DataFormat可选项源码查看出处1、访问https://poi.apache.org/apidocs/4.1/ ,全局搜索CellStyle2、找到其对应的类-BuiltinFormats 干啥子 解决表格样式的设置&#xff0c;找到DataFormat可选值。 遇到的问题 在使…

java中的Date类,DataFormat类及Calendar类的使用详解

Date类的构造方法 Date类拥有多个构造函数&#xff0c;只是部分已经过时&#xff0c;但是其中有未过时的构造函数可以把毫秒值转成日期对象。 /* * Date类的long参数的构造方法 * Date(long ) 表示毫秒值 * 传递毫秒值,将毫秒值转成对应的日期对象 * 结果为…

Java 常用类Data和Format类 使用教程

一、Date类使用方法。 1. new Date() 返回当前时间 Date date new Date(); System.out.println(date);//输出当前的时间。源码解释为&#xff1a; 2. new Date(10006060*24); 返回 从 Fri Jan 01 08:00:00 CST 1970经过1天的时间 long time 1000*60*60*24; Date date n…

矩阵按键行列反转扫描法

51单片机 | 矩阵键盘行扫描 ———————————————————————————————————————————— 分类&#xff1a; 按结构原理分&#xff1a; 触点式开关按键无触点开关按键 接入方式 独立式按键矩阵式键盘 ———————————————————…

基于GD32矩阵按键程序实现

目录 一、简介 二、原理图 三、程序实现 一、简介 矩阵键盘&#xff0c;也称矩阵按键&#xff0c;是为了节约单片机IO口占用所引入的一种外设。 二、矩阵按键的原理图 三、程序实现 GPIO初始化引脚代码 /*!\brief Init Key Function\param[in] none\param[out] non…

4、按键(独立/矩阵按键)

一、 独立按键原理 按键在闭合和断开时&#xff0c;触点会存在抖动现象。 PS:定义小灯时&#xff0c;如果定义为#define led P2&#xff0c;这样按下K1时八个灯就会同时熄灭或点亮&#xff0c;&#xff0c;当然&#xff0c;如果要其中几个灯点亮&#xff0c;就分别定义…

蓝桥杯单片机 独立按键与矩阵按键

文章目录 前言一、蓝桥杯按键原理图二、独立按键与矩阵按键处理思路 1.独立按键2.矩阵按键代码实现总结 前言 按键设计一般分为两种&#xff1a;独立按键和矩阵键盘。按键数量较少的用前者&#xff0c;按键数量较多的用后者。虽然两种设计都是操作按键&#xff0c;但是其键盘扫…

单片机STM32入门——(3)矩阵按键

单片机STM32入门——&#xff08;3&#xff09;矩阵按键 1.理论分析1.1键盘扫描方式1.2行扫描逻辑1.3列扫描逻辑 2.程序编写2.1按键扫描程序2.1.1按键初始化2.1.2按键扫描程序头文件2.1.3行扫描函数2.1.3列扫描函数 2.2主程序 1.理论分析 1.1键盘扫描方式 我们所用到的键盘为…

51单片机之按键(独立按键矩阵按键)

难的东西学不会是因为简单的知识没学好 基础不牢&#xff0c;地动山摇 按键 1.1基础温习 &#xff08;1&#xff09;按键的物理结构 &#xff08;2&#xff09;单片机引脚有两种状态&#xff08;I/O&#xff09;输入【读】或者输出【写】 &#xff08;3&#xff09;上拉电阻…

51学习-矩阵按键篇

使用并行接口方式连接键盘&#xff0c;对独立式键盘而言&#xff0c;8根I/O口线可以接 **8** 个按键&#xff0c;而对矩阵式键盘而言&#xff0c;8根I/O口线最多可以接 **64** 个按键。 项目(工程)的时候&#xff0c;我们经常要用到比较多的按键&#xff0c;而且IO资源紧张&am…