动态代理原理
来源:互联网 发布:linux c 获取时间 格式 编辑:程序博客网 时间:2024/05/16 13:55
先看一个动态代理的小例子
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->beforehelloCalOpInterceptor->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; }
到此,就真相大白了。
0 0
- 动态代理原理
- 反射、动态代理原理
- java动态代理原理
- spring 动态代理 原理
- 动态代理原理
- Java动态代理原理
- jdk动态代理原理
- JDK动态代理原理
- jvm动态代理原理
- 动态代理实现原理
- JDK动态代理原理
- jdk动态代理原理
- java动态代理原理
- JDK动态代理原理
- Java动态代理--jdk代理原理深究
- java动态代理机制原理
- Java动态代理及原理
- Cglib实现动态代理原理
- 对文件批量改名
- 手机内存配置参数说明(内存溢出问题解决方法)
- [置顶] Linux连接无线网的方法
- dbus在windows上的编译
- 二维数组的查找问题
- 动态代理原理
- Maven学习总结(一)——Maven入门
- One Person - 1243
- 彻底弄清Hive安装过程中的几个疑问点
- matlab plot画图详解
- 详解Spring MVC 4常用的那些注解
- Maven学习总结(二)——Maven项目构建过程练习
- JAVA List 移除元素
- 天声人語