java动态代理之Cglib动态代理

  静态代理和JDK动态代理都有一个前提,那就是被代理类必须实现了接口,否则,这两种代理就不能实现。为了解决这个限制,就出现了Cglib动态代理。一些第三方框架多数都用的是Cglib代理,如spring框架的AOP的实现。

  这里要说明一点,静态代理和JDK代理是基于JDK的,而Cglib代理不是基于JDK的,而是第三方框架自己实现的机制,如spring就实现了Cglib代理机制。

  静态代理和JDK动态代理都是通过实现接口,来生成代理类以扩展目标类的功能。Cglib动态代理由于没有接口可以实现,所以采取了继承目标类来生成代理类以扩展目标类的功能。据我猜测,Cglib动态代理生成的子类应该重写了目标类的方法,以此达到扩展的目的。

  静态代理和JDK动态代理通过实现目标类的接口,来确保代理类和被目标类拥有相同的方法。Cglib则是通过继承目标类来确保代理类和目标类拥有相同的方法。三种代理都是通过某种“重写”方法的方式来实现目标类功能的扩展。

 

  言归正传,下面贴出代码。

package com.zaoren.proxy;

public class ProductDao {
    public void save() {
        System.out.println("------------保存产品信息");
    }
}

 

package com.zaoren.proxy;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
 * Cglib代理工厂
 * @author thinkpad
 *
 */
public class CglibProFac implements MethodInterceptor {
    private Object target;
    
    public CglibProFac(Object target) {
        this.target = target;
    }
    
    public Object getInstance() {
        Enhancer enhancer = new Enhancer();//enhance 增强、增加、提高质量
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object obj, Method method, Object[] arg2, MethodProxy proxy) throws Throwable {
        System.out.println("------------>>>其他操作");
        method.invoke(obj, arg2);
        System.out.println("------------>>>其他操作2");
        return null;
    }

}

  可以看出,代理工厂类实现了MethodInterceptor接口,并实现了接口规定的intercept方法。

  有一个target属性,用于存放目标类的实例。

  有一个构造方法,用于传入目标类的实例并创建代理工厂实例。

  有一个getInstance方法,用于获取代理实例。

  

测试代码为:

package com.zaoren.proxy;

public class MainClass {

    public static void main(String[] args) {
        ProductDao productDao = new ProductDao();
        ProductDao proDaoProxy = (ProductDao)new CglibProFac(productDao).getInstance();
        proDaoProxy.save();
    }

}

  测试的结果却不理想,控制台输出了非常多的相同报错信息,类似于死循环的那种情况。我通过断点,发现错误是在执行proDaoProxy.save()时报的,我非常疑惑,代码里没有循环结构,检查了一遍也没有发现循环结构相关的代码,所以不可能是循环结构导致的。所以只能是另一种类似的情况导致的,即自己调用自己。

  果然,再次检查代码,发现了一个不对劲的地方,intercept方法中的反射调用的第一个参数是obj,而不是target。这种情况如何会导致死循环呢?

  第一点,要知道Cglib动态代理的实现机制。继承目标类的子类重写了父类的方法,子类方法的内容就是调用代理工厂的intercept方法。

  第二点,要知道obj是什么东西,obj是JVM生成的代理对象。

  然后来看method.invoke(obj, arg2)做了什么。根据第一点,我们知道这句代码是代理类save方法的一部分。又因为第一个参数是代理实例,所以这句代码的含义就是:在代理类save方法的内部调用代理类的save方法。这不就是所谓的自己调用自己吗?这就相当于在A方法的内部有这样一句代码:this.A();会产生无穷嵌套调用,即死循环。

  反射调用的第一个参数应该是target,即目标类实例,在代理类的方法中调用目标类的方法不会产生自己调用自己的死循环现象,修改后的代理工厂代码如下:

package com.zaoren.proxy;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
/**
 * Cglib代理工厂
 * @author thinkpad
 *
 */
public class CglibProFac implements MethodInterceptor {
    private Object target;
    
    public CglibProFac(Object target) {
        this.target = target;
    }
    
    public Object getInstance() {
        Enhancer enhancer = new Enhancer();//enhance 增强、增加、提高质量
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object obj, Method method, Object[] arg2, MethodProxy proxy) throws Throwable {
        System.out.println("------------>>>其他操作");
        method.invoke(target, arg2);
        System.out.println("------------>>>其他操作2");
        return null;
    }

}

 

 控制台输出为:

结果符合我们的期望。

posted on 2019-12-05 11:13  星辰划过指尖  阅读(175)  评论(0编辑  收藏  举报