How CGLIB Generates Bean Copy Bytecode: A Deep Dive with Mica
This article examines the bytecode generated by CGLIB during Bean copying, compares scenarios with and without type conversion, explores Mica’s enhanced BeanCopier implementation, and provides concrete Java code examples to illustrate how field type differences affect generated methods and performance.
1. Introduction
One month and eight days have passed since the previous article "mica cglib enhancement – [01] cglib bean copy introduction" and one month and one day since "Java Bean Copy Performance Comparison". This article continues the series by analyzing the bytecode level.
Note: For those interested in Java bytecode, refer to the official JVM specification PDF .
2. Bean Models
We define two models,
Userand
UserVo. The
birthdayfield has different types in the two classes.
2.1 User
<code>@Data
public class User {
private Integer id;
private String name;
private Integer age;
private LocalDateTime birthday;
}</code>2.2 UserVo
<code>@Data
public class UserVo {
private String name;
private Integer age;
private String birthday;
}</code>3. CGLIB Bean Copy Bytecode Analysis
3.1 Configure CGLIB Debug Mode
<code>// Set CGLIB source generation directory
String sourcePath = "/Users/lcm/git/mica/mica-example/web-example/src/test/java";
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, sourcePath);</code>3.2 Bean copy without type conversion
Bean copy code:
<code>// 1. Initialize user and set values
User user = new User();
user.setId(250);
user.setName("如梦技术");
user.setAge(30);
user.setBirthday(LocalDateTime.now());
// 2. Initialize userVo
UserVo userVo = new UserVo();
// 3. Create BeanCopier without type conversion
BeanCopier copier = BeanCopier.create(User.class, UserVo.class, false);
// 4. Copy objects without type conversion (converter can be null)
copier.copy(user, userVo, null);
// 5. Print result
System.out.println(userVo); // UserVo(name=如梦技术, age=30, birthday=null)</code>Generated bytecode (simplified):
<code>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());
}
}</code>Note: Because the
birthdayfield types differ, no
setBirthdaymethod is generated.
3.3 Bean copy with type conversion
Bean copy code:
<code>// 1. Initialize user and set values
User user = new User();
user.setId(250);
user.setName("如梦技术");
user.setAge(30);
user.setBirthday(LocalDateTime.now());
// 2. Initialize userVo
UserVo userVo = new UserVo();
// 3. Create BeanCopier with type conversion
BeanCopier copier = BeanCopier.create(User.class, UserVo.class, true);
// 4. Copy objects using a Converter
copier.copy(user, userVo, new Converter() {
@Override
public Object convert(Object o, Class aClass, Object o1) {
if (o == null) {
return null;
}
// Use Mica's conversion utility
return ConvertUtil.convert(o, aClass);
}
});
// 5. Print result
System.out.println(userVo); // UserVo(name=如梦技术, age=30, birthday=19-4-30 下午9:45)</code>Generated bytecode (simplified):
<code>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(); }
}</code>Note: With type conversion, the generated bytecode includes calls to the conversion logic for the
birthdayfield.
4. Mica Bean Copy Bytecode
4.1 Without type conversion
Bean copy code using Mica's
BeanUtil.copy:
<code>// 1. Initialize user and set values
User user = new User();
user.setId(250);
user.setName("如梦技术");
user.setAge(30);
user.setBirthday(LocalDateTime.now());
// 2. Use Mica BeanUtil copy
UserVo userVo = BeanUtil.copy(user, UserVo.class);
// 3. Print result
System.out.println(userVo); // UserVo(name=如梦技术, age=30, birthday=null)</code>Generated bytecode (simplified):
<code>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());
}
}</code>4.2 With type conversion
Bean copy code using Mica's
BeanUtil.copyWithConvert:
<code>// 1. Initialize user and set values
User user = new User();
user.setId(250);
user.setName("如梦技术");
user.setAge(30);
user.setBirthday(LocalDateTime.now());
// 2. Use Mica BeanUtil copy with conversion
UserVo userVo = BeanUtil.copyWithConvert(user, UserVo.class);
// 3. Print result
System.out.println(userVo); // UserVo(name=如梦技术, age=30, birthday=19-4-30 下午10:04)</code>Generated bytecode (simplified):
<code>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(); }
}</code>Note: Mica optimizes the generated bytecode by omitting type conversion for fields with matching types, while still applying conversion for mismatched fields like
birthday.
5. Conclusion
The author has performed extensive optimizations on Bean Copy in Mica, including type‑conversion optimization, support for chained beans, and Map support. Interested readers are encouraged to try the library.
References
[1] "mica cglib enhancement – [01] cglib bean copy introduction" – https://my.oschina.net/qq596392912/blog/3026059
[2] "Java Bean Copy Performance Comparison" – https://juejin.im/post/5c9d5cd1e51d455788281089
[3] Official JVM Specification (English) – https://docs.oracle.com/javase/specs/index.html
[4] "mica cglib enhancement – [01] cglib bean copy introduction" – https://my.oschina.net/qq596392912/blog/3026059
Java Architecture Diary
Committed to sharing original, high‑quality technical articles; no fluff or promotional content.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.