深入理解Java的动态代理
来源:互联网 发布:如何发淘宝链接给朋友 编辑:程序博客网 时间:2024/06/05 18:13
之前也转过一篇关于动态代理的贴子,写的挺好的,看过之后只学会了如何使用动态代理但是并不明白动态代理是如何实现的,今天就来深入的理解一下动态代理实现机制:
首先你要认识一点:动态代理是依赖反射的,只有认识到这一点我们的方向才不会错。
在认识动态代理之前请允许我再无耻的将动态代理的实现过程再贴一遍(知道怎么实现的小伙伴可以直接pass),这次不同的是代码是我自己写的:
package TestProxy;/** * @author:MindMrWang *2017年11月18日 *:function:抽象角色 */public interface Person { void run(); void eat(String food);}
package TestProxy;/** * @author:MindMrWang *2017年11月18日 *:function:真实角色 */public class Man implements Person{ public void run() { System.out.println("run"); } public void eat(String food) { System.out.println("eat"+food); }}
package TestProxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;/** * @author:MindMrWang *2017年11月18日 *:function:代理角色 */public class MyInvocation implements InvocationHandler { Object person; public MyInvocation(Object obj) { this.person=obj; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before invoke");//模拟一个方法调用 Object result = method.invoke(person, args); System.out.println("after invoke");//模拟一个方法调用 return result; }}
package TestProxy;import java.lang.reflect.Constructor;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;/** * @author:MindMrWang *2017年11月18日 *:function:测试类 */public class TestProxy { public static void main(String[] args) throws Exception{// Person man = new Man();//创建一个真实对象// InvocationHandler invocation = new MyInvocation(man);//创建代理角色// Person person = (Person)Proxy.newProxyInstance(man.getClass().getClassLoader(), man.getClass().getInterfaces(), invocation);// // person.eat("肉");//这样执行了两个方法,但是我们在执行这两个方法前后分别调用了before invoke和after invoke// person.run(); // 这就是动态代理的好处和作用// //在实际的运用中,可以在一个方法执行之前调用过滤器方法,结束过后调用日志方法,我们只需要在代理类中// //写好需要处理的方法,就可以对这类需要批量处理的方法进行切面处理,节省了我们大量的工作。 Person man = new Man();//创建一个真实对象 InvocationHandler invocation = new MyInvocation(man);//创建代理角色 //获得动态代理类的Class实例 Class proxyClass = Proxy.getProxyClass(man.getClass().getClassLoader(), man.getClass().getInterfaces()); //获得这个代理类构造器 Constructor constructor = proxyClass.getConstructor(InvocationHandler.class); //获得代理类实例 Person person = (Person)constructor.newInstance(invocation); //方法调用 person.eat("肉"); person.run(); }}控制台输出:before invokeeat肉after invokebefore invokerunafter invoke
上面提供了两种实现方法,但是我们实际的使用中更常使用的是通过newnewProxyInstance来创建代理类实例,Proxy类已经帮我们将一些创建细节封装了。
但是第二种方法更有助于我们对动态代理的理解。
为了搞清楚动态代理背后的玄机,我们一起来看看它的源码:
首先我们找到Proxy类的getProxyClass方法:
public static Class<?> getProxyClass(ClassLoader loader, Class<?>... interfaces) throws IllegalArgumentException { //克隆我们传入的接口参数对应我们上面的例子即为man.getClass().getInterfaces()获得的值,即man对应Class对象实现的接口,也就是我们的抽象角色 final Class<?>[] intfs = interfaces.clone(); //获得安全管理对象 final SecurityManager sm = System.getSecurityManager(); if (sm != null) { //进行代理检查checkProxyAccess(Reflection.getCallerClass(), loader, intfs); }//返回getProxyClass0 return getProxyClass0(loader, intfs); }
我们继续跟进 getProxyClass0方法,参数还是loader, intfs和我们传入的参数一致(intfs是克隆得来的);
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { //检查接口长度,相信没有那么长的接口吧 if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory //上面这句大概意思是如果代理类由所给loader类加载定义,并且由所给接口实现,将返回缓存副本(判断缓存里的代理类是否由我们给的参数实现)//通过注释我们可以看到,这是一个代理类缓存,如果该接口的代理类已经被缓存,那么直接取出,如果没有就由创造ProxyClassFactory一个代理类 return proxyClassCache.get(loader, interfaces); }
所以我们继续看看proxyClassCache的get方法:
//get方法的参数依旧是loader和interface public V get(K key, P parameter) { //Objects的静态方法判断接口是否为空 Objects.requireNonNull(parameter); expungeStaleEntries();//通过loader获得这个缓存key Object cacheKey = CacheKey.valueOf(key, refQueue); // lazily install the 2nd level valuesMap for the particular cacheKey//二级valueMapConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey); if (valuesMap == null) {//为空就调用map.putIfAbsent ConcurrentMap<Object, Supplier<V>> oldValuesMap = map.putIfAbsent(cacheKey, valuesMap = new ConcurrentHashMap<>()); //不为空就为oldValueMap if (oldValuesMap != null) { valuesMap = oldValuesMap; } } // create subKey and retrieve the possible Supplier<V> stored by that // subKey from valuesMap Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter)); //从valueMao取回supplier Supplier<V> supplier = valuesMap.get(subKey); Factory factory = null; while (true) { if (supplier != null) { // supplier might be a Factory or a CacheValue<V> instance V value = supplier.get(); if (value != null) { return value;//返回这个值一定代理类 } } // else no supplier in cache // or a supplier that returned null (could be a cleared CacheValue // or a Factory that wasn't successful in installing the CacheValue) // lazily construct a Factory if (factory == null) { factory = new Factory(key, parameter, subKey, valuesMap); } if (supplier == null) { supplier = valuesMap.putIfAbsent(subKey, factory); if (supplier == null) { // successfully installed Factory supplier = factory; } // else retry with winning supplier } else { if (valuesMap.replace(subKey, supplier, factory)) { // successfully replaced // cleared CacheEntry / unsuccessful Factory // with our Factory supplier = factory; } else { // retry with current supplier supplier = valuesMap.get(subKey); } } } }
上面的代码有点多,但是我们可以倒着看,看看最后的值是怎么返回的:
if (supplier != null) { // supplier might be a Factory or a CacheValue<V> instance V value = supplier.get(); if (value != null) { return value; } }
supplier可能是一个工厂或者是一个缓存值实例
当它不为空的时候Value=supplier.get();
那么我们来看看这个get方法:
private final class Factory implements Supplier<V> { private final K key; private final P parameter; private final Object subKey; private final ConcurrentMap<Object, Supplier<V>> valuesMap; Factory(K key, P parameter, Object subKey, ConcurrentMap<Object, Supplier<V>> valuesMap) { this.key = key; this.parameter = parameter; this.subKey = subKey; this.valuesMap = valuesMap; } @Override public synchronized V get() { // serialize access // re-check Supplier<V> supplier = valuesMap.get(subKey); if (supplier != this) { // something changed while we were waiting: // might be that we were replaced by a CacheValue // or were removed because of failure -> // return null to signal WeakCache.get() to retry // the loop return null; } // else still us (supplier == this) // create new value V value = null; try { //生成代理类 value = Objects.requireNonNull(valueFactory.apply(key, parameter)); } finally { if (value == null) { // remove us on failure valuesMap.remove(subKey, this); } } // the only path to reach here is with non-null value assert value != null; // wrap value with CacheValue (WeakReference) CacheValue<V> cacheValue = new CacheValue<>(value); // put into reverseMap reverseMap.put(cacheValue, Boolean.TRUE); // try replacing us with CacheValue (this should always succeed) if (!valuesMap.replace(subKey, this, cacheValue)) { throw new AssertionError("Should not reach here"); } // successfully replaced us with new CacheValue -> return the value // wrapped by it return value; } }
我们看见了这段代码:
Supplier<V> supplier = valuesMap.get(subKey);
所以之前的suppelier是放在一个map中存放的。
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
这段代码为supplier的get返回值,所以我们接着看valueFactory.apply方法:
这个方法是ProxyClassFactory的apply方法:里面详细介绍了代理类的创建过程,最后返回:
return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
上面的这段代码使我们得到了代理类Class实例,然后调用getConstructor,并以InvocationHandler.class为参数获得代理类构造器(这个属于反射的知识了),再使用这个构造器生成我们需要的代理类。
这次的分析就到这里,可能大家对这个知识有点模糊,但是也不要紧,因为对于动态代理的认识我们只需要知道它是通过我们需要代理的类加载器和interface抽象角色通过反射的方式获得代理类Class对象,进而获得构造器创建代理类实例。
限于博主水平有限,没能分析透彻,如有意见欢迎留言。
- 深入理解Java的动态代理
- 深入理解java动态代理
- 深入理解 Java 动态代理
- 对于java动态代理模式的深入理解
- 深入理解JAVA JDK动态代理机制
- 深入理解java动态代理机制
- 深入理解JAVA JDK动态代理机制
- 深入源码理解-java动态代理
- 深入理解 jdk 动态代理的实现
- 深入理解java代理
- 深入理解JDK动态代理
- 深入理解 动态代理+反射
- Java 动态代理的理解与Cglib动态代理实现
- 理解java动态代理
- 理解JAVA动态代理
- 理解java动态代理
- 理解JAVA动态代理
- 理解java动态代理
- 一些Linux命令简要笔记——软件管理
- windows操作系统有哪些版本
- x86实现ARM CLZ。。。
- 匿名内部类详解
- leetcode: 58. Length of Last Word
- 深入理解Java的动态代理
- java 简易聊天工具(TCP)
- PC、手机web页面直播hls视频
- centos6.5下安装redis
- Spring Boot教程
- Lambdas运行效率
- Log4j2官方文档翻译、学习笔记之三——Layouts的分类及常用类型示例
- nginx+tomcat+redis
- 一致性哈希算法学习及JAVA代码实现分析