Cglib动态代理实现原理
Cglib动态代理实现方式
我们先通过一个demo看一下Cglib是如何实现动态代理的。
首先定义个服务类,有两个方法并且其中一个方法用final来修饰。
public class PersonService {
public PersonService() {
System.out.println("PersonService构造");
}
//该方法不能被子类覆盖
final public Person getPerson(String code) {
System.out.println("PersonService:getPerson>>"+code);
return null;
} public void setPerson() {
System.out.println("PersonService:setPerson");
}
}
Cglib是无法代理final修饰的方法的,具体原因我们一会通过源码来分析。
然后,定义一个自定义MethodInterceptor。
public class CglibProxyIntercepter implements MethodInterceptor {
@Override
public Object intercept(Object sub, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("执行前...");
Object object = methodProxy.invokeSuper(sub, objects);
System.out.println("执行后...");
return object;
}
}
我们看一下intercept方法入参,sub:cglib生成的代理对象,method:被代理对象方法,objects:方法入参,methodProxy:代理方法
最后,我们写个例子调用一下,并将Cglib生成的代理类class文件输出磁盘方便我们反编译查看源码。
public class Test {
public static void main(String[] args) {
//代理类class文件存入本地磁盘
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(PersonService.class);
enhancer.setCallback(new CglibProxyIntercepter());
PersonService proxy= (PersonService) enhancer.create();
proxy.setPerson();
proxy.getPerson("1");
} }
我们执行一下会发现getPerson因为加final修饰并没有被代理,下面我们通过源码分析一下。
执行前...
PersonService:setPerson
执行后...
PersonService:getPerson>>1
生成代理类
执行Test测试类可以得到Cglib生成的class文件,一共有三个class文件我们反编译以后逐个说一下他们的作用。
PersonService$$EnhancerByCGLIB$$eaaaed75就是cglib生成的代理类,它继承了PersonService类。
public class PersonService$$EnhancerByCGLIB$$eaaaed75
extends PersonService
implements Factory
{
private boolean CGLIB$BOUND;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final Callback[] CGLIB$STATIC_CALLBACKS;
private MethodInterceptor CGLIB$CALLBACK_0;//拦截器
private static final Method CGLIB$setPerson$0$Method;//被代理方法
private static final MethodProxy CGLIB$setPerson$0$Proxy;//代理方法
private static final Object[] CGLIB$emptyArgs;
private static final Method CGLIB$finalize$1$Method;
private static final MethodProxy CGLIB$finalize$1$Proxy;
private static final Method CGLIB$equals$2$Method;
private static final MethodProxy CGLIB$equals$2$Proxy;
private static final Method CGLIB$toString$3$Method;
private static final MethodProxy CGLIB$toString$3$Proxy;
private static final Method CGLIB$hashCode$4$Method;
private static final MethodProxy CGLIB$hashCode$4$Proxy;
private static final Method CGLIB$clone$5$Method;
private static final MethodProxy CGLIB$clone$5$Proxy; static void CGLIB$STATICHOOK1()
{
CGLIB$THREAD_CALLBACKS = new ThreadLocal();
CGLIB$emptyArgs = new Object[0];
Class localClass1 = Class.forName("com.demo.proxy.cglib.PersonService$$EnhancerByCGLIB$$eaaaed75");//代理类
Class localClass2;//被代理类PersionService
Method[] tmp95_92 = ReflectUtils.findMethods(new String[] { "finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (localClass2 = Class.forName("java.lang.Object")).getDeclaredMethods());
CGLIB$finalize$1$Method = tmp95_92[0];
CGLIB$finalize$1$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "finalize", "CGLIB$finalize$1");
Method[] tmp115_95 = tmp95_92;
CGLIB$equals$2$Method = tmp115_95[1];
CGLIB$equals$2$Proxy = MethodProxy.create(localClass2, localClass1, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
Method[] tmp135_115 = tmp115_95;
CGLIB$toString$3$Method = tmp135_115[2];
CGLIB$toString$3$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
Method[] tmp155_135 = tmp135_115;
CGLIB$hashCode$4$Method = tmp155_135[3];
CGLIB$hashCode$4$Proxy = MethodProxy.create(localClass2, localClass1, "()I", "hashCode", "CGLIB$hashCode$4");
Method[] tmp175_155 = tmp155_135;
CGLIB$clone$5$Method = tmp175_155[4];
CGLIB$clone$5$Proxy = MethodProxy.create(localClass2, localClass1, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
tmp175_155;
Method[] tmp223_220 = ReflectUtils.findMethods(new String[] { "setPerson", "()V" }, (localClass2 = Class.forName("com.demo.proxy.cglib.PersonService")).getDeclaredMethods());
CGLIB$setPerson$0$Method = tmp223_220[0];
CGLIB$setPerson$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "setPerson", "CGLIB$setPerson$0");
tmp223_220;
return;
}
我们通过代理类的源码可以看到,代理类会获得所有在父类继承来的方法,并且会有MethodProxy与之对应,比如 Method CGLIB$setPerson$0$Method、MethodProxy CGLIB$setPerson$0$Proxy;
方法的调用
//代理方法(methodProxy.invokeSuper会调用)
final void CGLIB$setPerson$0() {
super.setPerson();
}
//被代理方法(methodProxy.invoke会调用,这就是为什么在拦截器中调用methodProxy.invoke会死循环,一直在调用拦截器)
public final void setPerson() {
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$setPerson$0$Method, CGLIB$emptyArgs, CGLIB$setPerson$0$Proxy);
} else {
super.setPerson();
}
}
调用过程:代理对象调用this.setPerson方法->调用拦截器->methodProxy.invokeSuper->CGLIB$setPerson$0->被代理对象setPerson方法
MethodProxy
拦截器MethodInterceptor中就是由MethodProxy的invokeSuper方法调用代理方法的,MethodProxy非常关键,我们分析一下它具体做了什么。
- 创建MethodProxy
public class MethodProxy {
private Signature sig1;
private Signature sig2;
private MethodProxy.CreateInfo createInfo;
private final Object initLock = new Object();
private volatile MethodProxy.FastClassInfo fastClassInfo;
//c1:被代理对象Class
//c2:代理对象Class
//desc:入参类型
//name1:被代理方法名
//name2:代理方法名
public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
MethodProxy proxy = new MethodProxy();
proxy.sig1 = new Signature(name1, desc);//被代理方法签名
proxy.sig2 = new Signature(name2, desc);//代理方法签名
proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
return proxy;
}
private static class CreateInfo {
Class c1;
Class c2;
NamingPolicy namingPolicy;
GeneratorStrategy strategy;
boolean attemptLoad; public CreateInfo(Class c1, Class c2) {
this.c1 = c1;
this.c2 = c2;
AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
if(fromEnhancer != null) {
this.namingPolicy = fromEnhancer.getNamingPolicy();
this.strategy = fromEnhancer.getStrategy();
this.attemptLoad = fromEnhancer.getAttemptLoad();
} }
}
- invokeSuper调用
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();
}
}
private static class FastClassInfo {
FastClass f1;//被代理类FastClass
FastClass f2;//代理类FastClass
int i1; //被代理类的方法签名(index)
int i2;//代理类的方法签名 private FastClassInfo() {
}
}
上面代码调用过程就是获取到代理类对应的FastClass,并执行了代理方法。还记得之前生成三个class文件吗?PersonService$$EnhancerByCGLIB$$eaaaed75$$FastClassByCGLIB$$355cb7ea.class就是代理类的FastClass,
PersonService$$FastClassByCGLIB$$a076b035.class就是被代理类的FastClass。
FastClass机制
Cglib动态代理执行代理方法效率之所以比JDK的高是因为Cglib采用了FastClass机制,它的原理简单来说就是:为代理类和被代理类各生成一个Class,这个Class会为代理类或被代理类的方法分配一个index(int类型)。
这个index当做一个入参,FastClass就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比JDK动态代理通过反射调用高。下面我们反编译一个FastClass看看:
//根据方法签名获取index
public int getIndex(Signature var1) {
String var10000 = var1.toString();
switch(var10000.hashCode()) {
case -2077043409:
if(var10000.equals("getPerson(Ljava/lang/String;)Lcom/demo/pojo/Person;")) {
return 21;
}
break;
case -2055565910:
if(var10000.equals("CGLIB$SET_THREAD_CALLBACKS([Lnet/sf/cglib/proxy/Callback;)V")) {
return 12;
}
break;
case -1902447170:
if(var10000.equals("setPerson()V")) {
return 7;
}
break;
//省略部分代码.....
//根据index直接定位执行方法
public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
eaaaed75 var10000 = (eaaaed75)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.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
case 4:
return var10000.newInstance((Callback)var3[0]);
case 5:
return var10000.newInstance((Callback[])var3[0]);
case 6:
var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
return null;
case 7:
var10000.setPerson();
return null;
case 8:
var10000.setCallbacks((Callback[])var3[0]);
return null;
case 9:
return var10000.getCallback(((Number)var3[0]).intValue());
case 10:
return var10000.getCallbacks();
case 11:
eaaaed75.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
return null;
case 12:
eaaaed75.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
return null;
case 13:
return eaaaed75.CGLIB$findMethodProxy((Signature)var3[0]);
case 14:
return var10000.CGLIB$toString$3();
case 15:
return new Boolean(var10000.CGLIB$equals$2(var3[0]));
case 16:
return var10000.CGLIB$clone$5();
case 17:
return new Integer(var10000.CGLIB$hashCode$4());
case 18:
var10000.CGLIB$finalize$1();
return null;
case 19:
var10000.CGLIB$setPerson$0();
return null;
//省略部分代码....
} catch (Throwable var4) {
throw new InvocationTargetException(var4);
} throw new IllegalArgumentException("Cannot find matching method/constructor");
}
FastClass并不是跟代理类一块生成的,而是在第一次执行MethodProxy invoke/invokeSuper时生成的并放在了缓存中。
//MethodProxy invoke/invokeSuper都调用了init()
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();
fci.f1 = helper(ci, ci.c1);//如果缓存中就取出,没有就生成新的FastClass
fci.f2 = helper(ci, ci.c2);
fci.i1 = fci.f1.getIndex(this.sig1);//获取方法的index
fci.i2 = fci.f2.getIndex(this.sig2);
this.fastClassInfo = fci;
this.createInfo = null;
}
}
} }
至此,Cglib动态代理的原理我们就基本搞清楚了,代码细节有兴趣可以再研究下。
最后我们总结一下JDK动态代理和Gglib动态代理的区别:
1.JDK动态代理是实现了被代理对象的接口,Cglib是继承了被代理对象。
2.JDK和Cglib都是在运行期生成字节码,JDK是直接写Class字节码,Cglib使用ASM框架写Class字节码,Cglib代理实现更复杂,生成代理类比JDK效率低。
3.JDK调用代理方法,是通过反射机制调用,Cglib是通过FastClass机制直接调用方法,Cglib执行效率更高。
Cglib动态代理实现原理的更多相关文章
- cglib动态代理之原理说明
cglib采用了非常底层的字节码技术,通过目标类的字节码,为目标类创建子类,并在子类中用方法拦截技术,拦截所有父类方法的调用,并对拦截方法进行增强. 1)底层采用字节码框架ASM,来转换字节码来生成新 ...
- cglib源码分析(四):cglib 动态代理原理分析
本文分下面三个部分来分析cglib动态代理的原理. cglib 动态代理示例 代理类分析 Fastclass 机制分析 一.cglib 动态代理示例 public class Target{ publ ...
- 【java高级编程】JDK和CGLIB动态代理区别
转载:https://blog.csdn.net/yhl_jxy/article/details/80635012 前言 JDK动态代理实现原理(jdk8):https://blog.csdn.net ...
- Cglib动态代理实现方式
Cglib动态代理实现方式 我们先通过一个demo看一下Cglib是如何实现动态代理的. 首先定义个服务类,有两个方法并且其中一个方法用final来修饰. public class PersonSer ...
- JDK和CGLIB动态代理区别
背景:虽然自己了解这两种代理的区别,但是面试时候还是答的很模糊,需要好好总结. 前言JDK动态代理实现原理(jdk8):https://blog.csdn.net/yhl_jxy/article/de ...
- spring框架中JDK和CGLIB动态代理区别
转载:https://blog.csdn.net/yhl_jxy/article/details/80635012 前言JDK动态代理实现原理(jdk8):https://blog.csdn.net/ ...
- CGLib动态代理原理及实现
JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了.CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采 ...
- java cglib动态代理原理及样例
cglib动态代理: http://blog.csdn.net/xiaohai0504/article/details/6832990 一.原理 代理为控制要访问的目标对象提供了一种途径.当访问 ...
- Java Proxy和CGLIB动态代理原理
动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译时就确定了,而动态代理的代理关 ...
随机推荐
- [React] 14 - Redux: Redux Saga
Ref: Build Real App with React #14: Redux Saga Ref: 聊一聊 redux 异步流之 redux-saga [入门] Ref: 从redux-thun ...
- [Linux] 设置系统时区
1. 检查当前时区 以 root 身份登录. # date Fri Sep :: UTC 其中 UTC 是指当前使用的时间系统为世界标准时间,也称世界协调时间.英文名称为 Coordinated Un ...
- 构建工具:grunt、Glup、webpack
相关代码已上传至github 怎么是项目构建? 编译项目中的js, sass, less: 合并js/css等资源文件: 压缩js/css/html等资源文件: JS语法的检查. 构建工具的作用? 简 ...
- php MongoDB driver 查询实例
//是否只查mx $mx_on_switch = I("post.mx_on_switch"); //mx模糊查询 $mx_vague_check = I("post.m ...
- 【laravel5.6】The Process class relies on proc_open, which is not available on your PHP installation.
部署服务器的时候,使用composer来安装依赖.遇到了 解决办法: 在php.ini中,找到disable_functions选项,看看后面是否有proc_open函数被禁用了,如果有的话,去掉即可
- EXCEL 2007施工进度横道图制作步骤及实战练习
[知识讲解] 1.将图表中的部分图形“隐藏”起来 如果为了实现某种特殊的图表效果,需要将图表中的部分图形“隐藏”起来,除了将该系列删除外(有时候这种方法不能达到所需要的效果),还可以通过下面的方法来实 ...
- hadoop Codec
- day11 十一、函数对象,名称空间,作用域,和闭包
一.函数对象 1.函数对象:函数名存放的就是函数的地址,所以函数名也是对象,称之为函数对象 a = 10 print(a,id(a)) def fn(): num = 10 print('fn fuc ...
- 蓝桥杯_算法训练_区间k大数查询
问题描述 给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个. 输入格式 第一行包含一个数n,表示序列长度. 第二行包含n个正整数,表示给定的序列. 第三个包含一个正整数m,表示询问个数 ...
- Away3D引擎学习入门笔记
(1). 准备工作,一些必须知道的东西 (创建时间:2014-06-05) A.必要的开发语言基础.至少要懂点ActionScript 3.0语法(ActionScript 3.0语法及API参考), ...