最近有个需求就是实体之间自动转换,网上肯定有很多现成的实现,不过还是自己写了一个,就当对java高级特性的一个熟悉的过程。这中间包含了泛型,反射,lamada表达式。对于想了解java高级特性的人来说,这也算一个不错的实战例子。

1,变化的需求

当0.1版本的时候,能做的就是将完全匹配的字段名称mapper过去,但是没有多久发现这个远不能满足需求。

0.2版本,将原来代码加了toLowerCase(),不在区分大小写。之后就发现有些字段名称不一样了。

0.3版本,可以添加一些全局设置,可以在全局找到相关字段,把不匹配的转换过去。

0.4....可以想象还有很多比如全局设置和自动匹配顺序问题,还有每次修改都要改动mapper源代码,风险性很高,所以进行了一次重构,也就是产生了现在的1.0版本。

2,1.0版本

将原来只有处理逻辑mapper类拆分为两部分,映射的AutoMapper类,以及映射的逻辑MapperPolicy接口。AutoMapper类能够根据配置的MapperPolicy的配置进行mapper,提高灵活性,也保证业务逻辑分隔。并且加入了注解配置的方式,进一步提高灵活性。不过这个版本也只是一个雏形,还不是一个能够广泛使用的版本,以后肯定还要升级到1.1,1.2......

闲话少说,show me code。

 public class AutoMapper {
//策略数组
private List<Supplier<MapperPolicy>> policyList = new ArrayList<>(); private boolean hasInit = false; //默认策略
private List<Supplier<MapperPolicy>> getDefaultMapperPolicy() {
List<Supplier<MapperPolicy>> defaultPolicyList = new ArrayList<>();
defaultPolicyList.add(() -> UseAnnotationMapperPolicy.getInstance());
defaultPolicyList.add(() -> IgnoreCaseMapperPolicy.getInstance());
return defaultPolicyList;
} //初始化
private void init() {
if (hasInit) {
return;
}
if (policyList == null || policyList.isEmpty()) {
policyList.addAll(getDefaultMapperPolicy());
hasInit = true;
}
} //重置策略
public AutoMapper clearPolicy() {
hasInit = false;
policyList.clear();
return this;
} //添加策略
public AutoMapper addPolicy(Supplier<MapperPolicy> mapperPolicySupplier) {
policyList.add(mapperPolicySupplier);
return this;
} //添加策略
public AutoMapper addPolicy(MapperPolicy mapperPolicy) {
return addPolicy(() -> mapperPolicy);
} //mapper核心类
public <T1, T2> T2 mapModel(T1 source, Class<T2> desc) {
init();
try {
T2 descObj = desc.newInstance();
Arrays.stream(desc.getDeclaredFields()).forEach(field -> {
Object descFieldObject = null;
for (Supplier<MapperPolicy> policySupplie : policyList) {
MapperPolicy policy = policySupplie.get();
Field sourceField = policy.getField(field, source);
if (sourceField == null) {
continue;
}
sourceField.setAccessible(true);
try {
descFieldObject = sourceField.get(source);
if (descFieldObject == null) {
continue;
} else {
break;
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
field.setAccessible(true);
try {
if(descFieldObject!=null){
field.set(descObj, descFieldObject);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
return descObj;
} catch (Exception ex) {
return null;
}
} public static AutoMapper getInstance() {
return new AutoMapper();
}
}

AutoMapper(核心类)

策略类:

 public interface MapperPolicy {
Field getField(Field descField, Object source);
}

策略接口

 public class IgnoreCaseMapperPolicy implements MapperPolicy {
@Override
public Field getField(Field descField, Object source) {
Field[] fields = source.getClass().getDeclaredFields();
if (fields.length == 0) {
return null;
}
List<Field> allMatchFields= Arrays.stream(fields).filter(field -> {
return field.getName().toLowerCase().equals(descField.getName().toLowerCase());
}).collect(Collectors.toList());
if(allMatchFields.isEmpty()){
return null;
}else {
return allMatchFields.get(0);
}
} public static IgnoreCaseMapperPolicy getInstance(){
return new IgnoreCaseMapperPolicy();
}
}

直接匹配策略(忽略大小写)

 public class SettingMapperPolicy implements MapperPolicy {
@Override
public Field getField(Field descField, Object source) {
if (allSettings.containsKey(descField.getName())) {
List<Supplier<String>> allSupplier = allSettings.get(descField.getName());
Field[] fields = source.getClass().getDeclaredFields();
List<Field> allMatchFields = Arrays.stream(fields).filter(field -> {
return allSupplier.stream().anyMatch(supplier -> {
return field.getName().toLowerCase().equals(supplier.get().toLowerCase());
});
}).collect(Collectors.toList());
if (allMatchFields.isEmpty()) {
return null;
} else {
return allMatchFields.get(0);
}
}
return null;
} private static Map<String, List<Supplier<String>>> allSettings = new HashMap<String, List<Supplier<String>>>(); public SettingMapperPolicy add(String sourceName, String descName) {
return add(descName, () -> sourceName);
} public SettingMapperPolicy add(String descName, Supplier<String> stringSupplier) {
if (!allSettings.containsKey(descName)) {
allSettings.put(descName, new ArrayList<>());
}
List<Supplier<String>> allSupplier = allSettings.get(descName);
allSupplier.add(stringSupplier);
return this;
}
}

全局设置策略

 @Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface UseMapper {
String[] fromName();
} public class UseAnnotationMapperPolicy implements MapperPolicy {
@Override
public Field getField(Field descField, Object source) {
UseMapper useMapper = descField.getAnnotation(UseMapper.class);
if(useMapper==null){
return null;
}
String[] sourceFieldNames = useMapper.fromName();
if (sourceFieldNames == null || sourceFieldNames.length == 0) {
return null;
}
Field[] sourceFields = source.getClass().getDeclaredFields();
if (sourceFields == null) {
return null;
}
List<Field> allMatchFields= Arrays.stream(sourceFields).filter(field -> {
return Arrays.asList(sourceFieldNames).stream().filter(fieldName -> {
return fieldName.toLowerCase().equals(field.getName().toLowerCase());
}).findFirst().isPresent();
}).collect(Collectors.toList());
if (allMatchFields.isEmpty()) {
return null;
} else {
return allMatchFields.get(0);
}
} public static UseAnnotationMapperPolicy getInstance(){
return new UseAnnotationMapperPolicy();
}
}

注解策略

3,测试代码

 //内部对象类
public class InnerField {
public String getInnerField() {
return innerField;
} public InnerField setInnerField(String innerField) {
this.innerField = innerField;
return this;
} private String innerField;
}
//转换源实体
public class TestSource {
public int getField1() {
return field1;
} public TestSource setField1(int field1) {
this.field1 = field1;
return this;
} public double getField2() {
return field2;
} public TestSource setField2(double field2) {
this.field2 = field2;
return this;
} public String getField3() {
return field3;
} public TestSource setField3(String field3) {
this.field3 = field3;
return this;
} public InnerField getField4() {
return field4;
} public TestSource setField4(InnerField field4) {
this.field4 = field4;
return this;
} private int field1;
private double field2;
private String field3;
private InnerField field4;
}
//转换目标实体类
public class TestDest {
public int getField1() {
return Field1;
} public void setField1(int field1) {
Field1 = field1;
} public double getField2() {
return Field2;
} public void setField2(double field2) {
Field2 = field2;
} public String getField3() {
return Field3;
} public void setField3(String field3) {
Field3 = field3;
} public InnerField getField4() {
return Field4;
} public void setField4(InnerField field4) {
Field4 = field4;
} public int getField5() {
return field5;
} public void setField5(int field5) {
this.field5 = field5;
} public double getField6() {
return field6;
} public void setField6(double field6) {
this.field6 = field6;
} public String getField7() {
return field7;
} public void setField7(String field7) {
this.field7 = field7;
} public InnerField getField8() {
return field8;
} public void setField8(InnerField field8) {
this.field8 = field8;
} public int getField9() {
return field9;
} public void setField9(int field9) {
this.field9 = field9;
} public double getField10() {
return field10;
} public void setField10(double field10) {
this.field10 = field10;
} public String getField11() {
return field11;
} public void setField11(String field11) {
this.field11 = field11;
} public InnerField getField12() {
return field12;
} public void setField12(InnerField field12) {
this.field12 = field12;
} private int Field1;
private double Field2;
private String Field3;
private InnerField Field4; @UseMapper(fromName = "field1")
private int field5;
@UseMapper(fromName = "field2")
private double field6;
@UseMapper(fromName = "field3")
private String field7;
@UseMapper(fromName = "field4")
private InnerField field8; private int field9;
private double field10;
private String field11;
private InnerField field12;
}

测试的实体类

Main函数,默认策略和自定义策略

 public static void main(String[] args) {
AutoMapper autoMapper= AutoMapper.getInstance().clearPolicy()
.addPolicy(UseAnnotationMapperPolicy.getInstance())//设置字段注解映射,忽略大小写的
.addPolicy(IgnoreCaseMapperPolicy.getInstance())//设置忽略大小写的字段映射
.addPolicy(()->{
return new SettingMapperPolicy() //设置全局映射
.add("field1","field9") //全局具体映射的字段1
.add("field2","field10") //全局具体映射的字段2
.add("field3","field11") //全局具体映射的字段3
.add("field4","field12"); //全局设置映射的字段4
});
TestSource testSource=new TestSource().setField1(1).setField2(2.0).setField3("field3").setField4(new InnerField().setInnerField("InnerField4"));
TestDest dest=autoMapper.mapModel(testSource,TestDest.class); AutoMapper autoMapper2= AutoMapper.getInstance();
TestDest dest2=autoMapper2.mapModel(testSource,TestDest.class);
}

4,代码部分解释

1,这里面用了链式编程,因为我实在不习惯每一次set都要一行代码,感觉巨蠢无比,不过链式编程也没有什么技术含量,只是return this而已。

2,内置接口Supplier的使用,这是为lamada表达式量身定做的内置接口。很多人觉得lamada表达式作用不大,但是确实能大大简化代码。本文中一共使用了俩次。

  第一次是SettingMapperPolicy中,设置映射是String到List<Supplier<String>>的映射,我们抛开List不谈,String和Supplier<String>有什么区别呢?其实区别挺大的。比如下面的代码(参见上面main方法),config这个字段就是从configsource对象中获取来的,当然这个对象可以是新构建的,也可以是上下文存在的对象。能极大的提高灵活性。

 .add("Config",()->new ConfigSource().getConfigName())

  第二次是AutoMapper的策略不是List<MapperPolicy>,而是List<Supplier<MapperPolicy>>,也是基于上面的理由。而且传递 Supplier<T>等内置的lamada支持对象,都是延时处理的,能大大降低程序运行的负担。

3,策略的威力。其实这是个典型策略模式。而且策略是可以组合的,通过不同的内置策略,进行不同的转换。不过和传统意义的设计模式却有差异。以前我学设计模式总想记住各个类的关系,时间过了几年后,发现设计模式的意义不在于类图,函数式编程会颠覆大部分结构的实现方式,但是其内在意义却不会变。所以学习设计模式多理解内涵更为重要。

5,不足

毕竟是花少量时间写的,和产品级别的差距不是一星半点。我这个只能满足我暂时的需求,这里面对于数组、集合、字典等以及子对象都不能自动转换。所以有使用的人注意一下。

												

实现AutoMapper(1.0版本)的更多相关文章

  1. AutoMapper 10.0使用教程

    这里有个目录 什么是AutoMapper 配置 使用MapperConfiguration配置 使用Profile Instances配置 Naming Conventions(命名约定) Repla ...

  2. .NET Core 2.0版本预计于2017年春季发布

    英文原文: NET Core 2.0 Planned for Spring 2017 微软项目经理 Immo Landwerth 公布了即将推出的 .NET Core 2.0 版本的细节,该版本预计于 ...

  3. 纪念BLives 1.0版本发布

    历时两个多月的时间,BLives程序1.0发布,在开发程序期间自己经历了很多,考试,恋爱,学业,自己很纠结 很伤心,有时候很无助,为了让自己有事干,我在考试备考期间去设计程序- -#,虽然程序设计的一 ...

  4. 【vuejs小项目——vuejs2.0版本】单页面搭建

    http://router.vuejs.org/zh-cn/essentials/nested-routes.html 使用嵌套路由开发,这里会出错主要把Vue.use(VueRouter);要进行引 ...

  5. geotrellis使用(二十)geotrellis1.0版本新功能及变化介绍

    目录 前言 变化情况介绍 总结 一.前言        之前版本是0.9或者0.10.1.0.10.2,最近发现更新成为1.0.0-2077839.1.0应该也能称之为正式版了吧.发现其中有很多变化, ...

  6. 【原】迎接微信winphone 5.0 版本的IE10样式兼容

    微信 Android 5.1 和 iPhone 5.1 已正式发布了,据说本12月底,微信将推出 Winphone 5.0版本,全面支持微信支付,它绑定 IE10 浏览器,那么做微信公众号的 H5 页 ...

  7. Mirantis OpenStack 8.0 版本大概性分析

    作为 OpenStack 领域标杆性企业之一的 Mirantis 在2016年3月初发布了最新的 MOS 8.0 版本.本文试着基于公开资料进行一些归纳分析. 1. 版本概况 1.1 概况 社区版本: ...

  8. 微信快速开发框架(六)-- 微信快速开发框架(WXPP QuickFramework)V2.0版本上线--源码已更新至github

    4月28日,已增加多媒体上传及下载API,对应MediaUploadRequest和MediaGetRequest ------------------------------------------ ...

  9. hadoop源码编译——2.5.0版本

    强迫症必治: WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using b ...

随机推荐

  1. 网络传输数据封装详解(IP,UDP,TCP)

    IP数据包也叫IP报文分组,传输在ISO网络7层结构中的网络层,它由IP报文头和IP报文用户数据组成,IP报文头的长度一般在20到60个字节之间,而一个IP分组的最大长度则不能超过65535个字节.  ...

  2. Scrapy爬取豆瓣电影top250的电影数据、海报,MySQL存储

    从GitHub得到完整项目(https://github.com/daleyzou/douban.git) 1.成果展示 数据库 本地海报图片 2.环境 (1)已安装Scrapy的Pycharm (2 ...

  3. HBuilder真机联调、手机运行

    第一步:先确认手机是否连接上 未连接状态 如下图所示为已连接状态 导致手机未成功连接的原因: (1)手机与电脑未用USB数据线连接(嘿嘿,这一部大家估计都做到了,可略过) (2)电脑上需要安装电脑版的 ...

  4. 基于.net的分布式系统限流组件

    在互联网应用中,流量洪峰是常有的事情.在应对流量洪峰时,通用的处理模式一般有排队.限流,这样可以非常直接有效的保护系统,防止系统被打爆.另外,通过限流技术手段,可以让整个系统的运行更加平稳.今天要与大 ...

  5. 你应该知道的 5 个 Docker 工具

    你可以在网上找到大量炫酷的Docker 工具,并且大部分是开源的,可以通过Github访问.在过去的两年里,我开始在开发项目中大量使用Docker.当你开始使用Docker,你会发现它比你想象的还要适 ...

  6. java中位运算

    1byte(字节)=8bit(比特) 1 0 0 0 0 0 0 0 1   2进制的1的原码 反码 补码 0 0 0 0 0 0 0 0   2进制的0的原码 反码 补码 -1 1 0 0 0 0 ...

  7. css 模拟radio的样式

    1.input 默认的 type 为 radio的样式,在具体场合中的改造 默认的样式这样: 但是我要这样的: 这样看来是不是比原来的好看多了. 2.优化radio的样式 <span class ...

  8. java语言为什么能跨平台

    参考https://blog.csdn.net/woailuo453786790/article/details/51660015 因为Java程序编译之后的代码不是能被硬件系统直接运行的代码,而是一 ...

  9. ASP.NET Core Identity Hands On(2)——注册、登录、Claim

    上一篇文章(ASP.NET Core Identity Hands On(1)--Identity 初次体验)中,我们初识了Identity,并且详细分析了AspNetUsers用户存储表,这篇我们将 ...

  10. 【BZOJ 3626】 [LNOI2014]LCA【在线+主席树+树剖】

    题目链接: TP 题解:   可能是我比较纱布,看不懂题解,只好自己想了…… 先附一个离线版本题解[Ivan] 我们考虑对于询问区间是可以差分的,然而这并没有什么卵用,然后考虑怎么统计答案. 首先LC ...