Java设计模式中代理模式是什么/JDK动态代理分为哪些,静态代理又怎么实现,又适合哪些场景
创始人
2024-05-08 13:37:28
0

继续整理记录这段时间来的收获,详细代码可在我的Gitee仓库SpringBoot克隆下载学习使用!

5.结构型模式

5.1 概述

  • 根据如何将类或对象按某种布局组成更大的结构,分为类结构模式对象结构模式,前者采用继承机制来组织接口和类,后者采用组合或聚合来组合对象
  • 对象结构模式比类结构模式具有更大灵活性—因组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”

5.2 代理模式

5.2.1 概述

  • 背景:由于某些原因需要给某对象提供一个代理以控制对该对象的访问
  • 特点:访问对象不适合或者不直接引用目标对象,代理对象作为可以访问对象和目标对象的中介
  • 根据代理类生成时机不同分为静态代理和动态代理,静态代理在编译器生成,后者在Java运行时动态生成
  • 动态代理分为JDK代理和CGLIB代理

5.2.2 结构

  • 抽象主题(Subject)类:通过接口或抽象类声明真实主题和代理对象实现的业务方法
  • 真实主题(Real Subject)类:实现抽象主题类的具体业务,是代理对象所代表的真实对象
  • 代理(Proxy)类:提供了与真实主题相同的接口,其内部含有对真实主题的引用,可以访问,控制或扩展真实主题的功能

5.2.3 静态代理

火车站卖票案例
代码:

//抽象主题
public interface SellTickets {  void sellTickets();  
}
//真实主题
public class TrainStation implements SellTickets{  @Override  public void sellTickets() {  System.out.println("火车站卖票");  }  
}
//代理类
public class ProxyPoint implements SellTickets{  private TrainStation station = new TrainStation();  @Override  public void sellTickets() {  System.out.println("代理商收取服务费");  station.sellTickets();  }  
}

5.2.4 JDK动态代理

5.2.4.1 案例

  • 使用案例同上
  • 抽象主题和真实主题同上
  • 代码

    注意:
    ProxyFactory不是代理模式中的代理类
public class ProxyFactory{  
//    声明目标对象  private TrainStation station = new TrainStation();  public SellTickets getProxyObjects()  {  /*ClassLoader loader, 类加载器,用于加载代理类,可以通过目标对象获取类加载器  Class[] interfaces,代理类实现的接口的字节码对象  InvocationHandler h 代理对象调用的调用处理程序  * */        SellTickets proxyInstance = (SellTickets) Proxy.newProxyInstance(  station.getClass().getClassLoader(),  station.getClass().getInterfaces(),  new InvocationHandler() {  /*Object 代理对象,和proxyInstance是同一个对象,在invoke中基本不用  Method 代理对象调用方法,对接口中方法进行封装的对象,这里是sellTickets  Object[] 调用方法参数  * */                    @Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  System.out.println("代理商收取服务费(JDK动态代理)");  
//                        执行目标对象的方法  Object invoke = method.invoke(station, args);  return invoke;  }  }  );  return proxyInstance;  }  
}//测试public static void main(String[] args) {  
//        获取代理对象  
//         创建代理工厂对象  ProxyFactory proxyFactory = new ProxyFactory();  
//        使用代理工厂创建代理对象  SellTickets proxyObjects = proxyFactory.getProxyObjects();  
//        调用买票方法  proxyObjects.sellTickets();  }

5.2.4.2 案例中的代理类

  • 使用arthas-boot.jar来查看具体的代理类等信息
  • 解压后使用CMD打开
    • 在测试类加代码使程序一直运行,如图![[Pasted image 20221231154138.png]]
    • 命令java -jar arthas-boot.jar,如图![[Pasted image 20221231154030.png]]
    • 继续输入2,如图![[Pasted image 20221231154059.png]]
    • 命令com.sun.proxy.$Proxy0,如图![[Pasted image 20221231154338.png]]
    • 代理类$Proxy0重点代码
    public final class $Proxy0 extends Proxy implements SellTickets {  
    private static Method m3;  
    public $Proxy0(InvocationHandler invocationHandler) {  super(invocationHandler);  
    }  
    static {  m3 = Class.forName("com.demo.patternDesign.ProxyModel.DynamicJDKProxy.SellTickets").getMethod("sellTickets", new Class[0]);  
    } 
    

// 代理类调用真实对象方法
public final void sellTickets() {
this.h.invoke(this, m3, null);
}
}
```

  • 执行流程
    • 测试类通过代理对象调用sell方法
    • 根据多态,执行代理类$Proxy0的sell方法
    • 代理类$Proxy0的sell方法又调用InvocationHandle接口中的自实现类对象的invoke方法(测试类加收取服务费那方法)
    • invoke方法通过反射执行真实对象类TrainStation中的sell方法

5.2.5 CGLIB动态代理

和之前一样的案例

  • CGLIB包坐标,CGLIB下载,关联包ASM下载
cglibcglib3.3.0

  • CGLIB包介绍:高性能代码生成包,可为没有实现接口类提供代理
  • 代码
// 代理工厂
public class ProxyFactory implements MethodInterceptor {  
//    创建真实对象  private TrainStation trainStation = new TrainStation();  public TrainStation getProxyObject(){  
//        创建Enhance对象,类似于JDK代理中的Proxy类  Enhancer enhancer = new Enhancer();  
//        设置父类的字节码对象  enhancer.setSuperclass(TrainStation.class);  
        设置回调函数  enhancer.setCallback(this);  
//        创建代理对象  TrainStation proxyObject = (TrainStation) enhancer.create();  return proxyObject;  }  @Override  public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {  System.out.println("代理商收取服务费(cglib代理)");  Object invoke = method.invoke(trainStation,objects);  return invoke;  }  
}
// 测试代码public static void main(String[] args) {  
//        创建代理工厂对象  ProxyFactory proxyFactory = new ProxyFactory();  
//        获取代理对象  TrainStation proxyObject = proxyFactory.getProxyObject();  proxyObject.sellTickets();}

5.2.6 3 种方式对比

5.2.6.1 JDK和CGLIB

  • CGLIB代理
    • 底层采用ASM字节码生成框架,使用字节码技术生成代理类
    • 不能对声明为final的类或方法进行代理,因CGLIB原理是动态生成被代理的子类
    • JDK1.6之前比JAVA使用反射效率要高
  • 两者效率问题
    • JDK1.6,1.7,1.8后在调用次数较少情况下JDK代理效率高于CGLIB代理效率
    • 仅当进行大量调用且在JDK1.6,1.7中CGLIB代理效率高
  • 选择:有接口使用JDK动态代理,没有接口使用CGLIB代理

5.2.6.2 动态和静态

  • 动态优点:声明的所有方法都被转移到调用处理器一个集中方法处理
  • 静态缺点:若接口增加方法,静态代理除了所有实现类需要实现此方法外,所有处理类也需要实现此方法,增加代码冗余与复杂性

5.2.7 优缺点

5.2.7.1 优点

  • 在客户端与目标对象之间起一个中介作用和保护目标对象作用
  • 可以扩展目标对象的功能
  • 能将客户端与目标对象分离,一定程度上降低系统耦合度

5.2.7.2 缺点

增加系统复杂度

5.2.8 使用场景

5.2.8.1 远程(Remote)代理

本地服务通过网络请求远程服务:可将网络通信部分隐藏,只保留本地服务一个接口,使其通过此接口访问远程服务提供功能,而不必过多关心通信细节。

5.2.8.2 防火墙(Firewall)代理

将浏览器配置成代理模式,防火墙将请求转发给互联网,当互联网响应时,代理服务器将信息再转发给浏览器

5.2.8.3 保护(Protect or Access)代理

控制一个对象访问,若需要,可以给不同对象以不同访问权限

相关内容

热门资讯

监控摄像头接入GB28181平... 流程简介将监控摄像头的视频在网站和APP中直播,要解决的几个问题是:1&...
Windows10添加群晖磁盘... 在使用群晖NAS时,我们需要通过本地映射的方式把NAS映射成本地的一块磁盘使用。 通过...
protocol buffer... 目录 目录 什么是protocol buffer 1.protobuf 1.1安装  1.2使用...
在Word、WPS中插入AxM... 引言 我最近需要写一些文章,在排版时发现AxMath插入的公式竟然会导致行间距异常&#...
【PdgCntEditor】解... 一、问题背景 大部分的图书对应的PDF,目录中的页码并非PDF中直接索引的页码...
Fluent中创建监测点 1 概述某些仿真问题,需要创建监测点,用于获取空间定点的数据࿰...
educoder数据结构与算法...                                                   ...
MySQL下载和安装(Wind... 前言:刚换了一台电脑,里面所有东西都需要重新配置,习惯了所...
修复 爱普生 EPSON L4... L4151 L4153 L4156 L4158 L4163 L4165 L4166 L4168 L4...
MFC文件操作  MFC提供了一个文件操作的基类CFile,这个类提供了一个没有缓存的二进制格式的磁盘...