JDK动态代理就是利用一个代理类去加载或调用实现特定接口的目标类的属性或方法。
什么是代理类?
A dynamic proxy class is a class that implements a list of interfaces specified at runtime such that a method invocation through one of the interfaces on an instance of the class will be encoded and dispatched to another object through a uniform interface。
这段话是官网的介绍。大概意思就是:一个动态代理类是一个在运行时实现一系列指定接口的类,因此在这个类被实例化的时候,调用其中一个接口的方法将被编码(这个我理解为动态生成代理类的字节码文件)和分派到另一个实现同一接口的类(这个就是目标类)。
实现动态代理的API基本都在java.lang.reflect下,每个代理类必须实现
InvocationHandler这个接口。这个接口只有一个方法,代理类就是用这个方法调用目标类的方法的。JDK代理能做很多事情,比如在调用目标类方法前检验权限,参数,token,打印日志等等。spring AOP就是基于JDK代理或CGLIB代理实现的。
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
- proxy 代理类对象。
- Method 方法对象。
- args 方法的参数。
统一接口:
public interface BaseProxy {
public void sayHello();
}
实现上一个接口的目标类:
public class TargetBean implements BaseProxy {
public void sayHello() {
System.out.println("execute target sayHello method!");
}
}
静态代理类:
public class ProxyBean implements InvocationHandler {
private Object targetObject;
public ProxyBean(){
}
public ProxyBean(Object targetObject){
this.targetObject = targetObject;
}
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
// TODO Auto-generated method stub
long st = System.nanoTime();
System.out.println("==============start execute method:"+method.getName());
Object result = method.invoke(targetObject, params);
long end = System.nanoTime();
System.out.println("============execute method "+method.getName()+" in "+(end-st)+" naoseconds");
return result;
}
}
测试:
//第一种方式
public static void invokeNewProxy() throws Exception{
//System.out.println(System.getProperties());
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
BaseProxy targetBean = new TargetBean();
//create a proxy instance
BaseProxy proxy =(BaseProxy) Proxy.newProxyInstance(BaseProxy.class.getClassLoader(),
targetBean.getClass().getInterfaces(),new ProxyBean(targetBean));
//invoke the target instance method through the proxy
proxy.sayHello();
}
//第二种方式
public static void invokeByreflect() throws Exception{
Class proxy = Proxy.getProxyClass(BaseProxy.class.getClassLoader(), BaseProxy.class);
Constructor constructor = proxy.getConstructor(InvocationHandler.class);
BaseProxy baseProxy = (BaseProxy)constructor.newInstance(new ProxyBean(new TargetBean()));
baseProxy.sayHello();
}
输出结果:
*==============start execute method:sayHello
execute target sayHello method!
============execute method sayHello in 276651 naoseconds*
看到这里很多人会有疑惑,为什么baseProxy.sayHello()会调用了目标类的sayHello()方法?接下来让我们一步步分析调用过程。
BaseProxy proxy =(BaseProxy) Proxy.newProxyInstance(BaseProxy.class.getClassLoader(),
targetBean.getClass().getInterfaces(),new ProxyBean(targetBean));
proxy.sayHello();
这段代码会实例化代理类,然后调用动态代理类的sayHello()方法,动态代理类会委托给静态代理类去动态调用目标类的sayHello()方法。
那么动态代理类是什么鬼呢?
Proxy.newProxyInstance(BaseProxy.class.getClassLoader(),
targetBean.getClass().getInterfaces(),new ProxyBean(targetBean));
这段就是用来生成动态代理类,添加这行代码System.getProperties().put(“sun.misc.ProxyGenerator.saveGeneratedFiles”, “true”);从源码能看出这个属性saveGeneratedFiles=true,动态代理类代码可以从内存输出到磁盘中。
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction(paramString, arrayOfByte) {
public Void run() {
try {
FileOutputStream localFileOutputStream = new FileOutputStream(ProxyGenerator.access$000(this.val$name) + ".class");
localFileOutputStream.write(this.val$classFile);
localFileOutputStream.close();
return null;
} catch (IOException localIOException) {
throw new InternalError("I/O exception saving generated file: " + localIOException);
}
}
});
}
如果目标类接口是publish,记得在应用路径下加上con/sum/proxy(是应用路径,不是包路径) 不然会抛出IO异常。
运行测试类代码的时候生成的动态代理类:
public final class $Proxy0 extends Proxy
implements BaseProxy
{
//为了阅读方便,省略部分方法和修改了部分代码
private static Method m3;
private static Method m1;
private static Method m0;
private static Method m2;
public $Proxy0(InvocationHandler paramInvocationHandler) throws exception
{
super(paramInvocationHandler);
}
public final void sayHello()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch(exception e){}
}
static
{
try
{
m3 = Class.forName("com.hal.proxy.BaseProxy").getMethod("sayHello", new Class[0]);
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]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
return;
}
catch (exception e){
}
}
从上面这段代码可以看出proxy.sayHello()调用了
this.h.invoke(this, m3, null);
也就是静态代理类的这段代码:
public Object invoke(Object proxy, Method method, Object[] params) throws Throwable {
// TODO Auto-generated method stub
long st = System.nanoTime();
System.out.println("==============start execute method:"+method.getName());
Object result = method.invoke(targetObject, params);
long end = System.nanoTime();
System.out.println("============execute method "+method.getName()+" in "+(end-st)+" naoseconds");
return result;
}
到这里,代理的调用已经很清晰了。
动态代理类方法-》静态代理类方法-》目标类方法。
至于动态代理类是如何生成,我觉得看源码比任何的描述或总结更有说服力,更能让人感叹代理的强大之处,看了源码,我相信对于反射和内部类的知识会理解的更加深刻。
以上纯属个人理解,如有任何纰漏,还望指正。