动态代理-cglib

Cglib 动态代理

使用CGLIB即使代理类没有实现任何接口也可以实现动态代理功能。

cglib的原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用。由于是通过创建子类来代理父类,因此不能代理被final修饰的类(代理final修饰的类会抛异常,代理final修饰的方法只会原样执行委托类的方法而不能做任何拦截)。

但是cglib有一个很致命的缺点:cglib的底层是采用著名的ASM字节码生成框架,使用字节码技术生成代理类,也就是通过操作字节码来生成的新的.class文件,而我们在android中加载的是优化后的.dex文件,也就是说我们需要可以动态生成.dex文件代理类,因此cglib在Android中是不能使用的。

使用方式

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class Test {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Person.class);
        enhancer.setCallback(new MethodInterceptor() {
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("Intercept!");
                methodProxy.invokeSuper(o, objects);
                return null;
            }
        });
        Person person = (Person) enhancer.create();
        person.sayHello();
    }
}

其中,先给Enchancer类设置一个需要被代理的类Person,然后设置Callback。Callback可以理解成生成的代理类的方法被调用时(比如说调用person.sayHello()时),会执行的逻辑。

Callback是一个空的接口,在Cglib中它的实现类有以下几种:

  • MethodInterceptor:

    可以实现类似于AOP编程中的环绕增强(around-advice)。

    public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
    MethodProxy proxy) throws Throwable;。代理类的所有方法调用都会转而执行这个接口中的intercept方法而不是原方法。如果需要在intercept方法中执行原方法可以使用参数method进行反射调用或者使用参数proxy,后者会快一些(反射调用比正常的方法调用的速度慢很多)。MethodInterceptor允许我们完全控制被拦截的方法,并且提供了手段对原方法进行调用,那为什么还会有其它的Callback接口实现呢?因为 MethodInterceptor的效率不高,它需要产生不同类型的字节码,并且需要生成一些运行时对象(InvocationHandler就不需要),所以Cglib提供了其它的接口供我们选择。

  • NoOp:

    方法调用委托给了被代理类的原方法(本例中是Person),不做任何其它的操作。

  • LazyLoader:

    提供了一个方法:Object loadObject() throws Exception;,loadObject()方法会在第一次被代理类的方法调用时触发,它返回一个代理类的对象,这个对象会被存储起来然后负责所有被代理类方法的调用,就像它的名字说的那样,一种lazy模式。如果被代理类或者代理类的对象的创建比较麻烦,而且不确定它是否会被使用,那么可以选择使用这种lazy模式来延迟生成代理。

  • Dispatcher:

    和LazyLoader接口相同,也是提供了loadObject()方法,这个方法同样地返回一个代理对象,这个对象同样可以代理原方法的调用。不过它们之间不同的地方在于,Dispatcher的loadObject()方法在每次发生对原方法的调用时都会被调用并返回一个代理对象来调用原方法。也就是说Dispatcher的loadObject()方法返回的对象并不会被存储起来,可以类比成Spring中的Prototype类型,而LazyLoader则是lazy模式的Singleton。

  • InvocationHandler

    ​```
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    public class Test {
        public static void main(String[] args) {
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(Person.class);
            enhancer.setCallback(new InvocationHandler() {
                public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
                    System.out.println("Intercept!");
                    return null;
                }
            });
            Person person = (Person) enhancer.create();
            person.sayHello();
        }
    }
    

    使用方式和MethodInterceptor差不多,需要注意的一点是,所有对invoke()方法的参数proxy对象的方法调用都会被委托给同一个InvocationHandler,所以可能会导致无限循环。

  • FixedValue:

    提供了一个loadObject()方法,不过这个方法返回的不是代理对象,而是原方法调用想要的结果。也就是说,在这个Callback里面,看不到任何原方法的信息,也就没有调用原方法的逻辑,不管原方法是什么都只会调用loadObject()并返回一个结果。听起来可能有些没有,但是配合CllbackFilter可以强制使某个方法返回固定的值,并且带来的开销很小。需要注意的是,如果loadObject()方法的返回值并不能转换成原方法的返回值类型,那么会抛出类型转换异常。

————————————————
原文链接:https://blog.csdn.net/Q_AN1314/article/details/79724334

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值