实现AutoMapper(1.0版本)
最近有个需求就是实体之间自动转换,网上肯定有很多现成的实现,不过还是自己写了一个,就当对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版本)的更多相关文章
- AutoMapper 10.0使用教程
这里有个目录 什么是AutoMapper 配置 使用MapperConfiguration配置 使用Profile Instances配置 Naming Conventions(命名约定) Repla ...
- .NET Core 2.0版本预计于2017年春季发布
英文原文: NET Core 2.0 Planned for Spring 2017 微软项目经理 Immo Landwerth 公布了即将推出的 .NET Core 2.0 版本的细节,该版本预计于 ...
- 纪念BLives 1.0版本发布
历时两个多月的时间,BLives程序1.0发布,在开发程序期间自己经历了很多,考试,恋爱,学业,自己很纠结 很伤心,有时候很无助,为了让自己有事干,我在考试备考期间去设计程序- -#,虽然程序设计的一 ...
- 【vuejs小项目——vuejs2.0版本】单页面搭建
http://router.vuejs.org/zh-cn/essentials/nested-routes.html 使用嵌套路由开发,这里会出错主要把Vue.use(VueRouter);要进行引 ...
- geotrellis使用(二十)geotrellis1.0版本新功能及变化介绍
目录 前言 变化情况介绍 总结 一.前言 之前版本是0.9或者0.10.1.0.10.2,最近发现更新成为1.0.0-2077839.1.0应该也能称之为正式版了吧.发现其中有很多变化, ...
- 【原】迎接微信winphone 5.0 版本的IE10样式兼容
微信 Android 5.1 和 iPhone 5.1 已正式发布了,据说本12月底,微信将推出 Winphone 5.0版本,全面支持微信支付,它绑定 IE10 浏览器,那么做微信公众号的 H5 页 ...
- Mirantis OpenStack 8.0 版本大概性分析
作为 OpenStack 领域标杆性企业之一的 Mirantis 在2016年3月初发布了最新的 MOS 8.0 版本.本文试着基于公开资料进行一些归纳分析. 1. 版本概况 1.1 概况 社区版本: ...
- 微信快速开发框架(六)-- 微信快速开发框架(WXPP QuickFramework)V2.0版本上线--源码已更新至github
4月28日,已增加多媒体上传及下载API,对应MediaUploadRequest和MediaGetRequest ------------------------------------------ ...
- hadoop源码编译——2.5.0版本
强迫症必治: WARN util.NativeCodeLoader: Unable to load native-hadoop library for your platform... using b ...
随机推荐
- android sqlite no such table
今天在学习android SQLite出现android sqlite no such table错误提示,提示的意思我没有创建我要插入的表,网上也没有搜索一下,也尝试了,发现还是没有解决到我的问题, ...
- shiro 权限管理配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...
- PAT1083:List Grades
1083. List Grades (25) 时间限制 400 ms 内存限制 65536 kB 代码长度限制 16000 B 判题程序 Standard 作者 CHEN, Yue Given a l ...
- centos6.9 升级内核版本
想在centos6.9上安装docket,不过因为内核版本是2.6的故而想升级到最新的内核版本 晚上有编译升级的比较麻烦,不过有助于理解内核升级,我使用的直接升级到最新版方法 1. 导入public ...
- 20.如何从app业务逻辑提炼api接口
在app后端的工作中,设计api是一个很考验设计能力的工作.在项目的初始阶段,只知道具体的业务逻辑,那怎么把业务逻辑抽象和提炼,设计出api呢?通过阅读本文,可解答以上疑惑. 在本文中,是用以前做过的 ...
- CORS(跨域资源共享) 的配置
http://blog.csdn.net/ohyoyo2014/article/details/24863197 兼容情况: 各种新版本的ie10,firefox,opera,safari,chrom ...
- bzoj4871 [Heoi2017]摧毁“树状图”
刷完了去年的省选题,发现自己dp已经凉凉了. 虽然暴力可以拿到80分的好成绩,但是正解的dp状态和转移还是没能想到,是时候补一波dp了 这道题我们肯定是要树形dp,存的肯定就是子树某种状态的最多的联通 ...
- BZOJ_2962_序列操作_线段树
Description 有一个长度为n的序列,有三个操作1.I a b c表示将[a,b]这一段区间的元素集体增加c,2.R a b表示将[a,b]区间内所有元素变成相反数,3.Q a b c表示询问 ...
- BZOJ_2427_[HAOI2010]软件安装_tarjan+树形DP
BZOJ_2427_[HAOI2010]软件安装_tarjan+树形DP 题意: 现在我们的手头有N个软件,对于一个软件i,它要占用Wi的磁盘空间,它的价值为Vi.我们希望从中选择一些软件安装到一台磁 ...
- BZOJ_4197_[Noi2015]寿司晚宴_状态压缩动态规划
BZOJ_4197_[Noi2015]寿司晚宴_状态压缩动态规划 Description 为了庆祝 NOI 的成功开幕,主办方为大家准备了一场寿司晚宴.小 G 和小 W 作为参加 NOI 的选手,也被 ...