- 浏览: 1603487 次
- 性别:
- 来自: 杭州
-
文章分类
最新评论
-
jsrgzhangzhiyong:
关于null值的转换还是感觉不太友好,就像 mapstruct ...
我也造了个轮子:BeanMapping(属性拷贝) -
he037:
a417930422 写道引用使用EPHEMERAL会引出一个 ...
基于zookeeper的分布式lock实现 -
seancheer:
qianshangding 写道首先节点启动后,尝试读取本地的 ...
zookeeper学习记录三(session,watcher,persit机制) -
雪夜归人:
您好,我想咨询一下,开源的canal都能支持mysql的哪些版 ...
Canal BinlogChange(mysql5.6) -
zhoudengyun:
copy 一份做记录,后续学习,请知悉
阿里巴巴开源项目: 基于mysql数据库binlog的增量订阅&消费
背景
最近公司在大力推进服务化平台,我在中间主要做一个orm框架,解决model和数据源之间的映射问题。这里不选择已有的hibrenate,主要考虑自己公司的一些特殊场景:比如多数据源(数据库,搜索引擎,memcached,nosql等),同时可以 做一些特殊优化,比如多数据源加载时采用并行加载(我之间做的一个工具包 (业务层)异步并行加载技术分析和设计)
也不多罗嗦,我主要负责从多个数据源搜集回来的数据,构造成对应的model对象。可以看一下具体的分析过程。
分析
每个数据源采集回来的数据可能比较凌乱,有map对象,POJO对象,至于这个映射过程。因为是在老系统上实施,以前都没有service的概念,在数据库的设计会很比较乱,都是来一个需求加几个字段的那一类型。 所以具体data -> model对象的映射过程,就需要额外考虑一些特殊功能。
需求整理:
- 处理的data是个map对象
- 处理的data是个pojo bean
- 映射过程中,处理的data属性名和目标对象model的属性会大不相同
- 映射过程中,data中的数据大多都是文本型,而对应的model会是根据业务场景定义的强类型,需要有类型转化
- 对应的data可能是一个平面型的map/bean对象,需要转化有层次结构,带立体的模型。比如model里包含一些子model等
- 需要处理特殊情况,比如data中有个属性是个json格式数据,需要在解析后再转化到某几个model属性上
设计
根据上面的需求分析,因为要解决不同属性名自己的映射关系,单纯的依赖bean属性的扫描无法满足。所以需要引入配置文件,定义mapping的映射关系。
整体设计示意图:
说明:
- 将属性mapping的动作抽象成Get / Set两个操作。 Get操作针对数据源对象,Set操作针对数据目标对象
-
在Get/Set中间,定义了一个ValueProcess处理插件的概念,允许扩展相关的功能插件 (自认为相比于BeanUtils/BeanCopier的非常好的亮点,扩展性良好)
1. 比如默认值
2. 类型转化器
3. json数据处理
4. 日志记录
对应的类关系图:
Get/Set操作类
ValueProcess设计类:
客户端使用类设计:
- BeanMapping : 本轮子的核心功能,通过基于配置方式的mapping映射,支持convetor,defaultValue,script的所有功能。要求使用之前必须提前配置bean-mapping映射。
- BeanCopy 和 BeanMap : 都是一些扩展功能,基于本轮子的核心架构不变的基础上,开发了BeanUtils.copyProperties() , BeanUtils.describe, BeanUtils.populate()的功能。 使用该api,可以不需要配置映射文件,会进行自动扫描,就是基于同名属性的处理前提。
- BeanMappingUtil : 提供了BeanMapping , BeanCopy , BeanMap的所有方法,提供静态的util方法处理。每次都会构造对应的BeanMapping等对象,注意:每次进行构造BeanCopy,BeanMap,解析的属性结果会有cache。所以使用该util不会有很明显的性能下降,无非就是多一些临时对象。
最后定义的配置文件示例: mapping.xml
<bean-mappings xmlns="http://mapping4java.googlecode.com/schema/mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://mapping4java.googlecode.com/schema/mapping http://mapping4java.googlecode.com/svn/trunk/src/main/resources/META-INF/mapping.xsd"> <!-- (bean-bean) mapping 测试 --> <bean-mapping batch="true" srcClass="com.agapple.mapping.object.SrcMappingObject" targetClass="com.agapple.mapping.object.TargetMappingObject" reversable="true"> <field-mapping srcName="intValue" targetName="intValue" /> <field-mapping targetName="integerValue" script="src.intValue + src.integerValue" /> <!-- 测试script --> <field-mapping srcName="start" targetName="start" /> <field-mapping srcName="name" targetName="targetName" /> <!-- 注意不同名 --> <field-mapping srcName="mapping" targetName="mapping" mapping="true" /> </bean-mapping> <bean-mapping batch="true" srcClass="com.agapple.mapping.object.NestedSrcMappingObject" targetClass="com.agapple.mapping.object.NestedTargetMappingObject" reversable="true"> <field-mapping srcName="name" targetName="name" defaultValue="ljh" /> <!-- 测试default value --> <field-mapping srcName="bigDecimalValue" targetName="value" targetClass="string" defaultValue="10" /> <!-- 测试不同名+不同类型+default value --> </bean-mapping> </bean-mappings>
说明:
- bean-mappings : 可以指定xsd,方便编写配置文件。通过IDE可以给于提示
- bean-mapping : 定义需要进行mapping映射的对象信息,需要指定class全路径
reversable主要表明此mapping动作是可逆的,默认为true。 batch主要是一优化特性,后面再细表。 -
field-mapping : 主要定义需要进行映射的属性之间的映射关闭
srcName : 源对象的属性名
srcClass : 源对象的class类型
targetName : 目标对象的属性名
targetClass : 目标对象的class类型
mapping : 表明是否需要进行递归映射,比如这里的,在处理NestedSrcMappingObject和NestedTargetMappingObject时,会递归进行映射处理
defaultValue : 很好理解,就是针对源属性为null时,默认值处理
convetor : 自定义类型转化器 (默认支持基本类型,BigDecimal,BigInteger,Array,List之间的转化处理)
script : 高级特性,支持自定义的映射规则,比如这里进行一个+运算。
targetName="integerValue" script="src.intValue + src.integerValue"
几点说明:
- srcClass/targetClass: 默认可不关注,系统会自动进行识别,在defaultValue和convetor转化处理。 还有一点:如果对象中出现同名的属性时,可以指定targetClass进行唯一定位
- defaultValue : 相比于BeanCopier和BeanUtils,一个特有的特性。 大家熟悉ibatis的配置就知道会有一个nullvalue配置,主要是方便客户端处理,避免进行一些无谓的==null/==0这样的处理
- convetor : 相比于BeanUtils, 这里允许声明属性之间的转化类,比如你注册了一个alias别名的convetor,就可以在这里声明进行使用,会优先使用你定义的(如果不指定,则会进行自动根据类型进行匹配)。
自动处理会有个局限性,就是convetor会是全局性。比如你定义了一个String到Date的处理,以后整个jvm中所有的场景都会按照你定义的进行转化,到时候你怎么死自己都不知道 - script: 系统的高级特性,做为系统的一些扩展点,可以支持一些比较复杂的处理,比如以后的json/xml等处理,只需注册你的functions。比如这里定义: script="src.intValue + src.integerValue"。
BeanMapping列子: BeanMappingTest.java
public BeanMapping srcMapping = BeanMapping.create(SrcMappingObject.class, TargetMappingObject.class); public BeanMapping targetMapping = BeanMapping.create(TargetMappingObject.class , SrcMappingObject.class); @Test public void testBeanToBean_ok() { SrcMappingObject srcRef = new SrcMappingObject(); srcRef.setIntegerValue(1); srcRef.setIntValue(1); srcRef.setName("ljh"); srcRef.setStart(true); TargetMappingObject targetRef = new TargetMappingObject();// 测试一下mapping到一个Object对象 srcMapping.mapping(srcRef, targetRef); SrcMappingObject newSrcRef = new SrcMappingObject();// 反过来再mapping一次 targetMapping.mapping(targetRef, newSrcRef); }
BeanCopy例子: (发觉和BeanCopier使用比较像) BeanCopyTest.java
public BeanCopy srcCopyer = BeanCopy.create(SrcMappingObject.class, TargetMappingObject.class); public BeanCopy targetCopyer = BeanCopy.create(TargetMappingObject.class , SrcMappingObject.class); @Test public void testCopy_ok() { SrcMappingObject srcRef = new SrcMappingObject(); srcRef.setIntegerValue(1); srcRef.setIntValue(1); srcRef.setName("ljh"); srcRef.setStart(true); TargetMappingObject targetRef = new TargetMappingObject();// 测试一下mapping到一个Object对象 srcCopyer.copy(srcRef, targetRef); SrcMappingObject newSrcRef = new SrcMappingObject();// 反过来再mapping一次 targetCopyer.copy(targetRef, newSrcRef); }
BeanMap例子: (支持bean<->map的转化处理) BeanMapTest.java
public BeanMap beanMap = BeanMap.create(SrcMappingObject.class); @Test public void testDescribe_Populate_ok() { SrcMappingObject srcRef = new SrcMappingObject(); srcRef.setIntegerValue(1); srcRef.setIntValue(1); srcRef.setName("ljh"); srcRef.setStart(true); NestedSrcMappingObject nestedSrcRef = new NestedSrcMappingObject(); nestedSrcRef.setBigDecimalValue(BigDecimal.ONE); srcRef.setMapping(nestedSrcRef); Map map = beanMap.describe(srcRef); SrcMappingObject newSrcRef = new SrcMappingObject();// 反过来再mapping一次 beanMap.populate(newSrcRef, map); // 从map属性设置到bean }
性能测试数据
不过前面功能说的如何天花乱坠,整个工具的性能相比也是大家比较会关注的一个点。 我这里大致做了下测试:构造一个CopyBean,基本涵盖了普通类型,对象处理等,进行批量处理
CopyBean :
public class CopyBean { private int intValue; private boolean boolValue; private float floatValue; private double doubleValue; private long longValue; private char charValue; private byte byteValue; private short shortValue; private Integer integerValue; private Boolean boolObjValue; private Float floatObjValue; private Double doubleObjValue; private Long longObjValue; private Short shortObjValue; private Byte byteObjValue; private BigInteger bigIntegerValue; private BigDecimal bigDecimalValue; private String stringValue; }
BeanCopy性能测试
对比的内容:
- BeanCopy.copy
- BeanCopy.simpleCopy (不做类型转化)
- Method.invoke
- FastMethod.invoke
- BulkBean
- BeanCopier
- HardCode (硬编码,直接手工挨个复制属性)
- PropertyUtils (不做类型转化)
- BeanUtils


开启batch优化(200w次):单位ns | 纯解释执行(排除JIT优化)(10w次):单位ns | |
BeanMapping.copy | 1189 | 72780 |
BeanMapping.simpleCopy | 1178 | 69381 |
Method | 1322 | 25882 |
FastMethod | 533 | 15961 |
BulkBean | 108 | 4420 |
BeanCopier | 18 | 1566 |
HardCopy | 17 | 1376 |
PropertyUtils | 22143 | 1037770 |
BeanUtils | 43980 | 1766392 |
- 首先注意一下,单位是ns。 1s = 1000ms = 1 * 10^9 ns
- BeanCopier和手工编码写的copy性能基本接近
- BeanMapping的copy和simpleCopy的区别,copy可以支持类型转化,simpleCopy不会处理类型转化。
- BeanMapping的性能是BeanUtils的近40倍左右,不过和BeanCopier还是有些差距,大概在50倍左右。 (换另一个概念,就是执行100w次,BeanCopier可以比BeanMapping节省1秒,可以比BeanUtils节省40多秒,比较直观吧)
最后
整个过程,完成功能代码大概只花了一周的时间,但是代码的重构/抽取,性能优化花了我近2周的时间。性能从最初的比BeanUtils慢,逐步的提升到了快几十倍,还是比较有成就感的。
还有一点,就是自己比较满意ValueProcess概念的设计,相比于BeanCopier或者BeanUtils扩展性好多了,比如自身系统的功能:日志记录,默认值,类型转化,script脚本(EL表达式)。都是通过扩展接口实现,也比较方便切换成不同的实现.。
有兴趣的同学,可以看下googlecode上的代码,有问题欢迎联系!
googlecode地址 : http://code.google.com/p/mapping4java
相关功能测试代码:
BeanCopyTest.java
3.1 KB
BeanMapTest.java
2.1 KB
BeanMappingTest.java
7.8 KB
ConfigTest.java
1.7 KB
ConvertorTest.java
8.5 KB
ScriptExecutorTest.java
2.2 KB
ScriptTest.java
2.7 KB
性能测试代码:
CopyPerformance.java
12.9 KB
MapPerformance.java
3.0 KB
需求搜集
结合了dozer的一些特性,也顺便整理了一下自己的后续action的一些功能点,做了适当取舍。
评论
针对eclipse的重构工具支持给跟上,不然想死的心都有了!
看来是内行人哈,需求记下了。无意中翻到有个类似的工具: dzoner。
功能是蛮相似的,但从一些别人介绍中是基于beanutils做的一些扩展,性能上估计存在一些问题。不过它支持的一些功能到是可以参考和实现一下。只是时间的问题
呵呵,您说的工具应该是dozer吧?
恩,笔误. 多谢指出,已经修正。
结合了dozer的一些特性,也顺便整理了一下自己的后续action的一些功能点,做了适当取舍。
issue: http://code.google.com/p/mapping4java/issues/list
针对eclipse的重构工具支持给跟上,不然想死的心都有了!
看来是内行人哈,需求记下了。无意中翻到有个类似的工具: dzoner。
功能是蛮相似的,但从一些别人介绍中是基于beanutils做的一些扩展,性能上估计存在一些问题。不过它支持的一些功能到是可以参考和实现一下。只是时间的问题
呵呵,您说的工具应该是dozer吧?
针对eclipse的重构工具支持给跟上,不然想死的心都有了!
看来是内行人哈,需求记下了。无意中翻到有个类似的工具: dozer。
功能是蛮相似的,但从一些别人介绍中是基于beanutils做的一些扩展,性能上估计存在一些问题。不过它支持的一些功能到是可以参考和实现一下。只是时间的问题
74.125.71.104 code.google.com
74.125.71.104 encrypted.google.com
74.125.71.104 suggestqueries.google.com
74.125.71.104 mail.google.com
74.125.71.104 groups.google.com
74.125.71.104 groups.google.com.hk
74.125.71.104 docs.google.com
74.125.71.104 docs0.google.com
74.125.71.104 docs1.google.com
74.125.71.104 spreadsheets.google.com
74.125.71.104 spreadsheets0.google.com
74.125.71.104 webcache.googleusercontent.com
74.125.71.104 sites.google.com
74.125.71.104 talkgadget.google.com
74.125.71.104 clients1.google.com
74.125.71.104 clients2.google.com
74.125.71.104 clients3.google.com
74.125.71.104 clients4.google.com
确实 这种情形应该大力发扬"约定大约配置"的价值。。。。
针对eclipse的重构工具支持给跟上,不然想死的心都有了!
不管是cglib的BeanCopier,还有本文中的BeanMapping,在第一次copy时都会涉及到bean属性的分析过程,所以第一次慢也是难免的。看一个工具的性能,得有warmup的过程
所以性能上的提升,更多的是在自身工具的执行代码优化上,包括反射调用优化。
结构和框架设计上基本已经定型,不会有改变,代码是基本稳定了。目前单元测试在70%左右,争取这周可以提升到90%以上,到时候可以出一个稳定的版本。
如果有兴趣使用的话,可以一起加入开发和测试

现在的对象多数都这种的。。
是支持多层转化的,只不过默认copy时,针对复合对象只是做引用复制(大部分还是处于这样的需求)。如果需要做多层复制,是可以通过mapping="true"参数控制
支持list,map转换,这也只是convetor转化器的扩展而已。如果是有特殊的对象转化处理,比如枚举值。也可以自定义convetor
hi,地址没错,可能你得翻个墙了
把地址从https改为http即可.
admin权限操作的时候才需要https.
一般的查看用http即可.
多谢提醒,这个也没多留意这个差别。 url都已经做了下修改
现在的对象多数都这种的。。
hi,地址没错,可能你得翻个墙了
把地址从https改为http即可.
admin权限操作的时候才需要https.
一般的查看用http即可.
呵呵,有同感,特别是针对老系统做迁移或者重构。
还有就是跨部门系统合作时,对方老喜欢搞弱类型的map,string传递,接口文档机制也没有很好的建立。对于我们应用开发是个痛苦的事,所以我喜欢包上一层map<->Bean的的转化,实现代码即文档。
只能是一种权衡。
使用强依赖,重构时候不易出错,但是编码复杂,并且是重复劳动力
利用弱依赖,方便编码,但是重构是个噩梦。
好在模型相对稳定,字段变化的频率不大。只要不滥用,还是在可控范围内。
呵呵,有同感,特别是针对老系统做迁移或者重构。
还有就是跨部门系统合作时,对方老喜欢搞弱类型的map,string传递,接口文档机制也没有很好的建立。对于我们应用开发是个痛苦的事,所以我喜欢包上一层map<->Bean的的转化,实现代码即文档。
哈哈,多谢支持。看来想重新写这个的初衷都是一样
还有就是看到你的五道杆头像,立马让我想到公司里的一班子人拍的照片
就自己写的普通的java代码,就是注意下程序的预热(类加载,jit编译优化),设置下基本的jvm内存参数。 选择一台服务器上去跑(我选的是Linux 2.6.18内核,x64 ,5G内存,8颗cpu。是台虚拟机,主机是4核4颗cpu,24G内存,虚拟了这样的机器4个)
发表评论
-
yugong QuickStart
2016-03-05 01:52 0几点说明 a. 数据迁移的方案可参见设计文档,oracl ... -
阿里巴巴开源项目: 阿里巴巴去Oracle数据迁移同步工具
2016-03-05 18:29 6606背景 08年左右,阿里巴巴开始尝试MySQL的相关 ... -
愚公performance
2016-03-02 17:29 0性能测试 全量测试 场景1 (单主键, ... -
yugong AdminGuide
2016-03-02 16:40 0环境要求 操作系统 数据库 迁移方案 部署 ... -
Tddl_hint
2014-01-27 13:52 0背景 工作原理 Hint格式 direct模 ... -
tddl5分库规则
2014-01-26 14:41 0背景 工作原理 构建语法树 元数据 基于 ... -
tddl5优化器
2014-01-22 15:12 0背景 工作原理 构建语法树 元数据 抽象语 ... -
Canal BinlogChange(mariadb5/10)
2014-01-20 17:25 4692背景 先前开源了一个 ... -
asynload quickstart
2013-10-08 22:49 0几点说明: 1. asyncload是做为一个j ... -
映射规则配置
2013-09-26 11:25 0背景 因为alibaba的特殊业务,比如: 同 ... -
网友文档贡献
2013-09-18 15:50 01. Otter源代码解析系列 链接:http://e ... -
Manager配置介绍
2013-09-16 13:00 0通道配置说明 多种同步方式配置 a. 单向同步 ... -
canal&otter FAQ
2013-09-05 17:30 0常见问题 1. canal和 ... -
阿里巴巴开源项目:分布式数据库同步系统otter(解决中美异地机房)
2013-08-22 16:48 40570项目背景 阿里巴巴B2B公司,因为业务的特性 ... -
Otter AdminGuide
2013-08-19 11:06 0几点说明 otter系统自带了manager,所以简化了一 ... -
Otter高可用性
2013-08-17 23:41 0基本需求 网络不可靠,异地机房尤为明显. man ... -
Otter数据一致性
2013-08-17 23:39 0技术选型分析 需要处理一致性的业务场景: 多地修改 ( ... -
Otter扩展性
2013-08-17 22:20 0扩展性定义 按照实现不同,可分为两类: 数据处理自定 ... -
Otter双向回环控制
2013-08-17 21:37 0基本需求 支持mysql/oracle的异构数据库的双 ... -
Otter调度模型
2013-08-17 20:13 0背景 在介绍调度模型之前,首先了解一下otter系统要解 ...
相关推荐
这个方法接收三个参数:目标对象`obj`,要定义或修改的属性名`prop`,以及一个描述符对象`descriptor`。描述符对象包含了关于属性的各种配置信息,如`configurable`、`enumerable`、`value`、`writable`等。 - `...
掌握C语言后,再学习其他编程语言也会事半功倍。 现在,让我们一起开启C语言学习之旅。这里有丰富教程、实用案例、详细代码解析,助你逐步掌握C语言核心知识和编程技巧。别再犹豫,加入我们,在C语言的海洋中尽情...
在这个场景中,“swt table 自己造个轮子”指的是使用SWT(Standard Widget Toolkit)库来创建一个自定义的表格组件。SWT是Eclipse项目的一部分,它提供了一套原生的GUI组件,用于Java应用程序,提供了与操作系统更...
不过让我感到有点困惑的是,怎么样才叫做不要重复制造轮子?如何才能站在巨人的肩旁上?现在网络如此发达,资源如此丰富,开源社区也发展的很好。有很多源代码可以下载,使用。那是不是说当我们在写一个程序的时候就...
在这个“STL仿造轮子”的项目中,我们旨在通过模仿SGI-STL(斯坦福大学图形小组的STL实现)来深入理解和学习C++以及数据结构的基本原理。 首先,STL的核心组件包括容器、迭代器、算法和函数对象。容器是STL的基础,...
通过这个程序,你可以轻松地与讯飞星火API建立连接,并实现与模型的交互。无论是简单的问答还是复杂的问题,它都能准确理解并给出满意的答案。而且,它还具备联系上下文的能力,能够更好地理解问题的语境,从而提供...
在IT行业中,"重复造轮子,表单验证"是一个常见的议题,特别是在软件开发中。当我们谈论表单验证时,通常是指在用户提交数据到服务器之前,在客户端(通常是Web浏览器)进行的数据验证过程。这个过程确保了用户输入...
这篇文档是关于北师大版小学数学二年级上册《需要几个轮子》课程的教学反思。这节课的主要目标是引导学生理解和应用乘法,特别是3的乘法口诀。教学过程中,教师通过创设小动物郊游的情境,激发学生兴趣,让学生自然...
WAF 绕过-权限控制之代码混淆及行为造轮子#Safedog 代码层手写及脚本绕过变量覆盖,加密混淆,异或生成#BT Aliyun 代码层手写及脚本绕过编码解码
4. 帮玩具宝宝数轮子:设置问题情境,让孩子们思考不同类型的车辆应该有多少个轮子,并动手操作。通过集体验证,确保每个孩子都能正确数清轮子。 5. 创意延伸:鼓励孩子们发挥想象力,设计自己的“参赛车辆”,并...
标题中的“为写简单HTML5展示页的同志们造的轮子基于AngularJS1xgulpswiper”表明这是一个专为构建HTML5展示页面而设计的项目,它整合了AngularJS 1.x、gulp和swiper这三种技术。AngularJS 1.x是Google维护的一个...
wheels前端造轮子~~~demoTODO(包含但不局限于以下轮子)Tab 选项卡文件上传通知提示框(弹出层)日期选择器分页单选框/复选框(, )更多...ps:造轮子过程中会参考成熟的 UI 框架,如:, ,轮子尽量减少对 jquery 的依赖
在Java编程语言中,封装和继承是面向对象编程(OOP)的两个核心概念。封装是一种将数据和方法捆绑在一起的机制,使数据受到保护,防止未经授权的访问和修改。继承则是从已有的类创建新类的过程,允许新类继承父类的...
# 【Unity造轮子】实现一个类csgo的武器轮盘功能 # 作者信息 姓名:向宇 博客:[https://xiangyu.blog.csdn.net/](https://xiangyu.blog.csdn.net/) # 文章说明: ...
这个压缩包文件"需要几个轮子_《需要几个轮子》练习四.rar"显然包含了书中的一个练习部分,可能是为了帮助读者加深对书本内容的理解和应用。主要的文件是一个名为"需要几个轮子_《需要几个轮子》练习四.pdf"的PDF...
在这个文件中,设计师会利用SolidWorks的各种工具来创建轮子的精确几何形状。设计过程可能包括绘制草图,定义参数,使用旋转、拉伸、倒角等特征来塑造轮子的轮廓。设计师还会考虑轮子的尺寸,如直径、宽度以及中心孔...
- **重点**:强调不同车子的轮子数量差异,例如独轮车有一个轮子,自行车有两个轮子,三轮车有三个轮子,而大部分汽车有四个轮子。 - **难点**:教授幼儿如何正确地将每辆车子与对应的轮子数量匹配,进行一一对应的...
在本文中,首页轮子的构建始于创建一个大的flex容器,即一个div元素,并通过为其设置display属性为flex来激活Flex布局。这个容器将作为整个页面的结构基础,其子元素(即页面的不同部分)将被依次放置在其中。 具体...