剖析 MyBatis 延迟加载底层原理(1)

引言

在 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 通过继承目标类,生成子类代理对象,重写父类的方法来实现拦截

简单来说就是:

  1. 使用CGLIB创建目标对象的代理对象
  2. 当调用目标方法时,进入拦截器invoke方法,发现目标方法是null值,执行sql查询
  3. 获取数据以后,调用set方法设置属性值,再继续查询目标方法,就有值了

总结

MyBatis 延迟加载通过动态代理模式实现,在需要访问关联对象时才触发数据库查询,有效提升了系统性能。剩下的在下一篇文章中。

相关推荐
青鱼入云8 分钟前
【面试场景题】电商订单系统分库分表方案设计
大数据·面试·职场和发展
程序员三藏8 分钟前
2025最新的软件测试面试八股文(800+道题)
自动化测试·软件测试·python·功能测试·测试工具·面试·职场和发展
期待のcode30 分钟前
Spring框架1—Spring的IOC核心技术1
java·后端·spring·架构
在未来等你1 小时前
Kafka面试精讲 Day 12:副本同步与数据一致性
大数据·分布式·面试·kafka·消息队列
Livingbody2 小时前
10分钟完成 ERNIE-4.5-21B-A3B-Thinking深度思考模型部署
后端
胡萝卜的兔3 小时前
go 日志的分装和使用 Zap + lumberjack
开发语言·后端·golang
ssshooter3 小时前
你知道怎么用 pnpm 临时给某个库打补丁吗?
前端·面试·npm
en-route3 小时前
如何在 Spring Boot 中指定不同的配置文件?
java·spring boot·后端
栀椩4 小时前
springboot配置请求日志
java·spring boot·后端
Swift社区5 小时前
如何解决 Spring Bean 循环依赖
java·后端·spring