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
關注我們
掃描上面二維碼,更多精彩內容每天推薦!