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

畢竟是花少量時間寫的,和產品級別的差距不是一星半點。我這個只能滿足我暫時的需求,這里面對於數組、集合、字典等以及子對象都不能自動轉換。所以有使用的人注意一下。

 

 
       


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM