定义
代理类在程序运行时创建的代理方式被成为动态代理。 也就是说,代理类并不需要在Java代码中定义,而是在运行时动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。
这么说比较抽象,我们以上文的静态代理为例,在执行doSomething方法前,打印真实类名称。试想,如果Subject接口有10个方法,每个方法执行前都要打印真实类名,那么我们要写一堆重复代码。通过使用动态代理,我们可以做一个“统一指示”,从而对所有代理类的方法进行统一处理,而不用逐一修改每个方法。下面我们来具体介绍下如何使用动态代理方式实现我们的需求。
示例
接口类
public interface Subject {
public void doSometing();
}
委托类
public class RealSubject implements Subject{
@Override
public void doSometing() {
System.out.println("I'm workding");
}
}
动态代理类
在使用动态代理时,我们需要定义一个位于代理类与委托类之间的中介类,也可以叫动态代理类,这个类被要求实现InvocationHandler接口:
public class DynamicProxy implements InvocationHandler{
//被代理的实例
private Object obj;
public DynamicProxy(Object _object){
this.obj = _object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(obj.getClass().getName());
Object result = method.invoke(obj, args);
return result;
}
}
InvocationHandler接口是JDK提供的动态代理接口,对被代理类的方法进行代理。这个接口的定义如下:
public interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args);
}
当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。。通过上述代码,就实现了,在执行委托类的所有方法前,打印委托类名。
4. 场景类
看看如何通过动态代理类实现动态代理:
public class Client {
public static void main(String args[]){
//我们要代理的真实对象
Subject subject = new RealSubject();
//创建动态代理类实例
InvocationHandler handler = new DynamicProxy(subject);
//获取类加载器
ClassLoader cl = subject.getClass().getClassLoader();
//动态产生一个代理类
Subject proxy = (Subject) Proxy.newProxyInstance(cl, subject.getClass().getInterfaces(), handler);
//通过代理类,执行doSomething方法;
proxy.doSometing();
}
}
执行结果如下:
model.proxy.RealSubject
I'm workding
总结
总结一下,一个典型的动态代理可分为以下四个步骤:
- 创建接口类
- 创建委托类
- 通过实现InvocationHandler接口创建自己的中介类
- 通过场景类,动态生成代理类
如果只是想用动态代理,看到这里就够了。但如果想知道为什么通过proxy对象,就能够执行中介类的invoke方法,以及生成的proxy对象是什么样的,可以继续往下看。
源码分析(JDK7)
下面代码,生成了一个RealSubject的代理类
//动态产生一个代理类
Subject proxy = (Subject) Proxy.newProxyInstance(cl, subject.getClass().getInterfaces(), handler);
我们可以猜测上面代码做了以下事情:
根据传入的第二个参数interfaces动态生成一个类,它实现interfaces中的接口,该例中即Subject接口的doSometing方法。假设动态生成的类为$Proxy0。
通过传入的第一个参数classloder将刚生成的$Proxy0类加载到jvm中。
利用第三个参数,调用$Proxy0的$Proxy0(InvocationHandler)构造函数 创建$Proxy0的对象
那么它到底是如何实现的呢?我们可以通过源码一窥究竟。
查看Proxy类的newProxyInstance方法,主要业务逻辑如下
//生成代理类class,并加载到jvm中
Class<?> cl = getProxyClass0(loader, interfaces);
//获取代理类参数为InvocationHandler的构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
//生成代理类,并返回
return newInstance(cons, ih);
getProxyClass0方法
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
在我们的Proxy类中有个属性proxyClassCache,这是一个WeakCache类型的静态变量。它指示了我们的类加载器和代理类之间的映射。所以proxyClassCache的get方法用于根据类加载器来获取Proxy类,如果已经存在则直接从cache中返回,如果没有则创建一个映射并更新cache表。
我们看一下创建一个Proxy类的流程
V value = supplier.get();
Factory是一个Supplier的实现类,get方法如下:
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
调用ProxyClassFactory的apply方法,生成proxy类的核心代码如下:
//Generate the specified proxy class.
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
至此我们就获取到了委托类对应的代理类型,接着我们通过该类的getConstructor方法获取该代理类的构造器,并传入InvocationHandler.class作为参数,生成代理类。
查看动态生成的代理类
通过上面的简单分析,我们知道了代理类的创建流程。但创建出来的代理类到底是什么样子的呢?
我们可以通过ProxyGenerator.generateProxyClass
创建一个看看:
public static void main(String[] args) throws IOException {
//注意是RealSubject
byte[] classFile = ProxyGenerator.generateProxyClass("TestProxyGen", RealSubject.class.getInterfaces());
File file = new File("D:/aaa/TestProxyGen.class");
FileOutputStream fos = new FileOutputStream(file);
fos.write(classFile);
fos.flush();
fos.close();
}
通过反编译工具查看生成的class文件内容如下:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import model.proxy.Subject;
public final class TestProxyGen extends Proxy
implements Subject
{
private static Method m1;
private static Method m0;
private static Method m3;
private static Method m2;
public TestProxyGen(InvocationHandler paramInvocationHandler)
throws
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
throws
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
public final int hashCode()
throws
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
public final void doSometing()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
public final String toString()
throws
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("model.proxy.Subject").getMethod("doSometing", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
}
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
这个类实现了doSometing
方法。通过这里,就能理解为什么调用代理类,会执行中介类的invoke方法了:
public final void doSometing()
throws
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (RuntimeException localRuntimeException)
{
throw localRuntimeException;
}
catch (Throwable localThrowable)
{
}
throw new UndeclaredThrowableException(localThrowable);
}