六、代理模式

2023-11-06

六、动态代理模式

1、模式结构和结构图

  1. 1 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法。

    1.2 真实主题(Real Subject)类:实现了抽象主题中的具体业务,是代理对象所代表的真实对象,是最终要引用的对象。

    1.3 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,它可以访问、控制或扩展真实主题的功能
    在这里插入图片描述

2、代码实战

需求背景:我们通过代理模式,来实现对某个方法的执行时间统计。

公共方法:

// 时间类
package proxy;

/**
 * 时间工具类
 *
 * @author 刀刀和阳
 */
public class DateTimeUtils {

    /**
     * 开始计时
     *
     * @return 当前时间
     */
    public static long start() {
        System.out.println("开始计时!");
        return System.currentTimeMillis();
    }

    /**
     * 结束计时
     *
     * @return 当前时间
     */
    public static long end() {
        System.out.println("结束计时!");
        return System.currentTimeMillis();
    }
}

公共方法接口

package proxy;

/**
 * 公共方法接口
 *
 * @author 刀刀和阳
 */
public interface Method {
    void doSomething();
}

具体的方法实现类:

package proxy;

/**
 * @author 刀刀和阳
 */
public class RealMethod implements Method {
    
    @Override
    public void doSomething() {
        System.out.println("实际的方法调用!");
    }
}

2.1 静态代理模式:

静态代理方法实现类:

package proxy.staticproxy;

import proxy.DateTimeUtils;
import proxy.Method;

/**
 * 静态代理方法实现类
 * 
 * @author 刀刀和阳
 */
public class ProxyMethod implements Method {
    private Method method;

    public ProxyMethod(Method method) {
        this.method = method;
    }

    @Override
    public void doSomething() {
        long start = DateTimeUtils.start();
        method.doSomething();
        long end = DateTimeUtils.end();
        System.out.println("总计耗时:" + (end - start) + "ms;");
    }
}

测试:

package proxy.staticproxy;

import proxy.Method;
import proxy.RealMethod;

/**
 * 实现某方法的调用时间统计
 *
 * @author 刀刀和阳
 */
public class Client {
    public static void main(String[] args) {
        Method realMethod = new RealMethod();
        ProxyMethod proxyMethod = new ProxyMethod(realMethod);
        proxyMethod.doSomething();
    }
}

执行结果:

开始计时!
实际的方法调用!
结束计时!
总计耗时:0ms;

2.2 动态代理模式

代理方法实现类

package proxy.dynamicproxy;

import proxy.DateTimeUtils;

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

/**
 * 动态代理方法实现类
 * 
 * @author 刀刀和阳
 */
public class ProxyMethod implements InvocationHandler {
    private Object object;

    public ProxyMethod(Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long start = DateTimeUtils.start();
        method.invoke(object, args);
        long end = DateTimeUtils.end();
        System.out.println("总计耗时:" + (end - start) + "ms;");
        return object;
    }
}

测试:

package proxy.dynamicproxy;

import proxy.Method;
import proxy.RealMethod;

import java.lang.reflect.Proxy;

/**
 * 动态代理测试方法
 * 
 * @author 刀刀和阳
 */
public class Client {
    public static void main(String[] args) {
        Method realMethod = new RealMethod();
        Method proxyMethod = (Method) Proxy.newProxyInstance(
                Method.class.getClassLoader(),
                new Class[]{Method.class},
                new ProxyMethod(realMethod));
        proxyMethod.doSomething();
    }
}

测试结果:

开始计时!
实际的方法调用!
结束计时!
总计耗时:0m=s;

3、总结

上述代码可以总结看出,静态代理和动态代理的区别在于:静态代理在代码运行前就已经确定需要代理的具体类是什么。而动态代理则是在运行时确定具体的代理对象。

其次,如果静态代理的公共接口发生了变化,则具体的实现接口和代理接口都要做相应的改变。违反了开闭原则。可以在上述案列中Method接口新增一个方法doOtherThing()方法即可感受到差别。

最后,上述案例中的动态代理通过JDK代理的方式,此外,还有通过cglib的方式来实现动态代理。我们在进阶中会具体讲述。

4、进阶

1、 jdk代理和cglib代理的区别

4.1 原理区别:

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。核心是实现InvocationHandler接口,使用invoke()方法进行面向切面的处理,调用相应的通知。

而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。核心是实现MethodInterceptor接口,使用intercept()方法进行面向切面的处理,调用相应的通知。

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP

2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP

3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

4.2 性能区别:

1、CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

2、在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理。

4.3 各自局限:

1、JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理。

唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

2、在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理。

4.3 各自局限:

1、JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理。

2、cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

六、代理模式 的相关文章

随机推荐

  • 在pycharm用python画图:matplotlib

    安装matplotlib 先找到自己的python位置 再进入Scripts文件夹 我的是C Users mi AppData Local Programs Python Python39 Scripts 一定要找对 否则下面的命令没有任何
  • Flex (SDK 4.5) 中直接使用 H.264 编码视频

    最近用到 Flex FMS 实现一个视频通信 而且需要用 H 264 编码 但 Flash 本身只能采用 VP6 H 263 编码 要想编码为 H 264 必须要利用第三方工具 Flash Media Live Encoder 这也是我不愿
  • 计算智能——感知器模型

    主要内容 1 感知器总述 2 感知器模型 3 感知器策略 建立损失函数 4 感知器算法 梯度下降和随机梯度下降 4 1梯度下降 4 2随机梯度下降 5 感知器MATLAB简单实现 5 1newp函数 5 2sim函数 5 3init函数 5
  • mysql提示表不存在的解决方法error: 1146: Table doesn't exist

    如果表真的不存在就新建对应表 如果存在 则 1 这种情况一般只要重启下数据库就能解决 2 或者把原来mysql安装目录data里的 ibdata1 文件也拷贝过去 不确定是否会影响MySQL里的原有数据库 请先备份ibdata1文件
  • HTML5是什么与什么合作推出的语言,H5和Html5是一回事吗?-- -H5和Html5问答

    经常有人问何为H5 或发个网页问是不是H5 真让回答 一两句也讲不清楚 所以先聊聊我理解的H5广告究竟如何定义 H5广告是什么 广告在生活中可是不新鲜了 不管你乐不乐意 带这个符号的传播物每天都在消耗着你的时间 你的精力 甚至还有你的情感
  • 内存数据库解析与主流产品对比(三)

    作者 实验室小陈 大数据开放实验室 在上一篇文章 内存数据库解析与主流产品对比 二 中 我们从数据组织和索引的角度介绍了内存数据库的特点和几款产品的技术实现 本文将继续解析内存数据库 从并发控制 持久化和查询处理的角度介绍几款技术 带来更多
  • Unity提高工作效率的终极指南

    本套课程指南通过关于如何更快 更智能地工作的最新技术 帮助Unity创作者节省时间并提高工作效率 你会学到 Unity的创建者节省了时间 提高了生产力 关于如何更快地使用程序员和艺术家工具集的技巧 无论是个人还是团队 Unity应该是一种快
  • 什么是布隆过滤器?如何使用?

    欢迎搜索 文章目录 一 布隆过滤器简介 二 布隆过滤器的结构 三 布隆过滤器应用 四 布隆过滤器的优缺点 五 布隆过滤器实战 六 总结 Redis缓存穿透可以通过布隆过滤器进行解决 那么什么是布隆过滤器呢 请往下看 通常你判断某个元素是否存
  • 在Unity中编写Shader的编译器环境配置(支持CG和HLSL)

    Unity默认使用的编译器VisualStudio带有扩展插件ShaderLabVS 但功能很差 所以还是选用VisualStudioCode作为编写Shader的编译器 一方面其能自动识别Shaderlab语法 并且还有丰富的Shader
  • vue脚手架中创建自定义指令

    局部自定义指令直接在组件内部创建
  • 使用tee命令 将bash -x 输出的内容保存到文件中

    tee 命令语法 tee ai help version 文件 参数 a或 append 附加到既有文件的后面 而非覆盖它 i或 ignore interrupts 忽略中断信号 help 在线帮助 version 显示版本信息 示例 ba
  • java -jar 启动脚本

    ccue sh 需 chomd x ccue sh 启动使用 ccue sh start bin sh ccue sh start 启动 stop 停止 restart 重启 status 状态 AppName ccue server ja
  • 【一分钟解决】Python报错ImportError: attempted relative import with no known parent package

    文章目录 报错关键词 常见问题汇总及排查 1 在脚本中使用相对导入 详细解决方案 1 看这段基本够了 使用相对导入的时机 2 扩展 如果你真的需要在包平级目录以外的位置调用包 参考链接 扩展 名词解释 脚本 script 模块 module
  • 【Python 3.7】分子运动:修改 rw_visual.py,将其中的 plt.scatter() 替换为 plt.plot() 。为 模拟花粉在水滴表面的运动路径

    Python 3 7 分子运动 修改 rw visual py 将其中的 plt scatter 替换为 plt plot 为 模拟花粉在水滴表面的运动路径 向 plt plot 传递 rw x values 和 rw y values 并
  • 前端canvas绘制水波球

    效果如下图 代码
  • 创建软链接(symbolic link)

    Linux ln命令是一个非常重要命令 它的功能是为某一个文件在另外一个位置建立一个同步的链接 类似windows下的快捷方式 Linux文件系统中 有所谓的链接 link 我们可以将其视为档案的别名 而链接又可分为两种 硬链接 hard
  • 计算机视觉学习总结:基本的图像操作和处理(一)

    PIL Python图像处理类库 PIL Python Imaging Library Python 图像处理类库 提供了通用的图像处理功能 以及大量有用的基本图像操作 比如图像缩放 裁剪 旋转 颜色转换等 基本操作 1 读取图片及灰度转换
  • acwing模板整理(第一讲)(基础算法)

    目录 一 归并排序模板 二 二分 需要满足单调性 整数二分和小数二分 三 高精度加减乘除 2 减法 3 乘法 4 除法 四 1 一维前缀和与差分数组 2 子矩阵的前缀和与差分矩阵 二维前缀和与二维差分矩阵 五 双指针算法 找单调性优化 双指
  • 测试——Web网站测试主要测试那些内容

    一般的网站的主要测试内容就分为以下几点 目录 功能测试 性能测试 安全测试 稳定测试 兼容性测试 压力测试 功能测试 功能测试常用到的有效方法 等价划分法 等价划分法就是把输入空间划分为几个 等价区间 在每个等价区间中只需要测试一个典型的数
  • 六、代理模式

    六 动态代理模式 1 模式结构和结构图 1 抽象主题 Subject 类 通过接口或抽象类声明真实主题和代理对象实现的业务方法 1 2 真实主题 Real Subject 类 实现了抽象主题中的具体业务 是代理对象所代表的真实对象 是最终要