深入浅出理解—代理模式
众所周知,Java中存在着23种设计模式,今天本少爷要来介绍的就是其中一个比较常见的设计模式-代理模式。
主要通过以下几个问题来解释代理模式:
- 什么是代理模式?
- 代理模式有什么好处?
- 在Java中是如何实现代理模式的?
- 代理模式的实际应用场景有哪些?
那接下来我们来一步一步解开代理模式的神秘面纱。
代理模式
我们知道在面向对象的世界中,万物都是对象,代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
通俗来讲,代理模式就是我们生活中的中介。比如,张三按照小卡片上的电话打过去寻求服务,一般不是由本人,可能是一个成年雄性接听电话,然而真正做事情的可能是另一个小姐姐。
通过使用代理模式,主要有以下两个好处:
1) 通过引入代理对象来间接访问目标对象,防止直接访问目标对象给系统带来的不必要复杂性;
2) 通过代理对象对访问进行控制
在Java中主要是通过接口的方式来实现代理模式的:
主要有以下三个角色:
1) 抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个 接口
2) 真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑在此。
3) 代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理。
代理模式中又分为了 静态代理 和 动态代理,两者的区别主要是代理对象的生成方式。
静态代理:代理对象的生成的是在JVM编译.java文件时生成一个.class文件,存在于硬盘中。

1) 首先定义一个抽象接口,也就是前面提到的 抽象角色
public interface Drive {
//所需要的代理方法
void drive();
}
2) 接下来定义两个真实角色类,让他去实现刚才的接口,在接口中做这个角色所需要的任务
public class Daslen implements Drive{
//真实角色实现代理接口,进行真实操作
@Override
public void drive() {
System.out.println("达乐森——>开车很流弊");
}
}
-------------------------------------------------------------------------------
public class Eason implements Drive{
@Override
public void drive() {
System.out.println("依乐森——>开车很奔放");
}
}
3)定义一个代理类,通过实现抽象接口来持有真实角色,同时在执行真实角色的动作 前/后 都可以添加自己所需要的操作
public class Agent implements Drive{
private Drive drive;
public Agent(Drive drive) {
this.drive = drive;
}
//代理前动作
private void beforeDrive(){
}
//代理后动作
private void afterDrive(){
}
@Override
public void drive() {
//通过代理类可以在实际操作代理对象之前和之后进行操作
beforeDrive();
this.drive.drive();
afterDrive();
}
}
4)进行调用
public class Client {
//静态代理
public static void main(String[] args) {
//真实角色->达乐森
Drive daslen = new Daslen();
//真实角色->依乐森
//当实现同一个代理接口,代理功能的时候,静态代理可以实现一个 实际代理类
//代理 具有相同代理功能的多个真实角色代理类
Drive Eason = new Eason();
//代理角色->Daslen
Agent agentDaslen = new Agent(daslen);
//通过代理角色操作真实角色
agentDaslen.drive();
//代理角色->Eason
Agent agentEason = new Agent(daslen);
agentEason.drive();
}
}
而当需要实现新的功能时,此时就需要去添加新的代理类对象,当这种情况出现成千上万的时候仍然使用静态代理的方式显然是不可取的,此时就需要通过 动态代理 ,通过反射从内存中去生成一个我们所需要的代理对象类:
Java中为我们提供了一系列的API,首先来看一下是如何使用的吧:
1)再定义一个接口,体现出与静态代理的区别:
public interface Message {
//所需要的代理方法
void message();
}
2) 动态代理的的实现
//动态代理
public static void main(String[] args) throws Exception {
proxy();
//真实角色
final Daslen daslen = new Daslen();
final Eason eason = new Eason();
//动态代理 ->通过动态获取实际代理对象
Object o = Proxy.newProxyInstance(Client.class.getClassLoader(),
new Class[]{Drive.class, Message.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
if (name.equals("drive")) {
method.invoke(daslen, args);
} else if (name.equals("message")) {
method.invoke(eason, args);
}
return null;
}
});
//通过代理对象操作实际对象
((Drive) o).drive();
((Message) o).message();
}
其中有一个 InvocationHandler ,可以理解为一个监听,类似于点击监听(onClickListenen),通过这个监听可以知道调用了真实角色的哪些方法,当有传参数时也可以通过这个Handler的 invoke 方法获取到。
而 Proxy.newProxyInstance生成的对象就是我们传入的 new Class[]{Drive.class, Message.class} 的代理类对象。
我们通过进入到 Proxy.newProxyInstance 的 newProxyInstance 方法中去找找是如何生成一个代理类对象的。
1)首先将传入的接口对象复制一次,然后调用 getProxyClass0 方法。
public static Object newProxyInstance(ClassLoader var0, Class<?>[] var1, InvocationHandler var2) throws IllegalArgumentException {
Objects.requireNonNull(var2);
//复制传入的接口对象
Class[] var3 = (Class[])var1.clone();
SecurityManager var4 = System.getSecurityManager();
if (var4 != null) {
checkProxyAccess(Reflection.getCallerClass(), var0, var3);
}
//生成代理类
Class var5 = getProxyClass0(var0, var3);
try {
if (var4 != null) {
checkNewProxyPermission(Reflection.getCallerClass(), var5);
}
//通过反射生成构造方法
final Constructor var6 = var5.getConstructor(constructorParams);
if (!Modifier.isPublic(var5.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
var6.setAccessible(true);
return null;
}
});
}
//生成接口对象实例
return var6.newInstance(var2);
} catch (InstantiationException | IllegalAccessException var8) {
throw new InternalError(var8.toString(), var8);
} catch (InvocationTargetException var9) {
Throwable var7 = var9.getCause();
if (var7 instanceof RuntimeException) {
throw (RuntimeException)var7;
} else {
throw new InternalError(var7.toString(), var7);
}
} catch (NoSuchMethodException var10) {
throw new InternalError(var10.toString(), var10);
}
}
当调用了 getProxyClass0 方法时,会调用到ProxyGenerator.generateProxyClass,通过以下这段代码可以查看通过内存的生成的代理类是什么
private static void proxy() throws Exception {
String name = Drive.class.getName() + "$Proxy0";
//生成代理指定接口的Class数据
byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Drive.class});
FileOutputStream fos = new FileOutputStream("lib/" + name + ".class");
fos.write(bytes);
fos.close();
}
以下是生成的代理类对象
public final class Drive$Proxy0 extends Proxy implements Drive {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public Drive$Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
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 drive() throws {
try {
super.h.invoke(this, m3, (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);
}
}
static {
try {
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.example.lib.dynamic_agency.Drive").getMethod("drive");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
从以上可以看得出来里面除了有我们传入的 Drive 方法,同时还有equals,toString以及 hashCode这三个方法,这间接也 说明了所有类都是继承自 Object类。
有一点值得注意的是调用这里面所有的方法,都会调用到 h 的 invoke 方法:

而这个 h 就是我们新建的传入的 InvocationHandler,因此,这样才能监听到调用了代理对象执行了哪个方法。
应用场景
说完对动态代理,下面来说一下代理模式的应用场景吧。
相信我们在开发的过程都会去请求网络,如果我们采用 Java 自带的网络框架去请求网络就会显得很臃肿,因此我们一般会选用一些网络请求框架进行封装,最后进行调用,整个框架如图:

如果在这种架构下需要更换网络框架会发生什么问题呢?
虽然对每个框架都进行了封装,但是每个框架的入参以及其他返回参数等都不一样,这就导致了工具层的 post 方法需要做出对应的修改,同时应用层在调用的地方也需要做出相应的修改,这显然是不符合开闭原则的。
而代理模式就可以很好的解决这个问题,在上面这种架构下的工具层下面在加多一层代理层,也就是一个网络请求接口,而在工具层中的 post 方法也实现了这个接口,在构造方法中传入一个代理对象,而不同的框架也去实现这个接口,在实现方法中去做不同的请求方法,架构如下图:

应用层也去实现代理层的接口,构造方法传入的代理层接口对象,在接口的实现方法中调用 传入的代理层接口对象 的post方法
这就印证了开闭原则
以上就是本篇文章的全部内容,希望对你有所帮助~