- 解决问题
- 原理
- 支持转换的情景
- 使用
- dependency
- 方式1(惰性加载)
- 方式2(通过注解预加载)
- 方式3(自定义预加载)
- 代码演示
- AStruct.java
- BStruct.java
- CStruct.java
- AInnerStruct.java
- BInnerStruct.java
- Good Luck~
Chameleon 是一款基于 javassist 动态字节码生成的高性能类型转换工具。
拥有比Spring的BeanUtils更高的性能。
通过动态加载类型转换类的方式,使Chameleon拥有很高的类型转换性能。 解决问题
- Spring 的 BeanUtils 类型转换效率相对不太理想;
- MapStruct 配置太过复杂;
- 在首次转换两个类时,通过 javassist 生成两个类型之间相互转化的字节码类,加载到JVM中,并缓存下来;
- 根据两个对象的Class,找到缓存中转化两者的动态实现类,调用生成的方法,完成转换。
支持转换的情景原理跟 MapStruct 相似,生成的转换类中使用Getter/Setter进行赋值,MapStruct 和 Chameleon 的效率相当;
不同的是,MapStruct 编译时生成转换类,Chameleon 运行时根据需要动态生成两者相互转换类;
Chameleon在惰性加载模式下,首次动态生成字节码并加载需要150ms左右;可以通过添加注解或者自定义适配选择器,来预加载类型转换类。
仅处理具有 getter/setter/is 函数的属性
-
类型相同,直接转换
1.1 类型为List>且泛型类相同,直接转换
1.2 类型为List>且泛型类不同,转换泛型类,再赋值
1.3 类型为List>且泛型类不同,目标值是String,原值不为空的情况下,将原值 toString 处理
-
类型不同,转换类型,再赋值
2.1 如果目标值是String,原值不为空的情况下,将原值 toString 处理
方式1(惰性加载)cn.muzin chameleon 1.0.1
不预加载转换类,在需要的时候加载转换类
// 1. 根据 Class 进行转换 AStruct aStruct = new AStruct(); // ignore aStruct Code ... BStruct bStruct = ChameleonUtil.transform(aStruct, BStruct.class); // 第三个参数为true时,子类型不一致,但字段相同,也可以转换 BStruct bStruct = ChameleonUtil.transform(aStruct, BStruct.class, true); // 2. 值拷贝 BStruct bStruct1 = new BStruct(); ChameleonUtil.transform(aStruct, bStruct1); // 3. 按照 Class,进行集合的转换 List方式2(通过注解预加载)aStructList = new ArrayList (); List bStructList = ChameleonUtil.transform(aStructList, BStruct.class);
在需要转换的类上面标记@ChameleonTransform注解,通过配置ChameleonTransformEnvironmentAdaptSelector
选择器,来预加载类型互转的转换类
注意:配置完成后,一定要调用ready方法!!!
// 添加 @ChameleonTransform 注解
@ChameleonTransform(dest = { BStruct.class, OtherStruct.class })
public class AStruct extends CStruct {
// class code ...
}
// 配置 注解适配选择器, 扫描指定包下面的所有类(可添加多个包名)
ChameleonUtil.addEnvironmentAdaptSelector(
new ChameleonTransformEnvironmentAdaptSelector()
.addbasePackage("cn.muzin.chameleon")
);
// 告诉 Chameleon 已经准备好了,开始配置组建。
ChameleonUtil.ready();
// 开始转换目标对象...
AStruct aStruct = new AStruct();
// ignore aStruct Code ...
BStruct bStruct = ChameleonUtil.transform(aStruct, BStruct.class);
方式3(自定义预加载)
实现EnvironmentAdaptSelector接口,自定义加载类型转换规则。
根据自己的需要返回类型之间的1对1、1对多关系。
// 根据需要返回相应的结构对 StructPair 结构对 +- StructToonePair 结构1对1 +- StructToMultiPair 结构1对多
示例:
// 实现 EnvironmentAdaptSelector 接口
public class EnvironmentAdaptSelectorImpl implements EnvironmentAdaptSelector {
public List selector() {
// your code...
return new ArrayList();
}
}
// 通过 Chameleon 或者 ChameleonUtil 添加该适配选择器
ChameleonUtil.addEnvironmentAdaptSelector(
new EnvironmentAdaptSelectorImpl()
);
// 告诉 Chameleon 已经准备好了,开始配置组建。
ChameleonUtil.ready();
// 开始转换目标对象...
AStruct aStruct = new AStruct();
// ignore aStruct Code ...
BStruct bStruct = ChameleonUtil.transform(aStruct, BStruct.class);
代码演示
以下@Data为lombok的@Data注解,不想使用lombok,可以将@Data去掉,生成Getter/Setter.
@ChameleonTransform 在使用注解适配选择器时配置,可以不配置注解进行惰性加载,或者根据自定义适配选择器进行预加载。
AStruct.java@ChameleonTransform(dest = { BStruct.class })
@Data
public class AStruct extends CStruct {
private String name;
private String age;
private String weight;
private String height;
private String idcard;
private String school;
private String profile;
private String photo;
private AInnerStruct inner;
private List innerList;
private Integer ttt;
private List signList;
private List strList;
}
BStruct.java
@Data
public class BStruct extends CStruct {
private String name;
private String age;
private String weight;
private String height;
private String idcard;
private String school;
private String profile;
private String photo;
private BInnerStruct inner;
private List innerList;
private String ttt;
private List signList;
private List strList;
}
CStruct.java
@Data
public class CStruct {
private String namec;
}
AInnerStruct.java
@ChameleonTransform(dest = { BInnerStruct.class })
@Data
public class AInnerStruct {
private String ppp;
}
BInnerStruct.java
@Data
public class BInnerStruct {
private String ppp;
}
ChameleonUtil.transform(struct, class, Boolean) 函数 的第三个值用于控制类型不匹配也尝试进行转换。
如上:AStruct和BStruct中存在inner和innerList, 在 第三个值为true的情况下,内部类型不匹配也可以进行转换。
示例:
long st = 0;
long et = 0;
// 设置 class 文件存储位置,不设置时,默认在临时目录下,便于debug时,查看class的情况。
ChameleonUtil.setTmpDir("/Users/sirius/bucket/project/IdeaProjects/chameleon/target/dclass");
// 配置 注解适配选择器
ChameleonUtil.addEnvironmentAdaptSelector(
new ChameleonTransformEnvironmentAdaptSelector()
.addbasePackage("cn.muzin.chameleon")
);
st = System.currentTimeMillis();
// 开始根据注解 预加载 转换类
ChameleonUtil.ready();
et = System.currentTimeMillis();
System.out.println((et - st) + " ms ready");
AStruct aStruct = new AStruct();
AInnerStruct aInnerStruct = new AInnerStruct();
aInnerStruct.setPpp("asdf");
aStruct.setAge("23");
aStruct.setName("23");
aStruct.setHeight("23");
aStruct.setIdcard("23");
aStruct.setPhoto("23");
aStruct.setSchool("23");
aStruct.setWeight("23");
aStruct.setNamec("23");
aStruct.setInner(aInnerStruct);
aStruct.setTtt(123);
List longs = new ArrayList();
longs.add(123L);
longs.add(127L);
longs.add(125L);
aStruct.setSignList(longs);
aStruct.setStrList(longs);
ArrayList aInnerStructs = new ArrayList<>();
aInnerStructs.add(aInnerStruct);
aInnerStructs.add(aInnerStruct);
aStruct.setInnerList(aInnerStructs);
for(int o = 0; o < 10; o++) {
st = System.currentTimeMillis();
for (int i = 0; i < 10000000; i++) {
BStruct bStruct = ChameleonUtil.transform(aStruct, BStruct.class, true);
}
et = System.currentTimeMillis();
System.out.println((et - st) + " ms transform");
}
结果:



