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 条评论