我眼中的代理模式

news2024/9/29 17:37:36

一.什么是代理模式?

        举个简单的例子就是比如你相亲的女孩想跟你要个10W彩礼,但是她不好意思直接跟你说啊。这时候就媒婆就说我去跟小伙子说。有什么回应我告诉你。然后媒婆就先夸女方怎么怎么优秀,然后落到中心思想要钱。这里面相亲的女孩子就是目标类,媒婆就是代理类。要钱就是目标类的方法,夸女方优秀就是在调取目标类之前之后你想做的一些事。

二,代理模式的分类

  1.静态代理

      静态代理中,我们对目标对象的每个方法都要手动完成,非常不灵活,就比如说接口一旦增加了方法,目标类和代理类都要跟着修改而且每个目标类都要写一个代理类。从JVM层面来说,静态代理就是在编译时就将接口实现类这些都变成了一个个实际的class对象

      静态代理实现的步骤

   (1)定义一个接口和一个实现类

    (2)创建一个代理类实现接口

    (3)在代理类中将目标对象注入进来,然后代理类中实现的方法中调用目标类对应的方法。这样的话我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做一些自己想做的事。接下来上代码:

接口

package com.chen.staticFactory;
//接口
public interface SmsService {
    String send(String message);
}

实现类

package com.chen.staticFactory;

public class SmsServiceImpl implements SmsService{
    @Override
    public String send(String message) {
        System.out.println("send message:"+message);
        return message;
    }
}

代理类

package com.chen.staticFactory;

public class SmsProxy implements SmsService{
    private final  SmsService smsService;

    public SmsProxy(SmsService smsService) {
        this.smsService = smsService;
    }

    @Override
    public String send(String message) {
        System.out.println("before method send()");
        smsService.send(message);
        System.out.println("after method send()");
        return null;
    }
}

测试类

package com.chen.staticFactory;

public class Main {
    public static void main(String[] args) {
        SmsService smsService  = new SmsServiceImpl();
        SmsProxy smsProxy = new SmsProxy(smsService);
        smsProxy.send("java");
    }
}

2.动态代理(很重要)!!!

   相比于静态代理来说,动态代理更加灵活,我们不需要针对每个目标类都创建一个代理类,并且不需要我们实现接口,我们可以直接代理实现类(CGLIB动态代理机制)

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

  (一)jdk本身的代理

jdk动态代理有两个核心 InvocationHandler接口和Proxy类时核心

Proxy类中使用频率最高的方法是:newProxyInstance() 这个方法是用来生成对象的

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        ......
    }

参数解释:

loader:类加载器,用于加载代理对象

interfaces:被代理类实现的一些接口

h 实现了InvocationHandler接口的对象

  要实现动态代理的话,还必须需要实现InvocationHandler 来自定义处理逻辑。 当我们的动态代理对象调用一个方法时,这个方法的调用就会被转发到实现InvocationHandler 接口类的 invoke 方法来调用。

 

参数解释:

proxy:动态生成的类

method:与代理类对象调用搞得方法相对应

args:当前method方法的参数

你通过Proxy 类的 newProxyInstance() 创建的代理对象在调用方法的时候,实际会调用到实现InvocationHandler 接口的类的 invoke()方法。 你可以在 invoke() 方法中自定义处理逻辑,比如在方法执行前后做什么事情。

实现步骤:

1.创建一个接口和实现类:

2.自定义一个类实现InvocationHandler接口重写invoke方法,并且在invoke方法中调用原生方法

3.通过Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,IncvocationHandler h)方法创建代理对象

上代码:

接口:

package com.chen.proxyFactory;

public interface SmsService {
    String send(String message);
}

实现了InvocationHandler的代理类

package com.chen.proxyFactory;

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

/**
 * 动态代理类
 */
public class DebugInvocationHandler implements InvocationHandler {

    private final Object target;

    public DebugInvocationHandler(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 result = method.invoke(target, args);
        System.out.println("after method" +method.getName());
        return result;
    }
}

代理对象工厂类(就是生成代理代理对象的)加上测试

package com.chen.proxyFactory;

import java.lang.reflect.Proxy;

public class JdkProxyFactory {
    public static Object getProxy(Object target){
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),new DebugInvocationHandler(target));
    }


    public static void main(String[] args) {
        SmsService proxy = (SmsService)JdkProxyFactory.getProxy(new SmsServiceImpl());
        proxy.send("java");
    }
}

(二)cglib代理

jdk自带的动态代理有一个缺点就是只能代理实现了接口的类,为了解决这个问题我们可以用CGLIB动态代理机制来避免

在 CGLIB 动态代理机制中 MethodInterceptor 接口和 Enhancer 类是核心。

就是需要定义一个实现MethodIntercepter的类,重写intercept方法。然后通过Enhancer类来动态获取被代理类,当代理类调用方法的时候,实际调用德军iu是MehtodIntercepter中的intercept方法

实现步骤

1.定义一个类

2.创建一个实现了MethodIntercepter方法的类并且重写intercept方法(跟jdk动态代理的invoke方法类似)

3.通过Enhancer类的create()创建代理类

上代码:

cglib依赖

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>

基础类

package com.chen.cgLibTest;

public class AliSmsService {
    public String send(String message){
        System.out.println("阿里 message:"+message);
        return message;
    }
}

代理类

package com.chen.cgLibTest;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class DebugMethodInterceptor implements MethodInterceptor {
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("调用之前");
        Object object = methodProxy.invokeSuper(o, objects);
        System.out.println("调用后");
        return object;
    }
}

获取代理类+测试

package com.chen.cgLibTest;

import net.sf.cglib.proxy.Enhancer;

public class CgLibProcyFactory {

    public static Object getProxy(Class<?> clazz){
        Enhancer enhancer = new Enhancer();
        enhancer.setClassLoader(clazz.getClassLoader());
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(new DebugMethodInterceptor());
        return enhancer.create();
    }

    public static void main(String[] args) {
       AliSmsService aliSmsService = (AliSmsService) CgLibProcyFactory.getProxy(AliSmsService.class);
        aliSmsService.send("haha");
    }
}

jdk动态代理和CGLIB动态代理对比

  • JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。 另外, CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。
  • 就二者的效率来说,大部分情况都是 JDK 动态代理更优秀,随着 JDK 版本的升级,这个优势更加明显。

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

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

相关文章

PL点亮LED灯

PL点亮LED灯 LED硬件 开发板在PL端给了一个LED灯&#xff0c;电压给低的时候才导通&#xff0c;才亮。然后需要看原理图确定具体是用的PL的哪个管脚以便进行控制。 Vivado 工程创建 打开创建新工程选择工程名和工程目录&#xff08;不能有中文路径&#xff0c;也不能太长&…

什么品牌的蓝牙耳机音质最好?高品质蓝牙耳机排行榜

近年来&#xff0c;大量的蓝牙耳机品牌厂商涌入市场&#xff0c;蓝牙耳机的需求不断提高&#xff0c;但质量一直是人们担心的问题&#xff0c;今天就为大家盘点几款当前音质表现不错的真无线蓝牙耳机。 一、南卡小音舱蓝牙耳机 发声单元&#xff1a;13.3mm 网友推荐系数&…

hex文件格式剖析,以及hex与bin文件互相转换

大家好&#xff0c;我是学电子的小白白。 熟悉单片机开发的朋友&#xff0c;应该经常见到*.hex后缀的文件&#xff0c;它是单片机和嵌入式工程编译输出的一种常见的目标文件格式&#xff08;比如keil就能编译输出hex文件&#xff09;&#xff0c;通过烧写工具把它下载到单片机…

【SpringBoot项目实战】图片压缩包上传、解压、存储等等一套流程教学

【SpringBoot项目实战】图片压缩包上传、解压、存储等等一套流程教学前言一、压缩包上传1、接口实现2、获取压缩包的文件名和文件路径二、压缩包解压并保存1、处理压缩包文件方法解压缩步骤&#xff1a;2、接口中实现处理压缩包三、总结前言 之前一直用的zip4j来对压缩包进行操…

开源项目推荐 | 中科院自动化所历时9年打造的类脑认知智能引擎“智脉”正式开源部署至OpenI启智社区

​人脑能够自组织地协同数百项认知功能&#xff0c;灵活适应复杂多变的环境。如何整合多尺度生物可塑性法则来构建具有生物合理性和计算高效性的神经网络模型是类脑人工智能和计算神经科学领域共同关注和面临的重要挑战。 中国科学院自动化研究所类脑认知智能研究组历时9年&am…

通关算法题之 ⌈动态规划⌋

动态规划 动态规划是什么&#xff1f;解决动态规划问题有什么技巧&#xff1f;如何学习动态规划&#xff1f; 首先&#xff0c;动态规划问题的一般形式就是求最值。动态规划其实是运筹学的一种最优化方法&#xff0c;只不过在计算机问题上应用比较多&#xff0c;比如说让你求…

蓝桥杯寒假集训第二天(分巧克力)

没有白走的路&#xff0c;每一步都算数&#x1f388;&#x1f388;&#x1f388; 题目描述&#xff1a; 有很多的巧克力块&#xff0c;需要设计一个程序&#xff0c;让手艺师傅切出来的巧克力既满足切出来的巧克力的份数达到客户要求的份数&#xff0c;并且切出来的巧克力块尽…

硬盘图片丢失怎么办?看看这常见的三种恢复方法

电脑硬盘能够帮助我们存储各种各样的数据&#xff0c;比如图片数据&#xff0c;而随着这些充满回忆的图片积累的越来越多&#xff0c;难免会手误删除或其他原因导致丢失&#xff0c;那么对于电脑硬盘上丢失的图片如何恢复呢&#xff1f;在这里你可以了解到硬盘图片丢失常见原因…

python—subprocess模块常用方法介绍

目录 subprocess.run() subprocess.Popen() subprocess.call() subprocess.check_call() subprocess.getstatusoutput() subprocess.getoutput() subprocess.check_output() subprocess是子流程&#xff0c;即进程的意思&#xff0c;该模块可以启动一个新进程&#xff0…

印度电线标准IS 694(R2020),印度插头标准IS 1293(R2020)

印度电线标准IS 694&#xff08;R2020) https://download.csdn.net/download/std86021/87328675POLYVINYLCHLORIDEINSULATEDUNSHEATHEDANDSHEATH更多下载资源、学习资料请访问CSDN下载频道.https://download.csdn.net/download/std86021/87328675 补丁一&#xff1a;IS694Amd…

已获1000 万美元战略融资,Web3链游#Delysium 有何魔力?

这周社区推文关注了AIGC在游戏领域中的创新应用&#xff0c;在Web3领域中&#xff0c;有哪些值得关注的链游项目呢&#xff1f;Mixlab小杜AIGC 与游戏机制创新玩法结合已成为潮流趋势&#xff0c;如游戏内容资产生成、智能NPC设计与自定义游戏玩法等都有非常大的探索空间。Web3…

【ACWING】【BFS】【844走迷宫】【845八数码】

一、走迷宫 给定一个 nm的二维整数数组&#xff0c;用来表示一个迷宫&#xff0c;数组中只包含 0 或 1&#xff0c;其中 0表示可以走的路&#xff0c;1 表示不可通过的墙壁。 最初&#xff0c;有一个人位于左上角 (1,1)处&#xff0c;已知该人每次可以向上、下、左、右任意一…

CVE复现1

1 CVE-2022-22980 spring Data MongoDB SpEL 表达式注入,Spring Data MongoDB应用程序在对包含查询参数占位符的SpEL表达式使用Query或Aggregation注解的查询方法进行值绑定时&#xff0c;如果输入未被过滤&#xff0c;则容易受到SpEL注入攻击。 影响范围 Spring Data Mongo…

190:vue+openlayers 调整卫星运动的播放速度,展示运动轨迹

第190个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers项目中利用两行根数计算卫星在某一时刻的位置点,利用时间循环,定位出不同的位置,呈现动画的效果。文中利用slider的改变来确定当前时间为开始时间,slider的值为加速倍数,每个setTimeout为10毫秒,更新…

【博客系统】前端页面

博客系统 实现一个简单的博客系统。 当前先完成页面设计部分&#xff0c;通过学习的前端知识来构建出网页。 主要分成四个部分&#xff1a; 博客列表页博客正文页博客登陆页博客编辑页 预期效果 博客列表页效果 博客详情页效果 博客登陆页效果 博客编辑页效果 实现博客列…

C++数据结构--跳表的思想--手把手教你实现跳表--0721

1、 跳表--skiplist skiplist本质上是一种查找结构&#xff0c;跟平衡搜索树和哈希表的价值是一样的。跳表首先是一个链表&#xff0c;它是在链表的基础上发展的。但一般的链表进行查找数据只能全部遍历&#xff0c;时间复杂度为O(n)。 William Pugh的优化&#xff1a; 假如每…

Kafka一次线上问题

线上问题&#xff1a; Kafka: 客户说Broker不时会发生些错误日志&#xff0c;也看到topic的tps下降很快很明显&#xff0c; 日志看是ISR在不断的伸缩&#xff0c; 监控发现发生问题时的CPU、IO、磁盘都没有瓶颈 再查看堆栈信息&#xff1a;可看到关键信息&#xff1a; 有锁…

R语言raster包批量读取单一或大量栅格图像

本文介绍基于R语言中的raster包&#xff0c;读取单张或批量读取多张栅格图像&#xff0c;并对栅格图像数据加以基本处理的方法。 1 包的安装与导入 首先&#xff0c;我们需要配置好对应的R语言包&#xff1b;前面也提到&#xff0c;我们这里选择基于raster包来实现栅格图像数据…

TestStand-单执行界面

文章目录用户界面运行流程例程创建前面板设置用户界面主窗口前面板控件配置用户事件用户界面运行流程 用户界面设计的基本元素&#xff1a;管理控件、可视化控件、连接、应用程序启动及关闭、注册事件、处理事件。 LabVIEW中通过Regesiter Event Callback注册事件。 LabVIEW-Te…

flink内存管理, 增加Task内存大小,减少ManageMemory, network内存的方法

问题描述 flink默认分配的内存&#xff0c;不合理&#xff0c;jvm 堆内存太小&#xff0c;其他内存太大。向yarn申请8G内存&#xff0c;最后分配到heap的大小才3.2G&#xff0c;不是让人抓狂吗&#xff1f; 以上是&#xff0c;向yarn申请8G内存&#xff0c;实时分配的内存是上…