java 脑洞开源工具

java脑洞 效率最高和最懂国人的对象转换工具 lp-conve

2022-07-22  本文已影响0人  灰色调诺言

脑洞的由来

开发过程中经常遇到

  1. 把EntityA的属性赋值给新的EntityB
  2. 把Entity的属性转换成Map结构
  3. 把Map结构的数据转换成Entity

当前常见解决方案

  1. 把Entity转换成json,再把json转换成目标Entity
  2. 第三方工具MapStruct
  3. spring的BeanUtils

效率对比

  1. json转换和BeanUtils都是基于Class的反射映射,效率不高
  2. MapStruct是根据注解生成字节码,效率高,但是不够灵活,而且没法满足场景二和场景三

介绍 lp-converter-processor

  1. 基于注解生成字节码,效率高。
  2. 基于jdk8的接口default机制实现灵活编入
  3. 有字段检查机制,提供安全检查能力
  4. 能兼容lombok
  5. 国人编写(doge 最懂国人的由来)

开始使用

1. jdk 要求 8+

2. 引入maven

        <dependency>
            <groupId>io.github.wqr503</groupId>
            <artifactId>lp-converter-processor</artifactId>
            <version>2.0.4</version>
        </dependency>

3. 基于注解@ConverterMapper和接口MapConverter和接口BeanConverter实现

  1. MapConverter 接口
  public interface MapConverter<S> extends Converter{

    // 转换 map - S
    default S convertFromMap(Map<String,Object> dataSource) {
      return null;    
    }

    //转换Map S -> map
    default Map<String,Object> convertToMap(S source) {
      return null;    
    }

    default Map<String,Object> postConvertToMap(S source, Map<String,Object> dataSource) {
        return dataSource;
    }

    default S preConvertFromMap(Map<String,Object> dataSource, S source) {
        return source;
    }

    default S postConvertFromMap(Map<String,Object> dataSource, S source) {
        return source;
    }

}

主要是convertFromMap 和 convertToMap 接口,convertFromMap 实现把Map结构的数据转换成Entity, convertToMap 实现 把Entity的属性转换成Map结构

  1. BeanConverter接口
/**
 * 对象转换
 */
public interface BeanConverter<S, T> extends Converter{

    //转换 S - T
    default T convertTo(S source) {
      return null;    
    }

    //合并 S - T
   default MergeResult<T> mergeTo(S source, T target) {
      return new MergeResult<>();    
    }

    default T postConvertTo(S source, T target) {
        return target;
    }

    default MergeResult<T> postMergeTo(S source, T target, MergeResult<T> result) {
        return result;
    }

}

主要是 convertTo 和 mergeTo 接口,convertTo 实现把EntityA 生成 新的EntityB, mergeTo 实现 把EntityA 合并到 EntityB,返回合并后的EntityB, MergeResult中包含是否有改变,和改变的字段

/**
 * 合并结果
 */
public class MergeResult<T> {

    /** 合并结果 */
    private T entity;

    /** 是否有改变 */
    private boolean change = false;

    /** 合并字段 */
    private Set<String> changeFieldNames = new HashSet<>();

}

4. 实战

1. 最简单的Bean -> Bean转换
    /**
     * 最简单的Bean -> Bean转换
     * MergeResult : 两个对象合并的结果
     *   change : 是否有变化
     *   changeFieldNames : target有赋值的字段
     *   entity : target对象
     */
@ConverterMapper
public interface TestBeanConverter extends BeanConverter<TestBeanConverter.EntityA, TestBeanConverter.EntityB> {

    @Data
    class EntityA {

        private HashMap<String, Integer> mapData;

        private int age;

    }

    @Data
    class EntityB {

        private Map<String, Integer> mapData;

        private long age;

    }

}
    private void testBaseBeanConverter() {
        TestBeanConverter.EntityA entityA = new TestBeanConverter.EntityA();
        entityA.setAge(1);
        HashMap<String, Integer> mapData = new HashMap<>();
        mapData.put("test", 123);
        entityA.setMapData(mapData);
        TestBeanConverter converter = ConverterHolder.getConverter(TestBeanConverter.class);
        TestBeanConverter.EntityB entityB = converter.convertTo(entityA);
        System.out.println("testBaseBeanConverter ---------------- 旧对象A:" + JSON.toJSONString(entityA));
        System.out.println("testBaseBeanConverter ---------------- 新对象:" + JSON.toJSONString(entityB));
        System.out.println("");
        MergeResult<TestBeanConverter.EntityB> entityBMergeResult = converter.mergeTo(entityA, entityB);
        System.out.println("testBaseBeanConverter ---------------- MergeResult:" + JSON.toJSONString(entityBMergeResult));
        System.out.println("");
    }

结果

testBaseBeanConverter ---------------- 旧对象A:{"age":1,"mapData":{"test":123}}
testBaseBeanConverter ---------------- 新对象:{"age":0,"mapData":{"test":123}}

testBaseBeanConverter ---------------- MergeResult:{"change":true,"changeFieldNames":["mapData"],"entity":{"age":0,"mapData":{"test":123}}}
2. 最简单的Bean -> Map转换
    /**
     * 最简单的Bean -> Map转换
     */
@ConverterMapper
public interface TestMapConverter extends MapConverter<TestMapConverter.EntityA> {

    @Data
    class EntityA {

        private HashMap<String, Integer> mapData;

        private int age;

    }

}
    private void testBaseMapConverter() {
        TestMapConverter.EntityA entityA = new TestMapConverter.EntityA();
        entityA.setAge(1);
        HashMap<String, Integer> mapData = new HashMap<>();
        mapData.put("test", 123);
        entityA.setMapData(mapData);
        TestMapConverter converter = ConverterHolder.getConverter(TestMapConverter.class);
        Map<String, Object> entityMap = converter.convertToMap(entityA);
        System.out.println("testBaseMapConverter ---------------- 旧对象:" + JSON.toJSONString(entityA));
        System.out.println("testBaseMapConverter ---------------- 新对象:" + JSON.toJSONString(entityMap));
        System.out.println("");
    }

结果

testBaseMapConverter ---------------- 旧对象:{"age":1,"mapData":{"test":123}}
testBaseMapConverter ---------------- 新对象:{"mapData":{"test":123},"age":1}
3. 配合spring注解注入使用
    /**
     * 配合spring注解注入使用,implSpringInterface 为 true
     */
@ConverterMapper(implSpringInterface = true)
public interface TestComponentConverter extends MapConverter<TestComponentConverter.EntityA> {

    @Data
    class EntityA {

        private HashMap<String, Integer> mapData;

        private int age;

    }

}
    @Resource
    private TestComponentConverter testComponentConverter;

    private void testComponentConverter() {
        TestComponentConverter.EntityA entityA = new TestComponentConverter.EntityA();
        entityA.setAge(1);
        HashMap<String, Integer> mapData = new HashMap<>();
        mapData.put("test", 123);
        entityA.setMapData(mapData);
        Map<String, Object> entityMap = testComponentConverter.convertToMap(entityA);
        System.out.println("testComponentConverter ---------------- 旧对象:" + JSON.toJSONString(entityA));
        System.out.println("testComponentConverter ---------------- 新对象:" + JSON.toJSONString(entityMap));
        System.out.println("");
    }

结果

testComponentConverter ---------------- 旧对象:{"age":1,"mapData":{"test":123}}
testComponentConverter ---------------- 新对象:{"mapData":{"test":123},"age":1}
4. ConverterMapper 参数
    /**
     * ConverterMapper 参数
     * ignoreEmpty - 忽略空值,如果是空值则不赋值, 为true, entityB的EmptyData会保留hasData值,为false,则被替换为null
     * ignoreGenericType - 忽略泛型,Map<String, Integer> 能和 Map 匹配
     * matchType -  为true,则listData不会生成,为false,则listData会生成, 并且会报错,类型转换错误
     * reNameField - 字段名别名映射
     * fieldNameFilter - 字段过滤(name,注释,正则)指定字段生效或者不生效
     */
@ConverterMapper(ignoreEmpty = true, ignoreGenericType = true,
    fieldNameFilter = @FieldNameFilter(assignIgnoreRegexFieldName = "ignore*"),
    reNameField = @ReNameField(sourceName = "oldName", targetName = "newName"))
public interface TestMapperFieldConverter extends BeanConverter<TestMapperFieldConverter.EntityA, TestMapperFieldConverter.EntityB> {

    @Data
    class EntityA {

        private Set<String> listData;

        private Map<String, Integer> mapData;

        private int age;

        private String oldName;

        private String emptyData;

        private String ignoreFiled;

    }

    @Data
    class EntityB {

        private List<String> listData;

        private Map mapData;

        private Long age;

        private String newName;

        private String emptyData;

        private String ignoreFiled;
    }

}
private void testMapperFieldConverter() {
        TestMapperFieldConverter.EntityA entityA = new TestMapperFieldConverter.EntityA();
        entityA.setAge(1);
        entityA.setListData(Sets.newHashSet("123"));
        entityA.setOldName("123");
        entityA.setIgnoreFiled("nameA");
        HashMap<String, Integer> mapData = new HashMap<>();
        mapData.put("test", 123);
        entityA.setMapData(mapData);
        TestMapperFieldConverter converter = ConverterHolder.getConverter(TestMapperFieldConverter.class);
        TestMapperFieldConverter.EntityB entityB = new TestMapperFieldConverter.EntityB();
        entityB.setEmptyData("hasData");
        entityB.setIgnoreFiled("nameB");
        System.out.println("testMapperFieldConverter ---------------- 旧对象A:" + JSON.toJSONString(entityA));
        System.out.println("testMapperFieldConverter ---------------- 旧对象B:" + JSON.toJSONString(entityB));
        converter.mergeTo(entityA, entityB);
        System.out.println("testMapperFieldConverter ---------------- 新对象B:" + JSON.toJSONString(entityB));
        System.out.println("");
    }

结果

testMapperFieldConverter ---------------- 旧对象A:{"age":1,"ignoreFiled":"nameA","listData":["123"],"mapData":{"test":123},"oldName":"123"}
testMapperFieldConverter ---------------- 旧对象B:{"emptyData":"hasData","ignoreFiled":"nameB"}
testMapperFieldConverter ---------------- 新对象B:{"emptyData":"hasData","ignoreFiled":"nameB","mapData":{"test":123},"newName":"123"}
5. ConverterMapping 参数
    /**
     * ConverterMapping 参数
     * 注释在convertFromMap,convertToMap, convertTo, mergeTo 方法上,覆盖ConverterMapper配置,要显示配置参数,默认值默认不生效
     * ignoreEmpty - 忽略空值,如果是空值则不赋值, 为true, entityB的EmptyData会保留hasData值,为false,则被替换为null
     * ignoreGenericType - 忽略泛型,Map<String, Integer> 能和 Map 匹配
     * matchType -  为true,则listData不会生成,为false,则listData会生成, 并且会报错,类型转换错误
     * reNameField - 字段名别名映射
     * fieldNameFilter - 字段过滤(name,注释,正则)指定字段生效或者不生效
     */
@ConverterMapper(ignoreEmpty = true, ignoreGenericType = true,
    fieldNameFilter = @FieldNameFilter(assignIgnoreRegexFieldName = "ignore*"),
    reNameField = @ReNameField(sourceName = "oldName", targetName = "newName"))
public interface TestMappingFieldConverter extends BeanConverter<TestMappingFieldConverter.EntityA, TestMappingFieldConverter.EntityB> {

    @Data
    class EntityA {

        private Set<String> listData;

        private Map<String, Integer> mapData;

        private int age;

        private String oldName;

        private String emptyData;

        private String ignoreFiled;

    }

    @Data
    class EntityB {

        private List<String> listData;

        private Map mapData;

        private Long age;

        private String newName;

        private String emptyData;

        private String ignoreFiled;
    }

    @Override
    @ConverterMapping(ignoreEmpty = false, fieldNameFilter = @FieldNameFilter(assignFieldName = "emptyData", assignRegexFieldName = "ignore*"))
    MergeResult<EntityB> mergeTo(EntityA source, EntityB target);

}
private void testMappingFieldConverter() {
        TestMappingFieldConverter.EntityA entityA = new TestMappingFieldConverter.EntityA();
        entityA.setAge(1);
        entityA.setListData(Sets.newHashSet("123"));
        entityA.setOldName("123");
        entityA.setIgnoreFiled("nameA");
        HashMap<String, Integer> mapData = new HashMap<>();
        mapData.put("test", 123);
        entityA.setMapData(mapData);
        TestMappingFieldConverter converter = ConverterHolder.getConverter(TestMappingFieldConverter.class);
        TestMappingFieldConverter.EntityB entityB = new TestMappingFieldConverter.EntityB();
        entityB.setEmptyData("hasData");
        entityB.setIgnoreFiled("nameB");
        System.out.println("testMappingFieldConverter ---------------- 旧对象A:" + JSON.toJSONString(entityA));
        System.out.println("testMappingFieldConverter ---------------- 旧对象B:" + JSON.toJSONString(entityB));
        converter.mergeTo(entityA, entityB);
        System.out.println("testMappingFieldConverter ---------------- 新对象B:" + JSON.toJSONString(entityB));
        System.out.println("");
    }

结果

testMappingFieldConverter ---------------- 旧对象A:{"age":1,"ignoreFiled":"nameA","listData":["123"],"mapData":{"test":123},"oldName":"123"}
testMappingFieldConverter ---------------- 旧对象B:{"emptyData":"hasData","ignoreFiled":"nameB"}
testMappingFieldConverter ---------------- 新对象B:{"ignoreFiled":"nameA"}
6. MapConverter指定方法
 /**
     * MapConverter指定方法
     * JudgeEmptyMethod - 判断参数是否为空,返回值为boolean,匹配规则:入参类型和Source的类型
     * DefaultValueMethod - 默认值,如果参数为空,则修改为默认值,匹配规则:返回的类型和Source参数的类型
     * TypeChangeMethod - 类型转换,比如int -> String, 匹配规则:参数1的类型和Source参数类型,返回类型和target参数类型
     * postConvertToMap - ConvertToMap 之后触发, 能改变最终返回的Map<String, Object>
     * preConvertFromMap - ConvertFromMap 之前触发
     * postConvertFromMap - ConvertFromMap 之后触发, 能改变最终返回的Entity
     *
     * 参数说明:
     * fieldNameFilter - 字段过滤(name,注释,正则)指定字段生效或者不生效
     * primary - 多个规则生效的情况下,提高优先度, 默认前面的方法优先度更高(不能完全保证),所以primary最好只有一个
     *
     * 特殊规则 :
     * 1. 由于fromMap方式入参是Object,所以要特殊处理:
     *  1)assignFromMap 为true时, 非fromMap方法不生效,
     *  2)assignFromMap 为false时(默认),assignFieldName在fromMap方法不生, 其他属性则无影响都会生效
     *  3)由于入参是Object, fromMap会先强制转换成target对应字段的类型,所以DefaultValueMethod,JudgeEmptyMethod
     * 是根据target的参数类型匹配的,而TypeChangeMethod则不受影响,还是根据参数1类型是Object,返回类型和target参数类型
     *
     * 2. ConverterMapper中的defaultValue如果为false,则DefaultValueMethod不生效
     *
     * 3. ConverterMapper中的ignoreEmpty如果为false,但是如果存在DefaultValueMethod或者JudgeEmptyMethod
     * 方法,则仍然生效
     *
     * 4. 所有方法的类型匹配都会递归向父类索引,也就是HashMap找不到则会找Map,最后到Object
     */
@ConverterMapper
public interface TestMapMethodConverter extends MapConverter<TestMapMethodConverter.EntityA> {

    @Data
    class EntityA {

        private HashMap<String, Integer> emptyData;

        private int changeData;

        private int assignData;

        private long notChangeData;

        private int fromMapData;

    }

    @TypeChangeMethod(assignFromMap = true)
    default int fromMapChangeType(Object data) {
        return 10086;
    }

    @JudgeEmptyMethod
    default boolean judgeEmpty(HashMap<String, Integer> map) {
        return map == null || map.size() <= 0;
    }

    @DefaultValueMethod
    default HashMap<String, Integer> defaultValue() {
        HashMap<String, Integer> dataMap = new HashMap<>();
        dataMap.put("defaultValue", 0);
        return dataMap;
    }

    @TypeChangeMethod
    default Object changeType(int obj) {
        return "changeValue";
    }

    @TypeChangeMethod(fieldNameFilter = @FieldNameFilter(assignFieldName = "assignData"))
    default String assignChangeType(int obj) {
        return "assignValue";
    }

    @Override
    default Map<String, Object> postConvertToMap(TestMapMethodConverter.EntityA source, Map<String, Object> dataSource) {
        System.out.println("ConvertToMap 之后触发");
        return dataSource;
    }

    @Override
    default TestMapMethodConverter.EntityA preConvertFromMap(Map<String, Object> dataSource, TestMapMethodConverter.EntityA source) {
        System.out.println("ConvertFromMap 之前触发");
        return source;
    }

    @Override
    default EntityA postConvertFromMap(Map<String, Object> dataSource, EntityA source) {
        System.out.println("ConvertFromMap 之后触发");
        return source;
    }
}
    private void testMapMethodConverter() {
        TestMapMethodConverter.EntityA entityA = new TestMapMethodConverter.EntityA();
        entityA.setChangeData(100);
        entityA.setNotChangeData(200);
        entityA.setAssignData(300);
        entityA.setFromMapData(1000);
        TestMapMethodConverter converter = ConverterHolder.getConverter(TestMapMethodConverter.class);
        System.out.println("testMapMethodConverter(toMap) ---------------- 旧对象A:" + JSON.toJSONString(entityA));
        Map<String, Object> dataMap = converter.convertToMap(entityA);
        System.out.println("testMapMethodConverter(toMap) ---------------- 新对象B:" + JSON.toJSONString(dataMap));
        System.out.println("");
        System.out.println("testMapMethodConverter(FromMap) ---------------- 旧对象Map:" + JSON.toJSONString(dataMap));
        TestMapMethodConverter.EntityA newEntityA = converter.convertFromMap(dataMap);
        System.out.println("testMapMethodConverter(FromMap) ---------------- 新对象:" + JSON.toJSONString(newEntityA));
    }

结果

testMapMethodConverter(toMap) ---------------- 旧对象A:{"assignData":300,"changeData":100,"fromMapData":1000,"notChangeData":200}
ConvertToMap 之后触发
testMapMethodConverter(toMap) ---------------- 新对象B:{"assignData":"assignValue","notChangeData":200,"emptyData":{"defaultValue":0},"changeData":"changeValue","fromMapData":"changeValue"}
testMapMethodConverter(FromMap) ---------------- 旧对象Map:{"assignData":"assignValue","notChangeData":200,"emptyData":{"defaultValue":0},"changeData":"changeValue","fromMapData":"changeValue"}
ConvertFromMap 之前触发
ConvertFromMap 之后触发
testMapMethodConverter(FromMap) ---------------- 新对象:{"assignData":10086,"changeData":10086,"emptyData":{"defaultValue":0},"fromMapData":10086,"notChangeData":200}

7. BeanConverter指定方法
/**
     * BeanConverter指定方法
     * JudgeEmptyMethod - 判断参数是否为空,返回值为boolean,匹配规则:入参类型和Source的类型
     * DefaultValueMethod - 默认值,如果参数为空,则修改为默认值,匹配规则:返回的类型和Source参数的类型
     * TypeChangeMethod - 类型转换,比如int -> String, 匹配规则:参数1的类型和Source参数类型,返回类型和target参数类型
     * judgeSame - 判断是否相同,如果相同则跳过字段,匹配规则: 参数1的类型和Source参数类型,参数2的类型和target参数类型
     * postMergeTo - ConvertTo 之后触发, 能改变最终返回的Entity
     * postConvertTo - MergeTo 之后触发, 能改变最终返回的Entity
     *
     * 参数说明:
     * fieldNameFilter - 字段过滤(name,注释,正则)指定字段生效或者不生效
     * primary - 多个规则生效的情况下,提高优先度, 默认前面的方法优先度更高(不能完全保证),所以primary最好只有一个
     *
     * 特殊规则 :
     * 1. 由于这里没有fromMap方法,assignFromMap保持为false, 如果设为true则导致该方法不生效
     *
     * 2. ConverterMapper中的defaultValue如果为false,则DefaultValueMethod不生效
     *
     * 3. ConverterMapper中的ignoreEmpty如果为false,但是如果存在DefaultValueMethod或者JudgeEmptyMethod
     * 方法,则仍然生效
     *
     * 4. judgeSame的前提是target和source的类型是一致或者存在TypeChangeMethod
     *
     * 5. 所有方法的类型匹配都会递归向父类索引,也就是HashMap找不到则会找Map,最后到Object
     */
@ConverterMapper
public interface TestBeanMethodConverter extends BeanConverter<TestBeanMethodConverter.EntityA, TestBeanMethodConverter.EntityB> {

    @Data
    class EntityA {

        private HashMap<String, Integer> emptyData;

        private int changeData;

        private int assignData;

        private long notChangeData;

        private String sameData;

        private String notSameData;

    }

    @Data
    class EntityB {

        private HashMap<String, Integer> emptyData;

        private int changeData;

        private String assignData;

        private long notChangeData;

        private int sameData;

        private long notSameData;

    }

    @TypeChangeMethod
    default long notSameChangeType(String obj) {
        return 2999;
    }

    @JudgeSameMethod
    default boolean judgeSame(String param1, long param2) {
        return false;
    }

    @JudgeSameMethod
    default boolean judgeSame(String param1, int param2) {
        return true;
    }

    @TypeChangeMethod
    default int sameChangeType(String obj) {
        return 10086;
    }

    @JudgeEmptyMethod
    default boolean judgeEmpty(HashMap<String, Integer> map) {
        return map == null || map.size() <= 0;
    }

    @DefaultValueMethod
    default HashMap<String, Integer> defaultValue() {
        HashMap<String, Integer> dataMap = new HashMap<>();
        dataMap.put("defaultValue", 0);
        return dataMap;
    }

    @TypeChangeMethod(fieldNameFilter = @FieldNameFilter(assignFieldName = "assignData"))
    default String assignChangeType(int obj) {
        return "assignValue";
    }

@Override
    default EntityB postConvertTo(EntityA source, EntityB target) {
        System.out.println("ConvertTo 之后触发");
        return target;
    }

    @Override
    default MergeResult<EntityB> postMergeTo(EntityA source, EntityB target, MergeResult<EntityB> result) {
        System.out.println("MergeTo 之后触发");
        return result;
    }

}
private void testBeanMethodConverter() {
        TestBeanMethodConverter.EntityA entityA = new TestBeanMethodConverter.EntityA();
        entityA.setChangeData(100);
        entityA.setNotChangeData(200);
        entityA.setAssignData(300);
        entityA.setSameData("same");
        entityA.setNotSameData("notSame");
        TestBeanMethodConverter.EntityB entityB = new TestBeanMethodConverter.EntityB();
        entityB.setSameData(1);
        entityB.setNotSameData(2);
        TestBeanMethodConverter converter = ConverterHolder.getConverter(TestBeanMethodConverter.class);
        System.out.println("testBeanMethodConverter---------------- 旧对象A:" + JSON.toJSONString(entityA));
        System.out.println("testBeanMethodConverter---------------- 旧对象B:" + JSON.toJSONString(entityB));
        MergeResult<TestBeanMethodConverter.EntityB> entityBMergeResult = converter.mergeTo(entityA, entityB);
        System.out.println("testBeanMethodConverter ---------------- 新对象B:" + JSON.toJSONString(entityBMergeResult));
        System.out.println("");
    }

结果

testBeanMethodConverter---------------- 旧对象A:{"assignData":300,"changeData":100,"notChangeData":200,"notSameData":"notSame","sameData":"same"}
testBeanMethodConverter---------------- 旧对象B:{"changeData":0,"notChangeData":0,"notSameData":2,"sameData":1}
MergeTo 之后触发
testBeanMethodConverter ---------------- 新对象B:{"change":true,"changeFieldNames":["assignData","notChangeData","emptyData","changeData","notSameData"],"entity":{"assignData":"assignValue","changeData":100,"emptyData":{"defaultValue":0},"notChangeData":200,"notSameData":2999,"sameData":1}}
7. primary的运用
    /**
     * primary的运用
     * CommonTypeChange是封装了常用方法的工具接口,大家可以酌情使用,通过primary = true 就能提高优先度,保证子接口的方法被优先调用
     */
@ConverterMapper
public interface TestCommonTypeChangeConverter extends BeanConverter<TestCommonTypeChangeConverter.EntityA, TestCommonTypeChangeConverter.EntityB>,
    CommonTypeChange {

    @Data
    class EntityA {

        private String changeData1;

        private String changeDate2;

    }

    @Data
    class EntityB {

        private long changeData1;

        private int changeDate2;

    }

    @TypeChangeMethod(primary = true)
    default long customMethod(String string) {
        return 999999L;
    }

}
    private void testCommonTypeChange() {
        TestCommonTypeChangeConverter.EntityA entityA = new TestCommonTypeChangeConverter.EntityA();
        entityA.setChangeData1("10000");
        entityA.setChangeDate2("10086");
        TestCommonTypeChangeConverter converter = ConverterHolder.getConverter(TestCommonTypeChangeConverter.class);
        System.out.println("testBeanMethodConverter---------------- 旧对象A:" + JSON.toJSONString(entityA));
        TestCommonTypeChangeConverter.EntityB entityB = converter.convertTo(entityA);
        System.out.println("testBeanMethodConverter ---------------- 新对象B:" + JSON.toJSONString(entityB));
        System.out.println("");
    }

结果

testBeanMethodConverter---------------- 旧对象A:{"changeData1":"10000","changeDate2":"10086"}
testBeanMethodConverter ---------------- 新对象B:{"changeData1":999999,"changeDate2":10086}

5. 总结

  1. 首先 lp-converter-processor 使用很方便,就和你用lombok一样
  2. lp-converter-processor 参数的识别必须有get/set方法,所以配合lombok使用效果更佳, 比如上述中@Data就是lombok的注解
  3. 其次 lp-converter-processor 很安全,生成的字节码你都能看得见


    image.png

    如果你不满意或者生成的字节码有问题,你还可以自己实现接口,然后调用ConverterHolder.registerConverter()方法覆盖字节码生成类

/**
 * Converter持有者,Converter都会注册到这里
 */
public class ConverterHolder {

    public static final String BEAN_SUFFIX = "_ConverterImpl";

    private static Map<Class<?>, Converter> converterMap = new HashMap<>();

    public static void registerConverter(Class<?> clazz, Converter converter, boolean replace) {
        if(converter != null) {
            Converter oldConverter = converterMap.get(clazz);
            if(oldConverter == null || replace) {
                converterMap.put(clazz, converter);
            }
        }
    }

    public static void registerConverter(Class<?> clazz, Converter converter) {
        registerConverter(clazz, converter, false);
    }

    public static <B extends Converter> B getConverter(Class<B> clazz) {
        try {
            Class.forName(clazz.getName() + BEAN_SUFFIX);
        } catch (ClassNotFoundException e) {

        }
        return  (B)converterMap.get(clazz);
    }

}
  1. 自动生成的工具普遍的风险就是没法把控自动生成是否按计划生成,lp-converter-processor 提供字段生成的监控 @CheckFieldSetting, 对应方法(mergeTo/convertTo)上的@CheckFieldSetting覆盖BeanConverter上@CheckFieldSetting
@ConverterMapper
@CheckFieldSetting
public interface TestBeanConverter extends BeanConverter<TestBeanConverter.EntityA, TestBeanConverter.EntityB> {

    @Data
    class EntityA {

        private HashMap<String, Integer> mapData;

        private int age;

    }

    @Data
    class EntityB {

        private Map<String, Integer> mapData;

        private long age;

    }

    @Override
    @CheckFieldSetting(fieldNameFilter = @FieldNameFilter(assignIgnoreFieldName = "age"))
    MergeResult<EntityB> mergeTo(EntityA source, EntityB target);

    @Override
    @CheckFieldSetting(fieldNameFilter = @FieldNameFilter(assignIgnoreFieldName = "age"))
    EntityB convertTo(EntityA source);

}

上面的如果放开mergeTo的监控

    @Override
    MergeResult<EntityB> mergeTo(EntityA source, EntityB target);

编译就会报错

java: com.cn.lp.converter.exception.ProcessorException: 生成class异常, 错误信息:com.cn.lp.converter.exception.ProcessorException: generate mergeTo fail: 
source Entity not has age field GetMethod type is long

    at com.cn.lp.converter.generator.MergeMethodBuilder.createMethod(MergeMethodBuilder.java:207)

结语

起初只是为了解决一个场景去翻了下MapStruct源码,然后发现这种编写字节码很有趣(MapStruct满足不了需求),所以就写了这么一个插件,这个插件在21年就写完了,21年到22年间不断修修补补,直到现在个人已经觉得没什么迭代空间,就决定分享大家使用,最后分享个bean-converter-demo项目,方便大家上手使用,如果大家使用上有什么问题或者建议欢迎留言
项目地址: https://gitee.com/wqrzsy/lp-demo/tree/master/bean-converter-demo

如果这篇文章对你有帮助请给个star


image.png
上一篇下一篇

猜你喜欢

热点阅读