原文 https://blog.csdn.net/makecontral/article/details/79593732

Cglib的实例

本文重在源码的分析,Cglib的使用不再复述。

//被代理类
public class InfoDemo {
public void welcome (String person){
System.out.println("welcome :" + person);
}
} public class CglibInfoProxy implements MethodInterceptor {
private Object target;
public Object newInstance(Object source){
target = source;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(this.target.getClass());
enhancer.setCallback(this);
return enhancer.create();
} @Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("before method!!!");
Object value = methodProxy.invokeSuper(o, objects);
//Object value = methodProxy.invoke(o, objects);
return value;
}
public static void main(String[] args) {
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\\\classes");
InfoDemo instance = (InfoDemo) new CglibInfoProxy().newInstance(new InfoDemo());
instance.welcome("zhangsan");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
代理类字节码分析 先来看main函数中new CglibInfoProxy().newInstance(new InfoDemo()),简单来说这个方法是将infoDemo作为一个父类,通过asm字节码生成一个子类来继承infoDemo。
InfoDemo$$EnhancerByCGLIB$$8b8da05b.class,就是生成的子类的字节码,称这个子类为InfoDemo的代理类。截取部分字节码显示: public class InfoDemo$$EnhancerByCGLIB$$8b8da05b extends InfoDemo implements Factory {
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$welcome$0$Method;
private static final MethodProxy CGLIB$welcome$0$Proxy;
//..........................................省略
static void CGLIB$STATICHOOK1() {
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class var0 = Class.forName("CglibTest.InfoDemo$$EnhancerByCGLIB$$8b8da05b");
Class var1;
CGLIB$welcome$0$Method = ReflectUtils.findMethods(new String[]{"welcome", "(Ljava/lang/String;)V"}, (var1 = Class.forName("CglibTest.InfoDemo")).getDeclaredMethods())[0];
CGLIB$welcome$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0");
//..........................................省略
} final void CGLIB$welcome$0(String var1) {
super.welcome(var1);
}
//先来判断这个代理类中是否设置了方法拦截。如果设置了就调用该拦截器的intercept方法。
//在本程序中,我们是设置了拦截器的。enhancer.setCallback(this);
public final void welcome(String var1) {
MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
if(this.CGLIB$CALLBACK_0 == null) {
CGLIB$BIND_CALLBACKS(this);
var10000 = this.CGLIB$CALLBACK_0;
}
if(var10000 != null) {
var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy);
} else {
super.welcome(var1);
}
}
//..........................................省略
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
在这个代理类中,这里有两点需要注意。
1.它会将父类中的每一个方法,生成两个与之对应。如父类中的welcome,在代理类中就会有CGLIBwelcomewelcome0,welcome的两个方法与之对应。
2.每一个方法都会静态块中,经过MethodProxy.create生成对应的方法代理。如
MethodProxy.create(var1, var0, "(Ljava/lang/String;)V", "welcome", "CGLIB$welcome$0"); 当main函数执行到instance.welcome(“zhangsan”);这个语句时,会进入到代理类中的public final void welcome(String var1) 方法。进而执行了var10000.intercept()方法。
根据CglibInfoProxy中的intercept,先是会输出一句“before method!!!”,然后调用methodProxy.invokeSuper(o, objects);
这里的methodProxy对应的是var10000.intercept(this, CGLIB$welcome$0$Method, new Object[]{var1}, CGLIB$welcome$0$Proxy)中的CGLIB$welcome$0$Proxy。 那么就相当于执行CGLIB$welcome$0$Proxy.invokeSuper(o, objects),那么来看看invokeSuper在做什么。 MethodProxy 源码分析 public class MethodProxy {
//下面的前三个变量在create方法中,都已经得到了初始值了。
private Signature sig1;
private Signature sig2;
private MethodProxy.CreateInfo createInfo;
//FastClassInfo是在调用methodProxy.invoke或者methodProxy.invokeSuper中,init()会触发,后面再来细看这个。
private volatile MethodProxy.FastClassInfo fastClassInfo; public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.FastClassInfo fci = this.fastClassInfo;
return fci.f2.invoke(fci.i2, obj, args);
} catch (InvocationTargetException var4) {
throw var4.getTargetException();
}
}
public Object invoke(Object obj, Object[] args) throws Throwable {
try {
this.init();
MethodProxy.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;
}
}
}
//本质就是要生成一个fastClassInfo,fastClassInfo里面存放着两个fastclass,f1,f2。
//还有两个方法索引的值i1,i2。
private void init() {
if(this.fastClassInfo == null) {
Object var1 = this.initLock;
synchronized(this.initLock) {
if(this.fastClassInfo == null) {
MethodProxy.CreateInfo ci = this.createInfo;
MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
//难道每一个方法,我们都去生成一个fastclass吗?
//不是的,每一个方法的fastclass都是一样的,只不过他们的i1,i2不一样。如果缓存中就取出,没有就生成新的FastClass
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;
}
}
}
}
//根据一个类的信息,返回的该对象的一个Fastclass
private static FastClass helper(MethodProxy.CreateInfo ci, Class type) {
Generator g = new Generator();
g.setType(type);
g.setClassLoader(ci.c2.getClassLoader());
g.setNamingPolicy(ci.namingPolicy);
g.setStrategy(ci.strategy);
g.setAttemptLoad(ci.attemptLoad);
return g.create();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
init()的过程就是生成FastClassInfo。
对于FastClass可以看看这篇文章https://www.cnblogs.com/cruze/p/3865180.html fci.f2.invoke(fci.i2, obj, args);fci存放了两个类的fastClass。
其中f1是被代理的类对应的是InfoDemo$$FastClassByCGLIB$$f4c7f3ac.class,
f2InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c.class
这也就是为什么,在classes文件中会生成三个class文件了,一个代理类,两个fastclass.
f2.invoke,那就说明会调用InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c这个类的方法。 代理类的FastClass 那再看看代理类的fastClass的字节码长啥样
贴出InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c的部分字节码: public class InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c extends FastClass { public InfoDemo$$EnhancerByCGLIB$$8b8da05b$$FastClassByCGLIB$$9065a6c(Class var1) {
super(var1);
}
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 12;
}
break;
case -1725733088:
if(var10000.equals("getClass()Ljava/lang/Class;")) {
return 24;
}
case 1013143764:
if(var10000.equals("CGLIB$welcome$0(Ljava/lang/String;)V")) {
return 17;
}
}
//----省略
} public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
8b8da05b var10000 = (8b8da05b)var2;
int var10001 = var1; try {
switch(var10001) {
case 0:
return var10000.toString();
case 1:
return new Integer(var10000.hashCode()); case 17:
var10000.CGLIB$welcome$0((String)var3[0]);
}
//----省略
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
可以看到,会执行到voke方法中的
var10000.CGLIB$welcome$0((String)var3[0]);,而CGLIB$welcome$0其实就是直接调用了super.welcome(var1)的方法。输出结束之后就会运行完毕。 那么,如果现在调用的是methodProxy.invoke(o, objects);而不是invokeSuper会是怎么样的情况呢?
通过上面MethodProxy的源码,可以看到当执行invoke的时候会执行到fci.f1.invoke(fci.i1, obj, args);
即会调用被代理类的fastclass,InfoDemo$$FastClassByCGLIB$$f4c7f3ac.class中的invoke。
打开该class文件我们会发现,执行的的是welcome()。 public Object invoke(Object obj, Object[] args) throws Throwable {
try {
switch(var10001) {
case 0:
var10000.welcome((String)var3[0]);
return null;
case 1:
}
}
1
2
3
4
5
6
7
8
9
那不就是和在main函数中instance.welcome(“zhangsan”)一样的步骤了嘛,就会又开始循环调用,一直到栈溢出报错。所以,invoke会造成OOM的问题。

  

Cglib源码分析 invoke和invokeSuper的差别(转)的更多相关文章

  1. cglib源码分析(一): 缓存和KEY

    cglib是一个java 字节码的生成工具,它是对asm的进一步封装,提供了一系列class generator.研究cglib主要是因为它也提供了动态代理功能,这点和jdk的动态代理类似. 一. C ...

  2. cglib源码分析--转

    原文地址:http://www.iteye.com/topic/799827 背景 前段时间在工作中,包括一些代码阅读过程中,spring aop经常性的会看到cglib中的相关内容,包括BeanCo ...

  3. cglib源码分析(四):cglib 动态代理原理分析

    本文分下面三个部分来分析cglib动态代理的原理. cglib 动态代理示例 代理类分析 Fastclass 机制分析 一.cglib 动态代理示例 public class Target{ publ ...

  4. cglib源码分析(二):Class name 生成策略

    一.如何获取动态生成的class 字节码 结合生成的class文件是一个学习cglib的比较好的方法.在cglib中,生成的class文件默认只存储在内存中,我们可以在代码中加入下面语句来获取clas ...

  5. cglib源码分析(三):Class生成策略

    cglib中生成类的工作是由AbstractClassGenerator的create方法使用相应的生成策略完成,具体代码如下: private GeneratorStrategy strategy ...

  6. spring源码分析之spring-core总结篇

    1.spring-core概览 spring-core是spring框架的基石,它为spring框架提供了基础的支持. spring-core从源码上看,分为6个package,分别是asm,cgli ...

  7. Spring AOP源码分析(三):基于JDK动态代理和CGLIB创建代理对象的实现原理

    AOP代理对象的创建 AOP相关的代理对象的创建主要在applyBeanPostProcessorsBeforeInstantiation方法实现: protected Object applyBea ...

  8. Spring AOP 源码分析 - 创建代理对象

    1.简介 在上一篇文章中,我分析了 Spring 是如何为目标 bean 筛选合适的通知器的.现在通知器选好了,接下来就要通过代理的方式将通知器(Advisor)所持有的通知(Advice)织入到 b ...

  9. spring源码分析(二)Aop

    创建日期:2016.08.19 修改日期:2016.08.20-2016.08.21 交流QQ:992591601 参考资料:<spring源码深度解析>.<spring技术内幕&g ...

随机推荐

  1. 构建工具build tools

    构建工具是从源代码自动创建可执行应用程序的程序(例如.apk for android app).构建包括将代码编译,链接和打包成可用或可执行的形式. 基本上,构建自动化是脚本或自动化软件开发人员在日常 ...

  2. Django框架(六)

    十一.Django组件-cookie与session 1.会话跟踪技术 (1) 什么是会话跟踪技术 我们需要先了解一下什么是会话!可以把会话理解为客户端与服务器之间的一次会晤,在一次会晤中可能会包含多 ...

  3. html5(七) Web存储

    http://www.cnblogs.com/stoneniqiu/p/4206796.html http://www.cnblogs.com/v10258/p/3700486.html html5中 ...

  4. Python _Mix*9

    1. 函数 函数是对功能的封装 语法: def 函数名(形参列表): 函数体(代码块) 代码块中有可能包含return 调用: 函数名(实参列表) def mix(a,b): #def 函数名(a和b ...

  5. 【阅读笔记】《C程序员 从校园到职场》第六章 配置文件,makefile 文件 (Part 2)

     Contents: 1.配置文件(通常以 ini 结尾) 2.makefile文件 (Linux) PS: 这篇文章的内容,不太理解. 一.配置文件 本文以一个实际的小软件为例,介绍了C语言中配置文 ...

  6. 第三组 通信一班 030 OSPFv2、OSPFv3综合实验

      一.        实验目的 掌握 OSPFv2. OSPFv3 的配置方法 掌握在帧中继环境下OSPFv2. OSPFv3 的配置方法 掌握 OSPFv2. OSPFv3 NSSA 的配置方法 ...

  7. js运行机制

    情况一 script标签里面的运行顺序是同步的 遇到settimeout的时候就会变异步,最后执行 执行顺序为1342 情况二 只输出a 情况三 输出4444 异步队列插入的时间和执行时间 for循环 ...

  8. c++ 生成dll文件并调用

    2 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18.

  9. ubuntu 部署 wiki.js

    1. 安装node  (还是官网的东西靠谱,虽然是english)  https://github.com/nodesource/distributions/blob/master/README.md ...

  10. 【oracle入门】数据库系统结构----三级模式

    概念模式:概念模式也称模式,是对数据库中全局数据路基结构的描述,是全体用户公共的数据视图.这种描述是抽象描述,不涉及具体硬件环境与平台,也与具有软件环境无关. 外模式:外模式也称子模式或者用户模式,他 ...