也不多罗嗦,我主要负责从多个数据源搜集回来的数据,构造成对应的model对象。可以看一下具体的分析过程。
每个数据源采集回来的数据可能比较凌乱,有map对象,POJO对象,至于这个映射过程。因为是在老系统上实施,以前都没有service的概念,在数据库的设计会很比较乱,都是来一个需求加几个字段的那一类型。 所以具体data -> model对象的映射过程,就需要额外考虑一些特殊功能。
需求整理:
根据上面的需求分析,因为要解决不同属性名自己的映射关系,单纯的依赖bean属性的扫描无法满足。所以需要引入配置文件,定义mapping的映射关系。
说明:
Get/Set操作类
ValueProcess设计类:
客户端使用类设计:
<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>
说明:
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); @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性能测试
对比的内容:
开启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 |
整个过程,完成功能代码大概只花了一周的时间,但是代码的重构/抽取,性能优化花了我近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的一些功能点,做了适当取舍。