欢迎访问 生活随笔!

生活随笔

当前位置: 首页 >

Java设计模式-代理模式 理论代码相结合

发布时间:2025/3/19 32 豆豆
生活随笔 收集整理的这篇文章主要介绍了 Java设计模式-代理模式 理论代码相结合 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

继建造者模式后,又继续开启了代理模式啦。😁
Java设计模式系列-代理模式。你我一起坚持,让我们一起加油,还不会就一起学一学,会了咱就复习一下吧。😁
很喜欢一句话:“八小时内谋生活,八小时外谋生存”

你好,如果喜欢,请一起坚持!! 望别日与君相见时,君已有所成。

共勉


一张旧图,恍惚间想到旧人

设计模式系列

  • Java设计模式-单例模式
  • Java设计模式-工厂模式(1)简单工厂模式
  • Java设计模式-工厂模式(2)工厂方法模式
  • Java设计模式-工厂模式(3)抽象工厂模式
  • Java设计模式-建造者模式
  • 持续更新中…

Java设计模式-代理模式 理论代码相结合

    • 一、前言
      • 1)概述:
      • 2)结构:
      • 3)静态代理和动态代理
    • 二、静态代理
      • 2.1、小案例
      • 2.2、代码
    • 三、动态代理
      • 1、代码
      • 2、动态代理分析
      • 3、分析流程
    • 四、自言自语

一、前言

在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点买。又如找女朋友、找保姆、找工作等都可以通过找中介完成。

在软件设计中,使用代理模式的例子也很多,例如,要访问的远程对象比较大(如视频或大图像等),其下载要花很多时间。还有因为安全原因需要屏蔽客户端直接访问真实对象,如某单位的内部数据库等。

1)概述:

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。动态代理又有JDK代理和CGLib代理两种。

2)结构:

代理(Proxy)模式分为三种角色:

  • 抽象角色: 通过接口或抽象类声明真实主题和代理对象实现的业务方法。
  • 真实角色: 实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。
  • 代理(Proxy)类 : 实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。

3)静态代理和动态代理

根据代理的创建时期,代理模式分为静态代理和动态代理。

  • 静态:由程序员创建代理类或特定工具自动生成源代码再对其编译,在程序运行前代理类的 .class 文件就已经存在了。
  • 动态在程序运行时,运用反射机制动态创建而成

二、静态代理

我们通过一个 客户要去买二手房的经历为例子,以前没有中介的时候,都是直接找到房东去买,现在房东忙着其他的事,没时间搞这个,房产中介就作为一个代理,帮助卖房子,再收手续费。现在我们只需要找房产中介就能搞定这件事情了。

2.1、小案例

先看看图:

2.2、代码

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

public interface SellHouse {/**卖房接口方法*/void sell(); }

Landlord (实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。 )

public class Landlord implements SellHouse{@Overridepublic void sell() {System.out.println("房东出售房子!!");} }

ProxyPoint (代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作)

这里的附加操作就是收手续费啦😁

public class ProxyPoint implements SellHouse{private Landlord landlord=new Landlord();@Overridepublic void sell() {System.out.println("房产中介收取中介费,帮助房东卖房子,!!");landlord.sell();} }

测试:

public class Client {public static void main(String[] args) {ProxyPoint point = new ProxyPoint();point.sell();/***房产中介收取中介费,帮助房东卖房子,!!* 房东出售房子!!*/} }

从上面测试代码中可以看出我们直接访问的是ProxyPoint类对象,也就是说ProxyPoint作为访问对象和目标对象的中介。同时也对sell方法进行了增强(代理点收取一些服务费用)。

现在可以看到,代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类

三、动态代理

例子还是上面那个哈,图就不给啦

接下来我们使用动态代理实现上面案例,先说说JDK提供的动态代理。Java中提供了一个动态代理类Proxy,Proxy并不是我们上述所说的代理对象的类,而是提供了一个创建代理对象的静态方法(newProxyInstance方法)来获取代理对象。

1、代码

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

public interface SellHouse {/**卖房接口方法*/void sell(); }

Landlord (实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。 )

public class Landlord implements SellHouse {@Overridepublic void sell() {System.out.println("房东出售房子!!");} }

ProxyFactory(它是代理类吗?)

public class ProxyFactory {private Landlord landlord = new Landlord();public SellHouse getProxyObject() {/*** 使用Proxy获取代理对象newProxyInstance()方法参数说明:ClassLoader loader : 类加载器,用于加载代理类,使用真实对象的类加载器即可Class<?>[] interfaces : 真实对象所实现的接口,代理模式真实对象和代理对象实现相同的接口InvocationHandler h : 代理对象的调用处理程序*/SellHouse sellHouse=(SellHouse) Proxy.newProxyInstance(landlord.getClass().getClassLoader(),landlord.getClass().getInterfaces(),new InvocationHandler() {/**InvocationHandler中invoke方法参数说明:proxy : 代理对象method : 对应于在代理对象上调用的接口方法的 Method 实例args : 代理对象调用接口方法时传递的实际参数*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("房产中介收取手续费");// 执行真实对象 如果有返回值就将它返回回去Object o = method.invoke(landlord, args);return o;}});return sellHouse;} }

昨天刚学到一招哈

再回到上面那个问题哈。

我们使用了JDK动态代理,ProxyFactory它是代理类?

答案:并不是的。ProxyFactory不是代理模式中所说的代理类,代理类是程序在运行过程中动态的在内存中生成的类

我们可以先在测试代码中打印一下哈。

System.out.println(proxyFactory.getClass()); System.out.println(house.getClass()); /** * 输出 * class com.crush.jdk_proxy.ProxyFactory * class com.sun.proxy.$Proxy0 */

我们可以看到真正动态生成的代理其实是class com.sun.proxy.$Proxy0。这个才是程序运行过程中。

接下来我用我昨天学到的东西,让大家一起看看,~~手法生疏 见谅见谅哈。~~😂

2、动态代理分析

慢慢来哈😁

我们可以通过阿里巴巴开源的 Java 诊断工具(Arthas【阿尔萨斯】Java 诊断工具-下载地址)

Arthas官方文档 (这个的在jdk8环境下用,使用到jdk8中的一个tools.jar的工具)

注意:(如果是其他的版本好像启动不了,我是电脑中有8和11,8没有配置环境变量,然后的话,就一直报错,我就将idea换成jdk8的版本,重新编译了,然后直接cmd在jdk8的环境下启动然后就还是可以)。

注:为方便监控,我在测试方法中加上了一句while(ture){}。

查看代理类的结构:

我们将我们获得的代理类的名字com.sun.proxy.$Proxy0 通过命令 jad来反编译 就可以获得如下数据

[arthas@3012]$ jad com.sun.proxy.$Proxy0 ClassLoader: +-sun.misc.Launcher$AppClassLoader@b4aac2+-sun.misc.Launcher$ExtClassLoader@c21c27Location:/** Decompiled with CFR.** Could not load the following classes:* com.crush.jdk_proxy.SellHouse*/ package com.sun.proxy;import com.crush.jdk_proxy.SellHouse; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException;public final class $Proxy0 extends Proxy implements SellHouse {private static Method m1;private static Method m2;private static Method m3;private static Method m0;public $Proxy0(InvocationHandler invocationHandler) {super(invocationHandler);}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m3 = Class.forName("com.crush.jdk_proxy.SellHouse").getMethod("sell", new Class[0]);m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);return;}catch (NoSuchMethodException noSuchMethodException) {throw new NoSuchMethodError(noSuchMethodException.getMessage());}catch (ClassNotFoundException classNotFoundException) {throw new NoClassDefFoundError(classNotFoundException.getMessage());}}public final boolean equals(Object object) {try {return (Boolean)this.h.invoke(this, m1, new Object[]{object});}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String toString() {try {return (String)this.h.invoke(this, m2, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final int hashCode() {try {return (Integer)this.h.invoke(this, m0, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final void sell() {try {this.h.invoke(this, m3, null);return;}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}} }

去掉无用信息后:

3、分析流程

程序运行过程中动态生成的代理类

//$Proxy0继承了Proxy实现了SellHouse public final class $Proxy0 extends Proxy implements SellHouse {private static Method m3;// 这里用的父类的构造方法 下面有public $Proxy0(InvocationHandler invocationHandler) {super(invocationHandler);}static {m3 = Class.forName("com.crush.jdk_proxy.SellHouse").getMethod("sell", new Class[0]);return;}public final void sell() {// 可以看到这里是真正执行的方法 这里的h 是父类中的 InvocationHandler 成员//invoke: 处理代理实例上的方法调用并返回结果。 当在与其关联的代理实例上调用方法时,将在调用处理程序上调用此方法。this.h.invoke(this, m3, null);return;} }

我们再接着看一下 Proxy关键东西哈。

public class Proxy implements java.io.Serializable {protected InvocationHandler h;protected Proxy(InvocationHandler h) {Objects.requireNonNull(h);this.h = h;} }

现在我们再看一下我们的代理生成类ProxyFactory

public class ProxyFactory {private Landlord landlord = new Landlord();public SellHouse getProxyObject() {SellHouse sellHouse=(SellHouse) Proxy.newProxyInstance(landlord.getClass().getClassLoader(),landlord.getClass().getInterfaces(),new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("房产中介收取手续费");Object o = method.invoke(landlord, args);return o;}});return sellHouse;} }//测试代码 public class Client {public static void main(String[] args) {ProxyFactory proxyFactory = new ProxyFactory();SellHouse house = proxyFactory.getProxyObject();house.sell();} }

执行流程如下:

1. 在测试类中通过代理对象调用sell()方法 2. 根据多态的特性,执行的是代理类($Proxy0)中的sell()方法 3. 代理类($Proxy0)中的sell()方法中又调用了InvocationHandler接口的子实现类对象的invoke方法 4. invoke方法通过反射执行了真实对象所属类(TrainStation)中的sell()方法

GCLB的动态代理就没有继续分析啦, 一些使用稍有不同,不过还是可以去了解的哈。😁

四、自言自语

你卷我卷,大家卷,什么时候这条路才是个头啊。😇(还是直接上天吧)

有时候也想停下来歇一歇,一直做一个事情,感觉挺难坚持的。😁

你好,如果你正巧看到这篇文章,并且觉得对你有益的话,就给个赞吧,让我感受一下分享的喜悦吧,蟹蟹。🤗

如若有写的有误的地方,也请大家不啬赐教!!

同样如若有存在疑惑的地方,请留言或私信,定会在第一时间回复你。

持续更新中

总结

以上是生活随笔为你收集整理的Java设计模式-代理模式 理论代码相结合的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得生活随笔网站内容还不错,欢迎将生活随笔推荐给好友。