最近有个需求就是实体之间自动转换,网上肯定有很多现成的实现,不过还是自己写了一个,就当对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. 启动SpringBoot的可执行jar 报错:target\spring-boot-hello-1.0-SNAPSHOT.jar中没有主清单属性

    打包成功,但是在执行时报错,没有主清单属性 解决: 增加红框内的依赖: <build> <plugins> <plugin> <groupId>org. ...

  2. Python库

    --Python库之Pandas库-------- 自主选择学习了Python中的Pandas库,以下是本人对Pandas库的认识: Pandas库是Python最受欢迎的库之一,主要用于数据的操作. ...

  3. java定时任务调度-Timer(1)

    一.定义 有且仅有一个后台线程对多个业务线程进行定时定频率的调度 二. Timer  ---->  Timer Task  (中有run();方法) 通过 new Timer().schedul ...

  4. 深入理解.net - 4.你必须知道的String

    为什么要单独写string,主要是它太常用了,同时又太特殊了,特殊到CLR对它的处理都和其它对象不一样.简直可以称为VIP用户啊.本文并不是一篇介绍如何使用string的文章,而是旨在阐述string ...

  5. linux timerfd系列函数总结

    网上关于timerfd的文章很多,在这儿归纳总结一下方便以后使用,顺便贴出一个timerfd配合epoll使用的简单例子 一.timerfd系列函数 timerfd是Linux为用户程序提供的一个定时 ...

  6. .net如何使用系统中没有安装的字体?

    不想安装到客户端的 Fonts 目录下面,但是我又想在程序中使用它. 这段代码放在哪里? 字体文件需要放到要安装的机器上吗?并不需要 System.Drawing.Text.PrivateFontCo ...

  7. c++右值引用以及使用

    前几天看了一篇文章<4行代码看看右值引用> 觉得写得不错,但是觉得右值引用的内容还有很多可以去挖掘学习,所以总结了一下,希望能对右值引用有一个更加深层次的认识 一.几个基本概念 1.1左值 ...

  8. php对接微信小程序支付

    前言:这里我就假装你已经注册了微信小程序,并且基本的配置都已经好了.注: 个人注册小程序不支持微信支付,所以我还是假装你是企业或者个体工商户的微信小程序,其他的商户号注册,二者绑定,授权,支付开通,就 ...

  9. iOS推送:Java服务器端发送表情(绘文字)

    http://blog.csdn.net/musou_ldns/article/details/8692520 功能的时候,客户要求能够给iphone发送表情图标,也就是绘文字. 手机环境:iOS5. ...

  10. c# 如何读取web.config中的内容(ConfigurationManager)

    1.web.config中写入 <appSettings>    <add key="TokenQPark" value="http://localho ...