java脑洞 效率最高和最懂国人的对象转换工具 lp-conve
2022-07-22 本文已影响0人
灰色调诺言
脑洞的由来
开发过程中经常遇到
- 把EntityA的属性赋值给新的EntityB
- 把Entity的属性转换成Map结构
- 把Map结构的数据转换成Entity
当前常见解决方案
- 把Entity转换成json,再把json转换成目标Entity
- 第三方工具MapStruct
- spring的BeanUtils
效率对比
- json转换和BeanUtils都是基于Class的反射映射,效率不高
- MapStruct是根据注解生成字节码,效率高,但是不够灵活,而且没法满足场景二和场景三
介绍 lp-converter-processor
- 基于注解生成字节码,效率高。
- 基于jdk8的接口default机制实现灵活编入
- 有字段检查机制,提供安全检查能力
- 能兼容lombok
- 国人编写(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实现
- 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结构
- 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. 总结
- 首先 lp-converter-processor 使用很方便,就和你用lombok一样
- lp-converter-processor 参数的识别必须有get/set方法,所以配合lombok使用效果更佳, 比如上述中@Data就是lombok的注解
-
其次 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);
}
}
- 自动生成的工具普遍的风险就是没法把控自动生成是否按计划生成,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