最近有個需求就是實體之間自動轉換,網上確定有不少現成的實現,不過仍是本身寫了一個,就當對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 }
策略類:測試
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,不足
畢竟是花少許時間寫的,和產品級別的差距不是一星半點。我這個只能知足我暫時的需求,這裏面對於數組、集合、字典等以及子對象都不能自動轉換。因此有使用的人注意一下。