Cglib 和 Mica Bean copy 生成字节码对比

栏目: Java · 发布时间: 7年前

内容简介:距离上上篇距离上一篇督促自己早日完成整个系列的文章,今天我将带领大家从字节码的层面来分析。

1. 前言

距离上上篇 【mica cglib 增强——【01】cglib bean copy 介绍】 已经过去一个月八一天。

距离上一篇 【Java Bean Copy 性能大比拼】 已过去一个月零一天。

督促自己早日完成整个系列的文章,今天我将带领大家从字节码的层面来分析。

:对于 java 字节码感兴趣的朋友也可以阅读 《Java虚拟机规范》,Oracle 官方也有 英文原版 的 pdf可供下载。

2. Bean 模型

我们列举2个模型 UserUserVo ,注意: birthday 字段类型不一样(敲黑板)。

2.1 User

@Data
public class User {
    private Integer id;
    private String name;
    private Integer age;
    private LocalDateTime birthday;
}

2.2 UserVo

@Data
public class UserVo {
    private String name;
    private Integer age;
    private String birthday;
}

3. Cglib Bean copy 字节码分析

3.1 配置 Cglib debug 模式

在第一篇 【mica cglib 增强——【01】cglib bean copy 介绍】 我们提到可以设置 cglib 源码生成目录。

// 设置 cglib 源码生成目录
String sourcePath = "/Users/lcm/git/mica/mica-example/web-example/src/test/java";
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, sourcePath);

3.2 Bean copy 时生成的字节码

不使用类型转换时

Bean copy 的代码如下:

// 1. 初始化 user,赋值
User user = new User();
user.setId(250);
user.setName("如梦技术");
user.setAge(30);
user.setBirthday(LocalDateTime.now());
// 2. 初始化 userVo
UserVo userVo = new UserVo();
// 3. 构造 BeanCopier,不是用类型转换
BeanCopier copier = BeanCopier.create(User.class, UserVo.class, false);
// 4. 拷贝对象,不是用类型转换,转换器可以使用 null
copier.copy(user, userVo, null);

// 5. 打印结果:UserVo(name=如梦技术, age=30, birthday=null)
System.out.println(userVo);

生成的字节码:

package org.springframework.cglib.empty;

import net.dreamlu.test.User;
import net.dreamlu.test.UserVo;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;

public class Object$$BeanCopierByCGLIB$$70f9539b extends BeanCopier {
    public Object$$BeanCopierByCGLIB$$70f9539b() {
    }

    public void copy(Object var1, Object var2, Converter var3) {
        UserVo var10000 = (UserVo)var2;
        User var10001 = (User)var1;
        var10000.setAge(((User)var1).getAge());
        var10000.setName(var10001.getName());
    }
}

注意: 由于 birthday 字段类型不一样,没有生成 set 方法。

使用类型转换时

Bean copy 的代码如下:

// 1. 初始化 user,赋值
User user = new User();
user.setId(250);
user.setName("如梦技术");
user.setAge(30);
user.setBirthday(LocalDateTime.now());

// 2. 初始化 userVo
UserVo userVo = new UserVo();
// 3. 构造 BeanCopier,不是用类型转换
BeanCopier copier = BeanCopier.create(User.class, UserVo.class, true);
// 4. 拷贝对象,不是用类型转换,转换器可以使用 null
copier.copy(user, userVo, new Converter() {
    @Override
    public Object convert(Object o, Class aClass, Object o1) {
        if (o == null) {
            return null;
        }
        // 直接使用 mica 中的类型转换工具
        return ConvertUtil.convert(o, aClass);
    }
});

// 5. 打印结果:UserVo(name=如梦技术, age=30, birthday=19-4-30 下午9:45)
System.out.println(userVo);

生成的字节码:

package org.springframework.cglib.empty;

import net.dreamlu.test.User;
import net.dreamlu.test.UserVo;
import org.springframework.cglib.beans.BeanCopier;
import org.springframework.cglib.core.Converter;

public class Object$$BeanCopierByCGLIB$$70f9539a extends BeanCopier {
    private static final Class CGLIB$load_class$java$2Elang$2EInteger;
    private static final Class CGLIB$load_class$java$2Elang$2EString;

    public Object$$BeanCopierByCGLIB$$70f9539a() {
    }

    public void copy(Object var1, Object var2, Converter var3) {
        UserVo var4 = (UserVo)var2;
        User var5 = (User)var1;
        var4.setAge((Integer)var3.convert(var5.getAge(), CGLIB$load_class$java$2Elang$2EInteger, "setAge"));
        var4.setBirthday((String)var3.convert(var5.getBirthday(), CGLIB$load_class$java$2Elang$2EString, "setBirthday"));
        var4.setName((String)var3.convert(var5.getName(), CGLIB$load_class$java$2Elang$2EString, "setName"));
    }

    static void CGLIB$STATICHOOK1() {
        CGLIB$load_class$java$2Elang$2EInteger = Class.forName("java.lang.Integer");
        CGLIB$load_class$java$2Elang$2EString = Class.forName("java.lang.String");
    }

    static {
        CGLIB$STATICHOOK1();
    }
}

注意: 使用类型转换后生成了 birthdayset ,仔细观察可以看到使用了类型转换之后生成的字节码都走了 类型转换 的逻辑。

4 Mica Bean copy的字节码

由于 Mica 的 Bean copy 是基于 Cglib 进行的增强查看字节码的方式和Cglib一样,设置的方式也和上面一样。

4.1 不使用类型转换

Bean copy 的代码如下:

// 1. 初始化 user,赋值
User user = new User();
user.setId(250);
user.setName("如梦技术");
user.setAge(30);
user.setBirthday(LocalDateTime.now());

// 2. 使用 mica 的 BeanUtil copy 方法
UserVo userVo = BeanUtil.copy(user, UserVo.class);

// 3. 打印结果:UserVo(name=如梦技术, age=30, birthday=null)
System.out.println(userVo);

生成的字节码:

package org.springframework.cglib.empty;

import net.dreamlu.mica.core.beans.MicaBeanCopier;
import net.dreamlu.test.User;
import net.dreamlu.test.UserVo;
import org.springframework.cglib.core.Converter;

public class Object$$MicaBeanCopierByCGLIB$$aa75e50d extends MicaBeanCopier {
    public Object$$MicaBeanCopierByCGLIB$$aa75e50d() {
    }

    public void copy(Object var1, Object var2, Converter var3) {
        UserVo var4 = (UserVo)var2;
        User var5 = (User)var1;
        var4.setAge(var5.getAge());
        var4.setName(var5.getName());
    }
}

注意: 不使用类型转换时生成的字节码同 Cglib 一致,只是使用更加简单一些。

4.2 使用类型转换时

Bean copy 的代码如下:

// 1. 初始化 user,赋值
User user = new User();
user.setId(250);
user.setName("如梦技术");
user.setAge(30);
user.setBirthday(LocalDateTime.now());

// 2. 使用 mica 的 BeanUtil copyWithConvert 方法
UserVo userVo = BeanUtil.copyWithConvert(user, UserVo.class);

// 3. 打印结果:UserVo(name=如梦技术, age=30, birthday=19-4-30 下午10:04)
System.out.println(userVo);

生成的字节码:

package org.springframework.cglib.empty;

import net.dreamlu.mica.core.beans.MicaBeanCopier;
import net.dreamlu.test.User;
import net.dreamlu.test.UserVo;
import org.springframework.cglib.core.Converter;

public class Object$$MicaBeanCopierByCGLIB$$aa75e0e7 extends MicaBeanCopier {
    private static final Class CGLIB$load_class$java$2Elang$2EString;

    public Object$$MicaBeanCopierByCGLIB$$aa75e0e7() {
    }

    public void copy(Object var1, Object var2, Converter var3) {
        UserVo var4 = (UserVo)var2;
        User var5 = (User)var1;
        var4.setAge(var5.getAge());
        var4.setBirthday((String)var3.convert(var5.getBirthday(), CGLIB$load_class$java$2Elang$2EString, "birthday"));
        var4.setName(var5.getName());
    }

    static void CGLIB$STATICHOOK1() {
        CGLIB$load_class$java$2Elang$2EString = Class.forName("java.lang.String");
    }

    static {
        CGLIB$STATICHOOK1();
    }
}

注意: 可以看到 Mica 中对生成的字节码进行了优化,对类型相同的拷贝不使用类型转换。

总结

在 Mica 中笔者对 Bean Copy 进行了大量的优化,包括类型转换优化,链式Bean支持,Map支持等,敢兴趣的朋友可以试用哦。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Go Web 编程

Go Web 编程

[新加坡]Sau Sheong Chang(郑兆雄) / 黄健宏 / 人民邮电出版社 / 2017-11-22 / 79

《Go Web 编程》原名《Go Web Programming》,原书由新加坡开发者郑兆雄(Sau Sheong Chang)创作、 Manning 出版社出版,人名邮电出版社引进了该书的中文版权,并将其交由黄健宏进行翻译。 《Go Web 编程》一书围绕一个网络论坛 作为例子,教授读者如何使用请求处理器、多路复用器、模板引擎、存储系统等核心组件去构建一个 Go Web 应用,然后在该应用......一起来看看 《Go Web 编程》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具