代理模式介绍(静态代理、jdk动态代理、cglib代理)

一、静态代理

(一)定义

1、定义

为其他对象提供一种代理以控制对这个对象的访问;

2、涉及到的角色

(1)抽象主题角色:真实主题和代理主题的共同接口,便于在使用真实主题的地方都可以使用代理主题;
(2)代理主题角色:代理类,负责控制对真实主题的引用,在需要的时候创建和删除真实主题,并且在真实主题处理完毕后做预处理和善后处理的工作;
(3)真实主题角色:被代理角色,业务逻辑的具体执行者;

(二)类图

在这里插入图片描述

(三)代码实现

1、抽象主题角色(业务接口)
package com.xiaobai.design_pattern.proxy;

/**
 * @author wangtw
 * @date 2023/12/1 23:03
 * @description 代理模式-抽象主题角色
 */
public interface IGamePlayer {

    /**
     * 打怪
     */
    void killBoss();

    /**
     * 升级
     */
    void upGrade();
}

2、真实主题角色(具体业务功能)
package com.xiaobai.design_pattern.proxy;

/**
 * @author wangtw
 * @date 2023/12/1 23:04
 * @description 代理模式-真实主题角色
 */
public class GamePlayer implements IGamePlayer{

    /**
     * 打怪角色
     */
    private String name;

    public GamePlayer(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    /**
     * 打怪
     */
    @Override
    public void killBoss() {
        System.out.println(this.name + "在打野怪");
    }

    /**
     * 升级
     */
    @Override
    public void upGrade() {
        System.out.println(this.name + "升了1级");
    }
}

3、代理主题角色(代理类)
package com.xiaobai.design_pattern.proxy;

import java.lang.reflect.Field;
import java.util.Date;

/**
 * @author wangtw
 * @date 2023/12/1 23:10
 * @description
 */
public class GamePlayerProxy implements IGamePlayer{

    private IGamePlayer gamePlayer;

    public GamePlayerProxy(IGamePlayer gamePlayer) {
        this.gamePlayer = gamePlayer;
    }

    private void start() {
        System.out.println("开始时间为" + new Date());
    }

    private void end() {
        System.out.println("结束时间为" + new Date());
    }

    /**
     * 获取角色名称
     * @param operateStep
     */
    private void operatePerson(String operateStep) {
        Class<? extends IGamePlayer> aClass = gamePlayer.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.getName().equals("name")) {
                declaredField.setAccessible(true);
                try {
                    Object name = declaredField.get(gamePlayer);
                    System.out.println(name + operateStep + "操作");
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public void killBoss() {
        operatePerson("开始");
        start();
        this.gamePlayer.killBoss();
    }

    @Override
    public void upGrade() {
        this.gamePlayer.upGrade();
        operatePerson("结束");
        end();
    }
}

4、测试类
package com.xiaobai.design_pattern.proxy;

import org.junit.jupiter.api.Test;

/**
 * @author wangtw
 * @date 2023/12/1 23:21
 * @description 代理模式测试类
 */
public class ClientDemo {

    @Test
    public void test() {
        IGamePlayer player = new GamePlayer("芈月");
        IGamePlayer proxy = new GamePlayerProxy(player);
        proxy.killBoss();
        proxy.upGrade();
    }
}

输出:

芈月开始操作
开始时间为Sat Dec 02 20:14:09 CST 2023
芈月在打野怪
芈月升了1级
芈月结束操作
结束时间为Sat Dec 02 20:14:09 CST 2023

(四)总结

1、代理模式的优点:
(1)职责清晰:真实角色负责处理实际的业务逻辑,不用关心非本职的事务,通过代理完成附加的事务;
(2)高扩展性:不同的需求可能会有不同的真实角色,只要实现了接口,代理类就可以完全在不做任何修改的情况下代理各种真实主题角色;
2、静态代理模式的缺点:
(1)若抽象主题角色增加功能,会影响代理类;
(2)不同的功能需求可能会有不同的代理类;

二、jdk动态代理

(一)使用动态代理的场景

在程序执行期间,使用jdk的反射机制创建代理类对象,动态指定要代理的对象;
不依赖于业务接口;
静态代理目标很多时,可以使用动态代理,避免静态代理的缺点;

(二)代码实现

1、创建代理类

代理类需要实现InvocationHandler接口,并重写invoke方法,生成代理类对象后,调用目标类实现方法时会调用生成代理对象中的invoke方法

package com.xiaobai.design_pattern.proxy;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * @author wangtw
 * @date 2023/12/2 21:36
 * @description jdk动态代理处理器
 */
public class JdkGamePlayerProxy implements InvocationHandler {

    private Object targetObject;

    public JdkGamePlayerProxy(Object object) {
        this.targetObject = object;
    }

    private void start() {
        System.out.println("开始时间为" + new Date());
    }

    private void end() {
        System.out.println("结束时间为" + new Date());
    }

    /**
     * 获取角色名称
     * @param operateStep
     */
    private void operatePerson(String operateStep) {
        Class<?> aClass = this.targetObject.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.getName().equals("name")) {
                declaredField.setAccessible(true);
                try {
                    Object name = declaredField.get(this.targetObject);
                    System.out.println(name + operateStep + "操作");
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     *
     * @param proxy 代理对象
     * @param method 方法
     * @param args 方法参数
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("============开始执行" + method.getName() + "方法==============");
        operatePerson("开始");
        start();
        Object invoke = method.invoke(this.targetObject, args);
        operatePerson("结束");
        end();
        System.out.println("============结束执行" + method.getName() + "方法==============");
        return invoke;
    }
}

2、测试方法

使用java.lang.reflect.Proxy#newProxyInstance生成代理对象

@Test
public void jdkProxyTest() {
    //生成代理类文件 在根目录的同级目录,com下
    System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    IGamePlayer player = new GamePlayer("澜");
    Class<? extends IGamePlayer> playerClass = player.getClass();
    //代理类 实现需要实现InvocationHandler接口,重写invoke方法 传入业务实现类对象
    JdkGamePlayerProxy jdkGamePlayerProxy = new JdkGamePlayerProxy(player);

    // 创建代理类对象
    /**
     *
     loader – the class loader to define the proxy class
     interfaces – the list of interfaces for the proxy class to implement
     h – the invocation handler to dispatch method invocations to
     */
    IGamePlayer proxy = (IGamePlayer) Proxy.newProxyInstance(playerClass.getClassLoader(),
            playerClass.getInterfaces(), jdkGamePlayerProxy);
    System.out.println(proxy.getClass());
    proxy.killBoss();
    proxy.upGrade();
}

在这里插入图片描述
输出:

class com.sun.proxy.$Proxy9
开始执行killBoss方法==
澜开始操作
开始时间为Sat Dec 02 23:27:05 CST 2023
澜在打野怪
澜结束操作
结束时间为Sat Dec 02 23:27:05 CST 2023
结束执行killBoss方法==
开始执行upGrade方法==
澜开始操作
开始时间为Sat Dec 02 23:27:05 CST 2023
澜升了1级
澜结束操作
结束时间为Sat Dec 02 23:27:05 CST 2023
结束执行upGrade方法==

jdk动态代理依赖于java.lang.reflect,里面有三个类:InvocationHandler,Method,Proxy

三、cglib动态代理

(一)说明

1、说明

cglib动态代理需要依赖第三方,以下代码使用spring生态的cglib包(org.springframework.cglib)实现cglib代理;

2、基础类介绍:

org.springframework.cglib.proxy.MethodInterceptor:方法拦截器类;
org.springframework.cglib.proxy.Enhancer:增强类;
org.springframework.cglib.proxy.MethodProxy:方法代理类

(二)代码实现

拦截器需要实现org.springframework.cglib.proxy.MethodInterceptor接口,重写intercept方法,使用org.springframework.cglib.proxy.MethodProxy#invoke执行目标方法

1、拦截器实现
package com.xiaobai.design_pattern.proxy;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Date;

/**
 * @author wangtw
 * @date 2023/12/3 20:17
 * @description
 */
public class CglibGamePlayerProxy implements MethodInterceptor {

    private Object targetObject;

    public CglibGamePlayerProxy(Object targetObject) {
        this.targetObject = targetObject;
    }

    private void start() {
        System.out.println("开始时间为" + new Date());
    }

    private void end() {
        System.out.println("结束时间为" + new Date());
    }

    /**
     * 获取角色名称
     * @param operateStep
     */
    private void operatePerson(String operateStep) {
        Class<?> aClass = this.targetObject.getClass();
        Field[] declaredFields = aClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            if (declaredField.getName().equals("name")) {
                declaredField.setAccessible(true);
                try {
                    Object name = declaredField.get(this.targetObject);
                    System.out.println(name + operateStep + "操作");
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("============开始执行" + method.getName() + "方法==============");
        operatePerson("开始");
        start();
        Object result = methodProxy.invoke(this.targetObject, objects);
        operatePerson("结束");
        end();
        System.out.println("============结束执行" + method.getName() + "方法==============");
        return result;
    }
}

2、测试方法

使用org.springframework.cglib.proxy.Enhancer对目标类进行增强,并设置回调拦截器

@Test
public void cglibProxyTest() {
    IGamePlayer gamePlayer = new GamePlayer("亚瑟");

    // 对目标方法进行增强
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(IGamePlayer.class); // 设置要代理的父类
    enhancer.setCallback(new CglibGamePlayerProxy(gamePlayer)); // 设置回调的拦截器

    IGamePlayer proxy = (IGamePlayer) enhancer.create(); // 创建代理对象

    proxy.killBoss();
    proxy.upGrade();
}

输出:

开始执行killBoss方法==
亚瑟开始操作
开始时间为Sun Dec 03 20:47:39 CST 2023
亚瑟在打野怪
亚瑟结束操作
结束时间为Sun Dec 03 20:47:39 CST 2023
结束执行killBoss方法==
开始执行upGrade方法==
亚瑟开始操作
开始时间为Sun Dec 03 20:47:39 CST 2023
亚瑟升了1级
亚瑟结束操作
结束时间为Sun Dec 03 20:47:39 CST 2023
结束执行upGrade方法==

参考:

韩敬海 设计模式(Java版)
Java-JDK动态代理
JDK动态代理
动态代理之 cglib 实现

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

p标签在div中居中

新建一个html文件&#xff0c;命名为test.html&#xff0c;用于讲解如何在css中让div中的p标签居中。 在test.html文件内&#xff0c;在div内&#xff0c;使用p标签创建一行文字&#xff0c;用于测试。 在test.html文件内&#xff0c;设置div标签的class属性为mydiv。 在…

HttpRunner自动化工具之实现参数化传递

参数化实现及重复执行 参数化测试&#xff1a;在接口测试中&#xff0c;为了实现不同组数据对同一个功能模块进行测试&#xff0c;需要准备多组测试数据对模块进行测试的过程。 在httprunner中可以通过如下方式实现参数化&#xff1a; 1、在YAML/JSON 中直接指定参数列表 2、…

Qt OpenCV 学习(一):环境搭建

对应版本 Qt 5.15.2OpenCV 3.4.9MinGW 8.1.0 32-bit 1. OpenCV 下载 确保安装 Qt 时勾选了 MinGW 编译器 本文使用 MinGW 编译好的 OpenCV 库&#xff0c;无需自行编译 确保下载的 MinGW 和上述安装 Qt 时勾选的 MinGW 编译器位数一致&#xff0c;此处均为 x86/32-bit下载地址…

【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(3)路由导航卫士、主页实现

项目笔记为项目总结笔记,若有错误欢迎指出哟~ 【项目专栏】 【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(1)spring boot项目搭建、vue项目搭建、微信小程序项目搭建 【java+vue+微信小程序项目】从零开始搭建——健身房管理平台(2)后端跨域、登录模块、sp…

LeetCode | 101. 对称二叉树

LeetCode | 101. 对称二叉树 OJ链接 在本函数里不好进行判断&#xff0c;我们另外定义一个函数来如果两个都相等为空&#xff0c;就返回true一个为空&#xff0c;一个不为空都不为空,就比较值然后递归1的左&#xff0c;2的右&#xff0c;1的右&#xff0c;2的左 bool _isSymm…

Gateway网关--java

网关是建立于请求到服务之前的,可以用网关限制访问量,添加过滤等 创建网关模块,引入相关pome依赖 配置yml 具体相关的作用可以参考 Spring Cloud Gateway 这样就可以了 基础的网关配置,我们的实现效果 我们可以通过10010端口访问,通过转发到nacos,再找到相应的模块,实现…

54.多级缓存

目录 一、传统缓存的问题、多级缓存方案。 二、JVM进程缓存。 1&#xff09;进程缓存和缓存。 2&#xff09;导入商品案例。 1.安装MySQL 2.导入SQL 3.导入Demo工程 4.导入商品查询页面 3&#xff09;初识Caffeine&#xff08;就是在springboot学过的注解方式的cache&…

掌握视频剪辑技巧:批量置入视频封面,提升视频品质

在当今数字化时代&#xff0c;视频已成为生活的重要组成部分。无论是观看电影、电视剧、综艺节目&#xff0c;还是分享个人生活、工作成果&#xff0c;视频都以其独特的魅力吸引着大众的视线。视频封面是视频内容的缩影&#xff0c;是观众对视频的第一印象。一个好的封面能吸引…

【计算机组成原理】存储器知识

目录 1、存储器分类 1.1、按存储介质分类 1.2、按存取方式分类 1.3、按信息的可改写性分类 1.4、按信息的可保存性分类 1.5、按功能和存取速度分类 2、存储器技术指标 2.1、存储容量 2.2、存取速度 3、存储系统层次结构 4、主存的基本结构 5、主存中数据的存放 5.…

LabVIEW远程监控

LabVIEW远程监控 远程监控的应用场景 从办公室远程监控工厂车间的测试设备。 在世界另一端的偏远地区监控客户现场的发电设备。 从公司远程监控外场的产品。 技术更新与方法 自2018年以来&#xff0c;NI对基于Web的应用程序支持大幅增长。一些最初的方法&#xff08;如Lab…

搭建CIG容器重量级监控平台

CIG简介 CIG监控平台是基于CAdvisor、InfluxDB和Granfana构建的一个容器重量级监控系统&#xff0c;用于监控容器的各项性能指标&#xff0c;通过三者的结合&#xff0c;CIG监控平台可以实现对容器性能的全面监控和可视化展示&#xff0c;为容器的性能和运行状态提供了一个全面…

React如何检查组件性能

可以使用Profiler来查看组件的渲染速度 Profiler的基本使用 需要将<Profiler>标签包裹在需要检查渲染速度的组件外部需要绑定id属性&#xff0c;该属性是唯一标识&#xff0c;用于区分其他Profiler需要onRender函数&#xff0c;该函数一共有六个参数&#xff0c;分别为…

数据结构入门————树(C语言/零基础/小白/新手+模拟实现+例题讲解)

目录 1. 树的概念及其结构 1.1 树的概念&#xff1a; 1.2 树的相关概念&#xff1a; 1.3 树的表示方法&#xff1a; ​编辑 1.4 树的应用&#xff1a; 2. 二叉树的概念及其结构 2.1 概念: 2.2 特点&#xff1a; 2.3 特殊二叉树&#xff1a; 2.4 二叉树的性质&#xf…

【文献阅读笔记】基于自监督的异常检测和定位:SSM

2022 IEEE TRANSACTIONS ON MULTIMEDIA 领域&#xff1a;异常检测 目标&#xff1a;图像输入数据 文章目录 1、模型2、方法2.1、random masking2.2、restoration network2.3、损失函数2.4、推理时的渐进细化 3、实验4、引用5、想法 1、模型 训练&#xff1a; 每个图像实时生成随…

MySQL索引优化实战二

分页查询优化 很多时候我们业务中实现分页功能时可能会用如下SQL来实现&#xff1a; select * from employees LIMIT 10000,10表示从表中中区从10001行开始的10行记录&#xff0c;看似只查了10条记录&#xff0c;但是这条SQL是先读取10010条记录&#xff0c;然后抛弃前10000条…

[MySQL]日期和时间函数

文章目录 1 获取日期、时间 CURDATE() &#xff0c;CURRENT_DATE()CURTIME() &#xff0c; CURRENT_TIME()NOW() / SYSDATE() / CURRENT_TIMESTAMP() / LOCALTIME() / LOCALTIMESTAMP()UTC_DATE()UTC_TIME()代码示例2 日期与时间戳的转换 UNIX_TIMESTAMP()UNIX_TIMESTAMP(date)…

ArcGIS制作某村土地利用现状图

1. 根据坐落单位名称属性选择并提取作图数据 (1) 将“作图线状地物”、“作图图班”和“村庄”图层加入ARCGIS&#xff08;右键Layers-Add data&#xff09;&#xff0c;选择相应路径下的文件加载即可。 (2) 按属性来提取作图村庄的地类图班、线状地物和村界文件&#xff08;…

Flutter PK jetpack compose区别和选择

Flutter诞生于Chrome团队&#xff0c;是一帮做Web的开发做的跨平台框架&#xff0c;从最开始的设计初衷&#xff0c;就是指向了跨平台这条路&#xff0c;而Compose&#xff0c;则是诞生于Android团队&#xff0c;是为了解决当前View的架构体系不能再继续适应申明式编程的范式而…

【BLE基础知识】--Slave latency设置流程及空中包解析

1、Slave latency基本概念 当BLE从设备对耗电量要求较高时&#xff0c;若需要节省耗电量&#xff0c;则可以通过设置Slave Latency参数来减少BLE从设备的耗电。 Slave Latency&#xff1a;允许Slave&#xff08;从设备&#xff09;在没有数据要发的情况下&#xff0c;跳过一定…

SCAU:分期还款(加强版)

分期还款(加强版) Time Limit:1000MS Memory Limit:65535K 题型: 编程题 语言: G;GCC;VC 描述 从银行贷款金额为d&#xff0c;准备每月还款额为p&#xff0c;月利率为r。请编写程序输入这三个数值&#xff0c;计算并输出多少个月能够还清贷款&#xff0c;输出时保留1位小…