Bootstrap

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 的执行和返回。

;