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设计模式-代理模式 理论代码相结合的全部内容,希望文章能够帮你解决所遇到的问题。
- 上一篇: 为什么阿里巴巴开发手册中强制要求 POJ
- 下一篇: Java设计模式-装饰器模式 理论代码相