设计模式----cglib动态代理

cglib动态代理

一、什么是CGLIB?

CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。

二、CGLIB原理

CGLIB原理:动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

CGLIB底层:使用字节码处理框架ASM,来转换字节码并生成新的类。不鼓励直接使用ASM,因为它要求你必须对JVM内部结构包括class文件的格式和指令集都很熟悉。

CGLIB缺点:对于final方法,无法进行代理

三、CGLIB的应用

广泛的被许多AOP的框架使用,例如Spring AOP和dynaop。Hibernate使用CGLIB来代理单端single-ended(多对一和一对一)关联。

四、CGLIB的API

1、Jar包:

cglib-nodep-2.2.jar:使用nodep包不需要关联asm的jar包,jar包内部包含asm的类.

cglib-2.2.jar:使用此jar包需要关联asm的jar包,否则运行时报错.

2、CGLIB类库:

本系列使用的CGLIB版本是2.2。

net.sf.cglib.core:底层字节码处理类,他们大部分与ASM有关系。

net.sf.cglib.transform:编译期或运行期类和类文件的转换

net.sf.cglib.proxy:实现创建代理和方法拦截器的类

net.sf.cglib.reflect:实现快速反射和C#风格代理的类

net.sf.cglib.util:集合排序等工具类

net.sf.cglib.beans:JavaBean相关的工具类

下面,将通过一个实例介绍使用CGLib实现动态代理

cglib动态代理实例

1、被代理类:

import com.study.shjimoshi.log.MyLogger;
public class MyLoggerImpl {

    public void info(String orderId, String orderName) throws Throwable {
        System.out.println("orderId:"+orderId+"orderName:"+orderName);
    }
}

2、拦截器:

定义一个拦截器。在调用目标方法时,CGLib会回调MethodInterceptor接口方法拦截,来实现你自己的代理逻辑,类似于JDK中的InvocationHandler接口

public class CglibMethodInterceptor implements MethodInterceptor {

    /**
     *
     * @param obj:目标对象
     * @param method:目标方法
     * @param args:目标参数
     * @param proxy:cglib方法代理对象
     * @return
     * @throws Throwable
     */
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("<<<<<日志收集开始...>>>>>>>");
        //proxy.invokeSuper(obj, args):调用MethodProxy中的invokeSuper方法,初始化fastclas文件,获取目标对象和方法的索引
        Object reuslt = proxy.invokeSuper(obj, args);
        System.out.println("<<<<<日志收集结束...>>>>>>>");
        return reuslt;
    }


}

参数:Object为由CGLib动态生成的代理类实例,Method为上文中实体类所调用的被代理的方法引用,Object[]为参数值列表,MethodProxy为生成的代理类对方法的代理引用。

返回:从代理实例的方法调用返回的值。

其中,proxy.invokeSuper(obj,arg):

调用代理类实例上的proxy方法的父类方法(即实体类TargetObject中对应的方法)

在这个示例中,只在调用被代理类方法前后各打印了一句话,当然实际编程中可以是其它复杂逻辑。

3、生成动态代理类:

/**
 * 采用继承方式,若方法上加入注解,代理类可以获取到被代理类的注解,如果注解加载接口上,代理类获取不到被代理类的注解
 */
public static void cglibTest() throws Throwable {
	//将生成的代理文件输出到 "D:\\workspace\\mayikt-vue\\com\\sun\\proxy"位置
    System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\workspace\\mayikt-vue\\com\\sun\\proxy");
    CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
    Enhancer enhancer = new Enhancer();
    // 设置代理类的付类
    enhancer.setSuperclass(MyLoggerImpl.class);
    // 设置回调对象
    enhancer.setCallback(cglibMethodInterceptor);
    // 创建代理对象
    MyLoggerImpl myLogger = (MyLoggerImpl) enhancer.create();
    myLogger.info("cglib","你好");

}

这里Enhancer类是CGLib中的一个字节码增强器,它可以方便的对你想要处理的类进行扩展,以后会经常看到它。

首先将被代理类TargetObject设置成父类,然后设置拦截器TargetInterceptor,最后执行enhancer.create()动态生成一个代理类,并从Object强制转型成父类型TargetObject。

最后,在代理类上调用方法.

cglib动态代理原理

调用上例,会再 "D:\workspace\mayikt-vue\com\sun\proxy"位置生成对应代理类和对应fastclass索引文件
在这里插入图片描述
在这里插入图片描述


public class MyLoggerImpl$$EnhancerByCGLIB$$8f1ef76d extends MyLoggerImpl implements Factory {
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static  ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static  Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static Object CGLIB$CALLBACK_FILTER;
    private static  Method CGLIB$info$0$Method;
    private static  MethodProxy CGLIB$info$0$Proxy;
    private static  Object[] CGLIB$emptyArgs;
    private static  Method CGLIB$equals$1$Method;
    private static  MethodProxy CGLIB$equals$1$Proxy;
    private static  Method CGLIB$toString$2$Method;
    private static  MethodProxy CGLIB$toString$2$Proxy;
    private static  Method CGLIB$hashCode$3$Method;
    private static  MethodProxy CGLIB$hashCode$3$Proxy;
    private static  Method CGLIB$clone$4$Method;
    private static  MethodProxy CGLIB$clone$4$Proxy;

    static void CGLIB$STATICHOOK1() throws ClassNotFoundException {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];

        //var0:代理类的calss对象
        Class var0 = Class.forName("com.study.shjimoshi.proxy.cglib.MyLoggerImpl$$EnhancerByCGLIB$$8f1ef76d");
        //var1:被代理类
        Class var1 = Class.forName("com.study.shjimoshi.log.impl.MyLoggerImpl");
        CGLIB$info$0$Method = ReflectUtils.findMethods(new String[]{"info", "(Ljava/lang/String;Ljava/lang/String;)V"}, (var1 = 	Class.forName("com.study.shjimoshi.log.impl.MyLoggerImpl")).getDeclaredMethods())[0];
        //var1:被代理类class对象,var0:代理类对象class,desc:入参,name1:被代理类方法,name2:代理类方法
        CGLIB$info$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;Ljava/lang/String;)V", "info", "CGLIB$info$0");

    }

    //代理方法(methodProxy.invokeSuper会调用)
    final void CGLIB$info$0(String var1, String var2) throws Throwable {
        super.info(var1, var2);
    }
    //被代理方法(methodProxy.invoke会调用,这就是为什么在拦截器中调用methodProxy.invoke会死循环,一直在调用拦截器)
    public final void info(String var1, String var2) throws Throwable {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            //调用拦截器 this:生成的cglib代理类的fastclass对象,CGLIB$info$0$Method:代理类方法,new Object[]{var1, var2}:目标参数,CGLIB$info$0$Proxy:代理类
            var10000.intercept(this, CGLIB$info$0$Method, new Object[]{var1, var2}, CGLIB$info$0$Proxy);
        } else {
            super.info(var1, var2);
        }
    }

    public void setCallbacks(Callback[] var1) {
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
    }

    static {
        try {
            CGLIB$STATICHOOK1();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

}

1. 初始化代理对象:

在生成MyLoggerImpl E n h a n c e r B y C G L I B EnhancerByCGLIB EnhancerByCGLIB8f1ef76d代理类的时候,先执行代理类的static代码块,调用CGLIB S T A T I C H O O K 1 ( ) ; 方 法 对 参 数 初 始 化 。 在 C G L I B STATICHOOK1();方法对参数初始化。 在CGLIB STATICHOOK1();CGLIBSTATICHOOK1();方法中初始化了CGLIB$info 0 0 0Method和CGLIB$info 0 0 0Proxy,再初始化CGLIB$info 0 0 0Proxy时,调用了MethodProxy.create去对目标方法info和代理方法CGLIB$info$0计算了出了对应的索引值

2. 设置回调方法
enhancer.setCallback(cglibMethodInterceptor); cglibMethodInterceptor会对目标方法进行增强。

3. 通过代理对象调用目标方法
myLogger.info(“cglib”,“你好”); myLogger是cglib生成的代理对象。通过代理对象调用目标方法。

在这里插入图片描述

cglib动态代理总结

  1. 是基于继承实现的,通过字节码技术,直接生成class文件并读取到内存中。
  2. 再通过fastclass索引机制调用代理方法,再代理方法中通过super.目标方法调用

cglib动态代理比jdk代理效率高:1.不需要拼接代理类,2.直接通过fastclass索引机制调用比反射机制调用效率更高。

此文档学习于蚂蚁课堂。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值