Cglib 和 Mica Bean copy 生成字節碼對比

1. 前言

距離上上篇【mica cglib 加強——【01】cglib bean copy 介紹】[1] 已通過去一個月零八天。java

距離上一篇【Java Bean Copy 性能大比拼】[2] 已過去一個月零一天。git

督促本身早日完成整個系列的文章,今天我將帶領你們從字節碼的層面來分析。web

:對於java 字節碼感興趣的朋友也能夠閱讀 《Java虛擬機規範》,Oracle 官方也有英文原版[3]的 pdf可供下載。spring


2. Bean 模型

咱們列舉2個模型 User 和 UserVo,注意:birthday 字段類型不同(敲黑板)。ide


2.1 User


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


2.2 UserVo







@Datapublic 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 介紹】[4]咱們提到能夠設置 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. 初始化 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,仔細觀察能夠看到使用了類型轉換以後生成的字節碼都走了類型轉換的邏輯。


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支持等,敢興趣的朋友能夠試用哦。

相關文章
相關標籤/搜索