原文 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. 若依项目整合eCharts实现图表统计功能

    eCharts是一款强大的图表统计工具,具体介绍可查看其官网 http://echarts.baidu.com/echarts2/index.html 下面记录一下如何在若依项目中使用eCharts. ...

  2. CentOS7下swap分区创建(添加),删除以及相关配置

    在添加swap分区之前我们可以了解下当前系统swap是否存在以及使用情况,可用: 1. free –h 或 swapon –s 了解硬盘使用情况(一般/dev/vda1为挂载硬盘): 1. df –h ...

  3. select * from 后有多个表的使用方法

    已知一个表的结构为: ------------------- 姓名 科目 成绩 张三 语文 20 张三 数学 30 张三 英语 50 李四 语文 70 李四 数学 60 李四 英语 90   怎样通过 ...

  4. 用Apache Ant在Weka中嵌入新算法

    本文将介绍一种新的添加新的算法到Weka中的方法,国内的论坛基本都是通过IDE(Eclipse或NetBeans)编译,详细教程请见上一篇博客.经研究,发现国外的网站很流行用Ant这个方法,教程奉上. ...

  5. linux 使用split分割大文件

    1.分割 -- split命令 可以指定按行数分割和按字节大小分割两种模式. (1) 按行数分割 $ split -l 300 large_file.txt new_file_prefix 加上-d, ...

  6. Sql server数据库连接Oracle库的步骤

    本地使用环境 操作系统: win10 64  ,SQL Server 2012 ,Oracle  Server 11g 第一步:安装好oracle客户端,并配置好TNS信息 ORCL = (DESCR ...

  7. protobuf example make backup

    # See README.txt. .PHONY: all cpp java python clean all: cpp #java python cpp: add_person_cpp list_p ...

  8. tensorFlow(二)线性回归

    需要TensorFlow基础,见TensorFlow(一) 原理默认了解不赘述 实例: 模型创建: #!/usr/bin/python # -*- coding: utf-8 -* import te ...

  9. 二十二. Python基础(22)--继承

    二十二. Python基础(22)--继承 ● 知识框架   ● 继承关系中self的指向 当一个对象调用一个方法时,这个方法的self形参会指向这个对象 class A:     def get(s ...

  10. github的优势

    1.GitHub作为托管平台只支持git版本库托管而不像其他开源项目托管平台还对CVS.SVN.Hg 等格式的版本库进行托管.GitHub 的哲学很简单,既然 Git 是最好的版本控制系统之一(对于很 ...