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

相关推荐
邓不利东1 小时前
Spring中过滤器和拦截器的区别及具体实现
java·后端·spring
头发那是一根不剩了1 小时前
Spring Boot 多数据源切换:AbstractRoutingDataSource
数据库·spring boot·后端
小杰来搬砖1 小时前
讲解instanceof 用法
后端
3Katrina1 小时前
JS事件机制详解(2)--- 委托机制、事件应用
前端·javascript·面试
城里的月光1 小时前
从900 MB到450 MB:生产中SpringBoot的JVM内存调优技巧
后端
Pedantic2 小时前
为什么 Swift 字符串不能用 `myString[3]` 随便取字符?
前端·后端
Apifox2 小时前
提交代码后如何自动触发 Apifox 的自动化测试?
前端·后端·测试
程序员NEO2 小时前
Spring AI 实现让你的 AI “三思而后行”
后端
天下一般2 小时前
go入门 - day1 - 环境搭建
开发语言·后端·golang
程序员NEO2 小时前
Spring AI 骚操作:让大模型乖乖听话,直接返回 Java 对象!
人工智能·后端