动态代理:Cglib代理原理解析

一、概要

cglib源码看了下,总结下自己理解,小结如下:

二、通过继承目标类来实现代理

要点描述

  1. 代理类:继承了目标类,则在代理类中通过调用父类方法,即调用到了目标类方法。整个代理过程无需强制持有目标类的实例
  2. 增强器/拦截器类:实现MethodInterceptor接口的intercept方法,以达成对目标类的代理或者说功能增强(方法参数见下文:增强器/拦截器接口源码)。
  3. 增强器/拦截器映射器:CallbackFilter。通过入参为Method类型,出参为int型增强器/拦截器数组的索引的accept方法,决定代理方法实际对应的是哪一个增强器/拦截器。(见下文:接口实现示例)
  4. 通过Enhancer类实例,可以设置继承的目标类,传入增强器/拦截器实例组(可选),及设置增强器/拦截器映射器(可选),最终调用Enhancer#create()方法创建代理实例。
  5. 对代理类方法的调用,实际是对增强器/拦截器组中的指定1个增强器/拦截器的intercept方法进行调用(如果没有设置增强器/拦截器,则通过super直接调用父类方法,即目标方法。不过目前还没找到能不设置的方法)
  6. intercept方法,一般通过代理实例上调用代理方法来反射调用到目标方法。
//1、正确:一般通过代理实例上反射调用代理方法来调用到目标方法。
Object returnVal = proxyMethod.invokeSuper(proxyObj,args);
//2、错误:在代理实例上反射调用目标方法,会递归调用到代理方法,陷入死循环,最终报错
//*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
Object returnVal = targetMethod.invoke(proxyObj,args);
//3、错误:在代理实例上反射调用目标方法。最终报错,结果同2
Object returnVal = proxyMethod.invoke(proxyObj,args);
//4、正确:构造增强器/拦截器实例时,传入目标类实例targetObj,则可以在此通过目标类实例反射调用目标方法。
Object returnVal = targetMethod.invoke(targetObj,args);

类图

这里写图片描述

三、通过实现目标类接口列表来实现代理

要点描述

  1. 代理类:实现了目标类接口列表,代理方法调用过程中,不可以在代理类中通过super调用目标方法,这样实际会报个Object类中不存在的方法的错误。
  2. 增强器/拦截器类:实现MethodInterceptor接口的intercept方法,以达成对目标类的代理或者说功能增强(方法参数见下文:增强器/拦截器接口源码)。
  3. 增强器/拦截器映射器:CallbackFilter。通过入参为Method类型,出参为int型增强器/拦截器数组的索引的accept方法,决定代理方法实际对应的是哪一个增强器/拦截器。(见下文:接口实现示例)
  4. 通过Enhancer类实例,可以设置继承的目标类,传入增强器/拦截器实例组(可选),及设置增强器/拦截器映射器(可选),最终调用Enhancer#create()方法创建代理实例。如果是一个没有接口的类,则也能创建代理实例成功,只是在使用代理方法时,会报数组越界异常ArrayIndexOutOfBoundsException: 0
  5. 对代理类方法的调用,实际是对增强器/拦截器组中的指定1个增强器/拦截器的intercept方法进行调用(如果没有设置增强器/拦截器,则通过super直接调用父类方法,即目标方法。不过目前还没找到能不设置的方法)
  6. intercept方法,一般通过代理实例上调用代理方法来反射调用到目标方法。
//1、错误:通过代理实例上反射调用代理方法来调用到目标方法。如调用buyHouse代理方法报java.lang.NoSuchMethodError: java.lang.Object.buyHouse(Ljava/lang/String;)V
Object returnVal = proxyMethod.invokeSuper(proxyObj,args);
//2、错误:在代理实例上反射调用目标方法,会递归调用到代理方法,陷入死循环,最终报错
//*** java.lang.instrument ASSERTION FAILED ***: "!errorOutstanding" with message transform method call failed at JPLISAgent.c line: 844
Object returnVal = targetMethod.invoke(proxyObj,args);
//3、错误:在代理实例上反射调用目标方法。最终报错,结果同2
Object returnVal = proxyMethod.invoke(proxyObj,args);
//4、正确:构造增强器/拦截器实例时,传入目标类实例targetObj,则可以在此通过目标类实例反射调用目标方法。
Object returnVal = targetMethod.invoke(targetObj,args);

类图

这里写图片描述

四、增强器/拦截器映射器接口实现示例

public class CglibCallbackFilter implements CallbackFilter {

    /**
     * 增强拦截器数组索引:购房-0
     */
    public final int INTERCEPTOR_BUY_HOUSE_INDEX = 0;

    /**
     * 增强拦截器数组索引:卖房-1
     */
    public final int INTERCEPTOR_SALE_HOUSE_INDEX = 1;

    @Override
    public int accept(Method method) {
        if (method.getName().equals("buyHouse")) {
            return INTERCEPTOR_BUY_HOUSE_INDEX;
        }else {
            return INTERCEPTOR_SALE_HOUSE_INDEX;
        }
    }
}

五、增强器/拦截器接口源码

    /**
     * 增强器/拦截器接口
     **/
    public interface MethodInterceptor extends Callback {

        /**
         * 增强/拦截/代理方法
         * @param proxyObj 代理类实例
         * @param targetMethod 目标类方法
         * @param args 目标类方法参数
         * @param proxyMethod 代理方法(包含目标类方法与代理方法,实际代理方法也只是单纯调用父类方法)
         * @return
         * @throws Throwable
         */
        Object intercept(Object proxyObj, Method targetMethod, Object[] args, MethodProxy proxyMethod) throws Throwable;
    }

六、Factory

如果在创建代理时设置了enhancer.setUseFactory(true);则在生成代理类时,此类会继承Factory接口。接口功能如下:
1. 创建新的代理实例(可传入增强器/拦截器[组])
2. 在创建代理类实例后,还可以动态的更换增强器/拦截器[组],只需指定下标或者传入数组即可。
3. 可以查看代理类实例当前增强器/拦截器组

public interface Factory {
    Object newInstance(Callback var1);

    Object newInstance(Callback[] var1);

    Object newInstance(Class[] var1, Object[] var2, Callback[] var3);

    Callback getCallback(int var1);

    void setCallback(int var1, Callback var2);

    void setCallbacks(Callback[] var1);

    Callback[] getCallbacks();
}

补充

实际上增强类通过MethodProxy调用目标类方法时,已经不是通过反射来完成了。而是通过FastClass机制,即对每一个方法,根据方法签名的哈希码来决定调用哪个方法,从而避免了反射。(代理类与目标类,各自会有个FastClass类)

于Cglib如何生成代理类的细节在此不再赘述,造重复的轮子了, 详见转载Cglib动态代理实现原理

上一篇:动态代理:JDK代理原理解析


以上为个人观点,若有错误或者疏漏,欢迎指正。^_^

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值