开发者社区> Dreamlu> 正文

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

简介: 1. 前言 距离上上篇【mica cglib 增强——【01】cglib bean copy 介绍】 已经过去一个月八一天。 距离上一篇【Java Bean Copy 性能大比拼】 已过去一个月零一天。
+关注继续查看

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支持等,敢兴趣的朋友可以试用哦。

相关链接

开源推荐

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
freemarker 模板生成 文件
最近公司比较忙,好久没写文章了,今天来一篇。 要生成的目标java文件 package com.cs.qdog.swift.objects; public class F32B { private Double amount; private String currency; public Double getAmount()
1159 0
MyEclipse 从数据库反向生成Hibernate实体类
         第一个大步骤 window-->open Perspective-->MyEclipse Java Persistence 进行了上面的 操作后会出现一个视图DB Brower:MyEclipse Derby,点击右键新建一个在出现的面板中,driver templat...
864 0
二维码生成和长按跳转页面及扫码预览页面及vue中的使用
二维码的应用在现在生活和项目中应用的越来越多,下面就对二维码的生成及使用做一下总结。 一、二维码的生成 qrcode.js及jquery-qrcode.js实现生成二维码: qrcode.
4499 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
19577 0
Netbeans源代码编辑技巧——使用代码补全和代码生成
原文 Netbeans源代码编辑技巧——使用代码补全和代码生成 使用代码补全生成代码 一般来说,代码补全对于自动填充缺失的代码是有帮助的,例如标识符和关键字。截至 NetBeans IDE 6.0,您现在甚至可以用代码补全来生成整个方法。
1018 0
Json 逆向生成 javaBean
我的开发环境:IDEA+maven+jdk1.8 1.下载idea插件GsonFormat 2. 添加依赖 该插件支持的json序列化工具: 我在这里用阿里的fastJson 依赖,想用gson、jackson的都可以,插件都支持 com.
1347 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
24726 0
IDEA 中 30 秒生成 Spring Cloud Alibaba 工程
近日,阿里巴巴发布了 Spring 的国内脚手架定制版 Aliyun Java Initializer,因为全中文界面和流畅速度,被广大开发者热传。Spring 脚手架为开发者提供了丰富的可选组件,并且可以选择多种打包方式,大大方便了开发人员的使用。
11254 0
+关注
Dreamlu
致力于线互联网开发实战经验,提供专业的技术咨询、教育和开发服务。
26
文章
1
问答
文章排行榜
最热
最新
相关电子书
更多
OceanBase 入门到实战教程
立即下载
阿里云图数据库GDB,加速开启“图智”未来.ppt
立即下载
实时数仓Hologres技术实战一本通2.0版(下)
立即下载