iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >Java结构型模式之代理模式怎么实现
  • 844
分享到

Java结构型模式之代理模式怎么实现

2023-07-05 04:07:22 844人浏览 薄情痞子
摘要

今天小编给大家分享一下Java结构型模式之代理模式怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一.介绍在代理模式(

今天小编给大家分享一下Java结构型模式之代理模式怎么实现的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。

一.介绍

在代理模式(Proxy Pattern)属于结构型模式。在代理模式中,我们对一个对象提供一个代理对象,使用代理对象控制原对象的引用,目的是为了透明的控制对象访问

二.UML类图

Java结构型模式之代理模式怎么实现

三.代理模式分类

Java中的代理按照代理类生成时机不同分为静态代理和动态代理,静态代理的代理类在编译器就生成,而动态代理的代理类在Java运行时动态生成。动态代理又分为jdk代理和CGLib代理。

四.静态代理

业务代码

public interface Pay {    void pay();}//真实类class Alipay implements Pay {    @Override    public void pay() {        System.out.println("支付宝支付");    }}//代理类class AlipayProxy implements Pay{    //组合真实对象    private final Alipay alipay = new Alipay();    @Override    public void pay() {        long startTime = System.currentTimeMillis();        alipay.pay();        System.out.println("执行了" + (System.currentTimeMillis()-startTime) + "毫秒"); //支付宝支付 执行了0毫秒    }}

测试代码

public class Client {    public static void main(String[] args) {        new AlipayProxy().pay();    }}

五.静态代理的优缺点

优点

  • 符合开闭原则

  • 功能增强无需改动原业务代码(解耦)

缺点

  • 一个具体类就要产生一个代理类,可能会造成类爆炸

六.动态代理

为了弥补静态代理的缺点,引入了动态代理

JDK动态代理(利用Java提供的代理机制)

业务代码

public interface Pay {    void pay();}//真实类class Alipay implements Pay {    @Override    public void pay() {        System.out.println("支付宝支付");    }}class PayProxy {    //组合真实对象    private Pay pay;    public PayProxy(Pay pay) {        this.pay = pay;    }    public Pay getProxy() {        return (Pay) Proxy.newProxyInstance(getClass().getClassLoader(), pay.getClass().getInterfaces(), new InvocationHandler() {            @Override            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                long startTime = System.currentTimeMillis();                Object result = method.invoke(pay, args);                System.out.println("执行了" + (System.currentTimeMillis() - startTime) + "毫秒");                return result;            }        });    }}

测试代码

public class Client {    public static void main(String[] args) {        PayProxy payProxy = new PayProxy(new Alipay());        Pay pay = payProxy.getProxy();        pay.pay(); //支付宝支付 执行了0毫秒    }}

我们通过arthas工具进行反编译,可以找到真正的代理类$Proxy0

//代理对象public final class $Proxy0 extends Proxy implements Pay {    private static Method m3;    public $Proxy0(InvocationHandler invocationHandler) {        super(invocationHandler);    }    static {        // 通过反射获取名叫pay的menthod        m3 = Class.forName("com.designpattern.structure.proxy.v2.Pay").getMethod("pay", new Class[0]);        return;    }    public final void pay() {        // h是invocationHandler对象        this.h.invoke(this, m3, null);        return;    }}

总结执行流程如下

  1. 测试代码里执行了pay.pay()

  2. 根据多态的特性,执行的是代理类($Proxy0)中的pay方法

  3. 代理类($Proxy0)中的pay方法中执行了invocationHandler对象的invoke方法

  4. invocationHandler对象的invoke方法就是业务代码中传入的匿名内部类中重写的invoke方法

  5. 在重写的invoke方法中通过反射调用真实对象alipay的pay方法

CGLib动态代理

JDK动态代理要求必须定义接口,如果没有定义接口,就可以使用CGLib动态代理,CGLib为JDK的动态代理提供了很好的补充

首先引入cglib-3.3.0.jar与asm-9.0.jar

业务代码

//真实对象class Alipay implements Pay {    @Override    public void pay() {        System.out.println("支付宝支付");    }}class AlipayProxy implements MethodInterceptor {    //组合真实对象    private Alipay alipay = new Alipay();    public Alipay getProxy(){        Enhancer enhancer = new Enhancer();        enhancer.setSuperclass(Alipay.class);        //设置回调函数        enhancer.setCallback(this);        //返回代理对象        return (Alipay) enhancer.create();    }    @Override    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {        long startTime = System.currentTimeMillis();        Object result = method.invoke(alipay, args);        System.out.println("执行了" + (System.currentTimeMillis() - startTime) + "毫秒");        return result;    }}

测试代码

public class Client {    public static void main(String[] args) {        Alipay proxy = new AlipayProxy().getProxy();        proxy.pay(); //支付宝支付 执行了0毫秒    }}

七.JDK代理与CGLIB代理对比

  • JDK代理要求必须定义接口,CGLib不用

  • CGLib的原理是动态生成被代理类的子类,所以类和方法都不能定义成final

  • CGLib代理速度>JDK代理速度的场景:JDK1.6之前、JDK1.6与JDK1.7进行大量调用,其余场景JDK代理速度更快(因此在有接口的情况下推荐使用JDK动态代理)

八.代理模式的优缺点

优点

  • 保护真实对象,使用代理对象与客户端交互

  • 符合开闭原则

  • 客户端与真实对象之间解耦

缺点

  • 代理类的创建,增加了系统复杂度

九.使用场景

功能扩展:日志监控事务

控制管理:权限、限流

远程代理:FeignClient、RMI

动态逻辑:mybatis mapper、jpa

延迟加载:虚代理

十.通用的动态代理实现(拓展)

上文提到静态代理是一个具体类产生一个代理类,可能会造成类爆炸,我们现在反观动态代理则是一个接口产生一个代理类,也可能会造成类爆炸,所以这里给出一个较为通用的实现

业务代码

//记录执行的时间的通用类public class TimeRecordProxy<T> {    private final T target;    public TimeRecordProxy(T target) {        this.target = target;    }    @SuppressWarnings("unchecked")    public T getProxy() {        return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(),                target.getClass().getInterfaces(),                this::invoke);    }    private Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {        long startTime = System.currentTimeMillis();        Object result = method.invoke(target, args);        System.out.println("执行了" + (System.currentTimeMillis()-startTime) + "毫秒");        return result;    }}

测试代码

public class Client {    public static void main(String[] args) {        TimeRecordProxy<Pay> timeRecordProxy = new TimeRecordProxy<>(new Alipay());        timeRecordProxy.getProxy().pay(); //支付宝支付 执行了0毫秒    }}

以上就是“Java结构型模式之代理模式怎么实现”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网精选频道。

--结束END--

本文标题: Java结构型模式之代理模式怎么实现

本文链接: https://www.lsjlt.com/news/349310.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • c#文本框只读属性怎么设置
    c# 文本框只读属性的设置 问题:如何设置 C# 文本框的只读属性? 回答: 要设置文本框的只读属性,可以使用 ReadOnly 属性。 详细解释: ReadOnly 属性是一个布尔值属...
    99+
    2024-05-14
    c#
  • 如何使用 Golang ORM 工具与数据库交互?
    使用 gorm orm 工具与数据库交互,可通过以下步骤轻松实现:安装和初始化(1)、定义模型(2)、建立映射(3)、创建记录(4)、读取记录(5)、更新记录(6)、删除记录(7)、事务...
    99+
    2024-05-14
    golang orm mysql git iphone
  • c++中double与float的区别
    c++++ 中 double 与 float 的区别 在 C++ 中,double 和 float 都是浮点数类型,但它们在精度、范围和内存占用方面存在差异。 精度: double:双...
    99+
    2024-05-14
    c++ 内存占用
  • 如何在 Golang 中处理数据库错误?
    在 go 中处理数据库错误的步骤包括:使用专门的 go mysql 驱动程序。实现 error 接口以创建自定义错误。检测错误,记录足够的信息,并基于错误类型执行适当的恢复操作。 如何...
    99+
    2024-05-14
    golang 数据库错误 mysql git 数据丢失
  • c++中int怎么转string
    在 c++ 中将 int 转换为 string 的方法有:使用 to_string() 函数直接转换。使用 stringstream 类。使用 sprintf() 函数。 如何在 C+...
    99+
    2024-05-14
    c++
  • 优化 C++ 服务器架构以提高吞吐量
    优化 c++++ 服务器吞吐量策略:线程池:预先创建线程池,快速响应请求。非阻塞 i/o:在等待 i/o 时执行其他任务,提升吞吐量。http/2:使用二进制协议,支持多路复用和内容压缩...
    99+
    2024-05-14
    优化 服务器架构 c++
  • 使用 C++ 堆分配器管理服务器架构中的内存
    使用 c++++ 堆分配器管理服务器内存可提高性能和稳定性。堆分配器负责分配和释放动态内存,跟踪空闲/已分配内存元数据。在服务器架构中,它用于分配应用程序对象、缓冲区和数据结构。选择堆分...
    99+
    2024-05-14
    c++ 内存管理 并发访问
  • c#怎么获取字符串中的数字
    从 c# 字符串中提取数字的方法有五种:正则表达式、循环和 char.isdigit()、int.tryparse()、string.split() 和 int.parse()、linq...
    99+
    2024-05-14
    git c#
  • C++ 异常处理在服务器架构中的最佳实践
    c++++ 异常处理在服务器架构的最佳实践:定义清晰的异常层次结构,使用自定义异常类型封装相关信息。使用异常安全函数,及时在适当范围内处理异常。提供有意义的错误消息,帮助用户了解错误并采...
    99+
    2024-05-14
    c++ 异常处理
  • c#怎么拼接字符串
    在 c# 中拼接字符串有三种方法:使用加法(+)运算符、string.concat() 方法和 stringbuilder 类。最简单的方法是使用 + 运算符将字符串连接起来,...
    99+
    2024-05-14
    c#
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作