为其他对象提供一个代理对象,从而让其他对象操作该代理对象来间接完成对某个指定对象的访问,这个就是代理模式。
而在 Java 中,针对代理对象的创建方式,又划分了 静态代理 和 动态代理
静态代理
代理模式的一个特征就是 代理类和指定对象有着同样的方法调用
,因此一般都是通过接口来设计代理模式。
-
首先定义个接口:
public interface Subject { void request(); void cancel(); }
-
定义接口的实现类:
public class RealSubject implements Subject { @Override public void request() { System.out.println(getClass().getCanonicalName() + "发出了请求"); } @Override public void cancel() { System.out.println(getClass().getCanonicalName() + "取消了请求"); } }
-
定义一个代理对象:
public class ProxySubject implements Subject { private Subject mSubject; public ProxySubject(Subject subject) { this.mSubject = subject; } @Override public void request() { if (mSubject != null) { mSubject.request(); } } @Override public void cancel() { if (mSubject != null) { mSubject.cancel(); } } }
-
最后直接调用即可:
public class Client { public static void main(String[] args) { RealSubject realSubject = new RealSubject(); ProxySubject proxySubject = new ProxySubject(realSubject); proxySubject.request(); proxySubject.cancel(); } }
以上就是简单的静态代理模式,通过上面的代码我们可以知道代理模式可以帮我们解决 Java 中让人讨厌的空指针问题。
正确来说,我们可以通过代理模式来在执行指定对象的方法之前,添加各种私货,这也正是 AOP(面向切面编程 ) 的原理,我们能在 AOP 的一个切点执行之前或之后加入一些操作,就是因为这些切点其实都给代理了,在代理的过程加入一些我们期待的操作,如上面的判断非空的操作。
除此之外,代理模式能很好地隔离各个代码模块,同时代理模式还可以延迟操作对象的拷贝,及时断开和操作对象的关系,如果在某个操作对象是非常耗费资源的,那么我们也应该使用代理模式去操作它,减少一些不要的开销。
动态代理
静态代理是代码编程成 class 时 生成代理对象,而动态代理则是代码运行时在内存里面生成一个代理对象,也就是 proxy 对象。
在 Java 的动态代理机制中,有两个关键的 API 是必须知道的,一个是 InvocationHandler(Interface)
、另一个则是 Proxy(class)
先看一下 InvocationHandler 这个接口,因为生成一个动态代理类这个是不可缺的
public interface InvocationHandler {
/**
* @param proxy 动态生成的代理对象
* @param method 我们所要调用操作对象的某个方法的Method对象
* @param args 调用操作对象某个方法时接受的参数
* @return 操作对象的方法返回的值或null
* @throws Throwabl 可能遇到的异常
*/
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
搞明白接口方法的参数后,那么就好办了:
public class SubjectInvocationHandler implements InvocationHandler {
private Subject mSubject;
public SubjectInvocationHandler(Subject subject) {
this.mSubject = subject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//这里完成操作方法调用的反射处理
return method.invoke(mSubject, args);
}
}
然后再去看看 Proxy 这个类,它的作用就是用来动态创建一个代理对象的类,通常我们使用 newProxyInstance
这个方法来创建代理对象:
/**
* @param loader 定义了由哪个ClassLoader对象来对生成的代理对象进行加载
* @param interfaces 一个Interface对象的Class数组,表示生成的代理对象需要实现那些接口
* @param h 一个InvocationHandler对象,表示的是当这个动态代理对象在调用方法的时候
* ,会关联到哪一个InvocationHandler对象上
* @return 实现了指定接口的代理对象
*/
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
把这个 API 也搞明白后,接下来也好办了:
public class Client {
public static void main(String[] args) {
Subject subject = new RealSubject();
//生成一个InvocationHandler
SubjectInvocationHandler handler = new SubjectInvocationHandler(subject);
//生成动态代理对象
Subject proxySubject = (Subject)Proxy.newProxyInstance(subject.getClass()
.getClassLoader(), new Class[]{Subject.class}, handler);
proxySubject.request();
proxySubject.cancel();
}
}
看一下控制台的输出:
proxy.RealSubject发出了请求
proxy.RealSubject取消了请求
可以看到,动态代理可以很方便的对操作对象的方法进行统一的处理,而不用修改每个代理对象中的方法,同时也是一个解耦的好方法,比如著名的网络请求框架 retrofit
的核心模式就是动态代理。这里虽然用到不少反射,但其实在反射次数不多的情况下,对性能影响其实是可以忽略不计的。
应该有人会疑惑到底是怎么生成代理对象的,为什么代理对象执行的方法都会通过 InvocationHandler 中的invoke 方法来执行,让我们带着疑问去看下源码
动态代理的原理分析
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces
,InvocationHandler h)
{
//得到全部的接口
final Class<?>[] intfs = interfaces.clone();
....
//生成实现指定接口的代理对象的Class对象
Class<?> cl = getProxyClass0(loader, intfs);
....
//获取代理对象的构造方法对象
final Constructor<?> cons = cl.getConstructor(constructorParams);
....
//初始化代理对象
return cons.newInstance(new Object[]{h});
}
从上面的代码就可以知道,这里是直接生成代理对象的 Class 对象,然后再进行得到我们需要的代理对象,这里就不具体分析是如何生成 Class 对象了,知道它是存放在内存之中就足够了,我们可以通过 JDK 自带的方法将它转为 byte 数组,然后打印到本地:
byte[] buffer = ProxyGenerator.generateProxyClass(subject.getClass().getSimpleName()
, subject.getClass().getInterfaces());
try {
FileOutputStream fos = new FileOutputStream("out/SubjectProxy.class");
fos.write(buffer);
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
运行就可以看到生成的 class 文件了:
通过 IDE 的反编译可以很轻松看到里面的代码:
public final class RealSubject extends Proxy implements Subject {
private static Method m1;
private static Method m4;
private static Method m2;
private static Method m3;
private static Method m0;
public RealSubject(InvocationHandler var1) throws {
super(var1);
}
//发射生成接口的方法对象
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m4 = Class.forName("proxy.Subject").getMethod("cancel");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("proxy.Subject").getMethod("request");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
public final void cancel() throws {
try {
//这里调用了InvocationHandler的方法,再联想到刚才实现的InvocationHandler对象
//简单明了
super.h.invoke(this, m4, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void request() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
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);
}
}
}
动态代理直接为我们生成了一个 RealSubject 的代理类(好想吐槽这名字),这个类文件是存放在内存中的,我们运行代码创建代理对象就是通过反射获得这个类的构造方法,然后创建的代理实例。
而 InvocationHandler 其实就是个中间人,它真正持有了操作对象的实例,通过组合的方式完成了代理对象对操作对象的方法调用,也就是上面看到了代理对象 RealSubject 里面通过调用 InvocationHandler 的 invoke 方法最终完成了对操作对象的方法调用。