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