0
点赞
收藏
分享

微信扫一扫

Cglib代理—和你想象中不一样的代理模式

Cglib主要功能是为没有实现接口的类提供代理,它为JDK代理提供了很好的补充。

Cglib的原理:
继承目标类,动态生成一个代理子类。代理类(子类)会重写父类(目标对象)的所有可重写的方法(private或者final方法不可重写)。并在子类中采用MethodInterceptor技术拦截所有父类方法的调用。执行方法时,采用FastClass技术,通过类与方法索引找到对应方法,比JDK的反射运行方法模式效率高。

CGLIB的使用

1. 目标类

public class PersonService {
public PersonService() {
System.out.println("Person初始化方法...");
}
public void setPerson() {
System.out.println("PersonService:setPerson方法...");
}
}

2. 创建代理对象
创建代理对象时,两个要素是目标对象以及回调方法

    @Test
public void test(){
//打印反编译后的类
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
Enhancer enhancer=new Enhancer();
//代理的目标类
enhancer.setSuperclass(PersonService.class);
//设置拦截器
enhancer.setCallback(new CglibProxyInterceptor());
//创建代理对象
PersonService proxy = (PersonService) enhancer.create();
//代理对象调用方法。
proxy.setPerson();
}

3. 调用代理对象的方法

由下面代理对象源码(精简版)可知,代理对象实际上是目标对象的子类,它不仅重写了目标对象的方法,而且新增了一个代理方法CGLIB$setPerson$0()即可以直接调用目标对象的方法。

代理类setPerson方法,首先判断代理对象的CGLIB$CALLBACK_0属性,即是否存在回调方法,若存在回调方法(即被MethodInterceptor拦截),那么调用回调方法的intercept()方法;若不存在回调方法,直接调用父类(目标类)方法。

public class c4f79b51 extends PersonService
implements Factory

private static final Method CGLIB$setPerson$0$Method;
//每个代理对象的方法都存在一个MethodProxy对象。
private static final MethodProxy CGLIB$setPerson$0$Proxy;


static void CGLIB$STATICHOOK1()
{

Method[] tmp50_47 = ReflectUtils.findMethods(new String[] { "setPerson", "()V" }, (localClass2 = Class.forName("com.tellme.Impl.PersonService")).getDeclaredMethods());
CGLIB$setPerson$0$Method = tmp50_47[0];
//MethodProxy对象进行初始化
CGLIB$setPerson$0$Proxy = MethodProxy.create(localClass2, localClass1, "()V", "setPerson", "CGLIB$setPerson$0");
}
//代理生成的方法,直接调用父类的方法
final void CGLIB$setPerson$0()
{
super.setPerson();
}
//代理对象调用setPerson方法,实际上就是执行该方法。
public final void setPerson()
{
//判断目标类是否设置了回调(即是否设置了MethodInterceptor)。
//代理对象的CGLIB$CALLBACK_0属性是否有值
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
//若设置了回调,则调用MethodInterceptor方法的Intercept方法
MethodInterceptor tmp17_14 = this.CGLIB$CALLBACK_0;
if (tmp17_14 != null)
//this(当前代理对象) CGLIB$setPerson$4$Method(目标类的方法) CGLIB$emptyArgs(方法参数) CGLIB$setPerson$0$Proxy(代理类生成的代理方法)
return tmp17_14.intercept(this, CGLIB$setPerson$0$Method, CGLIB$emptyArgs, CGLIB$setPerson$0$Proxy);;
//执行父类(目标类)的方法
super.setPerson();
}

4. 回调拦截器方法:

在代理对象源码中,我们看到它最终会调用MethodInterceptor拦截器方法。

而我们的拦截器目的是执行目标方法的前后打印日志。
即我们最终还是得执行目标方法的setPerson()方法,实际上通过Object o1 = methodProxy.invokeSuper(o, objects);代码进行调用。

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class CglibProxyInterceptor implements MethodInterceptor {
/**
*
* @param o 最终生成的代理对象
* @param method 被拦截的方法
* @param objects 被拦截方法的参数集合
* @param methodProxy 代理对象中的方法
*/

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("被执行前");
//执行代理对象中的方法
Object o1 = methodProxy.invokeSuper(o, objects);
System.out.println("被执行后");
return o1;
}
}

5. 源码:invokeSuper()分析

    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
try {
//初始化一个FastClassInfo类
init();
FastClassInfo fci = fastClassInfo;
//该方法实际上执行的是哪个方法?
return fci.f2.invoke(fci.i2, obj, args);
}
catch (InvocationTargetException e) {
throw e.getTargetException();, "CGLIB$setPerson$0");
}
}

CGLIB的组成

  • f1:PersonService$$EnhancerByCGLIB$$eaaaed75$$FastClassByCGLIB$$355cb7ea.class就是代理类的FastClass。
  • f2:PersonService$$FastClassByCGLIB$$a076b035.class就是目标类的FastClass。
  • i1:表示setPerson方法的下标;
  • i2:表示的是CGLIB$setPerson$0方法(即super.setPerson())下标。

上述代码的参数含义:
fci.f2:代理对象的FastClass对象;
fci.i2:CGLIB$setPerson$0方法的下标;
obj:方法调用者;
args:方法参数

public Object invoke(int index, Object obj, Object[] args){
//代理对象调用,将传入的obj对象强转为代理对象类型
PersonService$EnhancerByCGLIB$c4f79b51t = (PersonService$EnhancerByCGLIB$c4f79b51) obj;
switch(index){
case 1:
//根据下标,找到CGLIB$setPerson$0方法,实际上执行目标对象的方法
t.CGLIB$setPerson$0(args);
return null;
case 2:
t.setPerson(args);
return null;
}
return null;
}

7. invoke()为什么造成死循环

若是我们在MethodInterceptor中调用invoke方法,会造成死循环。

invoke方法内部的核心代码是fci.f1.invoke(fci.i1, obj, args)

在Cglib代理中,会生成3个代理对象

是否递归执行拦截方法。主要决定因素是invoke方法中obj参数是代理对象还是目标对象。

public Object invoke(int index, Object obj, Object[] args){
PersonService t= (PersonService) obj;
switch(index){
case 1:
//(多态的应用)实际上依旧是调用的代理对象的setPerson方法
t.setPerson(args);
return null;
}
return null;
}
举报

相关推荐

0 条评论