实现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 }
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,不足

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

 


posted @ 2019-03-29 17:08  码对象  阅读(833)  评论(0编辑  收藏  举报