我也造了个轮子:BeanMapping(属性拷贝)
背景????最近公司在大力推进服务化平台,我在中间主要做一个orm框架,解决model和数据源之间的映射问题。这里不选择已有的hibrenate,主要考虑自己公司的一些特殊场景:比如多数据源(数据库,搜索引擎,memcached,nosql等),同时可以 做一些特殊优化,比如多数据源加载时采用并行加载(我之间做的一个工具包?(业务层)异步并行加载技术分析和设计)
?也不多罗嗦,我主要负责从多个数据源搜集回来的数据,构造成对应的model对象。可以看一下具体的分析过程。
?
分析?? 每个数据源采集回来的数据可能比较凌乱,有map对象,POJO对象,至于这个映射过程。因为是在老系统上实施,以前都没有service的概念,在数据库的设计会很比较乱,都是来一个需求加几个字段的那一类型。 所以具体data -> model对象的映射过程,就需要额外考虑一些特殊功能。
?
需求整理:?
??根据上面的需求分析,因为要解决不同属性名自己的映射关系,单纯的依赖bean属性的扫描无法满足。所以需要引入配置文件,定义mapping的映射关系。
?
整体设计示意图:?
说明:?
?
将属性mapping的动作抽象成Get / Set两个操作。 Get操作针对数据源对象,Set操作针对数据目标对象在Get/Set中间,定义了一个ValueProcess处理插件的概念,允许扩展相关的功能插件 (自认为相比于BeanUtils/BeanCopier的非常好的亮点,扩展性良好)Get/Set操作类

?
ValueProcess设计类:

客户端使用类设计:

?
?
几个客户端API说明:??
<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" srctargetreversable="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" srctargetreversable="true"> <field-mapping srcName="name" targetName="name" defaultValue="ljh" /> <!-- 测试default value --> <field-mapping srcName="bigDecimalValue" targetName="value" targetdefaultValue="10" /> <!-- 测试不同名+不同类型+default value --> </bean-mapping></bean-mappings>
说明:
bean-mappings : ?可以指定xsd,方便编写配置文件。通过IDE可以给于提示bean-mapping : 定义需要进行mapping映射的对象信息,需要指定class全路径targetName="integerValue" 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);@Testpublic 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);@Testpublic 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.invokeFastMethod.invoke?BulkBeanBeanCopierHardCode (硬编码,直接手工挨个复制属性)PropertyUtils ?(不做类型转化)BeanUtils
说明: 因为beanUtils,PropertyUtils的性能太差,基本上其他的柱状图都看不清楚。
整个过程,完成功能代码大概只花了一周的时间,但是代码的重构/抽取,性能优化花了我近2周的时间。性能从最初的比BeanUtils慢,逐步的提升到了快几十倍,还是比较有成就感的。
?
还有一点,就是自己比较满意ValueProcess概念的设计,相比于BeanCopier或者BeanUtils扩展性好多了,比如自身系统的功能:日志记录,默认值,类型转化,script脚本(EL表达式)。都是通过扩展接口实现,也比较方便切换成不同的实现.。
?
有兴趣的同学,可以看下googlecode上的代码,有问题欢迎联系!
?
googlecode地址 : ?http://code.google.com/p/mapping4java
?
相关功能测试代码:
BeanCopyTest.java3.1 KBBeanMapTest.java2.1 KBBeanMappingTest.java7.8 KBConfigTest.java1.7 KBConvertorTest.java8.5 KBScriptExecutorTest.java2.2 KBScriptTest.java2.7 KB
?
性能测试代码:?
CopyPerformance.java12.9 KBMapPerformance.java3.0 KB
?
需求搜集结合了dozer的一些特性,也顺便整理了一下自己的后续action的一些功能点,做了适当取舍。?
?
issue列表: http://code.google.com/p/mapping4java/issues/list
