JDK动态代理源码解析
动态代理、静态代理优缺点
关于JDK的动态代理,最为人熟知的可能要数Spring AOP的实现,默认情况下,Spring AOP的实现对于接口来说就是使用的JDK的动态代理来实现的,而对于类的代理使用CGLIB来实现。那么,什么是JDK的动态代理呢?
JDK的动态代理:就是在程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程。
代理过程:JDK提供Proxy类来实现动态代理--->newProxyInstance来获得代理实现类--->InvocationHandler接口--->invoke方法
优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性。这是代理的共有优点。动态代理只有在用到被代理对象的时候才会对被代理类进行类加载。 而静态代理在编译器编译时就已经开始占内存了。。
缺点:
1)代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
2)如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
弱引用
被弱引用关联的对象只能生存到下一次垃圾收集发生之前。 当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。 在JDK 1.2之后,提供了WeakReference类来实现弱引用。jdk动态代理使用WeakCache 类 对代理类对象进行缓存, 代理类的引用就是采用的虚引用key 继承自WeakReference 。
借助JDK实现一个动态代理
JDK动态代理步骤
步骤一、编写代理接口
步骤二、编写接口实现类
步骤三、编写InvocationHandler 实现类
步骤四、编写InvocationHandler 实现类
输出:
前面代码示例展示了jdk动态代理怎么玩儿。 下面我们来分析一下, jdk底层到底搞了啥就生成了代理类:
首先我们从InvocationHandler接口入手:
然后我们从获取代理类的方法中的Proxy.newProxyInstance
点进去, 可以在Proxy 中看到该静态方法的签名:
该方法的注释:
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 嘛
再看整个方法里面的核心逻辑(删除部分校验逻辑):
首先来看看它是如何生成代理类的:
其中newInstance只是调用Constructor.newInstance来构造相应的代理类实例,这里重点是看getProxyClass0这个方法的实现:
其中代理缓存是使用WeakCache实现的,如下
观察WeakCache 构造参数, 里面传入了两个工厂实例, 两个工厂均实现了BiFunction 函数接口, KeyFactory 用来生产 缓存 key ,ProxyClassFactory 用来生产字节码 。
看看WeakCache
类用什么来进行的缓存:
可以看到使用两层ConcurrentHashMap来存的:(key, sub-key) -> value} 其中key 和 value是弱引用, sub-key 是强引用。其中的key 就是我们指定的类加载器。 sub-key 是通过subKeyFactory 工厂方法产生, value 是通过valueFactory 工厂产生, 在上图WeakCache 中都能找到。
具体的缓存逻辑这里暂不关心,只需要关心ProxyClassFactory是如何生成代理类的,ProxyClassFactory是Proxy的一个静态内部类,实现了WeakCache的内部接口BiFunction的apply方法:
ProxyGenerator是sun.misc包中的类,它没有开源,但是可以反编译来一探究竟:
saveGeneratedFiles这个属性的值从哪里来呢:
GetBooleanAction实际上是调用Boolean.getBoolean(propName)来获得的,而Boolean.getBoolean(propName)调用了System.getProperty(name),所以我们可以设置sun.misc.ProxyGenerator.saveGeneratedFiles这个系统属性为true来把生成的class保存到本地文件来查看。
这里要注意,当把这个属性设置为true时,生成的class文件及其所在的路径都需要提前创建,否则会抛出FileNotFoundException异常。如:
即我们要在运行当前main方法的路径下创建com/sun/proxy目录,并创建一个$Proxy0.class文件,才能够正常运行并保存class文件内容。
反编译$Proxy0.class文件,如下所示:
可以看到,动态生成的代理类有如下特性:
至此JDK动态代理的实现原理就分析的差不多了。同时我们可以想像一下Spring AOP提供的各种拦截该如何实现,就已经很明了了,如下所示:
上面是对于Spring AOP使用JDK动态代理实现的基本框架代码,当然具体的实现肯定比这个复杂得多,但是基本原理不外乎如是。所以理解基本原理对于理解其他的代码也是很有好处的。
到了这里我们捋一捋调用过程:
JDK动态代理源码解析的更多相关文章
- java动态代理源码解析
众所周知,java动态代理同反射原理一直是许多框架的底层实现,之前一直没有时间来分析动态代理的底层源码,现结合源码分析一下动态代理的底层实现 类和接口 java动态代理的主要类和接口有:java.la ...
- jdk 动态代理源码分析
闲来无事,撸撸源码 使用方法 直接看代码吧.. package com.test.demo.proxy; import java.lang.reflect.InvocationHandler; imp ...
- 动态代理学习(二)JDK动态代理源码分析
上篇文章我们学习了如何自己实现一个动态代理,这篇文章我们从源码角度来分析下JDK的动态代理 先看一个Demo: public class MyInvocationHandler implements ...
- JDK动态代理源码学习
继上一篇博客设计模式之代理模式学习之后http://blog.csdn.net/u014427391/article/details/75115928,本博客介绍JDK动态代理的实现原理,学习一下JD ...
- JDK动态代理源码分析
先抛出一个问题,JDK的动态代理为什么不支持对实现类的代理,只支持接口的代理??? 首先来看一下如何使用JDK动态代理.JDK提供了Java.lang.reflect.Proxy类来实现动态代理的,可 ...
- 深入剖析JDK动态代理源码实现
动态代理.静态代理优缺点优点:业务类只需要关注业务逻辑本身,保证了业务类的重用性.这是代理的共有优点.动态代理只有在用到被代理对象的时候才会对被代理类进行类加载. 而静态代理在编译器就已经开始占内存了 ...
- 设计模式之JDK动态代理源码分析
这里查看JDK1.8.0_65的源码,通过debug学习JDK动态代理的实现原理 大概流程 1.为接口创建代理类的字节码文件 2.使用ClassLoader将字节码文件加载到JVM 3.创建代理类实例 ...
- 【趣味设计模式系列】之【代理模式2--JDK动态代理源码解析】
1. 图解 上图主要描述了JDK动态代理的执行过程,下面做详细分析. 2. Proxy源码分析 上一篇,在使用JDK动态代理的时候,借助于Proxy类,使用newProxyInstance静态方法,创 ...
- 【趣味设计模式系列】之【代理模式3--Cglib动态代理源码解析】
1. 图解 上图主要描述了Cglib动态代理的主要执行过程,下面做详细分析,以下源码使用的Cglib版本为3.2.12. 2. Enhancer源码分析 public Object create() ...
随机推荐
- Hystrix隔离策略
Hystrix的资源隔离策略分为两种:线程池和信号量.说到资源隔离,我们就要明白为什么需要资源隔离. 在一个分布式系统中,服务之间都是相互调用的,如下图所示:例如,我们容器(Tomcat)配置的线程个 ...
- 深入理解kafka设计原理
最近开研究kafka,下面分享一下kafka的设计原理.kafka的设计初衷是希望作为一个统一的信息收集平台,能够实时的收集反馈信息,并需要能够支撑较大的数据量,且具备良好的容错能力. 1.持久性 k ...
- [译]ABP vNext微服务演示,项目状态和路线图
译注: ABP的主要负责人hikalkan最近又发布了一篇博客, 说明了ABP vNext的微服务演示,项目状态和路线图.其中特意对ABP的中文社区进行了感谢! 本文翻译自该博客文章(https:// ...
- thinkphp常用标签总结
三元运算标签 {$v['member_id'] == 1 ? 正常 : 不正常} volist标签 <volist name="data" id="v"& ...
- 【图文详细教程】maven3安装配置+eclipse离线安装maven3插件《《唯一成功的教程~~~2018-01-09》》
环境搭建前提: 1.电脑上已经安装了1.7以及以上版本的JDK(因为我提供的maven版本是最新的3.3.9的,要求最低JDK1.7) 2.配置好了ecplise并且能正常启动 第一步:下载maven ...
- 数据库部分(MySql)_1
SQL规范 以 “ ; ” 结尾:关键字之间要有空格(可以由多个空格):SQL语句中可以一个或多个换行:关键字不区分大小写. 数据库相关SQL 查询所有数库库: show databases; 创建数 ...
- 性能监控(6)–JAVA下的jinfo命令
jinfo可以用来查看正在运行的java应用程序的扩展参数,设置支持在运行时,修改部分参数. Jinfo的语法为: Usage: jinfo [option] <pid> (to conn ...
- 最长子串(FZU2128)
最长子串 Time Limit:3000MS Memory Limit:65536KB 64bit IO Format:%I64d & %I64u Submit Status ...
- blfs(systemv版本)学习笔记-编译安装i3-wm平铺式窗口管理器
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! i3-wm项目的官网:https://i3wm.org/ 首先需要lfs基础上编译安装完整的xorg服务 我的xorg服务编译安 ...
- c语言学习笔记-continue
我的邮箱地址:zytrenren@163.com欢迎大家交流学习纠错! 一.continue函数意义 用于跳过本次循环余下语句,转去判断是否需要执行下次循环 二.continue使用案例 编写代码,对 ...