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

相关推荐
魔都吴所谓2 小时前
【go】map基础操作
开发语言·后端·golang
2301_793086874 小时前
Springboot 04 starter
java·spring boot·后端
无限大67 小时前
只出现一次的数字:从暴力美学到位运算神技的进化之路
后端·面试
宇寒风暖7 小时前
Flask 框架全面详解
笔记·后端·python·学习·flask·知识
你的人类朋友7 小时前
❤️‍🔥为了省内存选择sqlite,代价是什么
数据库·后端·sqlite
还是鼠鼠7 小时前
tlias智能学习辅助系统--SpringAOP-进阶-通知顺序
java·后端·mysql·spring·mybatis·springboot
Pitayafruit7 小时前
Spring AI 进阶之路01:三步将 AI 整合进 Spring Boot
spring boot·后端·ai编程
用户21411832636028 小时前
零成本搭建 AI 应用!Hugging Face 免费 CPU 资源实战指南
后端
程序员曦曦9 小时前
15:00开始面试,15:06就出来了,问的问题有点变态。。。
自动化测试·软件测试·功能测试·程序人生·面试·职场和发展
澡点睡觉9 小时前
golang的包和闭包
开发语言·后端·golang