实现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。
1 public class automapper { 2 //策略数组 3 private list<supplier<mapperpolicy>> policylist = new arraylist<>(); 4 5 private boolean hasinit = false; 6 7 //默认策略 8 private list<supplier<mapperpolicy>> getdefaultmapperpolicy() { 9 list<supplier<mapperpolicy>> defaultpolicylist = new arraylist<>(); 10 defaultpolicylist.add(() -> useannotationmapperpolicy.getinstance()); 11 defaultpolicylist.add(() -> ignorecasemapperpolicy.getinstance()); 12 return defaultpolicylist; 13 } 14 15 //初始化 16 private void init() { 17 if (hasinit) { 18 return; 19 } 20 if (policylist == null || policylist.isempty()) { 21 policylist.addall(getdefaultmapperpolicy()); 22 hasinit = true; 23 } 24 } 25 26 //重置策略 27 public automapper clearpolicy() { 28 hasinit = false; 29 policylist.clear(); 30 return this; 31 } 32 33 //添加策略 34 public automapper addpolicy(supplier<mapperpolicy> mapperpolicysupplier) { 35 policylist.add(mapperpolicysupplier); 36 return this; 37 } 38 39 //添加策略 40 public automapper addpolicy(mapperpolicy mapperpolicy) { 41 return addpolicy(() -> mapperpolicy); 42 } 43 44 //mapper核心类 45 public <t1, t2> t2 mapmodel(t1 source, class<t2> desc) { 46 init(); 47 try { 48 t2 descobj = desc.newinstance(); 49 arrays.stream(desc.getdeclaredfields()).foreach(field -> { 50 object descfieldobject = null; 51 for (supplier<mapperpolicy> policysupplie : policylist) { 52 mapperpolicy policy = policysupplie.get(); 53 field sourcefield = policy.getfield(field, source); 54 if (sourcefield == null) { 55 continue; 56 } 57 sourcefield.setaccessible(true); 58 try { 59 descfieldobject = sourcefield.get(source); 60 if (descfieldobject == null) { 61 continue; 62 } else { 63 break; 64 } 65 } catch (illegalaccessexception e) { 66 e.printstacktrace(); 67 } 68 } 69 field.setaccessible(true); 70 try { 71 if(descfieldobject!=null){ 72 field.set(descobj, descfieldobject); 73 } 74 } catch (illegalaccessexception e) { 75 e.printstacktrace(); 76 } 77 }); 78 return descobj; 79 } catch (exception ex) { 80 return null; 81 } 82 } 83 84 public static automapper getinstance() { 85 return new automapper(); 86 } 87 }
策略类:
1 public interface mapperpolicy { 2 field getfield(field descfield, object source); 3 }
1 public class ignorecasemapperpolicy implements mapperpolicy { 2 @override 3 public field getfield(field descfield, object source) { 4 field[] fields = source.getclass().getdeclaredfields(); 5 if (fields.length == 0) { 6 return null; 7 } 8 list<field> allmatchfields= arrays.stream(fields).filter(field -> { 9 return field.getname().tolowercase().equals(descfield.getname().tolowercase()); 10 }).collect(collectors.tolist()); 11 if(allmatchfields.isempty()){ 12 return null; 13 }else { 14 return allmatchfields.get(0); 15 } 16 } 17 18 public static ignorecasemapperpolicy getinstance(){ 19 return new ignorecasemapperpolicy(); 20 } 21 }
1 public class settingmapperpolicy implements mapperpolicy { 2 @override 3 public field getfield(field descfield, object source) { 4 if (allsettings.containskey(descfield.getname())) { 5 list<supplier<string>> allsupplier = allsettings.get(descfield.getname()); 6 field[] fields = source.getclass().getdeclaredfields(); 7 list<field> allmatchfields = arrays.stream(fields).filter(field -> { 8 return allsupplier.stream().anymatch(supplier -> { 9 return field.getname().tolowercase().equals(supplier.get().tolowercase()); 10 }); 11 }).collect(collectors.tolist()); 12 if (allmatchfields.isempty()) { 13 return null; 14 } else { 15 return allmatchfields.get(0); 16 } 17 } 18 return null; 19 } 20 21 private static map<string, list<supplier<string>>> allsettings = new hashmap<string, list<supplier<string>>>(); 22 23 public settingmapperpolicy add(string sourcename, string descname) { 24 return add(descname, () -> sourcename); 25 } 26 27 public settingmapperpolicy add(string descname, supplier<string> stringsupplier) { 28 if (!allsettings.containskey(descname)) { 29 allsettings.put(descname, new arraylist<>()); 30 } 31 list<supplier<string>> allsupplier = allsettings.get(descname); 32 allsupplier.add(stringsupplier); 33 return this; 34 } 35 }
1 @target({elementtype.field}) 2 @retention(retentionpolicy.runtime) 3 public @interface usemapper { 4 string[] fromname(); 5 } 6 7 public class useannotationmapperpolicy implements mapperpolicy { 8 @override 9 public field getfield(field descfield, object source) { 10 usemapper usemapper = descfield.getannotation(usemapper.class); 11 if(usemapper==null){ 12 return null; 13 } 14 string[] sourcefieldnames = usemapper.fromname(); 15 if (sourcefieldnames == null || sourcefieldnames.length == 0) { 16 return null; 17 } 18 field[] sourcefields = source.getclass().getdeclaredfields(); 19 if (sourcefields == null) { 20 return null; 21 } 22 list<field> allmatchfields= arrays.stream(sourcefields).filter(field -> { 23 return arrays.aslist(sourcefieldnames).stream().filter(fieldname -> { 24 return fieldname.tolowercase().equals(field.getname().tolowercase()); 25 }).findfirst().ispresent(); 26 }).collect(collectors.tolist()); 27 if (allmatchfields.isempty()) { 28 return null; 29 } else { 30 return allmatchfields.get(0); 31 } 32 } 33 34 public static useannotationmapperpolicy getinstance(){ 35 return new useannotationmapperpolicy(); 36 } 37 }
3,测试代码
1 //内部对象类 2 public class innerfield { 3 public string getinnerfield() { 4 return innerfield; 5 } 6 7 public innerfield setinnerfield(string innerfield) { 8 this.innerfield = innerfield; 9 return this; 10 } 11 12 private string innerfield; 13 } 14 //转换源实体 15 public class testsource { 16 public int getfield1() { 17 return field1; 18 } 19 20 public testsource setfield1(int field1) { 21 this.field1 = field1; 22 return this; 23 } 24 25 public double getfield2() { 26 return field2; 27 } 28 29 public testsource setfield2(double field2) { 30 this.field2 = field2; 31 return this; 32 } 33 34 public string getfield3() { 35 return field3; 36 } 37 38 public testsource setfield3(string field3) { 39 this.field3 = field3; 40 return this; 41 } 42 43 public innerfield getfield4() { 44 return field4; 45 } 46 47 public testsource setfield4(innerfield field4) { 48 this.field4 = field4; 49 return this; 50 } 51 52 private int field1; 53 private double field2; 54 private string field3; 55 private innerfield field4; 56 } 57 //转换目标实体类 58 public class testdest { 59 public int getfield1() { 60 return field1; 61 } 62 63 public void setfield1(int field1) { 64 field1 = field1; 65 } 66 67 public double getfield2() { 68 return field2; 69 } 70 71 public void setfield2(double field2) { 72 field2 = field2; 73 } 74 75 public string getfield3() { 76 return field3; 77 } 78 79 public void setfield3(string field3) { 80 field3 = field3; 81 } 82 83 public innerfield getfield4() { 84 return field4; 85 } 86 87 public void setfield4(innerfield field4) { 88 field4 = field4; 89 } 90 91 public int getfield5() { 92 return field5; 93 } 94 95 public void setfield5(int field5) { 96 this.field5 = field5; 97 } 98 99 public double getfield6() { 100 return field6; 101 } 102 103 public void setfield6(double field6) { 104 this.field6 = field6; 105 } 106 107 public string getfield7() { 108 return field7; 109 } 110 111 public void setfield7(string field7) { 112 this.field7 = field7; 113 } 114 115 public innerfield getfield8() { 116 return field8; 117 } 118 119 public void setfield8(innerfield field8) { 120 this.field8 = field8; 121 } 122 123 public int getfield9() { 124 return field9; 125 } 126 127 public void setfield9(int field9) { 128 this.field9 = field9; 129 } 130 131 public double getfield10() { 132 return field10; 133 } 134 135 public void setfield10(double field10) { 136 this.field10 = field10; 137 } 138 139 public string getfield11() { 140 return field11; 141 } 142 143 public void setfield11(string field11) { 144 this.field11 = field11; 145 } 146 147 public innerfield getfield12() { 148 return field12; 149 } 150 151 public void setfield12(innerfield field12) { 152 this.field12 = field12; 153 } 154 155 private int field1; 156 private double field2; 157 private string field3; 158 private innerfield field4; 159 160 @usemapper(fromname = "field1") 161 private int field5; 162 @usemapper(fromname = "field2") 163 private double field6; 164 @usemapper(fromname = "field3") 165 private string field7; 166 @usemapper(fromname = "field4") 167 private innerfield field8; 168 169 private int field9; 170 private double field10; 171 private string field11; 172 private innerfield field12; 173 }
main函数,默认策略和自定义策略
1 public static void main(string[] args) { 2 automapper automapper= automapper.getinstance().clearpolicy() 3 .addpolicy(useannotationmapperpolicy.getinstance())//设置字段注解映射,忽略大小写的 4 .addpolicy(ignorecasemapperpolicy.getinstance())//设置忽略大小写的字段映射 5 .addpolicy(()->{ 6 return new settingmapperpolicy() //设置全局映射 7 .add("field1","field9") //全局具体映射的字段1 8 .add("field2","field10") //全局具体映射的字段2 9 .add("field3","field11") //全局具体映射的字段3 10 .add("field4","field12"); //全局设置映射的字段4 11 }); 12 testsource testsource=new testsource().setfield1(1).setfield2(2.0).setfield3("field3").setfield4(new innerfield().setinnerfield("innerfield4")); 13 testdest dest=automapper.mapmodel(testsource,testdest.class); 14 15 automapper automapper2= automapper.getinstance(); 16 testdest dest2=automapper2.mapmodel(testsource,testdest.class); 17 }
4,代码部分解释
1,这里面用了链式编程,因为我实在不习惯每一次set都要一行代码,感觉巨蠢无比,不过链式编程也没有什么技术含量,只是return this而已。
2,内置接口supplier的使用,这是为lamada表达式量身定做的内置接口。很多人觉得lamada表达式作用不大,但是确实能大大简化代码。本文中一共使用了俩次。
第一次是settingmapperpolicy中,设置映射是string到list<supplier<string>>的映射,我们抛开list不谈,string和supplier<string>有什么区别呢?其实区别挺大的。比如下面的代码(参见上面main方法),config这个字段就是从configsource对象中获取来的,当然这个对象可以是新构建的,也可以是上下文存在的对象。能极大的提高灵活性。
1 .add("config",()->new configsource().getconfigname())
第二次是automapper的策略不是list<mapperpolicy>,而是list<supplier<mapperpolicy>>,也是基于上面的理由。而且传递 supplier<t>等内置的lamada支持对象,都是延时处理的,能大大降低程序运行的负担。
3,策略的威力。其实这是个典型策略模式。而且策略是可以组合的,通过不同的内置策略,进行不同的转换。不过和传统意义的设计模式却有差异。以前我学设计模式总想记住各个类的关系,时间过了几年后,发现设计模式的意义不在于类图,函数式编程会颠覆大部分结构的实现方式,但是其内在意义却不会变。所以学习设计模式多理解内涵更为重要。
5,不足
毕竟是花少量时间写的,和产品级别的差距不是一星半点。我这个只能满足我暂时的需求,这里面对于数组、集合、字典等以及子对象都不能自动转换。所以有使用的人注意一下。