先看一个动态代理的小例子
1,先创建一个接口CalOp和实现了这个接口的类CalOpImpl
public interface CalOp {
void show();
}
public class CalOpImpl implements CalOp {
public void show() {
System.out.println("hello");
}
}
2,创建一个拦截器CalOpInterceptor
public class CalOpInterceptor {
public void before() {
System.out.println("CalOpInterceptor->before");
}
public void after() {
System.out.println("CalOpInterceptor->after");
}
}
3,代理类CalOpProxy
public class CalOpProxy implements InvocationHandler {
private Object object;
private CalOpInterceptor interceptor ;
public Object bind(Object object,CalOpInterceptor interceptor) {
this.object = object;
this.interceptor = interceptor;
return Proxy.newProxyInstance(this.object.getClass().getClassLoader(),
this.object.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
interceptor.before();
Object result = method.invoke(this.object, args);
interceptor.after();
return result;
}
}
然后,我们就可以写一个main方法来测试一下
public class JRun {
public static void main(String[] args) {
CalOp calOp = new CalOpImpl();
CalOpInterceptor interceptor = new CalOpInterceptor();
CalOpProxy proxy = new CalOpProxy();
// 创建一个代理对象
CalOp proxyCalOp = (CalOp) proxy.bind(calOp, interceptor);
proxyCalOp.show();
}
}
运行,输出如下。跟我们想要的是一样的
CalOpInterceptor->before
hello
CalOpInterceptor->after
动态代理原理
在JRun的第7行(如下),我们生成了一个代理类对象proxyCalOp。
CalOp proxyCalOp = (CalOp) proxy.bind(calOp, interceptor);
然后通过访问proxyCalOp来实现对calOp的访问。让我们进入代码看一下这个代理类对象是怎么生成的。
bind方法
public Object bind(Object object,CalOpInterceptor interceptor) {
this.object = object;
this.interceptor = interceptor;
return Proxy.newProxyInstance(this.object.getClass().getClassLoader(),
this.object.getClass().getInterfaces(), this);
}
Proxy的newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
/*
* loader = 加载CalOp的classloader对象.因为是生成的新的代理类要赋值给一个CalOp对象。如果不是跟CalOp使用同一个classloader,会出现类型转换异常
* interfaces = CalOp这个接口。只会实现接口中的方法。如果多个接口中的方法重名,则只代理声明靠前的接口
* h = 一个CalOpProxy类的实例,对应bind方法中的this
*/
// 暂时先不管,后面会提到
if (h == null) {
throw new NullPointerException();
}
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
/*
* Look up or generate the designated proxy class.
* 生成一个新的类,该类实现了interface的所有接口。这里就是实现了CalOp的所有接口。具体实现下面会看到
* 并且由加载CalOp的classloader加载
*
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
// 下面使用反射,调用构造函数生成该类的实例。这个类的实例就是代理类的实例,就是JRun中的proxyCalOp对象
try {
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
// create proxy instance with doPrivilege as the proxy class may
// implement non-public interfaces that requires a special permission
return AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
return newInstance(cons, ih);
}
});
} else {
return newInstance(cons, ih);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString());
}
}
在上面,newProxyInstance函数的
Class<?> cl = getProxyClass0(loader, intfs);
中生成了一个代理类。我们可以通过一些方法拿到这个代理类的class文件,通过反编可以得到代理类的结构,如下。
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
package com.sun.proxy;
import com.proxytest.CalOp;
import java.lang.reflect.*;
public final class $Proxy0 extends Proxy
implements CalOp
{
public $Proxy0(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void show()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
private static Method m1;
private static Method m3;
private static Method m0;
private static Method m2;
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m3 = Class.forName("com.proxytest.CalOp").getMethod("show", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
}
catch(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
现在我们就应该很清楚,在JRun中的代理类对象proxyCalOp就是$Proxy0类的实例
CalOp proxyCalOp = (CalOp) proxy.bind(calOp, interceptor);
proxyCalOp.show();
proxyCalOp.show()的调用实际调用的是:
public final void show()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
我们通过跟踪参数传递,可以知道h就是CalOpProxy类的实例,那么super.h.invoke(this, m3, null)调用的就是CalOpProxy的方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
interceptor.before();
Object result = method.invoke(this.object, args);
interceptor.after();
return result;
}
到此,就真相大白了。