首页 > 编程语言 >代理设计模式之JDK动态代理&CGLIB动态代理原理与源码剖析

代理设计模式之JDK动态代理&CGLIB动态代理原理与源码剖析

时间:2024-06-11 11:33:53浏览次数:25  
标签:code 设计模式 代理 public proxy 动态 class Proxy

代理设计模式

代理模式(Proxy),为其它对象提供一种代理以控制对这个对象的访问。如下图

从上面的类图可以看出,通过代理模式,客户端访问接口时的实例实际上是Proxy对象,Proxy对象持有RealSubject的引用,这样一来Proxy在可以在实际执行RealSubject前后做一些操作,相当于是对RealSubject的Reques方法做了增强。

/**
 * @author kangming.ning
 * @date 2021/5/8 9:51
 */
public class Client {

    public static void main(String[] args) {
        Subject subject = new Proxy();
        subject.request();
    }
}

Proxy类对RealSubject的request方法进行了增强

/**
 * @author kangming.ning
 * @date 2021/5/8 9:49
 */
public class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy() {
        realSubject = new RealSubject();
    }

    @Override
    public void request() {
        System.out.println("proxy do something before");
        realSubject.request();
        System.out.println("proxy do something after");
    }
}

代理设计模式就是这么简单,但却很好用。像上面这种提前设计好的代理可以称为静态代理,因为它是针对某个需要增强的接口直接进行编码设计的。这种方式事实上用的很少,因为它需要针对每个需要增强的接口添加代理类,试想类似于mybatis的Mapper代理如果都是静态代理,是不是会导致我们编写大量代理类,实在麻烦。所以项目中通常都是使用动态代理,所谓动态代理,可以简单理解为代理类可以在运行时动态创建并加载到JVM中,下面做详细介绍。

动态代理

动态代理:从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。

其本质和静态代理是一样的,只不过被设计为可以动态生成代理类,使用更加方便,在实际的开发中有大量应用。比如spring的AOP(Aspect Oriented Programming) 切面编程这种唬人的技术,其本质不过就是利用代理模式对方法进行增强。当然spring aop用的是动态代理技术,再具体就是利用JDK动态代理或CGLIB动态代理对针对接口或类生成动态代理类,然后实际执行方法时,实际执行的代理类的逻辑,代理类可以在方法前后做一些操作(增强)。

JDK动态代理

JDK动态代理Proxy是JDK提供的一个用于创建动态代理的一个工具类。下面用一个简单例子说明如何应用JDK动态代理

首先还是需要有被代理的接口,自定股票买卖行为

/**
 *  股票接口
 * @author kangming.ning
 * @date 2024-01-19 16:40
 * @since 1.0
 **/
public interface StockService {

    /**
     * 购买股票接口
     * @param stockName 买的哪个股票
     * @param totalMoney
     */
    void buyStock(String stockName,double totalMoney);

    /**
     * 卖出股票接口
     * @param stockName 卖的哪个股票
     * @param totalMoney
     */
    void sellStock(String stockName,double totalMoney);
}

接口的实现 ,即买卖股票需要做的一些事情。

/**
 * @author kangming.ning
 * @date 2024-01-19 16:54
 * @since 1.0
 **/
public class StockServiceImpl implements StockService {
    @Override
    public void buyStock(String stockName, double totalMoney) {
        System.out.println("成功购买了股票" + stockName + " 共" + totalMoney + "元");
    }

    @Override
    public void sellStock(String stockName, double totalMoney) {
        System.out.println("成功卖出了股票" + stockName + " 共" + totalMoney + "元");
    }
}

没有代理的情况,买卖这些事情是需要股民自己去做的

/**
 * 没有代理的情况
 *
 * @author kangming.ning
 * @date 2024-01-22 09:50
 * @since 1.0
 **/
public class StockDirectClient {
    public static void main(String[] args) {
        StockService stockService = new StockServiceImpl();
        stockService.buyStock("001", 100);
        stockService.sellStock("002", 200);
    }
}

而有代理的情况,通常表现为,我们买卖的基金,其背后实际是大部分是股票(偏股基金),基金经理可以认为是我们的代理。

/**
 *  韭菜侠(又称为基民)
 * @author kangming.ning
 * @date 2024-01-19 16:57
 * @since 1.0
 **/
public class FragrantFloweredGarlicMan {
    public static void main(String[] args) {
        //韭菜侠发现投资商机,基金好像跌到底部了,果断去抄底
        StockService stockService = new StockServiceImpl();
        StockService proxy = (StockService)Proxy.newProxyInstance(
                //目标类的类加载器
                stockService.getClass().getClassLoader(),
                stockService.getClass().getInterfaces(),
                new StockInvocationHandler(stockService));
        proxy.buyStock("003",100);
        proxy.sellStock("004",200);
    }
}

 StockInvocationHandler如下

/**
 * @author kangming.ning
 * @date 2024-01-19 17:06
 * @since 1.0
 **/
public class StockInvocationHandler implements InvocationHandler {

    /**
     * 代理中的真实对象
     */
    private final Object target;

    public StockInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("before method " + method.getName());
        //执行被代理对象原方法
        Object invoke = method.invoke(target, args);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method " + method.getName());
        return invoke;
    }
}

 上面的打印结果类似

before method buyStock
成功购买了股票003 共100.0元
after method buyStock
before method sellStock
成功卖出了股票004 共200.0元
after method sellStock

可以看出,通过动态代理,对原接口调用前后都分别处理了额外的逻辑,和静态代理实现的效果是一致的。只是动态代理不需要事先编辑好相关代理类,而是在执行过程中动态生成代理类,这样一来,接口变动我们也不必修改代理类,所有调整适配工作都在InvocationHandler的实现类里处理。

JDK动态代理原理

 通过上面的案例,我们知道怎么去使用JDK代理了。下面探讨一下其实现原理。Proxy创建动态代理的方法如下

    /**
     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
     *
     * <p>{@code Proxy.newProxyInstance} throws
     * {@code IllegalArgumentException} for the same reasons that
     * {@code Proxy.getProxyClass} does.
     *
     * @param   loader the class loader to define the proxy class
     * @param   interfaces the list of interfaces for the proxy class
     *          to implement
     * @param   h the invocation handler to dispatch method invocations to
     * @return  a proxy instance with the specified invocation handler of a
     *          proxy class that is defined by the specified class loader
     *          and that implements the specified interfaces
     * @throws  IllegalArgumentException if any of the restrictions on the
     *          parameters that may be passed to {@code getProxyClass}
     *          are violated
     * @throws  SecurityException if a security manager, <em>s</em>, is present
     *          and any of the following conditions is met:
     *          <ul>
     *          <li> the given {@code loader} is {@code null} and
     *               the caller's class loader is not {@code null} and the
     *               invocation of {@link SecurityManager#checkPermission
     *               s.checkPermission} with
     *               {@code RuntimePermission("getClassLoader")} permission
     *               denies access;</li>
     *          <li> for each proxy interface, {@code intf},
     *               the caller's class loader is not the same as or an
     *               ancestor of the class loader for {@code intf} and
     *               invocation of {@link SecurityManager#checkPackageAccess
     *               s.checkPackageAccess()} denies access to {@code intf};</li>
     *          <li> any of the given proxy interfaces is non-public and the
     *               caller class is not in the same {@linkplain Package runtime package}
     *               as the non-public interface and the invocation of
     *               {@link SecurityManager#checkPermission s.checkPermission} with
     *               {@code ReflectPermission("newProxyInPackage.{package name}")}
     *               permission denies access.</li>
     *          </ul>
     * @throws  NullPointerException if the {@code interfaces} array
     *          argument or any of its elements are {@code null}, or
     *          if the invocation handler, {@code h}, is
     *          {@code null}
     */
    @CallerSensitive
    public static O

标签:code,设计模式,代理,public,proxy,动态,class,Proxy
From: https://blog.csdn.net/u012882823/article/details/139594311

相关文章

  • Java设计模式
    行为设计模式观察者模式定义观察者是一种行为设计模式,观察者模式通常由两个对象组成:观察者(事件和发布器)和被观察者(监听器)。当被观察者状态发生改变时,它会通知所有的观察者对象,使他们能够及时做出响应,所以也被称作“发布-订阅模式”。特点优点:降耦:观察者和被观察者......
  • 【安装笔记-20240608-Linux-动态域名更新服务之YDNS】
    安装笔记-系列文章目录安装笔记-20240608-Linux-动态域名更新服务之YDNS文章目录安装笔记-系列文章目录安装笔记-20240608-Linux-动态域名更新服务之YDNS前言一、软件介绍名称:YDNS主页官方介绍二、安装步骤测试版本:openwrt-23.05.3-x86-64注册填写子域名激活邮箱更......
  • 谁说.net core不好动态访问webservice?看这篇文章,C#快速实现动态访问webservice,兼容.ne
    前言:访问webservice,大多数人都是用服务引用的方式,但是这种方式比较麻烦,例如遇到服务更新了,你还需要手动更新你的服务引用,再重新发布,很麻烦。或者已有的一些例子,至少我看到的很多案例,动态访问也只能止步于使用.netframework环境,没看到有啥.netcore上面动态访问的案例。于是我就来......
  • 23种设计模式之代理模式
    代理模式1、概念代理模式:给某一个对象提供一个代理或占位符,并由代理对象来控制对原对象的访问代理模式是常用的结构型设计模式之一,在JavaRMI、WebService、SpringAOP等技术和框架中都使用了代理模式2、代理模式结构Subject(抽象主题角色):它声明了真实主题和代理主题的......
  • 【设计模式】结构型-桥接模式
    当抽象与实现,各自独立,桥接模式,如彩虹桥,连接两岸。文章目录一、类爆炸与代码重复二、桥接模式三、桥接模式的核心组成四、运用桥接模式五、桥接模式的应用场景六、小结推荐阅读一、类爆炸与代码重复场景假设:假设我们正在设计一个模拟城市交通的系统。在这个系统中,......
  • C语言——使用函数创建动态内存
    一、堆和栈的区别1)栈(Stack):栈是一种自动分配和释放内存的数据结构,存储函数的参数值、局部变量的值等。栈的特点是后进先出,即最后进入的数据最先出来,类似于我们堆盘子一样。栈的大小和生命周期是由系统自动管理的,不需要程序员手动释放。2)堆(Heap):堆是由程序员手动分配和释......
  • C++Primer Plus 第12章 类和动态内存分配 12.10编程练习第2题new,delete的指向深度拷
    系列文章目录提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加例如:本章练习第2题涉及标准函数及关键词toupper,tolower(),strcpy_s(),strcat_s(),strcmp,strlen(),new[],delete[].实现如下效果输出应与下面相似:Pleaseenteryourname:FrettaFarboMynameis......
  • nuxt框架中路由动态传参及结构分析之文章跳转详情页面传递文章id
    在nuxt里面我们会经常使用到路由传递参数,列如,登录,文章跳转详情页面等,下面我就以文章列表跳转文章详情页面记录一下。1、首先这个是我的目录结构:在文章列表页面:list.vue(layout目录下的这里其实是一个组件)里面我写了这样一段实现跳转传递,这里我使用到了<nuxt-link>(当然你有其他......
  • C++多态详解:静态多态与动态多态的实现
    C++中的多态是面向对象编程的重要特性,允许相同的接口调用不同的实现。多态性可以分为两类:静态多态和动态多态。1.静态多态(编译时多态)(1)函数重载(FunctionOverloading):函数重载是一种静态多态,允许同一个函数名在同一作用域内具有不同的参数列表。这些不同的版本在编译时......
  • 深入理解 C++ 动态内存管理:new vs malloc
    概述new/delete 是C++的关键字,需要编译器支持。malloc/free 是库函数,需要头文件支持。使用 new 申请内存分配时无需指定内存块大小,编译器会自动计算。而 malloc 需要明确指定所需内存的大小。new 会返回对象类型的指针,类型安全。而 malloc 返回 void*,需要进行强制......