MyBatis 支持使用 CGLIB 实现延迟加载,需配置 lazyLoadingEnabled=true、aggressiveLazyLoading=false 和 proxyFactory=CGLIB;CGLIB 通过继承生成代理子类,重写 getter 方法实现按需加载,要求目标类非 final、getter 非 final、存在无参构造函数。

MyBatis 默认使用 JAVASSIST 作为代理工厂,但确实支持切换为 CGLIB 实现延迟加载。CGLIB 是基于字节码增强的代理技术,它通过继承目标类生成子类代理(而非 JDK 动态代理的接口实现方式),因此能对没有接口的 POJO 类生效——这对大多数 MyBatis 实体类(如 User、Order)很关键。
启用 CGLIB 代理的配置方式
在 mybatis-config.xml 的 <settings> 中显式指定代理工厂:
-
必须开启延迟加载总开关:
lazyLoadingEnabled=true -
禁用积极加载行为:
aggressiveLazyLoading=false(否则调用toString()等方法也会触发加载) -
指定 CGLIB 为代理工具:
proxyFactory=CGLIB
完整配置示例:
<settings><setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
<setting name="proxyFactory" value="CGLIB"/>
</settings>
CGLIB 延迟加载的运行机制
当 MyBatis 返回一个带关联对象(如 <association> 或 <collection>)的结果时:
立即学习“Java免费学习笔记(深入)”;
- 它不会直接 new 出真实实体,而是用 CGLIB 创建该实体类的子类代理(例如
User$$EnhancerByCGLIB$$a1b2c3d4) - 这个子类重写了所有 getter 方法(如
getOrders()),并在方法内部插入拦截逻辑 - 首次调用
getOrders()时,代理检查对应字段是否为空;若为空,则执行预先绑定的 SQL(由select属性或嵌套查询定义),填充集合并缓存结果 - 后续再调用同一 getter,直接返回已加载的数据,不再查库
使用 CGLIB 的前提与注意事项
CGLIB 依赖字节码操作,需额外引入依赖,并满足一些约束:
-
添加 CGLIB 依赖(Maven):
cglib-nodep(推荐)或cglib,避免与 Spring 的 CGLIB 冲突 - 目标类不能是 final:CGLIB 通过继承生成代理,final 类无法被继承,会抛出异常
- getter 方法不能是 final:否则无法被子类重写,延迟逻辑失效
- 无参构造函数必须存在:CGLIB 实例化子类时需要调用父类构造器
-
不适用于接口类型字段:CGLIB 代理的是具体类,若关联属性声明为接口(如
Role role但实际映射到RoleImpl),需确保 MyBatis 能正确推断实现类,或改用 JAVASSIST(更擅长接口代理)
验证是否走 CGLIB 代理
可在代码中打印对象实际类型确认:
User user = userMapper.selectById(1L);System.out.println(user.getClass().getName()); // 输出类似 User$$EnhancerByCGLIB$$xxxxxx
如果看到含 EnhancerByCGLIB 的类名,说明 CGLIB 代理已生效。此时访问 user.getRoles() 才会真正发起关联查询。


















