设计模式之代理模式以及动态代理的原理
现实世界
在讲述之前先描述我们的一个生活场景-中介
请求服务有两种方式:
- 直接和服务者交互
- 中介
直接和服务者交互:
利用中介:
现实世界为什么需要中介呢,中介有更多的资源,其责任只是介绍,并不是真正的业务交互者。在现实世界中中介也存在缺点,中间商赚差价。
在代码世界,上述关系能更好的分离需求者和服务者,使职责更加清晰。一个中间者可代理多个类,拓展性比较好。
那么在代码世界如何实现上述关系呢。
代码世界
代理模式类图:
对上述图进行解析:
Interface
:抽象接口。
Client
:服务调用者,直接调用ProxyClass
,ProxyClass
会调用RealClass
ProxyClass
:中介,实现Interface
RealClass
:真实服务提供者,实现Interface
下面用代码实现此关系
实现功能:用户去按摩店按摩
静态代理
静态代理则是程序员手动编写的代码.
行为,抽象接口类
按摩接口
public interface Massage {
void massage();
}
中介,按摩店类
实现Massage
,内部声明一个需代理的对象,并在真正服务前后特殊处理
public class Parlor implements Massage{
Massage massage;
Parlor(Massage massage) {
this.massage = massage;
}
@Override
public void massage() {
//服务前
System.out.println("正帮助您安排技师。。。");
massage.massage();
//服务后
System.out.println("服务结束");
}
}
真实服务类:小红技师
实现代理类,并提供服务
public class XiaoHong implements Massage {
@Override
public void massage() {
System.out.println("XiaoHong 为您服务");
System.out.println("服务中。。。");
System.out.println("服务中。。。");
System.out.println("XiaoHong 服务结束");
}
}
客户类:Customer
客人,去按摩店,点小红技师进行服务
public class Customer {
public static void main(String[] args) {
Massage massage = new Parlor(new XiaoHong());
massage.massage();
}
}
运行结果
正帮助您安排技师。。。
XiaoHong 为您服务
。。。
。。。
XiaoHong 服务结束
服务结束
可以看到整个过程都是程序员手动编写,此实现则为静态代理,可在服务前后进行特殊操作处理(切面)。
此种实现存在一个问题,如果说需要代理的功能增多,那么程序员需要不断修改代理类,不便于后期维护。
动态代理
静态代理是程序员自己维护代理类,而动态代理是把代理类交给jdk去维护,jdk自动生成代理类。
代码改进
如果上述代码改为动态代理实现则如下:
public class Customer {
public static void main(String[] args) {
Massage massage = (Massage) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Massage.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("正帮助您安排技师。。。");
method.invoke(new XiaoHong(), null);
System.out.println("服务结束");
return null;
}
});
massage.massage();
//打印结果:
// 正帮助您安排技师。。。
// XiaoHong 为您服务
// 。。。
// 。。。
// XiaoHong 服务结束
// 服务结束
}
}
代理对象是通过Proxy
的newProxyInstance
来生成的,有对象即一定有类,对象一定是通过类创建的。
原理解析
源码为jdk11
Proxy#newProxyInstance
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) {
Objects.requireNonNull(h);
Class<?> caller = System.getSecurityManager() == null ? null : Reflection.getCallerClass();
//自动生成代理类,并获取代理类的构造
Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
return newProxyInstance(caller, cons, h);
}
Proxy#getProxyConstructor
private static Constructor<?> getProxyConstructor(Class<?> caller, ClassLoader loader, Class<?>... interfaces) {
...
//对类的构造进行缓存处理
return (Constructor)proxyCache.sub(intf).computeIfAbsent(loader, (ld, clv) -> {
//获取类,并获取类的构造进行返回
return (new Proxy.ProxyBuilder(ld, (Class)clv.key())).build();
});
...
}
本篇文章不分析缓存,只考虑代理类和代理对象是怎么生成的。
ProxyBuilder#build
Constructor<?> build() {
//获取代理类
Class proxyClass = defineProxyClass(this.module, this.interfaces);
...
//构造
final Constructor cons;
//获取构造,Proxy.constructorParams = new Class[]{InvocationHandler.class};构造的参数为InvocationHandler类
cons = proxyClass.getConstructor(Proxy.constructorParams);
...
//设置安全性
cons.setAccessible(true);
return cons;
}
ProxyBuilder#defineProxyClass
private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
...
//生成class文件的字节数组
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, (Class[])interfaces.toArray(Proxy.EMPTY_CLASS_ARRAY), accessFlags);
...
//获取Class,native方法不再分析
Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile, 0, proxyClassFile.length, loader, (ProtectionDomain)null);
...
return pc;
}
jdk
通过上述方法生成代理类。我们可以将byte
数组输出,查看生成的文件。
类似上面方法,我们借助generateProxyClass
方法输出byte
数组
private static void creatProxy() throws Exception {
String name = "Proxy0.class";
//生成代理指定接口的Class数据
File file = new File("D:\\" + name);
file.createNewFile();
byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Massage.class});
FileOutputStream fos = new FileOutputStream(file);
fos.write(bytes);
fos.close();
}
系统生成的类,为方便观察,在Massage
接口类加入了washFoot
方法。
public final class Proxy0 extends Proxy implements Massage {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m4;
private static Method m0;
//构造,生成的对象调用了此构造
public Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
//执行方法时,会把参数,当前方法,当前对象传给InvocationHandler
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void massage() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void washFoot() throws {
try {
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
//上述所有方法都调用构造传入的参数的invoke
//当类被加载时则调用下面代码块,拿到对象的默认方法和接口要实现的方法
static {
try {
//默认方法
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
//接口要实现的方法
m3 = Class.forName("com.hbsd.demo1.Massage").getMethod("massage");
m4 = Class.forName("com.hbsd.demo1.Massage").getMethod("washFoot");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
当通过上述类生成的对象调用方法时,默认调用构造传入InvocationHandler
参数的invoke
方法。
InvocationHandler
参数是之前newProxyInstance
的第三个参数,invoke
则也是执行的此参数的invoke
,返回Proxy#newProxyInstance的最后一行return newProxyInstance(caller, cons, h);
Proxy#newProxyInstance
private static Object newProxyInstance(Class<?> caller, Constructor<?> cons, InvocationHandler h) {
//h为传入的InvocationHandler,cons是获取的只需要一个参数InvocationHandler的构造
...
return cons.newInstance(h);
...
}
InvocationHandler.invoke
InvocationHandler
是之前调用newProxyInstance
时,new
的匿名内部类
在回过头分析此匿名内部类的invoke
方法
//proxy,当前的代理对象,在invoke里调用此对象的方法都将导致栈溢出,此对象的所有方法都会再次执行invoke。
//method,当前调用的方法
//当前调用方法传入的参数
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("正帮助您安排技师。。。");
//方法调用前 切面
method.invoke(new XiaoHong(), null);
//方法调用后 切面
System.out.println("服务结束");
return null;
}
可在方法调用前后添加切面
总结
代理模式:
- 隔离用户和真正服务者,职责隔离。
- 面向切面编程,可以在方法执行前和执行后加入特殊的处理。
静态代理:
程序员自己编写,随着后续代理功能的增多,需要不断对代理类进行维护。
动态代理:
由jdk自动退生成的类,由jdk帮助程序员维护,降低成本。
✨ 原 创 不 易 , 还 希 望 各 位 大 佬 支 持 一 下 \textcolor{blue}{原创不易,还希望各位大佬支持一下} 原创不易,还希望各位大佬支持一下
👍 点 赞 , 你 的 认 可 是 我 创 作 的 动 力 ! \textcolor{green}{点赞,你的认可是我创作的动力!} 点赞,你的认可是我创作的动力!
⭐️ 收 藏 , 你 的 青 睐 是 我 努 力 的 方 向 ! \textcolor{green}{收藏,你的青睐是我努力的方向!} 收藏,你的青睐是我努力的方向!
✏️ 评 论 , 你 的 意 见 是 我 进 步 的 财 富 ! \textcolor{green}{评论,你的意见是我进步的财富!} 评论,你的意见是我进步的财富!