引言
在 Java 开发的持久层框架领域,MyBatis 占据着重要地位。延迟加载作为 MyBatis 性能优化的关键功能之一,能有效减少不必要的数据库查询,提升系统响应速度。那么,MyBatis 延迟加载背后的底层原理究竟是什么呢?
延迟加载的基本概念回顾
延迟加载,也称作懒加载,核心思想是在需要访问关联对象数据时才触发数据库查询操作,而非在查询主对象时就一并加载所有关联数据。比如在一个电商系统中,查询用户信息时,若使用延迟加载,只有当需要查看该用户的订单信息时,才会去数据库查询订单数据,而不是在查询用户信息时就把订单数据也一并查出来。
MyBatis 延迟加载的底层实现原理
代理模式的运用
MyBatis 延迟加载的实现依赖于 Java 的代理模式,确切地说是动态代理。在 Java 中,动态代理主要有 JDK 动态代理和 CGLIB 动态代理两种方式,MyBatis 都支持。
JDK 动态代理
JDK 动态代理基于接口实现。当 MyBatis 开启延迟加载后,对于关联对象,会为其创建一个实现了相同接口的代理对象。当调用代理对象的方法时,代理对象会拦截该调用,判断关联对象是否已经加载,如果未加载,则触发相应的数据库查询操作,加载关联对象数据。 以下是一个简化的 JDK 动态代理示例:
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
// 定义接口
interface Department {
void showDepartmentInfo();
}
// 真实对象
class RealDepartment implements Department {
@Override
public void showDepartmentInfo() {
System.out.println("加载部门信息,执行数据库查询...");
}
}
// 代理处理器
class DepartmentProxyHandler implements InvocationHandler {
private Object target;
public DepartmentProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (target == null) {
// 模拟延迟加载,创建真实对象
target = new RealDepartment();
}
return method.invoke(target, args);
}
}
// 测试类
public class JdkProxyExample {
public static void main(String[] args) {
Department realDepartment = null;
DepartmentProxyHandler handler = new DepartmentProxyHandler(realDepartment);
Department proxyDepartment = (Department) Proxy.newProxyInstance(
Department.class.getClassLoader(),
new Class<?>[]{Department.class},
handler
);
proxyDepartment.showDepartmentInfo();
}
}
DepartmentProxyHandler
作为代理处理器当调用 showDepartmentInfo
方法时,如果真实对象还未创建,就会先创建真实对象,然后再调用其方法。基本不用jdk代理了
CGLIB 动态代理
CGLIB 动态代理可以为没有实现接口的类创建代理。MyBatis 在处理没有实现接口的关联对象时,会使用 CGLIB 动态代理。CGLIB 通过继承目标类,生成子类代理对象,重写父类的方法来实现拦截
简单来说就是:
- 使用CGLIB创建目标对象的代理对象
- 当调用目标方法时,进入拦截器invoke方法,发现目标方法是null值,执行sql查询
- 获取数据以后,调用set方法设置属性值,再继续查询目标方法,就有值了
总结
MyBatis 延迟加载通过动态代理模式实现,在需要访问关联对象时才触发数据库查询,有效提升了系统性能。剩下的在下一篇文章中。