jdk和cglib简单理解
之前使用cglib的时候不需要将classLoader作为参数传入,但动态代理却要,带着这个疑惑进入这个方法:
Proxy.newProxyInstance(classLoader, interfaces, InvocationHandler)
要在classLoader里去找interfaces,如果也加载进来了才能继续执行,并且用ProxyGenerator动态生成了一个代理类的字节码文件(使用了缓存技术,只需要生成一次),然后用classLoader将这个字节码文件加载进来。这就是classLoader的作用。
可以这样看生成的字节码类。
加入执行参数:
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")
生成的字节码文件就会保留下来,然后编译出来如下:
package demo; 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 Proxy implements IA {
private static Method m1;
private static Method m4;
private static Method m3;
private static Method m0;
private static Method m2; public $Proxy0(InvocationHandler paramInvocationHandler) {
super(paramInvocationHandler);
} public final boolean equals(Object paramObject) {
try {
return ((Boolean) this.h.invoke(this, m1,
new Object[] { paramObject })).booleanValue();
} catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
} public final int b(String paramString) {
try {
return ((Integer) this.h.invoke(this, m4,
new Object[] { paramString })).intValue();
} catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
} public final void a() {
try {
this.h.invoke(this, m3, null);
return;
} catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
} public final int hashCode() {
try {
return ((Integer) this.h.invoke(this, m0, null)).intValue();
} catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
} public final String toString() {
try {
return (String) this.h.invoke(this, m2, null);
} catch (RuntimeException localRuntimeException) {
throw localRuntimeException;
} catch (Throwable localThrowable) {
throw new UndeclaredThrowableException(localThrowable);
}
} static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m4 = Class.forName("demo.IA").getMethod("b",
new Class[] { Class.forName("java.lang.String") });
m3 = Class.forName("demo.IA").getMethod("a", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
}
可以发现所有接口方法的实现都委托给InvocationHandler的invoke方法了,这也就是实现代理模式的地方了。
--------------------------------------------------------------------------
cglib不需要传入ClassLoader,代码里会自己去找上下文的ClassLoader,这种设计使少传一个ClassLoader这种很少见的参数对初学者来说用起来要简单点。
可以设置System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "字节码文件保存位置",把cglib生成的动态字节码保存下来。
单间分析下生成的字节码
动态生成的继承类会改写我们使用的父类的所有方法,拦截下来交给设置的MethodInterceptor去执行。
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy)
第一个参数obj就是动态生成的子类。第二个参数是原始类的方法。
我们一般使用proxy.invokeSuper(obj,args)方法。这个很好理解,就是执行原始类的方法。还有一个方法proxy.invoke(obj,args),这是执行生成子类的方法。如果传入的obj就是子类的话,会发生内存溢出,因为子类的方法不挺地进入intercept方法,而这个方法又去调用子类的方法,两个方法直接循环调用了。
我们来看看MethodProxy,原始类里每一个方法都会在动态的子类里有一个对应的MethodProxy,而一个MethodProxy又对应了两个动态生成的FastClass类,一个是对应原始方法,一个对应新生成的子类,MethodProxy.invokeSuper就是交给对应原始方法那个FastClass,MethodProxy.invoke交给另一个。
这2个额外生成的类作用在于当我们调用一个方法时,不通过反射来调用,而是通过类似于数组下标的方式来定位方法,直接进行类方法的执行。
FastClass生成的代码类似这样的
public Object invoke(int paramInt, Object paramObject, Object[] paramArrayOfObject)
throws InvocationTargetException
{
// Byte code:
0: aload_2
1: checkcast 159 net/sf/cglib/mytest/A$$EnhancerByCGLIB$$f84d7df
4: iload_1 //paramInt参数入栈
5: tableswitch default:+403 -> 408, 0:+131->136..... //通过paramInt也就相当于数组小标志,定位到方法执行的代码段
.....
.....
148: aload_3
149: iconst_0
150: aaload
151: invokevirtual 166 net/sf/cglib/mytest/A$$EnhancerByCGLIB$$f84d7df:equals (Ljava/lang/Object;)Z //直接快速的执行方法
.....
.....
}
}
jdk和cglib简单理解的更多相关文章
- jdk和cglib简单理解(转)
之前使用cglib的时候不需要将classLoader作为参数传入,但动态代理却要,带着这个疑惑进入这个方法: Proxy.newProxyInstance(classLoader, interfac ...
- Spring AOP中的JDK和CGLib动态代理哪个效率更高?
一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理, ...
- JDK动态代理深入理解分析并手写简易JDK动态代理(下)
原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-05/27.html 作者:夜月归途 出处:http://www.guitu ...
- JDK动态代理深入理解分析并手写简易JDK动态代理(上)
原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-03/27.html 作者:夜月归途 出处:http://www.guitu ...
- 【Spring】AOP的代理默认是Jdk还是Cglib?
菜瓜:你觉得AOP是啥 水稻:我觉得吧,AOP是对OOP的补充.通常情况下,OOP代码专注功能的实现,所谓面向切面编程,大多数时候是对某一类对象的方法或者功能进行增强或者抽象 菜瓜:我看你这个理解就挺 ...
- java的静态代理和动态代理(jdk、cglib)
一.代理模式 代理的概念来自于设计模式中的代理模式,先了解一下代理模式 1.结构图 2.参与者 Subject:接口,定义代理类和实际类的共用接口 RealSubject:实际类,实现Subject这 ...
- JDK、CGlib动态代理详解
Java动态代理之JDK实现和CGlib实现(简单易懂) 一 JDK和CGLIB动态代理原理 1.JDK动态代理 利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生 ...
- java设计模式(一)动态代理模式,JDK与CGLIB分析
-本想着这个知识点放到Spring Aop说说可能更合适一点,但因为上一篇有所提到就简单分析下,不足之处请多多评论留言,相互学习,有所提高才是关键! 什么是代理模式: 记得有本24种设计模式的书讲到代 ...
- 关于RabbitMQ的简单理解
说明:想要理解RabbitMQ,需要先理解MQ是什么?能做什么?然后根据基础知识去理解RabbitMQ是什么.提供了什么功能. 一.MQ的简单理解 1. 什么是MQ? 消息队列(Message Que ...
随机推荐
- JqGrid: paging int asp.net
https://www.codeproject.com/Articles/1118363/GridView-with-Server-Side-Filtering-Sorting-and-Pa http ...
- jquery弹窗时禁止body滚动条滚动
当弹出一个jq提示窗口的时候,一般窗口右边还会有进度条的情况,禁止进度条方法禁止浏览器滚动条滚动: $('body').css({ "overflow-x":"hidde ...
- python自动化开发-4
装饰器之我见 python里的装饰器到底是个什么东东,初看起来,可能有的小伙伴会发懵啊,哈哈. 先来个装饰器的例子瞅瞅: Author:RYB # __*__coding:utf-8__*__ ''' ...
- Python 获取被调用函数名称,所处模块,被调用代码行
获取被调用函数名称,所处模块,被调用代码行 by:授客 QQ:1033553122 module2.py: #!/usr/bin/env python # -*- coding:utf-8 -*- _ ...
- 《Inside C#》笔记(二) 初识C#
一 程序的编译.构成 a) 编写C#代码一般用VS,但作者在这儿介绍了使用记事本编写C#代码并编译运行的过程,以便对VS有更深入的认识. 用记事本编写C#代码后,修改文本文件的后缀为.cs,然后用cs ...
- Related concepts of testing
根据是否知道源代码测试可以分为黑盒和白盒. 黑盒:功能测试. 白盒:知道源代码,要写测试代码. 根据测试的粒度. 方法测试: 单元测试: 集成测试: 系统测试: 根据测试的暴力程度. 压力测试:谷歌工 ...
- HTML5文件API之FileReader
在文件上传之前,我们总想预览一下文件内容,或图片样子,html5 中FileReader正好提供了2种方法,可以在不上传文件的情况下,预览文件内容. 图片预览:readAsDataURL(file); ...
- [20170916]sqlplus set array最小2补充.txt
[20170916]sqlplus set array最小2补充.txt --//以前写的贴子,链接http://blog.itpub.net/267265/viewspace-1453652/--/ ...
- python第七十六天--堡垒机完成
堡垒机windows ,linux 都通过测试初始化说明: #进入根目录 1.初始化表结构 #python3 bin/start.py syncdb 2.创建堡垒机用户 #python3 bin/st ...
- 【第三篇】SAP ABAP7.5x新语法之程序结构&SubScreen
公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文地址:SAP ABAP7.5x系列之程序结构& ...