使用 Chameleon 进行高性能java类型转换,支持不同类型结构的转换

2 篇文章 0 订阅
1 篇文章 0 订阅


Chameleon 是一款基于 javassist 动态字节码生成的高性能类型转换工具。
拥有比Spring的BeanUtils更高的性能。
通过动态加载类型转换类的方式,使Chameleon拥有很高的类型转换性能。

解决问题

  1. Spring 的 BeanUtils 类型转换效率相对不太理想;
  2. MapStruct 配置太过复杂;

原理

  1. 在首次转换两个类时,通过 javassist 生成两个类型之间相互转化的字节码类,加载到JVM中,并缓存下来;
  2. 根据两个对象的Class,找到缓存中转化两者的动态实现类,调用生成的方法,完成转换。

原理跟 MapStruct 相似,生成的转换类中使用Getter/Setter进行赋值,MapStruct 和 Chameleon 的效率相当;

不同的是,MapStruct 编译时生成转换类,Chameleon 运行时根据需要动态生成两者相互转换类;

Chameleon在惰性加载模式下,首次动态生成字节码并加载需要150ms左右;可以通过添加注解或者自定义适配选择器,来预加载类型转换类。

支持转换的情景

仅处理具有 getter/setter/is 函数的属性

  1. 类型相同,直接转换

    1.1 类型为List<?>且泛型类相同,直接转换

    1.2 类型为List<?>且泛型类不同,转换泛型类,再赋值

    1.3 类型为List<?>且泛型类不同,目标值是String,原值不为空的情况下,将原值 toString 处理

  2. 类型不同,转换类型,再赋值

    2.1 如果目标值是String,原值不为空的情况下,将原值 toString 处理

使用

dependency

<dependency>
   <groupId>cn.muzin</groupId>
   <artifactId>chameleon</artifactId>
   <version>1.0.1</version>
</dependency>

方式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<AStruct> aStructList = new ArrayList<AStruct>();
List<BStruct> bStructList = ChameleonUtil.transform(aStructList, BStruct.class);

方式2(通过注解预加载)

在需要转换的类上面标记@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<StructPair> selector() {
      // your code...
      return new ArrayList<StructPair>();
   }

}

// 通过 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<AInnerStruct> innerList;
    private Integer ttt;
    private List<Long> signList;
    private List<Long> 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<BInnerStruct> innerList;
    private String ttt;
    private List<String> signList;
    private List<Long> 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) 函数 的第三个值用于控制类型不匹配也尝试进行转换。
如上:AStructBStruct中存在innerinnerList, 在 第三个值为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<Long> longs = new ArrayList<Long>();
longs.add(123L);
longs.add(127L);
longs.add(125L);

aStruct.setSignList(longs);
aStruct.setStrList(longs);

ArrayList<AInnerStruct> 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");
}

结果:
在这里插入图片描述

Good Luck~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值