Cglib 和 Mica Bean copy 生成字节码对比
1. 前言
距离上上篇【mica cglib 增强——【01】cglib bean copy 介绍】 已经过去一个月八一天。
距离上一篇【Java Bean Copy 性能大比拼】 已过去一个月零一天。
督促自己早日完成整个系列的文章,今天我将带领大家从字节码的层面来分析。
注
:对于java 字节码感兴趣的朋友也可以阅读 《Java虚拟机规范》,Oracle 官方也有英文原版的 pdf可供下载。
2. Bean 模型
我们列举2个模型 User
和 UserVo
,注意: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(); } }
注意:
使用类型转换后生成了 birthday
的 set
,仔细观察可以看到使用了类型转换之后生成的字节码都走了类型转换的逻辑。
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支持等,敢兴趣的朋友可以试用哦。
相关链接
-
示例项目
:https://github.com/lets-mica/mica-example - mica 源码 Github:https://github.com/lets-mica
- mica 源码 Gitee(码云):https://gitee.com/596392912/mica
- 文档地址(官网):https://www.dreamlu.net/docs/
- 文档地址(语雀-可关注订阅):https://www.yuque.com/dreamlu/mica
开源推荐
-
Avue
一款基于vue可配置化的神奇框架:https://gitee.com/smallweigit/avue -
pig
宇宙最强微服务(架构师必备):https://gitee.com/log4j/pig -
SpringBlade
完整的线上解决方案(企业开发必备):https://gitee.com/smallc/SpringBlade -
IJPay
支付SDK让支付触手可及:https://gitee.com/javen205/IJPay
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
我很喜欢玩游戏,那么我就适合做游戏程序员吗?
作者:黄小斜 文章来源:【程序员江湖】 游戏在今天的普及度已经不是端游时代可以比肩的了。如今人手一台手机、平板就可以吃鸡、打农药,不仅是男生,也有很多女生加入了游戏圈。相信现在在看文章的你也玩游戏,虽然爱玩的程度不同,但是至少都是感兴趣的,当然你也知道,手游行业利润高,游戏程序员自然也吃香,能一边赚钱一边玩游戏,岂不是人生一大幸事呢?其实当年我也是这么想的。 为成为游戏程序员而读研 大学的时候学的专业和计算机不太沾边,对学的东西不太感兴趣,每天的生活就是上课开黑打游戏,在大学的男生宿舍里,这样的情况确实也比较普遍。恰逢做毕业设计的时候,在课题列表上看到了一个关于“unity3D游戏开发”的课题,我特别感兴趣,于是果断选择了它。当时的水平就是刚刚c语言入门,有多菜可想而知,于是跟着网上的视频撸了一个劣质的《炉石传说》出来,连一局游戏都打不完的那种。 但是从那时候开始,我就感觉游戏开发确实挺有趣的。这么多年来,国内的游戏大厂也只有腾讯网易两家,网易游戏的游戏研发招聘要求就是至少要985研究生,腾讯虽然没有指定学历要求,但是要求也绝对不低。 除了学历要求之外,最要命的就是技术要求了,精通C+...
- 下一篇
TCP 粘包拆包
粘包问题 在 TCP 这种字节流协议上做应用层分包是网络编程的基本需求。分包指的是在发生一个消息(message)或一帧(frame)数据时,通过一定的处理,让接收方能从字节流中识别并截取(还原)出一个个消息。因此,“粘包问题”是个伪命题 短连接分包 对于短连接的 TCP 服务,分包不是一个问题,只要发送方主动关闭连接,就表示一个消息发送完毕,接收方 read() 返回0,从而知道消息的结尾 TCP 发送机制 为了提高 TCP 的传输效率,TCP 有一套自己的发送机制 TCP 维持一个变量,它等于最大报文段长度 MSS。只要缓存中存放的数据达到 MSS 字节时,就组装成一个 TCP 报文段发送出去 由发送方的应用进程指明要求发送报文段,即 TCP 支持的推送(push)操作 发送方的一个计时器期限到了,这时把当前已有的缓存数据装入报文段(但长度不能超过 MSS)发送出去 长连接分包 对于长连接的 TCP 服务,分包有四种方法 消息长度固定 使用特殊的字符或字符串作为消息的边界,例如 HTTP 协议的 headers 以“rn”为字段的分隔符 在每条消息的头部加一个长度字段,这恐怕是最常...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
-
Docker使用Oracle官方镜像安装(12C,18C,19C)
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- CentOS8编译安装MySQL8.0.19
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- MySQL8.0.19开启GTID主从同步CentOS8
- CentOS7,8上快速安装Gitea,搭建Git服务器
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果
推荐阅读
最新文章
- Docker使用Oracle官方镜像安装(12C,18C,19C)
- CentOS6,CentOS7官方镜像安装Oracle11G
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2配置默认Tomcat设置,开启更多高级功能
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- Hadoop3单机部署,实现最简伪集群
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2编写第一个Controller,响应你的http请求并返回结果