距离上上篇【mica cglib 加强——【01】cglib bean copy 介绍】[1] 已通过去一个月零八天。java
距离上一篇【Java Bean Copy 性能大比拼】[2] 已过去一个月零一天。git
督促本身早日完成整个系列的文章,今天我将带领你们从字节码的层面来分析。web
注
:对于java 字节码感兴趣的朋友也能够阅读 《Java虚拟机规范》,Oracle 官方也有英文原版[3]的 pdf可供下载。spring
咱们列举2个模型 User
和 UserVo
,注意:birthday
字段类型不同(敲黑板)。ide
@Datapublic class User { private Integer id; private String name; private Integer age; private LocalDateTime birthday;}
@Datapublic class UserVo { private String name; private Integer age; private String birthday;}
在第一篇【mica cglib 加强——【01】cglib bean copy 介绍】[4]咱们提到能够设置 cglib 源码生成目录。工具
// 设置 cglib 源码生成目录String sourcePath = "/Users/lcm/git/mica/mica-example/web-example/src/test/java";System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, sourcePath);
Bean copy 的代码以下:性能
// 1. 初始化 user,赋值User user = new User();user.setId(250);user.setName("如梦技术");user.setAge(30);user.setBirthday(LocalDateTime.now());// 2. 初始化 userVoUserVo userVo = new UserVo();// 3. 构造 BeanCopier,不是用类型转换BeanCopier copier = BeanCopier.create(User.class, UserVo.class, false);// 4. 拷贝对象,不是用类型转换,转换器能够使用 nullcopier.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
方法。spa
Bean copy 的代码以下:debug
// 1. 初始化 user,赋值User user = new User();user.setId(250);user.setName("如梦技术");user.setAge(30);user.setBirthday(LocalDateTime.now());
// 2. 初始化 userVoUserVo userVo = new UserVo();// 3. 构造 BeanCopier,不是用类型转换BeanCopier copier = BeanCopier.create(User.class, UserVo.class, true);// 4. 拷贝对象,不是用类型转换,转换器能够使用 nullcopier.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
,仔细观察能够看到使用了类型转换以后生成的字节码都走了类型转换的逻辑。
因为 Mica 的 Bean copy 是基于 Cglib 进行的加强查看字节码的方式和Cglib同样,设置的方式也和上面同样。
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
一致,只是使用更加简单一些。
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支持等,敢兴趣的朋友能够试用哦。