当前位置: 移动技术网 > IT编程>开发语言>Java > 实现AutoMapper(1.0版本)

实现AutoMapper(1.0版本)

2019年03月30日  | 移动技术网IT编程  | 我要评论

最近有个需求就是实体之间自动转换,网上肯定有很多现成的实现,不过还是自己写了一个,就当对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 }
automapper(核心类)

策略类:

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,不足

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

 



                    

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网