自动转换和强制转换
自从我上一篇帖子以来已经过去了一个多月:夏天炎热阳光充沛,但我现在要重新上班。 本周的帖子将介绍豆子的转化。
过去,我曾对DTO及其在应用程序中的不加区分的使用表示赞赏 。 但是,我确实承认有时需要DTO: 本文指出了可能存在的某些情况。 此外,DTO可能还不够,这就带来了对视图对象的需求。 大型项目中的分层体系结构就是这种情况。
在所有情况下,每种类型的对象之间的转换都不仅成为开发人员的繁琐工作,而且还成为一堆潜在的错误。 编写的代码最愚蠢,开发人员更可能不会考虑它,并且如果发现一个bug,将很难找到它。 我以前曾经历过:我记得当时不是很开心,当时我不得不调试代码,而我并不是仅仅发现2小时后才发现该错误是由于设置器无效而引起的。 在这种情况下使用工具(例如Eclipse或NetBeans getter / setter生成)可以很好地防止这些讨厌的潜在错误。
对于bean到bean的转换,有一些可用的工具:
- Apache Commons Beanutils (历史记录)
- Spring
BeantUtils
类及其copyProperties()
方法(绑定到Spring) - 变形
- EZMorph
- 等等
就个人而言,我使用的是Dozer (一种从Java Bean到Java Bean的映射器),它比简单的复制属性功能强大且可高度配置。
建立
注意:我将以最简单的方式使用推土机(没有Spring)。 当然,使用其他配置将需要不同的步骤。
设置推土机只需执行以下操作:
- 将Maven依赖项添加到推土机
<dependency> <groupId> net.sf.dozer </groupId> <artifactId> dozer </artifactId> <version> 5.2.2 </version> </dependency>
- 添加XML Beans运行时依赖项。 我认为这是项目的错误,因为这是Dozer的必需依赖项
<dependency> <groupId> org.apache.xmlbeans </groupId> <artifactId> xmlbeans </artifactId> <version> 2.4.0 </version> <scope> runtime </scope> </dependency>
- 在类路径的根目录下创建一个名为dozerBeanMapping.xml的XML文件
<?xml version="1.0" encoding="UTF-8"?> <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"> </mappings>
做完了!
最简单的映射
在最简单的情况下,您可能不需要做很多事情,因为不同类型的对象将具有相同的属性(名称和类型)。
首先要做的是获取映射器本身的实例。 使其成为单例是最佳实践:实际上,Dozer提供了一个单例工厂。
此后,Dozer提供了两种简单的选择:要么为您创建一个bean(下面的第3行),要么填充一个已经创建的bean(第7行)。
Mappermapper=DozerBeanMapperSingletonWrapper.getInstance();
PersonTransferObjectpersonTo=mapper.map(personDo,PersonTransferObject.class);
/* PersonTransferObject personTo = new PersonTransferObject();
mapper.map(personDo, personTo) */;
当您使用框架(例如Struts)创建框架并在必须重写的方法中传递Bean时,后者非常有趣。
映射配置
到目前为止,Dozer还没有比Commons Beanutils或Spring BeanUtils有用。 它的能力在于其配置能力。 让我们举一个简单的示例:假设您需要将Transfer Object传递给Struts表单(我知道Struts已经过时了,但是我必须在日常工作中使用它,所以请您忍受-该示例在其他示例中也可以使用上下文)。 大多数时候,Struts表单将具有严格的String属性,因此可以将其显示在HTML页面上并从HTML页面进行解析。 本质上,Struts表单是一个View Object。
本质上,当前的问题是如何将具有类型属性的bean映射到具有String属性的bean。 在Dozer中自动映射数值,真正的麻烦在于Date属性。 由于推土机是可配置的,因此只需在映射文件中添加以下内容:
<configuration>
<date-format> dd/MM/yyyy </date-format>
</configuration>
您可能会争辩说,在某些情况下,您会需要其他格式:没问题,因为它只是默认格式。 日期格式可以在类映射级别甚至在字段映射级别覆盖!
进阶设定
在国际化的应用程序中,以前的配置是不够的,因为每个用户都应在自己的语言环境中显示日期。 尽管如此,Dozer还是预料到了这种用例,并为您提供了选择映射的方法。
到目前为止,我们还依赖于默认映射:提供两个对象,并且由于它们具有相同的属性,因此Dozer可以完成其工作。 为了在映射之间进行选择,您必须在映射文件中明确声明它们:
<mapping>
<class-a> ch.frankel.blog.dozer.domain.PersonTransferObject </class-a>
<class-b> ch.frankel.blog.dozer.form.PersonForm </class-b>
</mapping>
此配置不会更改前面的示例,它只是显式声明该映射。 现在,为了进行上下文映射,只需添加映射的map-id属性。 而且,由于我之前已经解释过可以在映射级别更改日期解析/格式,因此我们也可以这样做:
<mappingmap-id="en"date-format="MM/dd/yyyy">
<class-a> ch.frankel.blog.dozer.domain.PersonTransferObject </class-a>
<class-b> ch.frankel.blog.dozer.form.PersonForm </class-b>
</mapping>
<mappingmap-id="fr"date-format="dd/MM/yyyy">
<class-a> ch.frankel.blog.dozer.domain.PersonTransferObject </class-a>
<class-b> ch.frankel.blog.dozer.form.PersonForm </class-b>
</mapping>
为了使用所需的映射,只需在映射调用中传递适当的标识符即可:
mapper.map(personTo,personForm,"en");
超越配置
如果所有其他方法均失败,则始终可以创建自己的转换器。 例如,假设您想依靠表示层中的业务键在表示层中隐藏对象的数据库主键。 由于主密钥丢失了,而您拥有的只是业务密钥,因此在将传输对象转换回实体时,必须从数据库层检索PK。
可以为整个应用程序,特定的映射甚至特定的字段配置自定义转换器。 意思是在后一种情况下,您可以吃蛋糕也可以吃。 对于上述情况,只需在配置文件中使用它:
<mapping>
<class-a> ch.frankel.blog.dozer.domain.PersonTransferObject </class-a>
<class-b> ch.frankel.blog.dozer.entity.PersonDataObject </class-b>
<fieldcustom-converter="ch.frankel.blog.dozer.converter.PersonPrimaryKeyConverter">
<a> businessId </a>
<b> id </b>
</field>
</mapping>
定制转换器只是ch.frankel.blog.dozer.converter.PersonPrimaryKeyConverter
的实现,它具有单个方法public Object convert(Object existingDestinationFieldValue, Object sourceFieldValue, Class<?> destinationClass, Class<?> sourceClass)
。
附加功能
这篇小文章并未涉及Dozer可以做什么的表面:
- 集合映射
- 枚举映射
- 领域排除
- 定制豆工厂
- 定制的获取/设置方法
- 参考映射
- 事件框架
- 表达语言
- 等等
结论
关于我正在设计的应用程序的大小和使用,我倾向于遵循KISS原则,并且在大多数情况下,我会让我的实体流经这些层。 在其他情况下,我赞成使用自动转换工具来减少错误概率和开发人员的厌倦感。 推土机是迄今为止我使用过的最好的工具:它具有足够的可配置性,可以在简单的用例中轻松使用,而功能强大的则可以在复杂的用例中使用。 此外,其文档质量非常好。
这篇文章的源代码可以在这里在Eclipse / Maven的格式。
自动转换和强制转换