欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

实现AutoMapper(1.0版本)

程序员文章站 2022-04-28 10:40:16
最近有个需求就是实体之间自动转换,网上肯定有很多现成的实现,不过还是自己写了一个,就当对java高级特性的一个熟悉的过程。这中间包含了泛型,反射,lamada表达式。对于想了解java高级特性的人来说,这也算一个不错的实战例子。 1,变化的需求。 当0.1版本的时候,能做的就是将完全匹配的字段名称m ......

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

实现AutoMapper(1.0版本)
 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(核心类)

策略类:

实现AutoMapper(1.0版本)
1 public interface mapperpolicy {
2     field getfield(field descfield, object source);
3 }
策略接口
实现AutoMapper(1.0版本)
 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 }
直接匹配策略(忽略大小写)
实现AutoMapper(1.0版本)
 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 }
全局设置策略
实现AutoMapper(1.0版本)
 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,测试代码

实现AutoMapper(1.0版本)
  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,不足

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