jdk动态代理实现总结和范例
2016-05-11 23:20
483 查看
最近在研究spring aop,其中代理在aop扮演着一个很重要的角色,现在来总结一下动态代理(这里只总结动态代理,代理模式和静态代理略)。
动态代理是跟静态代理的目的都一样,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
先来看看动态代理的一个简单的例子:
业务接口ITalk
业务实现类 People
代理类MyProxy
客户端代码:
输出结果为:
主业务之前做的事
people talk
主业务之后做的事
执行完毕,似乎一切都很神奇,为什么People的talk方法的多输出了‘主业务之前做的事’和‘主业务之后做的事’?不是只有‘people talk’吗?这就是动态代理的威力······
调试一下程序,可以发现客户端代码中的talk引用的对象是$Proxy0,由此我们可以知道,这个对象跟Proxy有很大的联系。
jdk中动态代理的实现有两个重要的角色:java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。
InvocationHandler接口:
Proxy的newProxyInstance方法:
神奇的事情都发生在Proxy的newProxyInstance方法,里面调用了一个重要的方法getProxyClass0()
继续往proxyClassCache.get(loader, interfaces)里探究,最后发现Proxy的一个内部类ProxyClassFactory中的apply方法
细细研究,其实可以发现Proxy.newProxyInstance的作用如下:
1、根据传入的interfaces参数动态产生一个类,并实现interfaces中的接口,上面的例子就是生成的动态类实现了ITalk。并且继承了Proxy类,重写hashcode,equals,toString方法,具体实现在ProxyGenerator.generateProxyClass(…); 并生成了$Proxy0类
2,通过传入的classloader将刚产生的类加载到jvm中。
3,、第三个参数为InvocationHandler,调用新产生的$Proxy0的构造函数,Proxy(InvocationHandle h),并且用interfaces参数遍历其所有接口的方法,并生成Method对象初始化对象的几个Method成员变量
4、将Proxy0的实例返回给客户端。
因为Proxy0实现了ITalk,所以可以转化为ITalk类型,并且调用ITalk的talk方法,而这里,其实也就是调用了InvocationHandle中的invoke方法:
在$Proxy0中的talk方法:
至此,就可以接受不同的委托而进行动态的代理了。
动态代理是跟静态代理的目的都一样,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
先来看看动态代理的一个简单的例子:
业务接口ITalk
public interface ITalk { void talk(); }
业务实现类 People
public class People implements ITalk { @Override public void talk() { System.out.println("people talk"); } }
代理类MyProxy
public class MyProxy implements InvocationHandler { private Object target; public Object bind(Object target) throws NoSuchMethodException, SecurityException{ this.target = target; return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("切面之前业务"); result = method.invoke(target, args); System.out.println("切面之后业务"); return result; } }
客户端代码:
public static void main(String[] args) { ITalk talk = (ITalk)new MyProxy().bind(new People()); talk.talk(); }
输出结果为:
主业务之前做的事
people talk
主业务之后做的事
执行完毕,似乎一切都很神奇,为什么People的talk方法的多输出了‘主业务之前做的事’和‘主业务之后做的事’?不是只有‘people talk’吗?这就是动态代理的威力······
调试一下程序,可以发现客户端代码中的talk引用的对象是$Proxy0,由此我们可以知道,这个对象跟Proxy有很大的联系。
jdk中动态代理的实现有两个重要的角色:java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口。
InvocationHandler接口:
public interface InvocationHandler { public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; }
Proxy的newProxyInstance方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); final SecurityManager sm = System.getSecurityManager(); if (sm != null) { checkProxyAccess(Reflection.getCallerClass(), loader, intfs); } Class<?> cl = getProxyClass0(loader, intfs); if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); }
神奇的事情都发生在Proxy的newProxyInstance方法,里面调用了一个重要的方法getProxyClass0()
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } return proxyClassCache.get(loader, interfaces); }
继续往proxyClassCache.get(loader, interfaces)里探究,最后发现Proxy的一个内部类ProxyClassFactory中的apply方法
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { Class<?> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // package to de 4000 fine proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL; for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString()); } } }
细细研究,其实可以发现Proxy.newProxyInstance的作用如下:
1、根据传入的interfaces参数动态产生一个类,并实现interfaces中的接口,上面的例子就是生成的动态类实现了ITalk。并且继承了Proxy类,重写hashcode,equals,toString方法,具体实现在ProxyGenerator.generateProxyClass(…); 并生成了$Proxy0类
2,通过传入的classloader将刚产生的类加载到jvm中。
3,、第三个参数为InvocationHandler,调用新产生的$Proxy0的构造函数,Proxy(InvocationHandle h),并且用interfaces参数遍历其所有接口的方法,并生成Method对象初始化对象的几个Method成员变量
4、将Proxy0的实例返回给客户端。
因为Proxy0实现了ITalk,所以可以转化为ITalk类型,并且调用ITalk的talk方法,而这里,其实也就是调用了InvocationHandle中的invoke方法:
在$Proxy0中的talk方法:
public final void talk{ try { super.h.invoke(this, m3, null); //该段则执行了InvocationHandler.invoke(); super.h既是InvocationHandler return; } catch (Error e) { } catch (Throwable throwable) { throw new UndeclaredThrowableException(throwable); } }
至此,就可以接受不同的委托而进行动态的代理了。
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- Android IPC进程间通讯机制
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- 介绍一款信息管理系统的开源框架---jeecg
- 聚类算法之kmeans算法java版本
- java实现 PageRank算法
- PropertyChangeListener简单理解
- c++11 + SDL2 + ffmpeg +OpenAL + java = Android播放器
- 插入排序
- 冒泡排序
- 堆排序
- 快速排序
- 二叉查找树