美文网首页java 脑洞开源工具
java脑洞 效率最高和最懂国人的对象转换工具 lp-conve

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

作者: 灰色调诺言 | 来源:发表于2022-07-22 10:43 被阅读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

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/lwjyirtx.html