继建造者模式后,又继续开启了代理模式啦。😁 Java设计模式系列-代理模式。你我一起坚持,让我们一起加油,还不会就一起学一学,会了咱就复习一下吧。😁 很喜欢一句话:“八小时内谋生活,八小时外谋生存” 你好,如果喜欢,请一起坚持!! 望别日与君相见时,君已有所成。 共勉
一张旧图,恍惚间想到旧人
设计模式系列:
在有些情况下,一个客户不能或者不想直接访问另一个对象,这时需要找一个中介帮忙完成某项任务,这个中介就是代理对象。例如,购买火车票不一定要去火车站买,可以通过 12306 网站或者去火车票代售点买。又如找女朋友、找保姆、找工作等都可以通过找中介完成。 在软件设计中,使用代理模式的例子也很多,例如,要访问的远程对象比较大(如视频或大图像等),其下载要花很多时间。还有因为安全原因需要屏蔽客户端直接访问真实对象,如某单位的内部数据库等。
为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不适合或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
Java中的代理按照代理类生成时机不同又分为静态代理和动态代理。静态代理代理类在编译期就生成,而动态代理代理类则是在Java运行时动态生成。动态代理又有JDK代理和CGLib代理两种。
代理(Proxy)模式分为三种角色:
根据代理的创建时期,代理模式分为静态代理和动态代理。
我们通过一个 客户要去买二手房的经历为例子,以前没有中介的时候,都是直接找到房东去买,现在房东忙着其他的事,没时间搞这个,房产中介就作为一个代理,帮助卖房子,再收手续费。现在我们只需要找房产中介就能搞定这件事情了。
先看看图:
SellHouse (抽象角色:通过接口或抽象类声明真实主题和代理对象实现的业务方法。)
public interface SellHouse {
/**卖房接口方法*/
void sell();
}
Landlord (实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。 )
public class Landlord implements SellHouse{
@Override
public void sell() {
System.out.println("房东出售房子!!");
}
}
ProxyPoint (代理角色:实现抽象角色,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作)
这里的附加操作就是收手续费啦😁
public class ProxyPoint implements SellHouse{
private Landlord landlord=new Landlord();
@Override
public 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方法)来获取代理对象。
SellHouse (抽象角色:通过接口或抽象类声明真实主题和代理对象实现的业务方法。)
public interface SellHouse {
/**卖房接口方法*/
void sell();
}
Landlord (实现抽象角色,定义真实角色所要实现的业务逻辑,供代理角色调用。 )
public class Landlord implements SellHouse {
@Override
public 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 : 代理对象调用接口方法时传递的实际参数
*/
@Override
public 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
。这个才是程序运行过程中。
接下来我用我昨天学到的东西,让大家一起看看,~~手法生疏 见谅见谅哈。~~😂
慢慢来哈😁
我们可以通过阿里巴巴开源的 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@c21c27
Location:
/*
* 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);
}
}
}
去掉无用信息后:
程序运行过程中动态生成的代理类
//$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() {
@Override
public 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的动态代理就没有继续分析啦, 一些使用稍有不同,不过还是可以去了解的哈。😁
你卷我卷,大家卷,什么时候这条路才是个头啊。😇(还是直接上天吧)
有时候也想停下来歇一歇,一直做一个事情,感觉挺难坚持的。😁
你好,如果你正巧看到这篇文章,并且觉得对你有益的话,就给个赞吧,让我感受一下分享的喜悦吧,蟹蟹。🤗
如若有写的有误的地方,也请大家不啬赐教!!
同样如若有存在疑惑的地方,请留言或私信,定会在第一时间回复你。
持续更新中