动态代理、静态代理优缺点

优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。动态代理只有在用到被代理对象的时候才会对被代理类进行类加载。 而静态代理在编译器就已经开始占内存了。。
缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。

弱引用

被弱引用关联的对象只能生存到下一次垃圾收集发生之前。 当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。 在JDK 1.2之后,提供了WeakReference类来实现弱引用。jdk动态代理使用WeakCache 类 对代理类对象进行缓存, 代理类的引用就是采用的虚引用key 继承自WeakReference

借助JDK实现一个动态代理

JDK动态代理步骤

步骤一、编写代理接口

package com.cbam.demo.dynamicProxy;/*** CopyRright (c)2014-2016 Haerbin Hearglobal Co.,Ltd* Project: demo* Comments:* Author:cbam* Create Date:2017/3/29* Modified By:* Modified Date:* Modified Reason:*/
public interface HelloService {void sayHello(String name);
}

步骤二、编写接口实现类

package com.cbam.demo.dynamicProxy;/*** CopyRright (c)2014-2016 Haerbin Hearglobal Co.,Ltd* Project: demo* Comments:* Author:cbam* Create Date:2017/3/29* Modified By:* Modified Date:* Modified Reason:*/
public class HelloServiceImpl implements HelloService {@Overridepublic void sayHello(String name) {System.out.println("Hello " + name);}
}

步骤二、编写接口实现类

package com.cbam.demo.dynamicProxy;/*** CopyRright (c)2014-2016 Haerbin Hearglobal Co.,Ltd* Project: demo* Comments:* Author:cbam* Create Date:2017/3/29* Modified By:* Modified Date:* Modified Reason:*/
public class HelloServiceImpl implements HelloService {@Overridepublic void sayHello(String name) {System.out.println("Hello " + name);}
}

步骤三、编写InvocationHandler 实现类

package com.cbam.demo.dynamicProxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** CopyRright (c)2014-2016 Haerbin Hearglobal Co.,Ltd* Project: demo* Comments:* Author:cbam* Create Date:2017/3/29* Modified By:* Modified Date:* Modified Reason:*/
public class ProxyInvocationHandler implements InvocationHandler {//要代理的对象private Object target;public ProxyInvocationHandler(Object target) {this.target = target;}public Object getProxy() {return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(),this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("----- before -----");Object rtn = method.invoke(target, args);System.out.println("----- after -----");return rtn;}
}

步骤四、编写InvocationHandler 实现类

package com.cbam.demo.dynamicProxy;import sun.misc.ProxyGenerator;import java.io.FileOutputStream;
import java.io.IOException;/*** CopyRright (c)2014-2016 Haerbin Hearglobal Co.,Ltd* Project: demo* Comments:* Author:cbam* Create Date:2017/3/29* Modified By:* Modified Date:* Modified Reason:*/
public class Main {public static void main(String[] args) {HelloService helloService = new HelloServiceImpl();ProxyInvocationHandler proxyInovationHandler = new ProxyInvocationHandler(helloService);HelloService proxy = (HelloService) proxyInovationHandler.getProxy();proxy.sayHello("梁舒");}
}

输出:

----- before -----
Hello 梁舒
----- after -----

前面代码示例展示了jdk动态代理怎么玩儿。 下面我们来分析一下, jdk底层到底搞了啥就生成了代理类:
首先我们从获取代理类的方法中的Proxy.newProxyInstance

 public Object getProxy() {return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), target.getClass().getInterfaces(),this);}

点进去, 可以在Proxy 中看到该静态方法的签名:

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException

该方法的注释:

Returns an instance of a proxy class for the specified interfaces that dispatches method invocations to the specified invocation handler.返回一个特定接口的代理类的实例, 代理类对象可以分发method调用到专门的调用处理器。

待会儿就能理解这句话的含义了。根据javaDoc 得出:
第一个参数为一个类加载器, 就是指定一个类加载器来加载所生成的代理类的字节码而已。
第二个参数为代理类要去实现的接口
第三个参数就是我们所定义的invocation handler 我们毕竟刚才传递的是this
再看整个方法里面的核心逻辑(删除部分校验逻辑):

  @CallerSensitivepublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{Objects.requireNonNull(h);//接口克隆了一份final Class<?>[] intfs = interfaces.clone();/** Look up or generate the designated proxy class.* 查询或着生成指定的代理类, 之所以查询是因为, 之前曾经生成* 的代理类可能被缓存了。 在这里只是生成了代理类class对象* 而已*/Class<?> cl = getProxyClass0(loader, intfs);//在这里根据class对象拿到代理类构造方法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;}});}//这里真正的创建了 一个代理对象的实例, //我们留一个疑问, 为什么传过去一个包含h的数组???return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {。。。。。}

我们接下来把getProxyClass0() 方法逻辑拉出来

  private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}return proxyClassCache.get(loader, interfaces);}

可以看到Cache字眼了, 如果缓存中存在代理类, 直接从Cache取出, 否则生成并缓存。这里有意思的是如何进行的缓存? 如何判断的代理类是否是同一个?

缓存逻辑

继续看下proxyClassCache.get() 方法逻辑, 这个方法在WeakCache 类中实现。而proxyClassCache 只是Proxy 类的一个WeakCache 类组合对象, 在Proxy 类是这样定义的:

private static final WeakCache<ClassLoader, Class<?>[], Class<?>>proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

观察WeakCache 构造参数, 里面传入了两个工厂实例, 两个工厂均实现了BiFunction 函数接口, KeyFactory 用来生产 缓存 keyProxyClassFactory 用来生产字节码 。

看看WeakCache 类用什么来进行的缓存:

可以看到使用两层ConcurrentHashMap来存的:(key, sub-key) -> value} 其中keyvalue是弱引用, sub-key 是强引用。其中的key 就是我们指定的类加载器。 sub-key 是通过subKeyFactory 工厂方法产生, value 是通过valueFactory 工厂产生, 在上图WeakCache 中都能找到。
怎么获取到key

Object cacheKey = CacheKey.valueOf(key, refQueue);

点开valueOf, 可以看到CacheKeyWeakCache 的一个实现了WeakReference 的静态内部类, 其中有个静态valueOf 方法来产生 我们上面所说的第一个参数keysuper(key, refQueue); 这句话意味着, 将类加载器作为了弱引用的keyrefQueue 表示弱引用队列(会被清理)。

 private static final class CacheKey<K> extends WeakReference<K> {private static final Object NULL_KEY = new Object();static <K> Object valueOf(K key, ReferenceQueue<K> refQueue) {return key == null? NULL_KEY: new CacheKey<>(key, refQueue);}private final int hash;private CacheKey(K key, ReferenceQueue<K> refQueue) {super(key, refQueue);this.hash = System.identityHashCode(key);  // compare by identity}

怎么拿到的subKey

Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));

看下subKeyFactory.apply() 方法代码, 方法实现还是在Proxy 类的KeyFactory 静态内部工厂中。Key1 类并没有对实例的弱引用

 private static final class KeyFactoryimplements BiFunction<ClassLoader, Class<?>[], Object>{@Overridepublic Object apply(ClassLoader classLoader, Class<?>[] interfaces) {switch (interfaces.length) {case 1: return new Key1(interfaces[0]); // the most frequentcase 2: return new Key2(interfaces[0], interfaces[1]);case 0: return key0;default: return new KeyX(interfaces);}}}

怎么拿到的Supplier ? 我们缓存Map 的第二层Map 中的也就是我们的代理类, 存的就是我们下面这个工厂实例, 实现了Supplier , 其实Factroy类 就是对我们缓存Map 第二层Map的一个封装。 只有当我们拿到Supplier调用里面的 get 方法时才会返回真正的代理类。注意当调用 get 方法首先从缓存的第二层Map中取, 如果没有
那么调用

 value = Objects.requireNonNull(valueFactory.apply(key, parameter));

其中valueFactory 是我们上面说过的java.lang.reflect.Proxy.ProxyClassFactory 这个工厂类来生产字节码的, 这个类的核心逻辑代码是:

   /** Generate the specified proxy class.*///生成字节码, 并返回在堆中实例完的对象byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);try {return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);

待会我们利用这段代码, 来生成一下我们的字节码文件。
下面就是包装代理类的工厂的代码:

private final class Factory implements Supplier<V> {private final K key;private final P parameter;private final Object subKey;// 工厂封装的缓存Map的第二个Mapprivate 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;}@Overridepublic synchronized V get() { // serialize access// re-checkSupplier<V> supplier = valuesMap.get(subKey);if (supplier != this) {return null;}V value = null;try {// 这里是缓存中没有找到代理类后所进行的逻辑,上面已经解释value = Objects.requireNonNull(valueFactory.apply(key, parameter));} finally {if (value == null) { // remove us on failurevaluesMap.remove(subKey, this);}}assert value != null;CacheValue<V> cacheValue = new CacheValue<>(value);if (valuesMap.replace(subKey, this, cacheValue)) {// put also in reverseMapreverseMap.put(cacheValue, Boolean.TRUE);} else {throw new AssertionError("Should not reach here");}// successfully replaced us with new CacheValue -> return the value// wrapped by itreturn value;}}

有了上面的准备, 下面看:proxyClassCache.get()

public V get(K key, P parameter) {Objects.requireNonNull(parameter);expungeStaleEntries();
// 由上面所说的生成缓存Map的第一个参数keyObject cacheKey = CacheKey.valueOf(key, refQueue);
//懒惰实例化缓存Map的第二个参数, 其中cacheKey作为第一个参数(加载器嘛)ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);//挺好理解if (valuesMap == null) {ConcurrentMap<Object, Supplier<V>> oldValuesMap= map.putIfAbsent(cacheKey,valuesMap = new ConcurrentHashMap<>());if (oldValuesMap != null) {valuesMap = oldValuesMap;}}//这里拿到subKey(缓存Map中那个subKey) , 来获取可能的  //存储在valuesMap中的SupplierObject subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));//supplier所指向的实例就是Factroy实例, 包装了第二层MapSupplier<V> supplier = valuesMap.get(subKey);Factory factory = null;while (true) {if (supplier != null) {//有代理类缓存就直接返回V value = supplier.get();if (value != null) {return value;}}if (factory == null) {factory = new Factory(key, parameter, subKey, valuesMap);}//这里的扔进去的supplier 实质上是一个实现了supplier的工厂实例if (supplier == null) {supplier = valuesMap.putIfAbsent(subKey, factory);if (supplier == null) {// successfully installed Factorysupplier = factory;}// else retry with winning supplier} else {if (valuesMap.replace(subKey, supplier, factory)) {// successfully replaced// cleared CacheEntry / unsuccessful Factory// with our Factorysupplier = factory;} else {// retry with current suppliersupplier = valuesMap.get(subKey);}}}}

到了这里我们捋一捋调用过程:
首先Proxy.newProxyInstance() -> Class<?> cl = getProxyClass0(loader, intfs); -> proxyClassCache.get(loader, interfaces) ->
1、 Object cacheKey = CacheKey.valueOf(key, refQueue); //拿到第一层key
2、Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));// 拿到第二层subKey
3、Supplier<V> supplier = valuesMap.get(subKey); 拿到代理类的包装类。
缓存总结: 缓存逻辑的keyvalue 都是工厂方法产生, 能够学习到怎么利用弱引用来写一个缓存管理器。

回过头,再来看我们生成的代理类

利用上面那段生成字节码的代码, 我们也来生成一下, 看看这个代理类长什么样子:

package com.cbam.demo.dynamicProxy;import sun.misc.ProxyGenerator;import java.io.FileOutputStream;
import java.io.IOException;/*** CopyRright (c)2014-2016 Haerbin Hearglobal Co.,Ltd* Project: demo* Comments:* Author:cbam* Create Date:2017/3/29* Modified By:* Modified Date:* Modified Reason:*/
public class Main {public static void main(String[] args) {HelloService helloService = new HelloServiceImpl();ProxyInvocationHandler proxyInovationHandler = new ProxyInvocationHandler(helloService);HelloService proxy = (HelloService) proxyInovationHandler.getProxy();proxy.sayHello("梁舒");String path = "D:/$Proxy0.class";byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",HelloServiceImpl.class.getInterfaces());FileOutputStream out = null;try {out = new FileOutputStream(path);out.write(classFile);out.flush();} catch (Exception e) {e.printStackTrace();} finally {try {out.close();} catch (IOException e) {e.printStackTrace();}}}
}

下面给出了反编译$Proxy0.class 之后的代码。代理类继承了Proxy 实现了HelloService 我们的代理接口
我们看看$Proxy0 构造函数? 是不是有感觉? 还记得当初留得疑问么

return cons.newInstance(new Object[]{h});

其中的m1 m2…..都是我们要代理的那些方法, 可以说我们用代理类调用的方法, 其实都是委托给我们自定义的InvocationHandler 来调用其invoke 方法实现的!而我们每次动态获取代理类其实很可能从缓存中取出来的!

import com.cbam.demo.dynamicProxy.HelloService;
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 Proxyimplements HelloService
{private static Method m1;private static Method m3;private static Method m2;private static Method m0;public $Proxy0(InvocationHandler paramInvocationHandler)throws {super(paramInvocationHandler);}public final boolean equals(Object paramObject)throws {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 sayHello(String paramString)throws {try{this.h.invoke(this, m3, new Object[] { paramString });return;}catch (Error|RuntimeException localError){throw localError;}catch (Throwable localThrowable){throw new UndeclaredThrowableException(localThrowable);}}public final String toString()throws {try{return (String)this.h.invoke(this, m2, null);}catch (Error|RuntimeException localError){throw localError;}catch (Throwable localThrowable){throw new UndeclaredThrowableException(localThrowable);}}public final int hashCode()throws {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("com.cbam.demo.dynamicProxy.HelloService").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);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());}}
}

好了, 上面的疑问,都解释差不多了。。。

深入剖析JDK动态代理源码实现相关推荐

  1. jdk动态代理源码分析(一)---代理的定义

    最近在看rpc的实现原理,发现大部分通用的rpc框架在实现远程调用的时候,都是通过java动态代理封装好了通信细节,让用户可以像调用本地服务一样调用远程服务.但是关于java动态代理有两个问题想不通: ...

  2. JDK动态代理源码解析

    分析版本jdk1.8 在分析jdk动态代理之前,先来了解java WeakReference弱引用的使用.运行期创建目标对象的代理非常耗时,使用缓存来存储生成的代理类显得尤为重要.jdk动态代理使用弱 ...

  3. 动态代理及JDK动态代理源码分析

    1.为什么要动态代理 现在有这样一个需求:在聊天系统中,把每一个所说的话记录到日志文件里面,初学者可能是这样来设计 在speak方法中调用log方法,记录到数据库.这样设计有明显的不足:1.log方法 ...

  4. jdk动态代理源码学习

    最近用到了java的动态代理,虽然会用,但不了解他具体是怎么实现,抽空看看了看他的源码. 说到Java的动态代理就不能不说到代理模式,动态代理也就是多了一个'动态'两字,在<大话设计模式> ...

  5. Java动态代理源码详解

    一.概述   前言:本文除了讲解JDK动态代理及CGLIB动态代理实例和应用外,还会讲解JDK动态代理源码实现过程以及自己写一手个JDK动态代理等.   动态代理在很多底层框架中都会用得到,比如在Sp ...

  6. javabean反射改字段内容_BAT程序员编写:深入理解 Java 反射和动态代理源码分析...

    什么是反射 反射(Reflection)是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序获取自身的信息,并且可以操作类或对象的内部属性. 通过反射机制,可以在运行时访问 Java ...

  7. 动态代理源码分析,实现自己的动态代理

    什么是代理 增强一个对象的功能 买火车票,app就是一个代理,他代理了火车站,小区当中的代售窗口 java当中如何实现代理 java实现的代理的两种办法 代理的名词 代理对象 增强后的对象 目标对象 ...

  8. Android动态代理源码分析

    前言 前面我们简单介绍了代理模式的使用Android设计模式详解之代理模式,我们也明白了动态代理模式的重要性,那动态代理究竟是如何实现将方法交给InvocationHandler.invoke执行的呢 ...

  9. jkd动态代理源码分析

    代理对象的生成方法是: Proxy.newProxyInstance(...),进入这个方法内部,一步一步往下走会发现会调用 ProxyGenerator.generateProxyClass(),这 ...

最新文章

  1. Ubuntu安装PostgreSQl
  2. animateWithDuration
  3. 论文 参考文献的格式说明
  4. vb怎么判断整数_VB数学函数大全
  5. 为啥Redis/Mongo这么快,就不能直接替代mysql吗?
  6. 关于uboot中tftp上传内存数据到tftp服务器
  7. 复旦大学肖仰华教授在线授课!从专家系统到知识图谱演进
  8. Android 系统各个版本上https的抓包
  9. MySQl 5.7版本的Data文件夹,my.ini文件,Uploads文件夹所在目录
  10. html引入阿里在线css文件夹,阿里字体css代码引入方法
  11. [OpenAirInterface实战-19] :OAI 软件无线电USRP UHD硬件驱动程序的使用与网络架构
  12. 用 m3u8 下载网页视频直接保存为 MP4
  13. 操作系统文件保护及文件共享
  14. win10重装系统后连不上公司服务器,Win10重装系统后网络连接不了,重装win10系统后不能上网解决方法...
  15. 微软输入法简体与繁体切换快捷键
  16. 神经网络调参:loss 问题汇总(震荡/剧烈抖动,loss不收敛/不下降)
  17. 计算机中ufc是什么意思中文,ufc是什么意思,ufc是什么意思中文
  18. w ndows7怎么安装,《联想Y460在Wndows7系统下完美安装XP系统的方法.doc
  19. minio 图片存储服务器的部署和使用
  20. 什么样的需求评审会是高效的?

热门文章

  1. windows 8 音乐(Xbox Music)详解
  2. 学PS基础:Photoshop 技能167个
  3. Web3D开发者兼职副业平台推荐
  4. 成都榆熙:拼多多产地直发模式如何解决了流通环节多的问题?
  5. 【canvas使用】
  6. kindle书摘-围城-相爱勿相伤
  7. 前沿|十位顶级大咖为您把脉容器技术大势
  8. 835616-60-9,4-Fluoro-thalidomide用于补充CRBN蛋白的沙利度胺基脑啡肽配体
  9. 95后国风艺术家联手AI,立志耗时一年,作百米长卷《新西湖繁胜全景图》献礼杭州亚运!...
  10. 应用计算机解数学模型之我见,初中数学建模教学之我见