zoukankan      html  css  js  c++  java
  • cglib代理

    简介:

      github地址:https://github.com/cglib/cglib,可以访问这个地址查看cglib源码和相关文档。

      简单的摘录了wiki上关于cglib的描述:

      cglib is a powerful, high performance and quality Code Generation Library,It is used to extend JAVA classes and implements interfaces at runtime.

      重点是后面这句话:它用于在【运行时】扩展JAVA类并实现接口。这也就是动态代理的精髓吧。

      我们都知道,动态代理可以通过jdk动态代理,那和cglib有什么区别呢。第一,jdk动态代理的对象必须实现了某个接口,所代理的对象是实现了某个接口的所有类。

      而cglib并没有这个要求。第二,cglib的效率比jdk要高许多。

    下面介绍一个梨子:

    首先定义一个目标类

    package demo.cglib;
    
    public class Target {
        
        public void first() {
            System.out.println("first");
        }
        
        public void second() {
            System.out.println("second");
        }
        
        public void third() {
            System.out.println("third");
        }
        
        public String toString() {
            return "target class";
        }
    }

    然后我们需要为目标类写一个方法拦截类。这个类需要实现MethodInterceptor接口,先看一下这个接口:

    package net.sf.cglib.proxy;
    
    /**
     * General-purpose {@link Enhancer} callback which provides for "around advice".
     * @author Juozas Baliuka <a href="mailto:baliuka@mwm.lt">baliuka@mwm.lt</a>
     * @version $Id: MethodInterceptor.java,v 1.8 2004/06/24 21:15:20 herbyderby Exp $
     */
    public interface MethodInterceptor
    extends Callback
    {
        /**
         * All generated proxied methods call this method instead of the original method.
         * The original method may either be invoked by normal reflection using the Method object,
         * or by using the MethodProxy (faster).
         * @param obj "this", the enhanced object
         * @param method intercepted Method
         * @param args argument array; primitive types are wrapped
         * @param proxy used to invoke super (non-intercepted method); may be called
         * as many times as needed
         * @throws Throwable any exception may be thrown; if so, super method will not be invoked
         * @return any value compatible with the signature of the proxied method. Method returning void will ignore this value.
         * @see MethodProxy
         */    
        public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,
                                   MethodProxy proxy) throws Throwable;
    
    }

    这个接口只有一个方法,我们的具体代理增强逻辑就是在这个方法中实现的。

    这里还有一个点,可以看到MethodInterceptor继承了Callback接口,那么我们来看看Callback接口的内容:

    package net.sf.cglib.proxy;
    
    /**
     * All callback interfaces used by {@link Enhancer} extend this interface.
     * @see MethodInterceptor
     * @see NoOp
     * @see LazyLoader
     * @see Dispatcher
     * @see InvocationHandler
     * @see FixedValue
     */
    public interface Callback
    {
    }

    嗯,它是个空接口,起到标志的作用,下面具体实现增强类的时候会看到它

    那么,了解了这些,开始写我们的拦截类把

    package demo.cglib;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    public class TargetInterceptor implements MethodInterceptor {
    
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("method:"+method.getName()+" ---begain---");
            Object result = proxy.invokeSuper(obj, args);
            System.out.println("method:"+method.getName()+" ---end---");
            return result;
        }
    
    }

    这里只是写了简单的方法执行前后的简单操作,下面测试一下

    package demo.cglib;
    
    import net.sf.cglib.proxy.Enhancer;
    
    public class DemoTest {
        public static void main(String[] args) {
            TargetInterceptor interceptor = new TargetInterceptor();
            Enhancer eh = new Enhancer();
            eh.setSuperclass(Target.class);
            eh.setCallback(interceptor);
            Target target = (Target)eh.create();
            
            target.first();
            target.second();
            target.third();
        }
    }    

    输出结果:

    method:first ---begain---
    first
    method:first ---end---
    method:second ---begain---
    second
    method:second ---end---
    method:third ---begain---
    third
    method:third ---end---

    到这里,似乎很完美了。我们代理只要实现MethodInterceptor接口的intercept方法,在里面实现我们的逻辑就好了。

    但是,现在我有一个需求:当调用first()方法时,我不希望被代理。调用时second()执行我们的拦截类实现的代理逻辑,而当调用third()方法时,会有一个

    默认的返回值。

    这里你可能说,我直接在intercept方法里判断啊,当方法名是first时,当方法是second时....

    这样做可以,但是不够优雅,而且像不被代理和返回默认值这样的方法。我们完全有理由在调用之初就被过滤掉,而不需要进入到intercept()这一层再去进行一个判断。

    下面讲一下过滤的实现方式。首先介绍两个实现了Callback接口的接口:

    package net.sf.cglib.proxy;
    
    /**
     * Methods using this {@link Enhancer} callback will delegate directly to the
     * default (super) implementation in the base class.
     */
    public interface NoOp extends Callback
    {
        /**
         * A thread-safe singleton instance of the <code>NoOp</code> callback.
         */
        public static final NoOp INSTANCE = new NoOp() { };
    }

    这个类的意思就是No Operation,也就是不进行操作

    package net.sf.cglib.proxy;
    
    /**
     * {@link Enhancer} callback that simply returns the value to return
     * from the proxied method. No information about what method
     * is being called is available to the callback, and the type of
     * the returned object must be compatible with the return type of
     * the proxied method. This makes this callback primarily useful
     * for forcing a particular method (through the use of a {@link CallbackFilter}
     * to return a fixed value with little overhead.
     */
    public interface FixedValue extends Callback {
        /**
         * Return the object which the original method invocation should
         * return. This method is called for <b>every</b> method invocation.
         * @return an object matching the type of the return value for every
         * method this callback is mapped to
         */
        Object loadObject() throws Exception;
    }

    这个接口需要实现loadObject,固定返回的类型

    这里我们给它一个简单的实现,返回固定值 “dog” 字符串

    package demo.cglib;
    
    import net.sf.cglib.proxy.FixedValue;
    
    public class TargetResultFixed implements FixedValue {
    
        @Override
        public Object loadObject() throws Exception {
            
            System.out.println("返回固定值");
            return "dog"; } }

    还要实现一个过滤接口,先来看一下这个接口

    package net.sf.cglib.proxy;
    
    import java.lang.reflect.Method;
    
    /**
     * Map methods of subclasses generated by {@link Enhancer} to a particular
     * callback. The type of the callbacks chosen for each method affects
     * the bytecode generated for that method in the subclass, and cannot
     * change for the life of the class.
     * <p>Note: {@link CallbackFilter} implementations are supposed to be
     * lightweight as cglib might keep {@link CallbackFilter} objects
     * alive to enable caching of generated classes. Prefer using {@code static}
     * classes for implementation of {@link CallbackFilter}.</p>
     */
    public interface CallbackFilter {
        /**
         * Map a method to a callback.
         * @param method the intercepted method
         * @return the index into the array of callbacks (as specified by {@link Enhancer#setCallbacks}) to use for the method, 
         */
        int accept(Method method);
    
        /**
         * The <code>CallbackFilter</code> in use affects which cached class
         * the <code>Enhancer</code> will use, so this is a reminder that
         * you should correctly implement <code>equals</code> and
         * <code>hashCode</code> for custom <code>CallbackFilter</code>
         * implementations in order to improve performance.
        */
        boolean equals(Object o);
    }

    这里主要的实现逻辑在accept内,下面是实现类

    package demo.cglib;
    
    import java.lang.reflect.Method;
    
    import net.sf.cglib.proxy.CallbackFilter;
    
    public class TargetMethodFilter implements CallbackFilter {
    
        @Override
        public int accept(Method method) {
            if(method.getName().equals("first")) {
                return 0;
            }
            if(method.getName().equals("second")) {
                return 1;
            }
            return 2;
        }
    
    }

    在这里说明一下,这里返回的数字代表着所调用的代理类的序号。之前我们已经看到过,代理增强需要设置Callback,就是上面的enhancer.setCallback()方法,但是上面

    只用了一个拦截类。当有多个拦截类时,我们定义一个Callback数组,然后设置setCallbacks(),上面返回的数字,就代表所调用的对应数组中哪个拦截方式。

    下面来测试一下

    package demo.cglib;
    
    import net.sf.cglib.proxy.Callback;
    import net.sf.cglib.proxy.CallbackFilter;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.NoOp;
    
    public class DemoTest {
        public static void main(String[] args) {
            TargetInterceptor interceptor = new TargetInterceptor();
            Callback resultFixed = new TargetResultFixed();
            Callback noOp = NoOp.INSTANCE;
            // 这里定义了拦截类数组
            Callback[] callbacks = {noOp,interceptor,resultFixed};
            //过滤逻辑类
            CallbackFilter filter = new TargetMethodFilter();
            Enhancer eh = new Enhancer();
            eh.setSuperclass(Target.class);
            // 设置拦截数组,过滤方法返回的整数将对应数组中的具体拦截类,然后被执行
            eh.setCallbacks(callbacks);
            eh.setCallbackFilter(filter);
            Target target = (Target)eh.create();
            
            target.first();
            target.second();
            target.third();
        }
    }    

    输出结果:

    first
    method:second ---begain---
    second
    method:second ---end---
    返回固定值

    当然上面介绍的知识cglib的一些简单的应用和原理,了解更过可以去github上参考项目源码和文档,当然更多的应该是

    多去实践和测试。很多东西都是很简单的东西堆砌拼凑出伟大的东西,就像计算机不就是0和1么,但是无数的0和1各种不同的拼凑

    就可以代表无穷的信息。从小到大,从简单到复杂,重要的是掌握原理和精髓。

  • 相关阅读:
    查找算法之——符号表(引入篇)
    排序算法之——优先队列经典实现(基于二叉堆)
    C# Timer和多线程编程、委托、异步、Func/Action
    Tomcat汇总-部署多个项目(不同端口)
    数据库汇总(MySQL教材)
    基础知识
    常用工具&网址
    Phython开发
    单元测试
    软件项目过程和文档
  • 原文地址:https://www.cnblogs.com/justenjoy/p/7593152.html
Copyright © 2011-2022 走看看