当前位置: 首页 > article >正文

Mybatis框架之代理模式 (Proxy Pattern)

MyBatis 框架中大量使用了代理模式 (Proxy Pattern),尤其在 Mapper 接口 的实现上。代理模式使得 MyBatis 能够在不直接实现接口的情况下动态地提供接口的实现,从而简化数据库操作代码,同时提供更强大的功能。下面将详细解读 MyBatis 中的代理模式的工作原理及其实现。

1. 什么是代理模式 (Proxy Pattern)?

代理模式 是一种结构型设计模式,它为某个对象提供一个代理对象,以控制对这个对象的访问。代理对象通常会对请求进行预处理或后处理,然后将请求传递给实际的目标对象。

代理模式的特点

  • 控制访问:通过代理对象来控制对目标对象的访问。
  • 延迟加载:可以在代理中实现懒加载。
  • 增强功能:可以在调用目标对象之前或之后执行额外的操作(例如日志、权限检查、事务管理等)。

2. MyBatis 中代理模式的应用

在 MyBatis 中,代理模式的主要应用场景是 Mapper 接口。开发者只需要定义 Mapper 接口,而无需提供接口的实现类。MyBatis 会在运行时为这些接口创建动态代理对象,通过代理对象来执行 SQL 语句。

2.1 MyBatis 如何使用代理模式
  • Mapper 接口:用户定义的接口,用于声明数据库操作方法(如 getUserByIdinsertUser 等)。
  • Mapper 动态代理:MyBatis 通过 JDK 动态代理 为 Mapper 接口生成代理对象。
  • SqlSession.getMapper() 方法:用于获取 Mapper 接口的代理实例。当调用代理实例的方法时,会由 MyBatis 拦截并执行相应的 SQL 语句。

3. 代理模式的工作流程

3.1 工作原理
  1. 开发者定义一个 Mapper 接口,声明数据库操作方法。
  2. 通过 SqlSession.getMapper(Class<T> clazz) 方法获取接口的代理对象。
  3. 调用代理对象的方法时,MyBatis 会通过 MapperProxy 拦截方法调用。
  4. MapperProxy 通过 MappedStatement 查找对应的 SQL 语句,并执行相应的数据库操作。
  5. 将查询结果封装成接口方法的返回类型(如 List<User>)。
3.2 Mapper 代理示意图
UserMapper (接口)
    ↓
SqlSession.getMapper(UserMapper.class)
    ↓
MapperProxy (JDK 动态代理)
    ↓
MappedStatement (映射 SQL)
    ↓
执行 SQL 并返回结果

4. 实际代码示例

4.1 创建 Mapper 接口 (UserMapper.java)
package com.example.mapper;

import com.example.model.User;
import java.util.List;

public interface UserMapper {
    // 查询所有用户
    List<User> getAllUsers();

    // 根据 ID 查询用户
    User getUserById(int id);
}
4.2 编写 Mapper XML 文件 (UserMapper.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
    PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mapper.UserMapper">
    <select id="getAllUsers" resultType="com.example.model.User">
        SELECT * FROM users;
    </select>

    <select id="getUserById" parameterType="int" resultType="com.example.model.User">
        SELECT * FROM users WHERE id = #{id};
    </select>
</mapper>
4.3 MyBatis 配置文件 (mybatis-config.xml)
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
    PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/mydatabase"/>
                <property name="username" value="root"/>
                <property name="password" value="password"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="com/example/mapper/UserMapper.xml"/>
    </mappers>
</configuration>
4.4 使用 SqlSession 获取 Mapper 代理对象 (MyBatisExample.java)
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import com.example.mapper.UserMapper;
import com.example.model.User;
import java.io.InputStream;
import java.util.List;

public class MyBatisExample {
    public static void main(String[] args) {
        String resource = "mybatis-config.xml";
        try (InputStream inputStream = Resources.getResourceAsStream(resource)) {
            // 创建 SqlSessionFactory
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

            // 打开 SqlSession
            try (SqlSession session = sqlSessionFactory.openSession()) {
                // 获取 Mapper 接口的代理对象
                UserMapper userMapper = session.getMapper(UserMapper.class);

                // 调用代理对象的方法
                List<User> users = userMapper.getAllUsers();
                users.forEach(user -> System.out.println(user.getName()));

                // 根据 ID 查询用户
                User user = userMapper.getUserById(1);
                System.out.println("User ID 1: " + user.getName());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

5. MyBatis 代理模式的实现细节

  • MapperProxy:MyBatis 使用 MapperProxy 类来实现 JDK 动态代理。MapperProxy 实现了 InvocationHandler 接口,用于拦截 Mapper 接口方法的调用。

  • MapperMethodMapperProxy 会将拦截到的方法调用委托给 MapperMethod 对象。MapperMethod 根据方法名查找对应的 MappedStatement,然后执行相应的 SQL 语句。

MapperProxy 示例(简化版)
public class MapperProxy implements InvocationHandler {
    private SqlSession sqlSession;
    private Class<?> mapperInterface;

    public MapperProxy(SqlSession sqlSession, Class<?> mapperInterface) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String statementId = mapperInterface.getName() + "." + method.getName();
        return sqlSession.selectList(statementId, args);
    }
}

6. 代理模式的优势

  • 解耦:开发者只需定义接口,无需编写实现类,降低代码耦合度。
  • 简化代码:减少重复的数据库操作代码,提高开发效率。
  • 动态性:通过动态代理机制,在运行时动态生成代理对象,减少硬编码。
  • 灵活扩展:可以轻松添加拦截器,实现如日志记录、权限校验、事务控制等功能。

7. 代理模式的不足

  • 性能开销:动态代理在方法调用时有一定的性能开销,特别是在高并发场景下。
  • 调试困难:由于没有实际的实现类,调试时无法直接跳转到方法实现,调试复杂度增加。
  • 学习成本:对于不熟悉动态代理机制的开发者,理解 MyBatis 的内部工作原理可能有一定的难度。

8. 总结

MyBatis 通过代理模式大幅简化了数据库操作代码,使得开发者可以更专注于业务逻辑而不是 SQL 操作。MyBatis 代理模式的核心是使用 JDK 动态代理机制,在运行时为 Mapper 接口生成代理对象,从而将接口方法映射到相应的 SQL 语句执行。代理模式的使用提高了 MyBatis 的灵活性和扩展性,是其重要的设计亮点之一。


http://www.kler.cn/a/405086.html

相关文章:

  • java 并发编程 (2)Thread 类和 Runnable 接口详解
  • TabNet 模型示例
  • 一次封装,解放双手:Requests如何实现0入侵请求与响应的智能加解密
  • 国标GB28181视频平台EasyCVR视频融合平台H.265/H.264转码业务流程
  • docker安装zabbix +grafana
  • 为什么VScode不能连服务器,MobaXterm可以连
  • Redis三剑客:缓存雪崩、缓存穿透、缓存击穿
  • 国标GB28181设备管理软件EasyGBS国标GB28181视频平台:RTMP和GB28181两种视频上云协议的区别
  • RNN简单理解;为什么出现Transformer:传统RNN的问题;Attention(注意力机制)和Self-Attention(自注意力机制)区别;
  • SQLAlchemy,ORM的Python标杆!
  • 嵌入式硬件电子电路设计(六)LDO低压差线性稳压器全面详解
  • 音视频入门基础:MPEG2-TS专题(6)——FFmpeg源码中,获取MPEG2-TS传输流每个transport packet长度的实现
  • 开源许可协议
  • 【Swift】字符串和字符
  • springboot第83集:理解SaaS多租户应用的架构和设计,设备介入,网关设备,安全,实时实现,序列化...
  • python-自定义排序函数sorted()
  • OpenCV基本图像处理操作(六)——直方图与模版匹配
  • 二叉树路径相关算法题|带权路径长度WPL|最长路径长度|直径长度|到叶节点路径|深度|到某节点的路径非递归(C)
  • 一篇文章了解机器学习(下)
  • linux命令面试题及参考答案
  • 5G NR:TDD和FDD的技术差异
  • 数据结构 ——— 判断一棵树是否是完全二叉树
  • 数学建模学习(137):使用Python进行频数分析
  • c#基本数据类型占用字节长度/取值范围/对应.net类型
  • 【机器学习】聚类算法原理详解
  • python: generator model using sql server 2019