说到动态代理,首先想到的是好像不怎么用,即便是用,也用的框架如Spring,框架中的AOP(面向切面)底层原理就是动态代理,框架都帮我做好了,我知道他使用了这个,那就ok了,事实上,笔者觉得这是远远不够的,因此,进行了一个简单的总结,算是入门这动态代理。
上一个Demo看看先:
Interface接口:
public interface Interface {
public void test();
public void test2(int id);
}
InterfaceImpl实现类:
public class InterfaceImpl implements Interface {
@Override
public void test() {
System.out.println("test....");
}
@Override
public void test2(int id) {
System.out.println("test2...."+id);
}
}
ProxyClass代理类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyClass implements InvocationHandler {
private Interface inter;
public ProxyClass(Interface inter) {
super();
this.inter = inter;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("before...");
method.invoke(inter, args);
System.out.println("After...");
return null;
}
}
测试类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
Interface inter=new InterfaceImpl();
InvocationHandler handler=new ProxyClass(inter);
Interface interf=(Interface) Proxy.newProxyInstance(inter.getClass().getClassLoader(), inter.getClass().getInterfaces(), handler);
interf.test();
System.out.println("-----------------------");
interf.test2(1503);
}
}
测试结果:
before...
test....
After...
-----------------------
before...
test2....1503
After...
上述Demo可以清楚的看到,实现JDK动态代理需要使用的两样东西分别是InvocationHandler接口和Proxy类。那么接下来分别介绍一下他们:
InvocationHandler接口:
InvocationHandler是由代理实例的调用处理程序实现的接口 。 每个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的invoke方法。 (通俗的讲,就是代理对象每次调用真实对象的方法时,都会去找实现InvocationHandler接口的处理程序中的invoke方法,语句还是相对抽象,但是参考上面的Demo,应该会更便于理解)
Object invoke(Object proxy,Method method,Object[] args)throws Throwable
参数 :
- proxy - 调用该方法的代理实例
- method -所述方法对应于调用代理实例上的接口方法的实例。 方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。
- args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。 原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。
Proxy类:提供了构造方法,也提供了静态方法创建代理对象,实际上使用更多的是其静态方式创建代理对象,下面就静态的方法创建进行总结:
public static Object newProxyInstance(ClassLoader loader, class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
参数 :
- loader - 类加载器来定义代理类
- interfaces - 代理类实现的接口列表
- h - 调度方法调用的调用处理函数(通俗理解就是怎么处理的这个过程,更通俗的讲,就是InvocationHandler实现类,也就是Demo中的ProxyDemo对象)
以上就是InvocationHandler接口和Proxy类的主要核心知识点。那么我想,很多小伙伴过来仅仅想看的不是上面两个方法的解析或者是怎么写一个Demo,而是想看看他们的联系,也就是说为什么我的动态代理对象执行了真实对象的方法后,程序会自动走到实现了InvocationHandler接口的ProxyClass实现类中,去执行里面的invoke方法?大家接下来可以根据笔者的思路一步步往下看:
首先,你可以在测试类中打印一下代理对象,看看是属于InvocationHandler,还是Proxy类:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
Interface inter=new InterfaceImpl();
InvocationHandler handler=new ProxyClass(inter);
Interface interf=(Interface) Proxy.newProxyInstance(inter.getClass().getClassLoader(), inter.getClass().getInterfaces(), handler);
// 得到代理对象的类型
System.out.println(interf.getClass().getName());
System.out.println("-----------------------");
interf.test();
System.out.println("-----------------------");
interf.test2(1503);
}
}
com.sun.proxy.$Proxy0
-----------------------
before...
test....
After...
-----------------------
before...
test2....1503
After...
$Proxy0?不是InvocationHandler也不是Proxy呀?笔者一开始也在这里疑惑,这是什么呢??$Proxy0是在JVM运行时产生的类,但这个类怎么看到呢?于是乎网上百度了一番,找到了解决的方案:运行如下代码:
import java.io.File;
import java.io.FileOutputStream;
import sun.misc.ProxyGenerator;
public class Test {
public static void main(String[] args) throws Exception {
byte[] classFile = ProxyGenerator.generateProxyClass("Proxy0",
InterfaceImpl.class.getInterfaces());
File file = new File("../Proxy0.class");
FileOutputStream fos = new FileOutputStream(file);
fos.write(classFile);
fos.flush();
fos.close();
}
}
在你的项目空间中去找$Proxy0.class文件,即可发现该在根目录下:使用反编译工具打开该class文件打开即可:
import h.l.Test.Interface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class Proxy0
extends Proxy
implements Interface
{
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public Proxy0(InvocationHandler paramInvocationHandler)
{
super(paramInvocationHandler);
}
public final boolean equals(Object paramObject)
{
try
{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void test()
{
try
{
this.h.invoke(this, m3, null);
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()
{
try
{
return (String)this.h.invoke(this, m2, null);
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final void test2(int paramInt)
{
try
{
this.h.invoke(this, m4, new Object[] { Integer.valueOf(paramInt) });
return;
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()
{
try
{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}
catch (Error|RuntimeException localError)
{
throw localError;
}
catch (Throwable localThrowable)
{
throw new UndeclaredThrowableException(localThrowable);
}
}
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
m3 = Class.forName("h.l.Test.Interface").getMethod("test", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m4 = Class.forName("h.l.Test.Interface").getMethod("test2", new Class[] { Integer.TYPE });
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
return;
}
catch (NoSuchMethodException localNoSuchMethodException)
{
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
}
catch (ClassNotFoundException localClassNotFoundException)
{
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
上述这段代码就是$Proxy0的源码,在代理对象$proxy0调用test方法的时候,代码体内有个super.h.invoke(this, m3, null);于是就会调用父类的invoke方法,也就是实现InvocationHandler接口的处理程序中的invoke方法,即Demo中的ProxyClass中的invoke方法。
除了JDK动态代理,还有CGLIB代理。
JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法(继承)(ps:以后有时间总结)
注:以上文章仅是个人学习过程总结,若有不当之处,望不吝赐教。