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动态代理实现原理的更多相关文章

  1. cglib动态代理之原理说明

    cglib采用了非常底层的字节码技术,通过目标类的字节码,为目标类创建子类,并在子类中用方法拦截技术,拦截所有父类方法的调用,并对拦截方法进行增强. 1)底层采用字节码框架ASM,来转换字节码来生成新 ...

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

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

  3. 【java高级编程】JDK和CGLIB动态代理区别

    转载:https://blog.csdn.net/yhl_jxy/article/details/80635012 前言 JDK动态代理实现原理(jdk8):https://blog.csdn.net ...

  4. Cglib动态代理实现方式

    Cglib动态代理实现方式 我们先通过一个demo看一下Cglib是如何实现动态代理的. 首先定义个服务类,有两个方法并且其中一个方法用final来修饰. public class PersonSer ...

  5. JDK和CGLIB动态代理区别

    背景:虽然自己了解这两种代理的区别,但是面试时候还是答的很模糊,需要好好总结. 前言JDK动态代理实现原理(jdk8):https://blog.csdn.net/yhl_jxy/article/de ...

  6. spring框架中JDK和CGLIB动态代理区别

    转载:https://blog.csdn.net/yhl_jxy/article/details/80635012 前言JDK动态代理实现原理(jdk8):https://blog.csdn.net/ ...

  7. CGLib动态代理原理及实现

    JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了.CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采 ...

  8. java cglib动态代理原理及样例

     cglib动态代理: http://blog.csdn.net/xiaohai0504/article/details/6832990 一.原理 代理为控制要访问的目标对象提供了一种途径.当访问 ...

  9. Java Proxy和CGLIB动态代理原理

    动态代理在Java中有着广泛的应用,比如Spring AOP,Hibernate数据查询.测试框架的后端mock.RPC,Java注解对象获取等.静态代理的代理关系在编译时就确定了,而动态代理的代理关 ...

随机推荐

  1. centos7.0 yum 安装php服务器

    https://blog.csdn.net/jiaoshenmo/article/details/50923900 首先收一下:centos7.0用yum直接安装apache.php他们的默认版本是a ...

  2. C#自定义Button按钮控件

    C#自定义Button按钮控件 在实际项目开发中经常可以遇到.net自带控件并不一定可以满足需要,因此需要自定义开发一些新的控件,自定义控件的办法也有多种,可以自己绘制线条颜色图形等进行重绘,也可以采 ...

  3. Get Started with the Google Fonts API

    Get Started with the Google Fonts API This guide explains how to use the Google Fonts API to add fon ...

  4. SQL逻辑查询语句执行顺序 需要重新整理

    一.SQL语句定义顺序 1 2 3 4 5 6 7 8 9 10 SELECT DISTINCT <select_list> FROM <left_table> <joi ...

  5. DOM常用事件绑定方式与实例

    一.常用的事件 onclick 点击事件 模态框实例 <input type="button" id="b1" style="width:50p ...

  6. nodejs的koa2框架

    官网文档 cnpm i --save-dev koa2 koa-router koa-body koa-static request npm install --save koa2 const koa ...

  7. STM FLASH在线编程 升级

    注意字节到 stm flash 顺序是反的 例如 12 34 56 78 世纪写入内存 应该是 78 56 34 12

  8. pycharm 下的djiango使用

    创建工程可以在虚拟环境下运行,创建工程后使用命令 在python 下的命令窗口(Terminal) python3 manage.py startapp django_web (或者 python3替 ...

  9. CCPC-Wannafly Winter Camp Day1 Div1 - 夺宝奇兵 - [贪心+线段树]

    题目链接:https://zhixincode.com/contest/3/problem/J?problem_id=43 样例输入 1 4 1110 11 110 21 210 31 315 415 ...

  10. [No0000160]常用C# 正则表达式大全

    正则表达式的本质是使用一系列特殊字符模式,来表示某一类字符串.正则表达式无疑是处理文本最有力的工具,而.NET提供的Regex类实现了验证正则表达式的方法.Regex 类表示不可变(只读)的正则表达式 ...