15 代理模式

15.1 代理模式概述

Proxy Pattern: 给某一个对象提供一个代理或占位符,由代理对象来控制对原对象的访问。

代理对象是客户端和目标对象的之前的桥梁,它接收来自客户端的请求并转发给目标对象,去掉客户端不能看到的内容或添加额外的服务。为了保证客户端使用的透明性,代理对象和原对象需要实现相同的接口。

代理模式结构图如下所示:

15.2 代理模式实现

15.2.1 抽象主题接口

代理对象和原访问对象都需要实现该接口,使得任何使用真实主题对象的地方都可以使用代理对象,客户端也可以针对抽象层编程。

public interface Subject {
public void request();
}

15.2.2 真实主题类

public class RealSubject implements Subject {
public void request() {
// 业务逻辑代码
}
}

15.2.3 代理类

代理类中包含对真实主题的引用,可以把客户端请求转发给真实主题并将请求结果返回给客户端。同时,代理类在将请求转发给真实主题之前和之后还可以执行一些其他操作,增加客户端需要的额外服务。

public class Proxy implements Subject {
private Subject realSubject = new RealSubject(); public void request() {
preRequest(); // 请求转发给真实主题
realSubject.request(); postRequest();
} public void preRequest() {
// 转发前业务代码
} public void postRequest() {
// 转发后业务代码
}
}

15.2.4 客户端调用

public class Client {
public static void main(String[] args) {
// 针对抽象层编程, 具体类名 Proxy 可以在配置文件写入
// 该代码等价于 Subject subject = new Proxy();
Subject subject = (Subject) XMLUtil.getBean(); // 通过代理类访问真实主题
subject.request();
}
}

15.3 远程代理

Remote Proxy: 远程代理使得客户端可以访问远程主机上的对象并调用其方法。

远程代理示意图如下所示:代理对象负责接收客户端请求,并通过网络通信访问远程对象,代理对象对于客户端是透明的。

15.3.1 Java RMI

Method Invocation是Java对象间通信最基本的方法,对于同一虚拟机内的对象通信,简单的方法调用即可实现。而对于位于不同主机上的虚拟机中的对象之间通信,则需要一套远程方法调用的机制,帮助我们获得远程主机上对象的引用,并在自己的虚拟机中像使用自己的对象一样使用它。

RMI 允许我们调用远程对象上的方法,可以将 Java 对象作为参数传递,并获得 Java 对象作为返回值。因此 RMI 需要使用对象序列化,在网络上传输对象,必要时还可以使用动态类加载和安全管理器安全地传输 Java 对象。

15.3.2 Stubs and skeletons

Java RMI 主要工作流程如下所示:

Stub: 被称为 ”存根“,保存在客户端,即代理模式中出现的代理类,其负责与远程对象通信

Skeletons: 被称为 ”骨架“,保存在服务端,用于接收 Stub 发送过来的请求,调用服务端对象的方法,并将返回值发送给 Stub

15.3.3 Java RMI Server

Java RMI 服务端主要是构建一个可以通过网络传输的结果类和一个可以被远程访问的目标类,同时需要将该目标类的实例对象注册到 RMI Registry

// 自定义远程接口, 需要继承自 Remote
public interface RemoteObject extends Remote {
public String remoteHello() throws RemoteException;
} /**
* 这个例子没用构建结果类,直接使用 String 类(可序列化)
* 如果要自定义对象结果类,只需要实现 Serializable 接口即可
*/
public class RemoteObjectImpl extends UnicastRemoteObject implements RemoteObject {
// 空的构造方法,不能使用默认构造,因为需要抛出异常
protected RemoteObjectImpl() throws RemoteException {} public String remoteHello() throws RemoteException() {
// 业务逻辑代码
// return String 对象
}
} // 注册
public class RMIServer {
public static void main(String[] args) throws RemoteException, AlreadyBoundException, MalformedURLException {
try {
Registry registry = LocateRegistry.createRegistry(1900); //实例化远程对象类
RemoteObject remoteObject = new RemoteObjectImpl();
//通过 Naming.bind()方法绑定别名与远程对象
Naming.bind("rmi://127.0.0.1:1900/remoteService", remoteObject);
}
catch(Exception e) {
e.printStackTrace();
}
}
}

15.3.4 Java RMI Client

public class RMIClient {
public static void main(String[] args) throws RemoteException, NotBoundException {
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 1900); //打印注册中心的远程对象列表
System.out.println(Arrays.toString(registry.list())); //通过别名获取远程对象代理 stub 并调用远程对象的方法
RemoteObject stub = (RemoteObject) registry.lookup("remoteService");
System.out.println(stub.remoteHello());
}
}

15.4 动态代理

在传统的代理模式中,客户端通过代理对象调用真实主题中的方法。这种情况下,代理类和真实主题类都事先存在,且代理方法也明确指定。每个代理类经过编译之后生成字节码文件,其实现的接口和代理的方法均被固定,这种代理模式被称为静态代理

静态代理扩展非常不便,无论是需要给不同的主题增加代理还是给同一主题代理不同方法,都需要增加新的代理类,或者修改原代码(不符合开闭原则)。

动态代理(Dynamic Proxy)技术可以让系统在运行时根据需要动态创建代理类,使同一个代理类能够代理多个不同的真实主题类和不同方法。

15.4.1 动态代理实现

  1. 构建两个不同的真实主题接口

    public interface SubjectA {
    public Boolean proxyMethod1(String s); public String proxyMethod2(String s);
    } public interface SubjectB {
    public String proxyMethod1(String s);
    }
  2. 构建具体主题类

    public class ConcreteSubjectA implements SubjectA{
    @Override
    public Boolean proxyMethod1(String s) {
    // 业务逻辑代码
    return true;
    } @Override
    public String proxyMethod2(String s) {
    String res = s + "Call SubjectA proxy method2 success!";
    return res;
    }
    } public class ConcreteSubjectB implements SubjectB{
    @Override
    public String proxyMethod1(String s) {
    String res = s + "Call SubjectB proxy method1 success!";
    return res;
    }
    }
  3. 构建请求处理程序,需要实现 InvocationHandler 接口

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method; public class DynamicProxyHandler implements InvocationHandler{
    // object: 需要动态创建的真实主题对象
    private Object object; public DynamicProxyHandler() {} public DynamicProxyHandler(Object object) {
    this.object = object;
    } /**
    * 实现 InvocationHandler 的 invoke() 方法
    * @param proxy: 代理对象
    * @param method: 代理方法
    * @param args: 代理方法参数列表
    */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    beforeInvoke(); 调用真实主题中定义的方法
    Object res = method.invoke(object, args); afterInvoke();
    return res;
    } // 织入一些调用前和调用后的处理逻辑, 可以为客户端提供额外的服务
    public void beforeInvoke() {
    // 真实主题代理方法调用前
    // 逻辑代码
    } public void afterInvoke() {
    // 真实主题代理方法调用后
    // 逻辑代码
    }
    }
  4. 客户端调用

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Proxy; public class Client {
    public static void main(String[] args) {
    InvocationHandler invocationHandler = null; SubjectA subjectA = new ConcreteSubjectA();
    SubjectB subjectB = new ConcreteSubjectB(); // 创建请求转发到的处理程序
    invocationHandler = new DynamicProxyHandler(subjectA);
    SubjectA proxyA = null; /**
    * 通过反射创建代理类实例对象
    * @param loader: 代理类的加载器
    * @param interfaces: 代理类所实现的接口列表
    * @param handler: 客户端请求转发到的处理程序
    */
    proxyA = (SubjectA)Proxy.newProxyInstance(SubjectA.class.getClassLoader(), new Class[]{SubjectA.class}, invocationHandler);
    String res = proxyA.proxyMethod2("Hi ");
    System.out.println(res); invocationHandler = new DynamicProxyHandler(subjectB);
    SubjectB proxyB = null;
    proxyB = (SubjectB)Proxy.newProxyInstance(SubjectB.class.getClassLoader(), new Class[]{SubjectB.class}, invocationHandler);
    res = proxyB.proxyMethod1("Hi ");
    System.out.println(res);
    }
    }

15.5 代理模式优/缺点

代理模式使用场景:Java RMI (远程代理), Spring AOP(动态代理)

代理模式的优点主要如下:

  • 客户端可以针对抽象层编程,增加和更换代理类不需要修改原代码,符合开闭原则
  • 远程代理为位于不同地址空间对象的访问提供实现机制,将耗时耗资源的操作移至性能更好的远程主机,提高系统的运行效率
  • 动态代理让系统根据需求动态创建代理类,且可以让同一个代理类代理多种不同的方法,相较于静态代理,显著降低了系统中类的个数

代理模式的缺点主要如下:

  • 一些代理模式可能会需要复杂的实现过程,如远程代理
  • 一些代理模式可能会造成请求的处理速度变慢,如保护代理

Java设计模式 —— 代理模式的更多相关文章

  1. Java设计模式-代理模式之动态代理(附源代码分析)

    Java设计模式-代理模式之动态代理(附源代码分析) 动态代理概念及类图 上一篇中介绍了静态代理,动态代理跟静态代理一个最大的差别就是:动态代理是在执行时刻动态的创建出代理类及其对象. 上篇中的静态代 ...

  2. JAVA 设计模式 代理模式

    用途 代理模式 (Proxy) 为其他对象提供一种代理以控制对这个对象的访问. 代理模式是一种结构型模式. 结构

  3. Java设计模式の代理模式

    目录  代理模式 1.1.静态代理   1.2.动态代理 1.3.Cglib代理 代理模式 代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是 ...

  4. Java设计模式 - 代理模式

    1.什么是代理模式: 为另一个对象提供一个替身或占位符以访问这个对象. 2.代理模式有什么好处: (1)延迟加载 当你需要从网络上面查看一张很大的图片时,你可以使用代理模式先查看它的缩略图看是否是自己 ...

  5. Java设计模式—代理模式

    代理模式(Proxy Pattern)也叫做委托模式,是一个使用率非常高的模式. 定义如下:     为其他对象提供一种代理以控制对这个对象的访问. 个人理解:        代理模式将原类进行封装, ...

  6. Java设计模式——代理模式实现及原理

    简介 Java编程的目标是实现现实不能完成的,优化现实能够完成的,是一种虚拟技术.生活中的方方面面都可以虚拟到代码中.代理模式所讲的就是现实生活中的这么一个概念:中介. 代理模式的定义:给某一个对象提 ...

  7. Java设计模式-代理模式(Proxy)

    其实每个模式名称就表明了该模式的作用,代理模式就是多一个代理类出来,替原对象进行一些操作,比如我们在租房子的时候回去找中介,为什么呢?因为你对该地区房屋的信息掌握的不够全面,希望找一个更熟悉的人去帮你 ...

  8. Java设计模式--代理模式+动态代理+CGLib代理

    静态代理 抽象主题角色:声明真实主题和代理主题的共同接口. 代理主题角色:代理主题内部含有对真实主题的引用,从而在任何时候操作真实主题对象:代理主题提供一个与真实主题相同的接口,以便在任何时候都可以代 ...

  9. Java设计模式——代理模式

    public interface People { public void work(); } public class RealPeople implements People { public v ...

  10. Java 之 设计模式——代理模式

    设计模式——代理模式 一.概述 1.代理模式 (1)真实对象:被代理的对象 (2)代理对象:代理真实对象的 (3)代理模式:代理对象代理真实对象,达到增强真实对象功能的目的 二.实现方式 1.静态代理 ...

随机推荐

  1. IOS文件下载时,文件名的处理

    string contentType = MimeMapping.GetMimeMapping(name);var isIOS = false; if (Request.UserAgent != nu ...

  2. Docker 环境规划 (Docker安装)

    一.环境规划 支持Java.dotNet.Vue项目构建 二.切换系统镜像源   1.备份      mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.rep ...

  3. github pic test

  4. yapi的一些基本操作

    一.yapi能干什么 强大的接口管理平台 提供mock功能 提供测试功能 项目管理功能 插件齐全 二.yapi的权限 项目权限 操作 游客 项目开发者 项目组长 超级管理员 浏览公开项目与接口 √ √ ...

  5. 关于vmwork中centos7的虚拟机创建

    1.准备vmwork软件和centos7的镜像文件 vmwork软件下载地址https://www.vmware.com/cn/products/workstation-pro/workstation ...

  6. windows远程linux桌面

    TigerVNC是一个高性能.平台无关的虚拟网络计算(Virtual Network Computing,VNC)实现,是一个客户端/服务器应用程序,允许用户在远程机器上启动并与图形应用程序进行交互. ...

  7. CH573 CH582 OTA例程讲解(使用固定库+扩大APP空间)

    例程中提供的两种OTA就不过多介绍了,在BLE目录下有一个PDF专门讲解:WCH蓝牙空中升级(BLE OTA) 方式一是带库升级,整个codeflash分成四个区域,Jump IAP,APP,OTA, ...

  8. android装包

    一.找到对应包体apk 二.数据线连接电脑及手机,弹出USB连接选项并选择传输文件 注:如果未弹出USB连接选项可尝试换根数据线解决 三.点击我的电脑找到本机设备 四.将对应包体文件拖入本机设备 五. ...

  9. 华大单片机HC32L13X软件设计时候要注意的事项

    1.系统启动时默认设置主频为内部4MHz; 2.调试超低功耗程序或者把SWD端口复用为GPIO功能都会把芯片的SWD功能关掉,仿真器将会与芯片失去连接,建议在main函数开始后加上1到2秒的延时,仿真 ...

  10. Java笔记第七弹

    案例:复制Java文件(打印流改进版) import java.io.*; public class Main{ public static void main(String[] args) thro ...