java动态代理第二讲之cglib代理

作者:liuguobing   阅读 (3923)  |  收藏 (0)  |  点赞 (0)

摘要

之前简单讲过动态代理,它是利用java反射和Proxy类对接口进行代理,今天我试着用cglib去实现


原文链接:java动态代理第二讲之cglib代理

jdk中的动态代理有很大的局限性,因为java的单一继承机制,使得无法直接对java类进行代理,于是就诞生了cglib。

cglib是基于字节码框架asm的,其实只要你想,你也可以直接用asm创建动态代理类,只是asm过于复杂,学起来费力不讨好。

cglib的代理对象是通过Enhance类创建,Enhance的功能就比Proxy多了,可以设置父类,可以添加接口,可以设置代理对象方法如何调用等等。先来看个例子:

public abstract class Bean {
    String sampleProperty;

    abstract public void addPropertyChangeListener(PropertyChangeListener listener);

    abstract public void removePropertyChangeListener(PropertyChangeListener listener);

    public String getSampleProperty(){
        return sampleProperty;
    }

    public void setSampleProperty(String value){
        this.sampleProperty = value;
    }

    public String toString(){
        return "sampleProperty is " + sampleProperty;
    }
}

先创建一个普通的抽象类Bean,下面是cglib创建Bean代理对象:

public class Beans implements MethodInterceptor {

    private PropertyChangeSupport propertySupport;

    public void addPropertyChangeListener(PropertyChangeListener listener) {

        propertySupport.addPropertyChangeListener(listener);

    }

    public static  Object newInstance( Class clazz ){
        try{
            Beans interceptor = new Beans();
            Enhancer e = new Enhancer();
            e.setSuperclass(clazz);
            e.setCallback(interceptor);
            Object bean = e.create();
            interceptor.propertySupport = new PropertyChangeSupport( bean );
            return bean;
        }catch( Throwable e ){
            e.printStackTrace();
            throw new Error(e.getMessage());
        }

    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        propertySupport.removePropertyChangeListener(listener);
    }

    static final Class C[] = new Class[0];
    static final Object emptyArgs [] = new Object[0];

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        Object retValFromSuper = null;
        try {
            if (!Modifier.isAbstract(method.getModifiers())) {
                retValFromSuper = proxy.invokeSuper(obj, args);
            }
        } finally {
            String name = method.getName();
            if( name.equals("addPropertyChangeListener")) {
                addPropertyChangeListener((PropertyChangeListener)args[0]);
            }else if ( name.equals( "removePropertyChangeListener" ) ){
                removePropertyChangeListener((PropertyChangeListener)args[0]);
            }
            if( name.startsWith("set") &&
                    args.length == 1 &&
                    method.getReturnType() == Void.TYPE ){

                char propName[] = name.substring("set".length()).toCharArray();

                propName[0] = Character.toLowerCase( propName[0] );
                propertySupport.firePropertyChange( new String( propName ) , null , args[0]);

            }
        }
        return retValFromSuper;
    }

    public static void main( String args[] ){

        Bean  bean =  (Bean)newInstance( Bean.class );

        bean.addPropertyChangeListener(
                new PropertyChangeListener(){
                    public void propertyChange(PropertyChangeEvent evt){
                        System.out.println(evt);
                    }
                }
        );

        bean.setSampleProperty("TEST");
        bean.setSampleProperty("HELLO");


    }
}

Beans是一个实现了MethodInteceptor接口的类,这个接口继承自Callback,这样的类可以给Enhance的setCallback调用,Enhance产生的代理对象调用方法时会调用这个接口的intercept方法,如上述代码所示,这个方法接受四个对象,前三个没什么好说的,跟jdk中的InvocationHandler类似,它就多了一个MethodProxy,这个是Cglib为原类的方法生成的一个对象,它有两个执行方法,invokeSuper,这个是使用父类(也就是原类)的方法调用,而invoke则是使用生成的代理类执行该方法,后者传入的obj不能是代理对象,否则会不断递归调用interceptor方法而导致内存溢出。

cglib的功能很强大,它还支持多个代理,使用CallbackFilter对方法使用哪种代理进行分类。cglib也有一个Proxy类,它跟jdk的proxy的api很类似。

cglib相比jdk的还有个好处是,它近乎等于静态的,因为是直接运行期生成的class代码,执行时并没有进行反射(假设intercept中是调用MethodProxy.invoke而不是Method.invoke),反射毕竟是费时的(不过似乎在jdk7以后jdk的效率更高)。

cglib我也不打算再细讲,知道这些就可以轻松实现动态代理了,我本人也只是刚使用,因为平时使用其他的类库都对他已经处理好,我并不需要直接拿来使用,所以一直没研究他,以后会不会继续研究未知。cglib本身也有局限,它也跳脱不了jdk的继承机制,对final类无能为力。动态代理比较多的场景在切面编程上,我打算花精力去研究aspectj这个框架,因为它是编译器将代码强行加入到源码中的,所以它没有了cglib的局限。

分类   默认分组
字数   3965

博客标签    cglib   动态代理  

评论