一个映射的框架在一个分层的体系架构中非常有用,特别是你在创建一个抽象的分层去包装一些特殊数据的变化 vs 这些数据传输到其它层(外部服务的数据对象、领域的数据对象、数据传输对象、内部服务数据对象)。因此一个映射框架非常适合于使用在映射器类型的类中,负责将数据从一个数据对象映射到另一个数据对象。
对于分布式系统,一个幅作用就在不同的系统中需要传输各自领域的数据对象,通常情况下,你不想把内部领域数据对象直接暴露给外部,同时也不想把外部数据对象暴露地内部。
数据对象之间的映射已经用传统手工编码值对象来解决装配(或转换器)之间的数据对象复制,大多数程序员会开发一些的自定义映射框架和花费无数个小时和成千上万行代码映射与他们不同的数据对象
一个通用的映射框架解决了这些问题,dozer是一个开源的映射框架,健壮、灵活、可重用性和可配置的。
数据对象映射是分层的面向服务的体系结构的一个重要组成部分,仔细选择你所使用的层映射,不走极端,因为做数据映射有时间和性能成本的限制。
为什么应用程序应该技术并行对象层次的体系,有几个不同原因如下:
和外部代码整合
序列化的要求
框架的整合
层次体系分离
在某些情况下你不直接控制就可以有效的保护您的代码库经常变化的对象层次结构, Dozer服务建立了应用程序和外部对象之间映射桥梁。映射是用反射的方式来进行的不会让你修改你的API。例如,如果对象从数据类型到字符串类型的转换,而代码可以继续运行,因为所有的这些转换都是自动进行的。
有些框架施加了序列化的限制,不允许发送任何java对象,其中最流行的框架就是Google Web Toolkit(GWT)框架,限制开发人员只能发送编译为javascipt和序列化标记的对象。Dozer框架有助于丰富的领域模型转换到表示层的模型,这刚好满足了GWT序列化的要求。
Dozer完美地整合了像Apache XmlBeans和JAXB框架,这有助于提供工厂类型、有助于领域模型和Xml对象的转换,以同样的方式作为普通对象到对象的映射。
在一个复杂的企业应用中,通常是有价值的独立设计几个层次开始,这些层次中每一个都依赖它们自己的抽象层。一个典型的简单例子就是表示层、领域层和持久层,每一个层次都有它们自己的java Beans数据模型,并且不是所有的数据都要传到体系的高层。例如相同的领域层对象根据表示层的需求会有不同的映射。
下载Dozer并且解压文档
把dozer.jar添加到你项目的类路径
添加第三方运行的jar包到你的项目类路径
如果你使用Maven,把下面这些依赖复制粘贴到你的项目中:
对于第一个映射,我们假设两个数据对象具有相同的属性名称
Mappermapper = new DozerBeanMapper();
DestinationObjectdestObject = mapper.map(sourceObject,DestinationObject.class);
执行这个Dozer映射以后,新的目标对象的实例将会有源对象属性的值,如果映射的属性的数据类型不相同,Dozer映射引擎将会自动执行数据类型转换,到此为止,我已经完成了第一个Dozer映射,以下的部分,我们将会用XML文件来映射。
ApacheIvy用户可以复制粘贴下面一行
如果两个不同类型的数据对象要映射,但这两个数据对象有不同的属性名称,你需要添加一个类映射到你的XML映射文件中,这些映射文件在使用的过程中被Dozer映射引擎解析。
当你把源数据对象的复制到目的数据对象的过程中,Dozer将会自动执行所有数据类型转换。Dozer映射引擎是又向的,如果你已经在XML文件中配制了从源对象类型到目标对象类型的映射,你就不需要再配制从目标对象类型到源对象类型的映射
提示:有相同名称的属性不用在XML映射文件中指示,Dozer会自动映射从源对象到目标对象中所有具有相同名称的属性。
yourDestinationFieldName
完整的Dozer映射的XML文件会下面这样的,自定义映射部分包含映射选项的更多信息,这样使用你的映射更加复杂。
<mappings xmlns="http://dozer.sourceforge.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net
http://dozer.sourceforge.net/schema/beanmapping.xsd">
yourDestinationFieldName
othercustom class mappings would go here…….
Dozer不独立于任何一个已存在的依赖注入框架, 然而,最常用的目标是技术最典型的应用包装,查一查Spring手册,你会发现,Dozer框架已经集成到Spring框架中了。
运行Dozer需要如下:
Dozer使用SLF4J来记录日志
Dozer需要第三方Jar包
所在第三方Jar包在运行时都要添加到运行程序的类路径中去
Dozer.jar一定要添加到你的类路径中去。
在我们开始使用XML映射文件之前,让我们看一个使用Dozer简单的例子,Dozer映射的实现有一个方法叫map,这个方法有两具参数,源数据对象,要么一个目标数据对象,要么一个目标数据类型。映射之后,将会返回映射的目标对象。
Mapper mapper = new DozerBeanMapper();
DestinationObject destObject = mapper.map(sourceObject,DestinationObject.class);
or
DestinationObject destObject = newDestinationObject();
mapper.map(sourceObject, destObject);
注意:在真实的项目开发中,不推荐你在每一次映射的过程中,都要创建一个新的Mapper实例,典型的做法是在每一个启动的VM中创建一个Mapper实例,如果你不用IOC框架,你可以定义Mapper的singleton=”true”,DozerBeanMapperSingletonWrapper是一个方便的类,已经在Dozer.jar中提供。
Dozer操作有两种模式:隐式模式和显示模式。隐式模式是默认激活的并试图为你解决映射:相同的属性名做映射,如果有更多的映射需要,你可以使用XML映射文件、注解或相关的API。
显式模式假设没有映射应该执行或“猜”,除非你指定专门的映射。这样代码量比隐式模式要多,但有时,你想完全控制整个映射过程,显式模式是一个不错的选择。隐式和显示模式的开关是“wildcard”。
Dozer的xml映射文件不会自动地被Dozer引擎执行,一个定制的XML映射文件需要被注入到Mapper的实现类中(org.dozer.DozerBeanMapper).这个类技术set方法和构造函数。
最好使用像spring一样的IOC框架,另一种方法是,用编程的方式把这些映射的配制文件注入到Mapper中,下面是用编程的方法来创建一个Bean mapper,注意,不推荐这种方法来创建bean mapper.每一次被映射的时候都会被创建,如果你使用这种方法,用单例就可以了。
List myMappingFiles = new ArrayList();
myMappingFiles.add("dozerBeanMapping.xml");
myMappingFiles.add("someOtherDozerBeanMappings.xml");
DozerBeanMapper mapper = newDozerBeanMapper();
mapper.setMappingFiles(myMappingFiles);
DestinationObject destObject = mapper.map(sourceObject, DestinationObject.class);
注意:Mapper实例应该被配制为单例,你应该用如下的方法来配制mapper,这样你就不用在每次映射的时候都要加载和重新初始化映射文件。这样做是不高效的,也是没必要的,DozerBeanMapper类是线程安全的。下面是dozer的Mapper bean被配制到Spring中的例子,spring相关的配制文件如下:
3.1.3 Dozer Bean的映射单例的包装
有一个方法可以来配制DozerBeanMapperSingletonWrapper,以便来使用你的映射文件。使用一个映射文件:一个文件叫做dozerBeanMapping.xml如果在你们类路径下,将会被加载。你可以在{dozer.home}/mappings目录下找到这样一个简单的文件。这个映射文件定义了java类和它们属性之间的所有关系,自定义的映射部分详细地定制了XMl映射部分。如下的例子显示如何使用DozerBeanMapperSingletonWrapper:
Mapper mapper = DozerBeanMapperSingletonWrapper.getInstance();
DestinationObject destObject = mapper.map(sourceObject,DestinationObject.class);
or
Mapper mapper = DozerBeanMapperSingletonWrapper.getInstance();
DestinationObject destObject = newDestinationObject();
mapper.map(sourceObject,destObject);
使用dozer的缺点之一是Xml。自从推土机开始Xmlhype期间超过五年前,是显而易见的选择。之后,Java 5注释使我们和新行业接受的配置行为是特定于域的语言风格。用于支持在形式的映射提供了API,但由于版本5.3.2dozer开始提供注释的支持.
使用注释的显而易见的原因是为了避免重复字段和方法在你的名字映射代码。注释可以把在映射属性本身从而减少的数量代码。然而在某些情况下当注释应该避免,甚至无法使用。一些是以下几点:
1. 你正在映射文的类,不在你的控制范围内,而在dozer库中;
2. 映射是相当复杂,需要很多的配制;
在第一种情况下你可以映射或第三方dto和JAXB生成实体不可能把注释放进去。在第二种情况下有选择的将大量的多行注释或隔离某些重复的实体名称的映射代码。过多标注bean可能有阅读和理解的困难。
注意:注解支持Dozer是实验性的,并且还不支持技术复杂的映射。然而,在最简单的映射情况下,用注解还是比XmL配制和API方便。
这个相方非常地简单,我可以把@Mapping标注放在属性的getter方法或直接放在属性上,如果Dozer发现,这是一个双向的映射,这就意味着,放一次注解,就可以创建双向的映射,类型映射(例如String-long)将会自动进行,全局定制的转换也可以被解析,注释只能工作在转换对象在默认通配的情况下,下面是一个演示的例子:
public class SourceBean {
privateLong id;
privateString name;
@Mapping("binaryData")
privateString data;
@Mapping("pk")
publicLong getId() {
return this.id;
}
public String getName() {
return this.name;
}
}
public class TargetBean {
private String pk;
private String name;
private String binaryData;
public void setPk(String pk) {
this.pk = pk;
}
public void setName(String name) {
this.name = name;
}
}
import staticorg.dozer.loader.api.FieldsMappingOptions.*;
import staticorg.dozer.loader.api.TypeMappingOptions.*;
…
BeanMappingBuilder builder = newBeanMappingBuilder() {
protectedvoid configure() {
mapping(Bean.class, Bean.class, TypeMappingOptions.oneWay(),
mapId("A"),
mapNull(true)
).exclude("excluded").fields("src", "dest",
copyByReference(),
collectionStrategy(true,
RelationshipType.NON_CUMULATIVE),
hintA(String.class),
hintB(Integer.class),
FieldsMappingOptions.oneWay(),
useMapId("A"),
customConverterId("id")
).fields("src", "dest",
customConverter("org.dozer.CustomConverter")
);
}
};
构造的builder对象被传到DozerBeanMapper实例中,可以添加多个Builder对象
DozerBeanMappermapper = new DozerBeanMapper();
mapper.addMapping(builder);
最后不要忘记了对FieldsMappingOptions和TypeMappingOptins类的静态引入。
在两个要被映射的对象类型或名称都一样的时候,用XML映射文件进行映射是一个不错的选择,你只需要添加一个类映射的到你的XML配制文件中,XML配制文件在Dozer执行的时候被加载。
Dozer在做映射的时候,默认执行所有类型转换,Dozer映射引擎是双向的。下面就是一个映射文件的例子:
<mappingsxmlns="http://dozer.sourceforge.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net
http://dozer.sourceforge.net/schema/beanmapping.xsd">
onePrime
oneFooPrime
一个映射元素都有多个映射元素,伴随着类级别的映射声明和域级别的映射,wildcard属性在默认的情况是true,这意味着那将自动地尝试映射两个类中的每一个属性,当wildcard属性被设置为false的时候,仅仅映射显示定义的映射域。
重要:属性相同的不必要在XML配置文件中指出,Dozer将会自动映射所有名称相同的属性到目标对象中。
Dozer将搜索整个类路径中寻找指定的文件,通常可接受的方式是在你的程序中指定你映射文件。你还可以从应用外部加载XMl映射配置文件。
从5.4.0以后,Dozer可以从提供的流中加载XMl映射文件
下面是两个类之间映射的例子,注意:当两上类之间的属性名称都相同的时候,显式的XML配置文件不是不必要的,定制的XMl配置文件只是在你需要定制属性之间的映射的时候才需要。
这些映射都是双向的,你不需要定义一个XML配制映射从TestObjectPrime到TestObject,如果这两个类有引用的复杂类型需要类型转换,你将要定义它们之间的映射,Dozer将会递归地映射两个类对象,类型转换也是自动进行的,Dozer也支持非属性映射,如果提供的两个类不能映射,它就简单地尝试映射有相同名称的属性。
匹配属性的名称是自动进行的,有相同名称的属性可以不在XMl配置文件中配置。
如果两个属性有不同的名称,那么他们可以被映射如下:
onePrime
数据类型转换是通过Dozer引擎自动进行的,当前Dozer支持以下的数据类型转换(都是双向的)
l Primitive to PrimitiveWrapper
l Primitive to Custom Wrapper
l Primitive Wrapper toPrimitive Wrapper
l Primitive to Primitive
l Complex Type to ComplexType
l String to Primitive
l String to Primitive Wrapper
l String to Complex Type ifthe Complex Type contains a String constructor
l String to Map
l Collection to Collection
l Collection to Array
l Map to Complex Type
l Map to Custom Map Type
l Enum to Enum
l Each of these can be mappedto one another: java.util.Date, java.sql.Date,java.sql.Time,java.sql.Timestamp, java.util.Calendar,java.util.GregorianCalendar
l String to any of thesupported Date/Calendar Objects.
l Objects containing atoString() method that produces a long representing time in (ms) to any supportedDate/Calendar object.
Dozer支持类级别的递归映射,如果你的映射类中有一些作为属性的复杂的类型定义,Dozer将会在映射文件中寻找类级别的映射,如果没有找到这些类之间的映射,Dozer将会映射复杂类型中具有同名称的属性的值。
从日期格式对于到字符串之间的映射可以在属性级别指定,类型转换可以自动进行。
dateObject
一个默认的日期格式也可以在类级别的映射中指定,这个默认的日期格式将会被应用到所有属性的映射中,除非它在属性级别中重写了。
dateObject
也可以在顶级的映射中指定默认的日期的映射格式。这个默认的日期格式将被用于所有属性的映射中,除非在低级别重写了。
dateObject
destField
从一个枚举值到另一个枚举值的映射
statusPrime
enum Status {
PROCESSING, SUCCESS, ERROR
}
public classUserGroup {
private Status status;
public Status getStatus() {
return status;
}
public void setStatus(Status status) {
this.status = status;
}
}
enum StatusPrime {
PROCESSING, SUCCESS, ERROR
}
public classUserGroupPrime {
private StatusPrime statusPrime;
public StatusPrime getStatusPrime() {
return statusPrime;
}
public void setStatusPrime(StatusPrime statusPrime) {
this.statusPrime = statusPrime;
}
}
Dozer会自动的在所有容器之间进行映射以及执行所有类型之间的转换。源容器中的每一个元素都映射到目标容易中,Hints被用来指示目标容易中被创建对象的类型,下面就是Dozer支持的容器之间的映射:
l List to List
l List to Array
l Array to Array
l Set to Set
l Set to Array
l Set to List
如果是使用JDK1.5以后的泛型,Hints是没有必要的,因为我们在开发的过程中都是使用的JDK1.6以上的版本,因此针对Hints就不作详细的说明,例子如下:
hintList
下面是针对容器之间映射作的一个总结,Arrays,Sets,Lists,下面给出了当hints给出或没有给出的时候,具体发生的:
• List to List
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest list will contain the same data types in the source
• Array to List
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest list will contain the same data types in the source
n If hint is speficied: Destlist will contain objects that match dest hint(s) type
• List to Array
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest array will contain data types defined by the array
n If hint is speficied: Destlist will contain objects that match dest hint(s) type (only if Object
• Array to Array
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest array will contain data types defined by the array
n If hint is speficied: Destlist will contain objects that match dest hint(s) type (only if Object
• Set to Set
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest list will contain the same data types in the source
n If hint is speficied: Destlist will contain objects that match dest hint(s) type
l Array to Set
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest list will contain the same data types in the source
n If hint is speficied: Destlist will contain objects that match dest hint(s) type
l Set to Array
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest array will contain data types defined by the array
n If hint is speficied: Destlist will contain objects that match dest hint(s) type (only if Object
l List to Set
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest list will contain the same data types in the source
n If hint is speficied: Destlist will contain objects that match dest hint(s) type
l Set to List
n Dest Hint req'd: NO
n Dest Hint allowed: YES
n If no dest hint specified:Dest list will contain the same data types in the source
n If hint is speficied: Destlist will contain objects that match dest hint(s) type
如果使用JDK1.5以上的泛型,就可以不用指定Hints了,从容器/数组到容器/数组的转换,Dozer在运行时会决定。
public classUserGroup {
private Set
public Set
return users;
}
public void setUsers(Set
users = aUsers;
}
}
public classUserGroupPrime {
private List
public List
return users;
}
public void setUsers(List
users = aUsers;
}
}
当从一个对象数组到List转换的时候,默认的List集合会包含和对象数组相同的数据:
listForArray
使用一个提示来为数据类型转换,因一个提示被指定,目标的List将会包含String类型的元素而不是Integer类型的元素。
listForArray
当从一个对象数组到另一个对象数组进行转换,默认的情况下,映射之后,目标数组和源数据包含相同的数据
theMappedArray
如果你在映射一个已经初始化的类,Dozer将会添加或更新对象到List中。如果你要映射Set、List或Array中已经包含有对象元素,在映射的过程中,将会调用contains()方法来决定是添加还是更新。这个决定是使用relationship-type属性来判断,默认值是cumulative。Relatinship-type可以被指定在属性级别,类级别和全局配置级别。
全局配置级别:
类级别:
someList
属性级别:
hintList
theMappedUnequallyNamedList
孤独数据是那些存在于目标容器而不存在于源容器中,Dozer将会移除这些孤独数据通过调用Remove方法在实际目标容器中,为了绝定这些孤独元素元素是否清除,Dozer使用了contains()方法去检查结果集中是否包含孤独元素,默认的设置是为false;
destList
Dozer能够映射一个java.util.Map到java.util.Map。如果Map中包含复杂的类型,它将会用递归的方法进行映射,如果目标Map中已经包含了元素,那么在映射的时候,就是添加元素到目标Map中。
standardMapWithHint
Dozer支持在一个类属性级别映射一个map支持的属性。这个map要么是实现了java.util.Map接口或是一个拥有唯一Get/Set方法定制的Map。
在下面这个例子中,属性A是一个基本的String类型,它被映射到属性B,属性B是一个HashMap,HashMap中的关系字key将是一个String类型的“StringProperty”,并且value是就是存储在属性A中。
hashMap
下面这个例子是属性A是String类型的,它被映射到属性B,属性B是一个HashMap。HashMap中的Key将会是“myStringProprty”,value将会是属性A的值,注意:属性A必须有一个唯一的setter方法。
下面这个例子是属性A是一个基本的String类型,它被映射到属性B中,属性B是一个定制的map,map中的key是“myCustomProperty”,value就是属性A的值,注意属性B有一个唯一的getter和setter方法名,如果你实用一个定制的map,你一定要显示的设置map的Get/Set方法名,如果你使用定制的map是实现了一个接口或一个抽象类,目标的中hints也要被设置。
<bmap-get-method="getValue" map-set-method="putValue"
key="myCustomProperty">customMap
<b map-get-method="getValue" map-set-method="putValue"
key="myCustomNullProperty">nullCustomMap
<bmap-get-method="getValue"
map-set-method="putValue">customMap
Dozer也可以直接映射一个复杂类型的对象到一个java.util.Map或一个定制的map中,下面这个例子显示地声明一个复合类型的对象(PropertyToMap)到一java.util.Map的映射,当做这样的映射的时候,你需要为mapping显示地定义一个唯一的map-id。这被用来区分那一个mapping在运行的时候被调用,PropertyToMap中每一个属性将会被映射到java.util.Map中。没必要显示的定义这些映射,排除的属性映射可以用来在运行的时候排除属性,如果属性的名称和定制map中的key不一样的时候,一定要设置定制map的key。
第二个例子显示怎样建立一个定制的map对象,唯一的不同之处是需不需要定义map-set-method和map-get-method的值,这关系到java.util.Map的get()和put()方法。
this
org.dozer.vo.map.CustomMap
this
下面这个例子显示使用这些映射,注意这些属性映射引用了一个map-id,第一个属性映射将会使用myTestMapping定义mapping
propertyToMapMap
nullPropertyToMapMap
propertyToCustomMapMap
在类级别,map支持的映射也能够被当作一个标准的映射,对于Dozer来说,有一个新的API,除源类的目标类之外,你可以传一个map引用的id.
// Example 1
PropertyToMap ptm =new PropertyToMap();
ptm.setStringProperty("stringPropertyValue");
ptm.addStringProperty2("stringProperty2Value");
Map map =Mapper.map(ptm, HashMap.class, "myTestMapping");
// Example 2
CustomMap customMap= mapper.map(ptm, CustomMap.class,
"myCustomTestMapping");
// Example 3
CustomMap custom =new CustomMap();
custom.putValue("myKey","myValue");
Mapper.map(ptm,custom, "myCustomTestMapping");
// Example 4 - MapBack
Map map = newHashMap();
map.put("stringProperty","stringPropertyValue");
PropertyToMapproperty = mapper.map(map, PropertyToMap.class,
"myTestMapping");
assertEquals("stringPropertyValue",property.getStringProperty());
索引映射就是支持需要索引进行读和写的映射
username1
username2
username2
secondName1
secondName2
primaryAlias
尝试属性映射是有可能的。一个例子就你有一个拥有一个String类型属性的对象,其它的对象也有一个String类型的属性,但是它在对象图的更深层次。在下面例子中,DestDeepObje 有一个嵌套的属性一个对象里面需要被映射,hints类型在深度属性映射中是被支持的,这个属性”copy-by-reference”被设置为one-way,并且relationship-type也可以被使用。
dest1
dest2
srcNestedObj.srcNestedObj2.src5
dest5
hintList
hintList2
hintList3
pets[1].offSpring[2].petName
Dozer通过使用field-exclude标签来技术排除字段,Dozer的排除字段也支持one-way属性
fieldToExclude
fieldToExclude
在mapping标签中有一个属性wildcard用为控制默认的映射是否被执行,默认的值是true,例如:
field1
这个例子只会影射field1字段的值,如果两个类中均有field2属性,也不会被自动映射
你可以绕过null值的映射,如果这个被指定,源对象的值为空的时候,目标字段的映射也会被绕过,目标字段的Setter方法也不会被调用,它可以在Mapping级别或类级别,例如:
to.one
to.one
你还可绕过空字段的映射,如果被指定,并且源目标所对应的字段为空的时候,到目标字段的映射将会被绕过,并且目标字段所对应的Setter方法不会被执行,它可以在Mapping级别或类级别上指示,例如:
to.one
org.dozer.vo.AnotherTestObjectPrime
to.one
单向映射只需要在mapping或field标签加上type为“one-way”的属性,也可用在field-exclude标签上
oneFooPrime
oneFooPrime2
oneFooPrime3
fieldToExclude
基于于上下文的映射可以指示使用map-id属性,Dozer也支持嵌套的上下文映射通过在字段级别指示一个map-id属性:
loanNo
contextList
loanNo
为了使用指定的上下文映射,我们在调用的时候,只要指定map-id属性就ok啦!
ContextMappingPrimecmpA =
mapper.map(cm,ContextMappingPrime.class, "caseA");
全局配置块是用来设置全局映射的默认设置,任何一个用户自定义的类型转换都可在全局配置中指定,全局配置也是可选的。Dozer支持多个配置文件,但是仅一个配置文件的全局配置就会惯穿所有配置文件,我们推荐一个唯一全局配置文件和多个分离的映射文件,隐式配置文件将会继承全局配置的默认值,下面是一个简单的全局配置块
每个单独的映射部分都可以设置自己的wildcard策略,即使有一个全局的wildcard策略,下面的映射部分不会允许wildcards
anAttributeToMapPrime
每一个单独的映射部分都可以设置自己的独立的日期格式,例如:
onePrime
可以针对每一个单独的映射,重写错误处理信息,例如:
onePrime
可以为每个单独的映射,重写一个空字符串的策略
onePrime
自定义转换被用来执行在两个对象之间自定义的映射,在全局配置部分,你可添加一些XML来告诉Dozer在类A和类B之间进行映射时,启用自定义转换的类型,当一个自定义转换在类A和类B之间被联合指定时,Dozer将会调用自定义转换去执行数据映射,而不是去调用标准的映射逻辑。
为了让Dozer能够识别自定义转换类型,自定义转换一定要实现org.dozer.CustomConverter接口,否则会抛出一个异常。
自定义转换类型在所有的配置文件中都启作用,这意味着,你可以在一个文件中定义一次自定义转换,然后这个自定义转换会应用到其它所有的与类A和类B之间的映射转换,下面是一个自定义转换的例子:
<mappingsxmlns="http://dozer.sourceforge.net"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://dozer.sourceforge.net
http://dozer.sourceforge.net/schema/beanmapping.xsd">
org.dozer.vo.TestCustomConverterHashMapObject
org.dozer.vo.TestCustomConverterHashMapPrimeObject
自定义类型转换也可以用在单独的字段级别上,在下面的例子中,Dozer将会调用自定义的转换来执行字段映射。
<field custom-converter=
"org.dozer.converters.StringAppendCustomConverter">
field1Prime
自定义的转换实例可以被重复地使用在单独的字段级别上。在下面的例子中,Dozer将会调用定义的转换来执行字段映射:
field1Prime
如果你想在自定义转换注入到Dozer之前做一些操作,你需要把自定义转换的实例注入到DozerBeanMapper中, 如果你不想用Spring,它们也可以用编程的方式被设置到DozerBeanMapper中:
<!DOCTYPE beansPUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<entry key="CustomConverterWithId1"
ref="configurableConverterBeanInstance1"/>
<entry key="CustomConverterWithId2"
ref="configurableConverterBeanInstance2"/>
注意:当源类型的值为空的时候,自定义转换也会被调用,于是,你需要在自定义类型的实现中显示地处理空值。
public classTestCustomConverter implements CustomConverter {
public Object convert(Object destination, Object source,
Class destClass, Class sourceClass) {
if (source == null) {
return null;
}
CustomDoubleObject dest = null;
if (source instanceof Double) {
// check to see if the object already exists
if (destination == null) {
dest = new CustomDoubleObject();
} else {
dest = (CustomDoubleObject) destination;
}
dest.setTheDouble(((Double)source).doubleValue());
return dest;
} else if (source instanceof CustomDoubleObject) {
double sourceObj = ((CustomDoubleObject)source).getTheDouble();
return new Double(sourceObj);
} else {
throw new MappingException("ConverterTestCustomConverter "
+ "used incorrectly. Arguments passed inwere:"
+ destination + " and " + source);
}
}
}
自定义类型也可被注入到DozerBeanMapper中,如果你需要在Dozer使用之间做一些操作。
<!DOCTYPE beansPUBLIC "-//SPRING//DTD BEAN//EN"
"http://www.springframework.org/dtd/spring-beans.dtd">
<bean id="customConverterTest"
class="org.dozer.converters.InjectedCustomConverter">
6.13.1 支持数组类型
可以为数据类型指定自定义转换。例如,如果你想用一个自定义转换来映射一个object数组到一个String类型的对象,可以用下面的映射处理规则:在解析映射文件的时候,Dozer通常调用用ClassLoader.loadClass()。对于数组,java希望类的名称是如下的格式…【Lorg.dozer.vo.SimpleObj】。
6.13.2 支持原始类型
可以为原始的类型指定自定义转换器。当定义自定义类型的时候,使用原始类型的包装类。在下面的例子中,当在SomeObject和整型的原始类型做映射的时候,Dozer将会调用指定的自定义转换器。注意,当在SomeObect和Integer做映射的时候,也会调用这个自定义的转换器。
6.13.3 配置自定义转换器
定义一个自定义转换器,能够通过配置参数被配置到映射中,在这种情况下,你应该实现ConfigurableCustomConverter接口,而不是普通的自定义转换器。可配置的转换器已经增加了额外的属性用来提供运行进的参数,参数提供的方式是通过使用custom-converter-param属性。
<fieldcustom-converter="org.dozer.converters.MathOperationConverter"
custom-converter-param="+">
amount
当有很多相同行为的情况下,可配置的转换器可以被用到,它可以被参数化,但是联合的数据太高了而不能实现简单的自定义转换了类型。
public class MathOperationConverterimplements ConfigurableCustomConverter {
public Object convert(Object destinationFieldValue,
Object sourceFieldValue,
Class destinationClass,
Class sourceClass, String param) {
Integer source = (Integer) sourceFieldValue;
Integer destination = (Integer)destinationFieldValue;
if ("+".equals(param)) {
return destination.intValue + source.intValue();
}
if ("-".equals(param)) {
return destination.intValue - source.intValue();
}
}
}
6.13.4新的自定义转换器API
当提供大量灵活自定义转换器API来描述以上情况是相当低级别的抽象,原因就在于转换器,编码不好理解并且不容易重复地使用到其它的Dozer映射中。然而,正常的情况应该是这样的:相同的转换逻辑应该调用同一个地方,而不是映射框架的bean。
Dozer最新版本有一个新的 –cleaner API为了定义用户自定义类型转换器,当带来更多控制执行流时,它给你更多的明显API,下面的实例表明在使用新的API时更简单。
public classNewDozerConverter extends DozerConverter
public NewDozerConverter() {
super(String.class, Boolean.class);
}
public Boolean convertTo(String source, Booleandestination) {
if ("yes".equals(source)) {
return Boolean.TRUE;
} else if ("no".equals(source)) {
return Boolean.FALSE;
}
throw new IllegalStateException("Unknownvalue!");
}
public String convertFrom(Boolean source, Stringdestination) {
if (Boolean.TRUE.equals(source)) {
return "yes";
} else if (Boolean.FALSE.equals(source)) {
return "no";
}
throw new IllegalStateException("Unknown value!");
}
}
6.13.5 数据结构转换器
有这些情况:需要执行可编程的数据结构转换,从一个list中复制每一个奇数据元素到一个map中作为key,每一个偶数元素作为value;在这种情况下依赖通常Dozer映射支持单独数据时,需要定义数据结构转换,对于这种情况,有可能要使用MapperAware接口,注入到当前的mapper实例中,并且是在自定义转换器外面。
public static classConverter extends DozerConverter implements MapperAware {
private Mapper mapper;
public Converter() {
super(List.class, Map.class);
}
public Map convertTo(List source, Map destination) {
Map originalToMapped = new HashMap();
for (Source item : source) {
Target mappedItem = mapper.map(item,Target.class);
originalToMapped.put(item, mappedItem);
}
return originalToMapped;
}
<…>
public void setMapper(Mapper mapper) {
this.mapper = mapper;
}
}
在映射到目标数据对象的过程中,可以配置Dozer使用自定义的bean工厂来创建目标对象的实例。而默认的情况下,Dozer用默认的构造函数来创建新目标对象的实例,这在大部分情况下,用户使用它是很方便的,便是如果你需要更灵活的方式,你可以指定自己的bean工厂来实例这些数据对象。
自定义的bean工厂一定要实现org.dozer.BeanFactory接口,在默认的情况下,Dozer映射引擎将会使用目标对象类的名称来调用工厂去实例化。
public interfaceBeanFactory {
public Object createBean(Object source, ClasssourceClass,
String targetBeanId);
}
接着,在自己的Dozer映射文件中,你仅仅需要指示一个bean-factory.xml对于任何你想使用的自定义工厂。
在下面的例子中,SimpleCustomBeanFactory将会用来创建任何InsideTestObjectPrime数据对象的实例。
com.example.vo.InsideTestObjectPrime
如果你的工厂创建bean实例是基于不同的id,而不是类名称,你可以指示一个factory-bean-id属性,在运行时,指示的factory-bean-id将会被传递到工厂中,而不是类名。
<class-b bean-factory="com.example.factories.SomeCustomBeanFactory"
factory-bean-id="someBeanLookupId">
com.example.vo.InsideTestObjectPrime
6.14.1 指示默认的工厂
另外,在Dozer的任何一个映射配置文件中,可以指示默认的bean 工厂,这个默认的bean工厂可以用到任何的映射中。
也可以映射级别指定bean 工厂,下面这个指定的工厂将会用在class-a和class-b之间的映射
6.14.2 spring工厂的注入
Bean工厂可以通过Spring或相似的控制反转技术注入到Dozer框架中
<bean id="sampleCustomBeanFactory"
class="org.dozer.factories.SampleCustomBeanFactory"/>
通过以bean的定义你的工厂,就可以把它注入到DozerBeanMapper类的实例中
可以在Dozer中使用自定义的静态创建方法来在映射期间创建目标对象的实例,它可以在字段级别或类级别设置
org.dozer.vo.InsideTestObject
labelPrime
用静态工厂方法引用不同的类的实例也是可以的,这个是通过给出类的全名称和方法名称,并用点号分开来实现:
6.16.1映射字段不用get()和set()方法
被使用的属性是可访问的,就表明可以直接访问那个字段,Dozer可以访问没有getter和setter方法的字段
6.16.2 get()和set()方法都是双向的
对于一些beans,可能有可能有非正统的getter和setter方法,Dozer支持用户指定特定的setter和getter方法。为在在这种情况下,实现一个双向映射,下面就是一个很好的例子,A中的源字段就指定了一个自定义的setter和getter方法来使用属性。
value
在这种情况下,我们调用addIntegerToList()方法把一个String映射到ArrayList,注意我们定义的是单身的映射,因为我们不能把一个ArrayList映射到一个String。
6.16.3 重载set()方法(双向)
有时候set()方法也可以被重载,为了区分,我们可以添加一个类型作为一个参数。
overloadSetField
6.16.4 遍历方法映射(双向)
Dozer也支持在映射级别遍历方法,在下面的例子中,appleComputers这个List集合将会被遍历到,并且每一个对象,方法addComputer都会被调用,任何一个字段被type=iterater标记都需要一个hint提示,get()方法能够返回一个Array,List或Iterator。
computers
下面的例子中,两个属性都有遍历的方法:
iterateCars
iterateCars
Dozer支持通过一个对象的引用来复制一个对象,没有这样的对象转换完成。这个方法可以使一个对象降低分配的数量,但是仅仅是在java bean对象被垃圾收集转换以后才可以应用,这种方法通常推荐在性能调优的映射过程,确保两个对象类型是相同的,否则运行时会出现异常,默认的值为false
copyByReferencePrime
在类级别也支持这种,在全局配置中定义你要用引用进行复制的对象类型即可
…
org.dozer.vo.NoExtendBaseObjectGlobalCopyByReference
在类级别上,wildcard表达式也是允许的,通过引用复制通过掩码被应用,这个掩码可以包括许多wildcard(*)字符。
…
org.dozer.vo.*
org.dozer.*.vo.*DTO
通过字段映射可以映射对象自身,在下面的例子中,SimpleAccount被映射到Address,也被映射到Account,假设Address是Account的一个属性,我们怎么样把SimpleAccount上的值映射到那个属性,可以用this关键来表示映射整个源对象。
address
street
在使用基类属性的时候,可以减少映射,相同字段名是没有必要在XML配置文件中指出,除非有hints。如果你在映射两个了类的时候,也要映射两个超类的相关属性,你可能偏向于在子类中重新生成基类对象的映射,下面就是一个很好的例子。
superAttr
attributePrime2
superAttr
attributePrime2
在这两个映射中,有一些字段是来自同一个超类,但是我们不得不在每一次子类映射的时候都要重复映射超类中的属性。有一个更好的方法可以单独映射基类,这可以针对每一个基类,特别是在大型的层次结构中,假设基类的名字在xml文件中是可以重视的。
superAttr
attributePrime
attributePrime2
手机扫一扫
移动阅读更方便
你可能感兴趣的文章