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

相关推荐
2302_8097983236 分钟前
【JavaWeb】Docker项目部署
java·运维·后端·青少年编程·docker·容器
zhojiew1 小时前
关于akka官方quickstart示例程序(scala)的记录
后端·scala
sclibingqing1 小时前
SpringBoot项目接口集中测试方法及实现
java·spring boot·后端
每次的天空2 小时前
Android第十三次面试总结基础
android·面试·职场和发展
JohnYan2 小时前
Bun技术评估 - 03 HTTP Server
javascript·后端·bun
周末程序猿3 小时前
Linux高性能网络编程十谈|C++11实现22种高并发模型
后端·面试
ZHOU_WUYI3 小时前
Flask与Celery 项目应用(shared_task使用)
后端·python·flask
憨憨睡不醒啊3 小时前
如何让LLM智能体开发助力求职之路——构建属于你的智能体开发知识体系📚📚📚
面试·程序员·llm
冒泡的肥皂3 小时前
强大的ANTLR4语法解析器入门demo
后端·搜索引擎·编程语言
IT_陈寒4 小时前
Element Plus 2.10.0 重磅发布!新增Splitter组件
前端·人工智能·后端