代理设计模式——静态代理和动态代理

news2024/11/25 6:55:43

代理模式

在代理模式(Proxy Pattern)中,一个类代表另一个类的功能。这种类型的设计模式属于结构型模式,在代理模式中,我们创建具有现有对象的对象,以便向外界提供功能接口。

意图:为其他对象提供一种代理以控制对这个对象的访问。

主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

何时使用:想在访问一个类时做一些控制。

如何解决:增加中间层。

关键代码:实现与被代理类组合。

两种机制

  • 静态代理
  • 动态代理 

静态代理

定义:代理和被代理对象在代理之前是确定的,他们都实现相同的接口或者继承相同的抽象类

两种实现机制

  • 继承
  • 聚合

模式:

静态代理的优点和缺点

  • 优点: 扩展原功能,不侵入原代码。
  • 缺点:如果通过继承方式实现,因为JAVA类只能继承一个父类,所以需要有多个代理类;如果通过聚合实现,当要实现多个接口代理时,代码会比较臃肿,需要为每个代理接口编写实现代码。

动态代理

  • JDK动态代理
  • CGLib动态代理

CGLib

CGLIB(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。Hibernate用它来实现PO(Persistent Object 持久化对象)字节码的动态生成;

jdk和cglib动态代理实现的区别

  1. CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些;
  2. JDK动态代理的对象必须实现一个或多个接口;
  3. cglib动态代理中生成的字节码更加复杂,生成的代理类是委托类的子类,且不能处理被final关键字修饰的方法;
  4. jdk采用反射机制调用委托类的方法,cglib采用类似索引的方式直接调用委托类方法;

JDK动态代理

核心思想:通过实现被代理类的所有接口,生成一个字节码文件后构造一个代理对象,通过持有反射构造被代理类的一个实例,再通过invoke反射调用被代理类实例的方法,来实现代理。

缺点:JDK动态代理的对象必须实现一个或多个接口

知识点

  • JDK实现动态代理需要实现类通过接口定义业务方法
  • JDK生成的代理类以"$Proxy"为开头进行命名
  • JDK代理生成的代理类的Method在static静态代码块中进行初始化;
  • public接口生成的代理类package为"com.sun.proxy";
  • JDK动态代理需要实现InvocationHandler接口;

代码:

package com.quancheng;

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

public class DynamicProxy {
    public static void main(String[] args) {
        Student stu = new Student();
        CusInvocationHandler handler = new CusInvocationHandler(stu);
        Play instance = (Play) Proxy.newProxyInstance(stu.getClass().getClassLoader(), stu.getClass().getInterfaces(), handler);
        instance.play();
    }
}

class CusInvocationHandler implements InvocationHandler {
    private Object target;

    public CusInvocationHandler(Object object) {
        this.target = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.err.println("代理方法处理");
        return method.invoke(target, args);
    }
}

interface Play {
    void play();
}

class Student implements Play {

    @Override
    public void play() {
        System.err.println("student ====>");
    }
}

生成的代理类示例:

import dynamic.proxy.UserService;  
import java.lang.reflect.*;  

public final class $Proxy11 extends Proxy  
    implements UserService  
{  

    // 构造方法,参数就是刚才传过来的MyInvocationHandler类的实例  
    public $Proxy11(InvocationHandler invocationhandler)  
    {  
        super(invocationhandler);  
    }  

    public final boolean equals(Object obj)  
    {  
        // 省略
    }  

    /** 
     * 被代理的方法 
     */  
    public final void add()  
    {  
        try  
        {  
            // 实际上就是调用MyInvocationHandler的public Object invoke(Object proxy, Method method, Object[] args)方法,第二个问题就解决了  
            super.h.invoke(this, m3, null);  
            return;  
        }  
        catch(Error _ex) { }  
        catch(Throwable throwable)  
        {  
            throw new UndeclaredThrowableException(throwable);  
        }  
    }  

    public final int hashCode()  
    {  
        // 省略
    }  

    public final String toString()  
    {  
        // 省略
    }  

    private static Method m1;  
    private static Method m3;  
    private static Method m0;  
    private static Method m2;  

    // 在静态代码块中获取了4个方法:Object中的equals方法、UserService中的add方法、Object中的hashCode方法、Object中toString方法  
    static   
    {  
        try  
        {  
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {  
                Class.forName("java.lang.Object")  
            });  
            m3 = Class.forName("dynamic.proxy.UserService").getMethod("add", new Class[0]);  
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);  
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);  
        }  
        catch(NoSuchMethodException nosuchmethodexception)  
        {  
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
        }  
        catch(ClassNotFoundException classnotfoundexception)  
        {  
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
        }  
    }  
}

cglib动态代理

核心思想:CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类(CGLib底层是通过继承实现的动态代理),并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础;底层:使用一个小而快的字节码处理框架ASM(Java字节码操控框架),来转换字节码并生成新的类

缺点:不能代理final修饰的类

示例代码:

//被代理的类即目标对象
public class A {
    public void execute(){
        System.out.println("执行A的execute方法...");
    }
}

//代理类
public class CGLibProxy implements MethodInterceptor {

    /**
     * 被代理的目标类
     */
    private A target;

    public CGLibProxy(A target) {
        super();
        this.target = target;
    }

    /**
     * 创建代理对象
     * @return
     */
    public A createProxy(){
        // 使用CGLIB生成代理:
        // 1.声明增强类实例,用于生产代理类
        Enhancer enhancer = new Enhancer();
        // 2.设置被代理类字节码,CGLIB根据字节码生成被代理类的子类
        enhancer.setSuperclass(target.getClass());
        // 3.//设置回调函数,即一个方法拦截
        enhancer.setCallback(this);
        // 4.创建代理:
        return (A) enhancer.create();
    }

    /**
     * 回调函数
     * @param proxy 代理对象
     * @param method 委托类方法
     * @param args 方法参数
     * @param methodProxy 每个被代理的方法都对应一个MethodProxy对象,
     *                    methodProxy.invokeSuper方法最终调用委托类(目标类)的原始方法
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
   //过滤不需要该业务的方法
      if("execute".equals(method.getName())) {
          //调用前验证权限(动态添加其他要执行业务)
          AuthCheck.authCheck();

          //调用目标对象的方法(执行A对象即被代理对象的execute方法)
          Object result = methodProxy.invokeSuper(proxy, args);

          //记录日志数据(动态添加其他要执行业务)
          Report.recordLog();

          return result;
      }else if("delete".equals(method.getName())){
          //.....
          return methodProxy.invokeSuper(proxy, args);
      }
      //如果不需要增强直接执行原方法
      return methodProxy.invokeSuper(proxy, args);

    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/849702.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

gradle 命令行单元测试执行问题

文章目录 问题:命令行 执行失败最终解决方案(1)ADB命令(2)Java 环境配置 问题:命令行 执行失败 命令行 执行测试命令 无法使用(之前还能用的。没有任何改动,又不能用了) …

“他“是怎么拿offer的?全网最全,性能测试面试题+答案(超全整理)

目录:导读 前言一、Python编程入门到精通二、接口自动化项目实战三、Web自动化项目实战四、App自动化项目实战五、一线大厂简历六、测试开发DevOps体系七、常用自动化测试工具八、JMeter性能测试九、总结(尾部小惊喜) 前言 1、什么是负载测试…

低功耗LoRaWAN国产低功耗LoRa+RF射频前端芯片XD6500S

目录 典型应用XD6500S简介芯片特性 LoRa系列选型参考 LoRa是为低数据速率、远距离距离和超低功耗而优化的扩频协议,用于LPWAN应用程序的通信。 典型应用 一、智慧农业   智慧农业大田解决方案利用传感设备、自动化控制设备、气象站实时监测采集田间土壤墒情、气象…

用spinal写《自己动手写cpu》中的代码--pc_reg模块

一 预期代码 二 spinal代码 package oriimport spinal.core._class pc_reg(width: Int) extends Component{val io = new Bundle {val pc = out UInt(width bits)val ce = out UInt (1 bits)val clk = in Bool()val rst = in Bool()}val ceClkDomain = ClockDomain(clock = i…

使用openssl生成https证书并应用配置到Nginx上

目录 内容概要 签发证书 Nginx配置 内容概要 使用openssl工具生成自签的证书文件,应用和配置nginx,实现https访问应用。 签发证书 1.创建密钥 openssl genrsa -des3 -out server.key 2048 注意:生成私钥,需要提供一个至少4位…

python中2等于2.0吗,python中【1:2】

本篇文章给大家谈谈python中2等于2.0吗,以及python中【1:2】,希望对各位有所帮助,不要忘了收藏本站喔。 变量和赋值 Python中的变量不需要声明, 直接定义即可. 会在初始化的时候决定变量的 “类型” 使用 来进行初始化和赋值操作 定义变量时…

字母串哈希模板题题解:哈希+前缀和+进制转换+预处理指数函数

一、链接 841. 字符串哈希 二、题目 给定一个长度为 nn 的字符串,再给定 mm 个询问,每个询问包含四个整数 l1,r1,l2,r2l1,r1,l2,r2,请你判断 [l1,r1][l1,r1] 和 [l2,r2][l2,r2] 这两个区间所包含的字符串子串是否完全相同。 字符串中只包…

UEFI build报错:‘build‘ is not recognized as an internal or external command

UEFI学习,某一次进行build时,提示: build is not recognized as an internal or external command,operable program or batch file. 用的命令是: C:\UEFIWorkspace>build -a X64 -p edk2\OvmfPkg\OvmfPkgX64.dsc -b NOOPT -…

k8s学习day03

第五章 Pod详解 本章节将详细介绍Pod资源的各种配置(yaml)和原理。 Pod介绍 Pod结构 每个Pod中都可以包含一个或者多个容器,这些容器可以分为两类: 用户程序所在的容器,数量可多可少 Pause容器,这是每个…

【Python学习】Python大版本新增内容精选

🌈据说,看我文章时 关注、点赞、收藏 的 帅哥美女们 心情都会不自觉的好起来。 前言: 🧡作者简介:大家好我是 user_from_future ,意思是 “ 来自未来的用户 ” ,寓意着未来的自己一定很棒~ ✨个…

在生产环境中部署Elasticsearch:最佳实践和故障排除技巧———索引与数据上传(二)

前言 「作者主页」:雪碧有白泡泡 「个人网站」:雪碧的个人网站 「推荐专栏」: ★java一站式服务 ★ ★ React从入门到精通★ ★前端炫酷代码分享 ★ ★ 从0到英雄,vue成神之路★ ★ uniapp-从构建到提升★ ★ 从0到英雄&#xff…

MQ百万级数据堆积如何处理

问题分析 如果,如果哈,RabbitMQ或者是kafka,这些消息队列出现大量的数据堆积,乃至是成千上万,我们作为一个开发工程师或者是架构师,我们如何去解决这种突发情况呢?可能大家会想,怎么…

面试题更新之-this指向问题

文章目录 this指向是什么常见情况下 this 的指向怎么修改this的指向 this指向是什么 JavaScript 中的 this 关键字用于引用当前执行代码的对象。它的指向是动态的,根据执行上下文的不同而变化。 常见情况下 this 的指向 全局作用域中的 this: 在全局作…

【游戏客户端】制作你玩我猜Like玩法

【游戏客户端】制作你玩我猜Like玩法 大家好,我是Lampard猿奋~~ “你画我猜”相信大家都不陌生,当初这款小游戏可谓茶余饭后必玩之选,风头一时无二。今天要和大家分享如何实现一个你玩我猜Like的玩法。 我们可以简单的把需求拆成两个个部分&…

图像预处理——CV

目录 1.图像预处理 1.1 图像显示与存储原理 1.2 图像增强的目标 1.3 点运算:基于直方图的对比度增强 1.4 形态学处理 1.5 空间域处理:卷积 1.6 卷积的应用(平滑、边缘检测、锐化等) 1.7 频率域处理:傅里叶变换…

Seata - 入门笔记

1、事务 访问并可能更新数据库中数据库中各种数据线的一个程序执行单元 原子性:事务是一个不可分割的工作单位,一个事务要么都做要么都不做 一致性:必须是使数据库从一个一致性到另一个一致性的状态,中间状态不能被观察到 隔离…

Python爬虫在电商数据挖掘中的应用

作为一名长期扎根在爬虫行业的专业的技术员,我今天要和大家分享一些有关Python爬虫在电商数据挖掘中的应用与案例分析。在如今数字化的时代,电商数据蕴含着丰富的信息,通过使用爬虫技术,我们可以轻松获取电商网站上的产品信息、用…

明年,HarmonyOS不再兼容Android应用!

2023年华为开发者大会,不知道各位老铁们是否观看了,一个震撼的消息就是,首次公开了HarmonyOS NEXT的概念,简而言之就是,这是一款专为开发者打造的预览版操作系统,旨在提供"纯正鸿蒙操作系统"的体…

2023年果粉装机必备软件有哪些?Macbook电脑需要安装什么软件

作为一名从Windows全面切换到Mac的果粉,在一开始的时候确实感到诸多不便,比如文件管理器操作别扭,鼠标移动不够跟手等等。 后来才发现,除了游戏,大多数问题都能用软件来解决。Mac虽然小众,但是用户群体有很…

84. 柱状图中最大的矩形

题目描述 给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。 求在该柱状图中,能够勾勒出来的矩形的最大面积。 示例 1: 输入:heights [2,1,5,6,2,3] 输出:10 解释:最…