动态代理-cglib分析
生成代理类文件的方式
jvm添加此启动参数,后面就是代理类class生成的地址
-Dcglib.debugLocation=~/baldhead/java/dynamic-proxy-cglib/src/main/java/com/baldhead/dynamic/proxy/cglib/class
添加这个参数之后,CGLIB就会把生成的代理Class文件存在指定的路径
生成动态代理对象流程
- CGLIB首先生成代理类
- 代码中的 static 静态代码块 会调用
CGLIB$STATICHOOK1();
方法,方法作用
3. 新建一个名字为CGLIB$THREAD_CALLBACKS
的ThreadLocal
,用来存放所设置的callback
4. 使用反射找到代理类中的所有方法,包括(toString
、hashCode
、equals
、clone
),名字为模板CGLIB$METHODNAME$数字编号$Method
并且给对应的方法创建代理方法 名字模板CGLIB$METHODNAME$数字编号$Proxy
- 调用构造方法创建代理对象
- 然后CGLIB会调用代理对象的
CGLIB$SET_THREAD_CALLBACKS
方法,将传入的callBack
存到ThreadLocal(CGLIB$THREAD_CALLBACKS)
中去 - 后续在对象执行需要代理的方法的时候,就会从
CGLIB$THREAD_CALLBACKS
中拿到所设置的CallBack
并调用它的intercept()
方法
代理对象的创建
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("com.baldhead.dynamic.proxy.cglib.Impl.UserService$$EnhancerByCGLIB$$e34eec9a");
Class var1;
CGLIB$test$0$Method = ReflectUtils.findMethods(new String[]{"test", "()V"}, (var1 = Class.forName("com.baldhead.dynamic.proxy.cglib.Impl.UserService")).getDeclaredMethods())[0];
CGLIB$test$0$Proxy = MethodProxy.create(var1, var0, "()V", "test", "CGLIB$test$0");
}
以上代码经过简化的,主要看下面给出的一行
CGLIB$test$0$Proxy = MethodProxy.create(var1, var0, "()V", "test", "CGLIB$test$0");
对应的方法如下
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
/**
* 这几个参数都可以找到入参对象
* c1: 被代理类对象的class,也就是原始对象的class
* c2: 代理类对象的 class
* desc: 方法的返回值类型
* name1: 原始代理方法的名称
* name2: 代理方法在代理类中的名称(CGLIB$test$0)
*/
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc);
proxy.sig2 = new Signature(name2, desc);
proxy.createInfo = new CreateInfo(c1, c2);
return proxy;
}
在MethodProxy中有三个很重要的属性
- sig1: 表示test方法
- sig2: 表示 CGLIB$test$0 方法
- createInfo: 表示原始类和代理类
invoke和invokeSuper方法
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
this.init();
FastClassInfo fci = this.fastClassInfo;
return fci.f1.invoke(fci.i1, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
} catch (IllegalArgumentException var5) {
if (this.fastClassInfo.i1 < 0) {
throw new IllegalArgumentException("Protected method: " + this.sig1);
} else {
throw var5;
}
}
}
public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
两个方法大差不差的,但是都用到了一个对象 fastClassInfo
这个对象是在 init()
方法中构造的
private void init() {
if (this.fastClassInfo == null) {
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
CreateInfo ci = this.createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(this.sig1);
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
this.createInfo = null;
}
}
}
}
fastClassInfo
对象中主要是有四个属性
- f1: 原始类对应的一个FastClass 代理对象
- f2: 代理类对应的一个FastClass 代理对象
- i1: test方法在原始类对应的一个FastClass代理对象中的下标
- i2: CGLIB$test$0方法在代理类对应的一个 FastClass 代理对象中的下标
这里产生了两个代理对象,你说好巧不巧,正好产生的代理,class有3个,其中有两个继承 FastClass, 另外一个继承原始类并且实现 Factory接口
其实这两个类类似,都是针对某一个类的FastClass代理类,所以我们好好看一下UserService所对应的FastClass该类主要有:
- 一个构造方法
public int getlndex(Signature var1)
public int getlndex(String var1, Classll var2)
public int getlndex(ClassI var1)
public Object invoke(int var1, Object ar2, Objectll var3)
public Object newlnstance(int var1, Objectll var2)
public int getMaxlndex0
顾名思义,FastClass的作用是提高方法的执行速度,按照正常的实现,当我们调用MethodProxy对象的invokel或invokeSuper0方法时,首先应该要做到的就是找到对应的Method对象,比如:
执行invoke0,要找到test方法对应的Method对象
执行invokeSuper0,要找到CGLIBstest$00方法对应的Method对象然后利用反射来执行Method。
那么FastClass的机制就是预先把UserService类或UserService代理类中的所有方法做一个索引,比如:
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch (var10000.hashCode()) {
case -2055565910:
if (var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
return 19;
}
break;
case -1659690448:
if (var10000.equals("CGLIB$test$4()V")) {
return 20;
}
break;
case -1457535688:
if (var10000.equals("CGLIB$STATICHOOK1()V")) {
return 12;
}
break;
case -1422510685:
if (var10000.equals("test()V")) {
return 7;
}
break;
case -1411872516:
if (var10000.equals("CGLIB$hashCode$2()I")) {
return 15;
}
break;
// 省略部分代码
}
return -1;
}
一旦调用 getIndex(Signature var1)
方法,就对得到对应方法返回的索引,例如这里就是test
方法返回的对应的索引就是7
再回到init
方法
private void init() {
if (this.fastClassInfo == null) {
synchronized(this.initLock) {
if (this.fastClassInfo == null) {
CreateInfo ci = this.createInfo;
FastClassInfo fci = new FastClassInfo();
fci.f1 = helper(ci, ci.c1);
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(this.sig1);
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
this.createInfo = null;
}
}
}
}
init方法中的两个 helper方法就是去生成原始类和代理类的 FactClass代理类,后面个两个getIndex方法
1. 第一个fci.f1.getIndex(this.sig1)
就是去获取原始类对应的FastClass代理类中 test方法的下标i1
2. 第二个 fci.f2.getIndex(this.sig2)
就是去获取代理类对应的FastClass代理类中$test$0方法的下标i2
然后会把两个下标都记录在 fastClassInfo 对象中
后面就是我们看到的invoke
和invokeSuper
中调用的两个方法
invoke
fci.f1.invoke(fci.i1, obj, args);
执行原始类对应的FastClass 代理类的invoke方法
invokeSuper
fci.f2.invoke(fci.i2, obj, args);
执行代理类对应的
FastClass
代理类的invoke
方法
例如: 原始类对应的FastClass 代码
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
UserService var10000 = (UserService)var2;
int var10001 = var1;
try {
switch (var10001) {
case 0:
var10000.test();
return null;
case 1:
return new Boolean(var10000.equals(var3[0]));
case 2:
return var10000.toString();
case 3:
return new Integer(var10000.hashCode());
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
这个代码比较简单,第一个参数就是执行方法的index,第二个参数就是原始类,第三个就是原始类的参数
如果传入的index 是0 ,那么就会去执行test
方法
代理类对应的FastClass
代理类的invoke
方法也是类似
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
UserService..EnhancerByCGLIB..e34eec9a var10000 = (UserService..EnhancerByCGLIB..e34eec9a)var2;
int var10001 = var1;
try {
switch (var10001) {
case 0:
return new Boolean(var10000.equals(var3[0]));
case 1:
return var10000.toString();
case 2:
return new Integer(var10000.hashCode());
case 3:
return var10000.clone();
case 4:
return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
case 5:
return var10000.newInstance((Callback[])var3[0]);
case 6:
return var10000.newInstance((Callback)var3[0]);
case 7:
var10000.test();
return null;
case 8:
e34eec9a.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
return null;
case 9:
e34eec9a.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
return null;
case 10:
var10000.setCallbacks((Callback[])var3[0]);
return null;
case 11:
return var10000.getCallback(((Number)var3[0]).intValue());
case 12:
return var10000.getCallbacks();
case 13:
var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
return null;
case 14:
return e34eec9a.CGLIB$findMethodProxy((Signature)var3[0]);
case 15:
e34eec9a.CGLIB$STATICHOOK1();
return null;
case 16:
var10000.CGLIB$test$0();
return null;
case 17:
return new Integer(var10000.CGLIB$hashCode$3());
case 18:
return new Boolean(var10000.CGLIB$equals$1(var3[0]));
case 19:
return var10000.CGLIB$toString$2();
case 20:
return var10000.CGLIB$clone$4();
}
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
}
throw new IllegalArgumentException("Cannot find matching method/constructor");
}
例如传入的index 是16 那么执行的就是 var10000.CGLIB$test$0();
如果传入的index是 7 那么执行的就是var10000.test();
var10000 是传入对象强转为UserService..EnhancerByCGLIB..e34eec9a
类的对象,UserService..EnhancerByCGLIB..e34eec9a
类其实就是UserService
的代理类
invokeSuper结论
所以当我们执行invokeSuper
方法的时候,不能传入原始类(UserService)
只能传入代理类对象,不然就无法转换成为代理类类型
所以FastClass
快的地方就是预先把所有的方法信息都生成了对应的index,在真正的去执行的时候不用再去找Method对象,直接传入对应方法的index就可以直接执行对应的方法了
动态代理-cglib分析的更多相关文章
- Java代理和动态代理机制分析和应用
本博文中项目代码已开源下载地址:GitHub Java代理和动态代理机制分析和应用 概述 代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问.代理类负责为委托类预处理消息 ...
- Proxy 代理模式 动态代理 cglib MD
Markdown版本笔记 我的GitHub首页 我的博客 我的微信 我的邮箱 MyAndroidBlogs baiqiantao baiqiantao bqt20094 baiqiantao@sina ...
- Java 动态代理机制分析及扩展
Java 动态代理机制分析及扩展,第 1 部分 王 忠平, 软件工程师, IBM 何 平, 软件工程师, IBM 简介: 本文通过分析 Java 动态代理的机制和特点,解读动态代理类的源代码,并且模拟 ...
- 获取JDK动态代理/CGLIB代理对象代理的目标对象。
问题描述:: 我现在遇到个棘手的问题,要通过spring托管的service类保存对象,这个类是通过反射拿到的,经过实验发现这个类只能反射取得sservice实现了接口的方法,而extends类的方法 ...
- java:java静态代理与动态代理简单分析
java静态代理与动态代理简单分析 转载自:http://www.cnblogs.com/V1haoge/p/5860749.html 1.动态代理(Dynamic Proxy) 代理分为静态代理和动 ...
- Java 动态代理机制分析及扩展,第 1 部分
Java 动态代理机制分析及扩展,第 1 部分 http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/ 本文通过分析 Java 动态代理的机制和特 ...
- 基于JDK的动态代理原理分析
基于JDK的动态代理原理分析 这篇文章解决三个问题: What 动态代理是什么 How 动态代理怎么用 Why 动态代理的原理 动态代理是什么? 动态代理是代理模式的一种具体实现,是指在程序运行期间, ...
- cglib源码分析(四):cglib 动态代理原理分析
本文分下面三个部分来分析cglib动态代理的原理. cglib 动态代理示例 代理类分析 Fastclass 机制分析 一.cglib 动态代理示例 public class Target{ publ ...
- 代理模式(静态代理、JDK动态代理原理分析、CGLIB动态代理)
代理模式 代理模式是设计模式之一,为一个对象提供一个替身或者占位符以控制对这个对象的访问,它给目标对象提供一个代理对象,由代理对象控制对目标对象的访问. 那么为什么要使用代理模式呢? 1.隔离,客户端 ...
- Java动态代理全面分析
代理模式 解说:给某一个对象提供一个代理,并由代理对象控制对原对象的引用: 代理模式需要以下几个角色: 1 主题:规定代理类和真实对象共同对外暴露的接口: 2 代理类:专门代理真实对象的类: 3 ...
随机推荐
- 云原生之旅 - 9)云原生时代网关的后起之秀Envoy Proxy 和基于Envoy 的 Emissary Ingress
前言 前一篇文章讲述了基于Nginx代理的Kuberenetes Ingress Nginx[云原生时代的网关 Ingress Nginx]这次给大家介绍下基于Envoy的 Emissary Ingr ...
- 【JAVA】详解在JAVA中int与Integer的区别以及背后的原因。
区别 首先我们要明确,这两点之间有什么区别? 主要有以下几点: 数据类型不同:int是基础数据类型,而 Integer是包装数据类型: 默认值不同:int的默认值是 0,而 Integer的默认值是 ...
- Re:从零开始教你使用 Sublime Text
目录 Re:从零开始教你使用 Sublime Text 0.前言 0-0.关于我为什么要写这篇文章 0-1.关于这篇文章会讲什么 0-2.适用人群 0-4.其他 0-5.无用的统计 1.安装和基础功能 ...
- 如何通过Java代码给Word文档添加水印?
Word中可以为文档添加的水印分为两种形式:文字水印和图片水印.水印是一种数字保护的手段,在文档上添加水印可以传达有用信息,或者在不影响正文文字显示效果的同时,为打印文档增添视觉趣味,能起到传递信息, ...
- cmd唤醒windows设置,并配置opsshd
1. 从cmd唤起windows设置 这个东西很有意思,大部分在运行窗口输入的内容,从cmd或powershell都能唤起,如:control控制面板,但偶尔有些操作就不能通用, 如: ms-sett ...
- Zabbix与乐维监控对比分析(一)——架构、性能篇
近年来,Zabbix凭借其近乎无所不能的监控及优越的性能一路高歌猛进,在开源监控领域独占鳌头:而作为后起的新锐IT监控平台--乐维监控,则不断吸收Zabbix,Prometheus等优秀开源平台的优点 ...
- day33-JSON&Ajax01
JSON&Ajax01 JSON 在线文档 AJAX 在线文档 1.JSON介绍 JSON指的是JavaScript对象表示法( JavaScript Object Notation),JSO ...
- 持续发烧,试试Dart语言的异步操作,效率提升500%
前言 昨天发了篇文章<Dart开发服务端,我是不是发烧(骚)了>,承蒙小编看得起上了首页. 今天持续发烧,再来写写如何使用 Dart 语言的异步操作.说起异步操作,玩 NodeJS 的同学 ...
- 对于async和await的使用方式、作用效果不怎么理解 ?没关系,初步看这篇就够了
结论 同步还是异步,区别如下: 同步:你使用 await 修饰符去调用一个异步(async)方法(是异步方法,不过是阻塞式的,可简单理解为同步): 异步:你获取异步方法返回的 Task,就是异步(后文 ...
- RequestMappingHandlerMapping请求地址映射的初始化流程!
之前的文章里,介绍了DispatcherSerlvet处理请求的流程. 其中一个核心的步骤是:请求地址映射,即根据request获取对应的HandlerExcecutionChain. 为了后续的请求 ...