實現AutoMapper(1.0版本)

最近有個需求就是實體之間自動轉換,網上確定有不少現成的實現,不過仍是本身寫了一個,就當對java高級特性的一個熟悉的過程。這中間包含了泛型,反射,lamada表達式。對於想了解java高級特性的人來講,這也算一個不錯的實戰例子。java

1,變化的需求編程

當0.1版本的時候,能作的就是將徹底匹配的字段名稱mapper過去,可是沒有多久發現這個遠不能知足需求。設計模式

0.2版本,將原來代碼加了toLowerCase(),不在區分大小寫。以後就發現有些字段名稱不同了。數組

0.3版本,能夠添加一些全局設置,能夠在全局找到相關字段,把不匹配的轉換過去。app

0.4....能夠想象還有不少好比全局設置和自動匹配順序問題,還有每次修改都要改動mapper源代碼,風險性很高,因此進行了一次重構,也就是產生了如今的1.0版本。ide

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

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

相關文章
相關標籤/搜索