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

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

相关文章
相关标签/搜索