最近有个需求就是实体之间自动转换,网上肯定有很多现成的实现,不过还是自己写了一个,就当对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。
public class AutoMapper {
//策略数组
private List<Supplier<MapperPolicy>> policyList = new ArrayList<>(); private boolean hasInit = false; //默认策略
private List<Supplier<MapperPolicy>> getDefaultMapperPolicy() {
List<Supplier<MapperPolicy>> defaultPolicyList = new ArrayList<>();
defaultPolicyList.add(() -> UseAnnotationMapperPolicy.getInstance());
defaultPolicyList.add(() -> IgnoreCaseMapperPolicy.getInstance());
return defaultPolicyList;
} //初始化
private void init() {
if (hasInit) {
return;
}
if (policyList == null || policyList.isEmpty()) {
policyList.addAll(getDefaultMapperPolicy());
hasInit = true;
}
} //重置策略
public AutoMapper clearPolicy() {
hasInit = false;
policyList.clear();
return this;
} //添加策略
public AutoMapper addPolicy(Supplier<MapperPolicy> mapperPolicySupplier) {
policyList.add(mapperPolicySupplier);
return this;
} //添加策略
public AutoMapper addPolicy(MapperPolicy mapperPolicy) {
return addPolicy(() -> mapperPolicy);
} //mapper核心类
public <T1, T2> T2 mapModel(T1 source, Class<T2> desc) {
init();
try {
T2 descObj = desc.newInstance();
Arrays.stream(desc.getDeclaredFields()).forEach(field -> {
Object descFieldObject = null;
for (Supplier<MapperPolicy> policySupplie : policyList) {
MapperPolicy policy = policySupplie.get();
Field sourceField = policy.getField(field, source);
if (sourceField == null) {
continue;
}
sourceField.setAccessible(true);
try {
descFieldObject = sourceField.get(source);
if (descFieldObject == null) {
continue;
} else {
break;
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
field.setAccessible(true);
try {
if(descFieldObject!=null){
field.set(descObj, descFieldObject);
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
});
return descObj;
} catch (Exception ex) {
return null;
}
} public static AutoMapper getInstance() {
return new AutoMapper();
}
}
AutoMapper(核心类)
策略类:
public interface MapperPolicy {
Field getField(Field descField, Object source);
}
策略接口
public class IgnoreCaseMapperPolicy implements MapperPolicy {
@Override
public Field getField(Field descField, Object source) {
Field[] fields = source.getClass().getDeclaredFields();
if (fields.length == 0) {
return null;
}
List<Field> allMatchFields= Arrays.stream(fields).filter(field -> {
return field.getName().toLowerCase().equals(descField.getName().toLowerCase());
}).collect(Collectors.toList());
if(allMatchFields.isEmpty()){
return null;
}else {
return allMatchFields.get(0);
}
} public static IgnoreCaseMapperPolicy getInstance(){
return new IgnoreCaseMapperPolicy();
}
}
直接匹配策略(忽略大小写)
public class SettingMapperPolicy implements MapperPolicy {
@Override
public Field getField(Field descField, Object source) {
if (allSettings.containsKey(descField.getName())) {
List<Supplier<String>> allSupplier = allSettings.get(descField.getName());
Field[] fields = source.getClass().getDeclaredFields();
List<Field> allMatchFields = Arrays.stream(fields).filter(field -> {
return allSupplier.stream().anyMatch(supplier -> {
return field.getName().toLowerCase().equals(supplier.get().toLowerCase());
});
}).collect(Collectors.toList());
if (allMatchFields.isEmpty()) {
return null;
} else {
return allMatchFields.get(0);
}
}
return null;
} private static Map<String, List<Supplier<String>>> allSettings = new HashMap<String, List<Supplier<String>>>(); public SettingMapperPolicy add(String sourceName, String descName) {
return add(descName, () -> sourceName);
} public SettingMapperPolicy add(String descName, Supplier<String> stringSupplier) {
if (!allSettings.containsKey(descName)) {
allSettings.put(descName, new ArrayList<>());
}
List<Supplier<String>> allSupplier = allSettings.get(descName);
allSupplier.add(stringSupplier);
return this;
}
}
全局设置策略
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface UseMapper {
String[] fromName();
} public class UseAnnotationMapperPolicy implements MapperPolicy {
@Override
public Field getField(Field descField, Object source) {
UseMapper useMapper = descField.getAnnotation(UseMapper.class);
if(useMapper==null){
return null;
}
String[] sourceFieldNames = useMapper.fromName();
if (sourceFieldNames == null || sourceFieldNames.length == 0) {
return null;
}
Field[] sourceFields = source.getClass().getDeclaredFields();
if (sourceFields == null) {
return null;
}
List<Field> allMatchFields= Arrays.stream(sourceFields).filter(field -> {
return Arrays.asList(sourceFieldNames).stream().filter(fieldName -> {
return fieldName.toLowerCase().equals(field.getName().toLowerCase());
}).findFirst().isPresent();
}).collect(Collectors.toList());
if (allMatchFields.isEmpty()) {
return null;
} else {
return allMatchFields.get(0);
}
} public static UseAnnotationMapperPolicy getInstance(){
return new UseAnnotationMapperPolicy();
}
}
注解策略
3,测试代码
//内部对象类
public class InnerField {
public String getInnerField() {
return innerField;
} public InnerField setInnerField(String innerField) {
this.innerField = innerField;
return this;
} private String innerField;
}
//转换源实体
public class TestSource {
public int getField1() {
return field1;
} public TestSource setField1(int field1) {
this.field1 = field1;
return this;
} public double getField2() {
return field2;
} public TestSource setField2(double field2) {
this.field2 = field2;
return this;
} public String getField3() {
return field3;
} public TestSource setField3(String field3) {
this.field3 = field3;
return this;
} public InnerField getField4() {
return field4;
} public TestSource setField4(InnerField field4) {
this.field4 = field4;
return this;
} private int field1;
private double field2;
private String field3;
private InnerField field4;
}
//转换目标实体类
public class TestDest {
public int getField1() {
return Field1;
} public void setField1(int field1) {
Field1 = field1;
} public double getField2() {
return Field2;
} public void setField2(double field2) {
Field2 = field2;
} public String getField3() {
return Field3;
} public void setField3(String field3) {
Field3 = field3;
} public InnerField getField4() {
return Field4;
} public void setField4(InnerField field4) {
Field4 = field4;
} public int getField5() {
return field5;
} public void setField5(int field5) {
this.field5 = field5;
} public double getField6() {
return field6;
} public void setField6(double field6) {
this.field6 = field6;
} public String getField7() {
return field7;
} public void setField7(String field7) {
this.field7 = field7;
} public InnerField getField8() {
return field8;
} public void setField8(InnerField field8) {
this.field8 = field8;
} public int getField9() {
return field9;
} public void setField9(int field9) {
this.field9 = field9;
} public double getField10() {
return field10;
} public void setField10(double field10) {
this.field10 = field10;
} public String getField11() {
return field11;
} public void setField11(String field11) {
this.field11 = field11;
} public InnerField getField12() {
return field12;
} public void setField12(InnerField field12) {
this.field12 = field12;
} private int Field1;
private double Field2;
private String Field3;
private InnerField Field4; @UseMapper(fromName = "field1")
private int field5;
@UseMapper(fromName = "field2")
private double field6;
@UseMapper(fromName = "field3")
private String field7;
@UseMapper(fromName = "field4")
private InnerField field8; private int field9;
private double field10;
private String field11;
private InnerField field12;
}
测试的实体类
Main函数,默认策略和自定义策略
public static void main(String[] args) {
AutoMapper autoMapper= AutoMapper.getInstance().clearPolicy()
.addPolicy(UseAnnotationMapperPolicy.getInstance())//设置字段注解映射,忽略大小写的
.addPolicy(IgnoreCaseMapperPolicy.getInstance())//设置忽略大小写的字段映射
.addPolicy(()->{
return new SettingMapperPolicy() //设置全局映射
.add("field1","field9") //全局具体映射的字段1
.add("field2","field10") //全局具体映射的字段2
.add("field3","field11") //全局具体映射的字段3
.add("field4","field12"); //全局设置映射的字段4
});
TestSource testSource=new TestSource().setField1(1).setField2(2.0).setField3("field3").setField4(new InnerField().setInnerField("InnerField4"));
TestDest dest=autoMapper.mapModel(testSource,TestDest.class); AutoMapper autoMapper2= AutoMapper.getInstance();
TestDest dest2=autoMapper2.mapModel(testSource,TestDest.class);
}
4,代码部分解释
1,这里面用了链式编程,因为我实在不习惯每一次set都要一行代码,感觉巨蠢无比,不过链式编程也没有什么技术含量,只是return this而已。
2,内置接口Supplier的使用,这是为lamada表达式量身定做的内置接口。很多人觉得lamada表达式作用不大,但是确实能大大简化代码。本文中一共使用了俩次。
第一次是SettingMapperPolicy中,设置映射是String到List<Supplier<String>>的映射,我们抛开List不谈,String和Supplier<String>有什么区别呢?其实区别挺大的。比如下面的代码(参见上面main方法),config这个字段就是从configsource对象中获取来的,当然这个对象可以是新构建的,也可以是上下文存在的对象。能极大的提高灵活性。
.add("Config",()->new ConfigSource().getConfigName())
第二次是AutoMapper的策略不是List<MapperPolicy>,而是List<Supplier<MapperPolicy>>,也是基于上面的理由。而且传递 Supplier<T>等内置的lamada支持对象,都是延时处理的,能大大降低程序运行的负担。
3,策略的威力。其实这是个典型策略模式。而且策略是可以组合的,通过不同的内置策略,进行不同的转换。不过和传统意义的设计模式却有差异。以前我学设计模式总想记住各个类的关系,时间过了几年后,发现设计模式的意义不在于类图,函数式编程会颠覆大部分结构的实现方式,但是其内在意义却不会变。所以学习设计模式多理解内涵更为重要。
5,不足
毕竟是花少量时间写的,和产品级别的差距不是一星半点。我这个只能满足我暂时的需求,这里面对于数组、集合、字典等以及子对象都不能自动转换。所以有使用的人注意一下。