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

Java-19 深入浅出 MyBatis - 用到的设计模式 源码剖析 代理设计模式

点一下关注吧!!!非常感谢!!持续更新!!!

大数据篇正在更新!https://blog.csdn.net/w776341482/category_12713819.html

在这里插入图片描述

目前已经更新到了:

  • MyBatis(正在更新)

代理模式

概念介绍

代理模式(Proxy Pattern):给某一个对象提供一个代理,并由代理对象控制对原对象的引用,代理模式的英文名字叫 Proxy,它是一种对象结构型模式,代理模式分为静态代理和动态代理。

主要角色

  • 抽象主题(Subject):定义了真实对象和代理对象的共同接口,使得代理对象可以用在任何需要真实对象的地方。
  • 真实主题(RealSubject):实现了抽象主题接口,是真正执行请求的对象。
  • 代理(Proxy):实现了抽象主题接口,内部包含对真实主题的引用。代理对象控制对真实对象的访问,并可以在访问前后执行额外的操作。

代理模式的分类

远程代理(Remote Proxy)

为位于不同地址空间的对象提供本地代理,通过代理与远程对象进行通信。
示例:RMI(Remote Method Invocation)。

虚拟代理(Virtual Proxy)

用于创建开销较大的对象,只有在真正需要时才创建该对象。
示例:图像加载器延迟加载图片。

保护代理(Protection Proxy)

控制对真实对象的访问权限,根据访问者的权限执行不同操作。
示例:文件权限管理。

智能引用代理(Smart Reference Proxy)

在访问真实对象时进行额外操作,例如计数、日志记录、缓存等。

代理模式的优点

  • 职责分离: 通过代理控制访问,可以将额外的职责(如安全性、日志记录)从真实对象中剥离出来。
  • 增强功能: 在访问真实对象时,代理可以动态添加功能。
  • 性能优化: 通过虚拟代理延迟加载,提升性能。

代理模式的缺点

  • 增加复杂性: 由于引入了代理类,导致类的数量增加,系统复杂度提升。
  • 性能问题: 某些场景下,代理可能增加系统的开销,尤其是远程代理。

编写代码

Person

创建一个抽象类,Person 接口,有一个 doSomething 的方法。

package icu.wzk.proxy;

public interface Person {
    void doSomething();
}

Bob

package icu.wzk.proxy;

public class Bob implements Person {

    @Override
    public void doSomething() {
        System.out.println("Bob do something!");
    }
}

JdkDynamicProxy

package icu.wzk.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkDynamicProxy implements InvocationHandler {

    private Person target;

    public JdkDynamicProxy(Person person) {
        this.target = person;
    }

    public Person getTarget() {
        return (Person) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this
        );
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before calling " + method.getName());
        Person result = (Person) method.invoke(target, args);
        System.out.println("After calling " + method.getName());
        return result;
    }
}

JdkDynamicProxyTest

package icu.wzk.proxy;

public class JdkDynamicProxyTest {

    public static void main(String[] args) {
        Person bob = new Bob();
        bob.doSomething();
        System.out.println("========================");
        Person bob2 = new Bob();
        JdkDynamicProxy jdkDynamicProxy = new JdkDynamicProxy(bob2);
        Person proxyBob = jdkDynamicProxy.getTarget();
        proxyBob.doSomething();
    }

}

MyBatis 的体现

代理模式可以认为是 MyBatis 的核心使用的模式,正是因为这个模式的存在,我们只需要编写 Mapper 接口,不需要具体的实现,由 MyBatis 后台帮我们完成 SQL 的执行。
当我们使用 Configuration 的 getMapper 方法时,会调用 mapper.Registry.getMapper 方法,而该方法又会调用 mapperProxyFactory.newInstance(sqlSession) 来生成一个具体的代理:

import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface;
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

    // Constructor
    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    // Getter for mapperInterface
    public Class<T> getMapperInterface() {
        return mapperInterface;
    }

    // Getter for methodCache
    public Map<Method, MapperMethod> getMethodCache() {
        return methodCache;
    }

    // Create a new instance of the proxy
    @SuppressWarnings("unchecked")
    protected T newInstance(MapperProxy<T> mapperProxy) {
        return (T) Proxy.newProxyInstance(
                mapperInterface.getClassLoader(),
                new Class[]{mapperInterface},
                mapperProxy
        );
    }

    // Create a new instance of the mapper with a given SqlSession
    public T newInstance(SqlSession sqlSession) {
        final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
        return newInstance(mapperProxy);
    }
}

在这里,通过 newInstance(SqlSession sqlSession)方法会得到一个 MapperProxy对象,然后调用 T newInstance(MapperProxy mapperProxy)生成代理对象然后返回,下面是 MapperProxy 的代码:

public class MapperProxy<T> implements InvocationHandler, Serializable {

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // 如果是Object类中的方法,直接调用
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);
            }
            // 如果是默认方法,使用invokeDefaultMethod处理
            else if (isDefaultMethod(method)) {
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            // 将异常包装并抛出
            throw ExceptionUtil.unwrapThrowable(t);
        }

        // 获取缓存的MapperMethod对象并执行
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        return mapperMethod.execute(sqlSession, args);
    }
}

该 MapperProxy 实现了 InvocationHandler 接口,并实现了该接口的 invoke 方法,通过这种方式,我们需要编写 Mapper 接口类,当真正执行一个 Mapper 接口的时候,会转发给 MapperProxy.invoke 方法,而该方法则会调用后续的preparedStatement 等一系列方法,完成 SQL 的执行和返回。


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

相关文章:

  • 服务器安装ESXI7.0系统及通过离线包方式升级到ESXI8.0
  • C语言-----扫雷游戏
  • MySQL查询LONG BLOB类型数据的大小
  • 数学规划问题2 .有代码(非线性规划模型,最大最小化模型,多目标规划模型)
  • 2025年最新深度学习环境搭建:Win11+ cuDNN + CUDA + Pytorch +深度学习环境配置保姆级教程
  • 软件安全性测试报告如何编写?
  • 【数据库】复习
  • import是如何“占领满屏“
  • QT模型/视图结构:ListModel与TableModel
  • 前端工程化面试题(一)
  • Apache AGE:基于PostgreSQL的图数据库
  • 如何通过AI“一键生成PPT”提升工作效率与创意表达
  • vscode配置django环境并创建django项目(全图文操作)
  • DemoFusion 技术浅析(三):渐进式上采样
  • MR30分布式IO模块赋能喷水织机
  • 自动驾驶3D目标检测综述(五)
  • 【Python】报错:Statement seems to have no effect
  • 文件操作---文件IO与标准IO
  • ftp服务器搭建-安装、配置及验证
  • 使用Redis的Bitmap实现签到功能
  • Java项目实战II基于微信小程序的消防隐患在线举报系统(开发文档+数据库+源码)
  • Kafka 常见面试题深度解析
  • OpenAI 12Days 第二天 强化微调(RFT):推动语言模型在科学研究中的应用
  • Ubuntu中配置交叉编译工具的三条命令的详细研究
  • 智能制造的加速器:RPA在制造业中的创新实践
  • 【Atcoder】【ABC383】B.Humidifier2题解