使用CGLIB--动态代理
2019-04-20 | 分类 Programing-language | 标签 Java Cglib Dynamic-proxy

CGLIB介绍

CGLIB是一个字节码生成框架,它提供了一套高层次的API处理Java字节码的转换,可以在运行时继承Java类和实现接口。CGLIB常被用于在AOP框架、测试组件以及数据访问框架中处理动态生成的代理对象和字段访问操作。

CGLIB除了用于创建动态代理对象,还提供了一系列的API,用于Bean对象的访问以及Java对象的反射。在本文中,我们主要关注CGLIB在创建动态代理类中的运用。

Enhancer类

在CGLIB库中,net.sf.cglib.proxy包下面的类主要负责实现动态代理。其中,Enhancer 类是CGLIB实现动态代理的核心类。Enhancer 类通过创建被代理类的子类来实现动态代理。不同于JDK 1.3中基于接口实现的动态代理,CGLIB通过继承的方式实现动态代理,除此之外,CGLIB也支持基于接口的动态代理。相对JDK自带的Proxy动态代理,CGLIB提供的能力更加强大。

下面来看一个通过Enhancer类创建动态代理的例子:

// Foo.java
public class Foo {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

// Main.java
public class Main {
    public static void main(String ...args) {
        Enhancer enhancer = new Enhancer();
        // 设置被代理的类对象
        enhancer.setSuperclass(Foo.class);
        enhancer.setCallback(new CallbackImpl());
        Foo fooProxy = (Foo) enhancer.create();
        fooProxy.setName("name");
        System.out.println(fooProxy.getName());

    }

    private static class CallbackImpl implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("intercept: " + method.getName());
            return proxy.invokeSuper(obj, args);
        }
    }
}

输出结果:

intercept: setName
intercept: getName
name

上面的例子中,我们使用Enhancer 类的enhancer.setSuperClass(Foo.class)方法设置需要被代理的类,然后我们需要设置一个回调对象 Callback ,如果你熟悉JDK的Proxy代理实现的话,就会发现这个回调对象和 InvocationHandler 比较类似,确实这里的 Callback 对象做的作用和 InvocationHandler 是一样的,提供代理的逻辑。这里我们使用 MethodInterceptor 这个Callback实现类。CGLIB提供了一些不同的Callback实现,在后面我们会一一介绍,这里只要知道我们采用的是 MethodInterceptor

MethodInterceptor 接口中定义了一个intercept(Object obj, Method method, Object[] args, MethodProxy proxy)方法,我们可以通过实现这个方法来做具体的代理逻辑。intercept方法的第一个参数obj是创建的代理对象;method是被调用的具体的方法对象;args是方法调用的参数列表;proxy这个对象是对前面method方法的封装,可以通过invokeSuper()方便得调用父类(也就是我们要代理的类)的方法。

Enhancer 类也提供了用于创建动态代理类的静态方法,比如上面例子中创建动态代理类的写法:

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Foo.class);
enhancer.setCallback(new CallbackImpl());
Foo fooProxy = (Foo) enhancer.create();

可以用public static Object create(Class type, Callback callback)静态方法代替:

Foo fooProxy = (Foo) Enhancer.create(Foo.class, new CallbackImpl());

基于接口的动态代理

CGLIB的动态代理是基于继承来实现的,但是CGLIB也支持通过接口的方式来实现动态代理。CGLIB的 Ehancer 类提供了 setInterfaces(Class[] interfaces)方法,可以设置需要被代理的类实现的接口,通过这种方式,达到和JDK中Proxy一样的效果。

// FooInterface.java
public interface FooInterface {
     void setName(String name);
     String getName();
}

// Foo.java
public class Foo implements FooInterface {
    private String name;

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }
}

// Main.java
public class Main {
    public static void main(String ...args) {
        Enhancer enhancer = new Enhancer();
        // 设置代理接口
        enhancer.setInterfaces(new Class[] {FooInterface.class});
        enhancer.setCallback(new CallbackImpl());
        FooInterface fooProxy = (FooInterface) enhancer.create();
        fooProxy.setName("name");
        System.out.println(fooProxy.getName());

    }

    private static class CallbackImpl implements MethodInterceptor {
        private FooInterface foo = new Foo();
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("intercept: " + method.getName());
            return method.invoke(foo, args);
        }
    }
}

输出结果:

intercept: setName
intercept: getName
name

通过 Enhancer 类的setInterfaces()方法,可以设置需要被代理的接口。如果你了解 JDK 1.3中的动态代理的话,可以发现在 CallbackImpl 中调用method的方式,和 InvocationHandler 是一样。特别的,CGLIB还提供了一个特殊的 Callback 类型net.sf.cglib.proxy.InvocationHandler

/**
 * {@link java.lang.reflect.InvocationHandler} replacement (unavailable under JDK 1.2).
 * This callback type is primarily for use by the {@link Proxy} class but
 * may be used with {@link Enhancer} as well.
 * @author Neeme Praks <a href="mailto:neeme@apache.org">neeme@apache.org</a>
 * @version $Id: InvocationHandler.java,v 1.3 2004/06/24 21:15:20 herbyderby Exp $
 */
public interface InvocationHandler
extends Callback
{
    /**
     * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object)
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

}

可以发现,CGLIB中的 net.sf.cglib.proxy.InvocationHandler 接口和 JDK 1.3中定义的 java.lang.reflect.InvocationHandler 定义的一样,也包含三个参数:objectmethodargs。同样的,在CGLIB包中的 java.lang.reflect.InvocationHandler.invoke() 方法中定义的这三个参数和JDK中定义的含义是一样的。上面的 CallbackImpl 实现如果替换成 java.lang.reflect.InvocationHandler,可以实现类似JDK 1.3的写法:

public class Main {
  ....
  private static class CallbackImpl implements InvocationHandler {
      private FooInterface foo = new Foo();

      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          System.out.println("intercept: " + method.getName());
          return method.invoke(foo, args);
      }
  }
}

组合接口实现和子类继承

CGLIB的 Enhancer 类支持同时通过接口实现和继承的方式创建动态代理。也就是说,我们在创建动态代理的时候,可以同时设置 Enhancer 类的 setSuperClass()方法和 setInterfaces()方法。这种方式类似于Java中的类继承机制,可以同时处理接口实现和类继承。

我们来看一个例子:

// FooInterface.java
public interface FooInterface {
     void setName(String name);
     String getName();
}

// Foo.java
public class Foo implements FooInterface {
    private String name;

    @Override
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }

    public void foo() {
        System.out.println("foo");
    }
}

public class Main {
    public static void main(String ...args) {
        Enhancer enhancer = new Enhancer();
        
        enhancer.setInterfaces(new Class[] {FooInterface.class});
        enhancer.setSuperclass(Foo.class);
        
        enhancer.setCallback(new CallbackImpl());
        
        Foo fooProxy = (Foo) enhancer.create();
        fooProxy.setName("name");
        System.out.println(fooProxy.getName());
        fooProxy.foo();

    }

    private static class CallbackImpl implements MethodInterceptor {
        private FooInterface foo = new Foo();

        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("intercept: " + method.getName());
            if (method.getName().equals("getName")) {
                return methodProxy.invoke(foo, args);
            }
            else if (method.getName().equals("setName")) {
                return methodProxy.invokeSuper(proxy, args);
            }
            else {
                return methodProxy.invoke(foo, args);
            }
        }
    }
}

输出结果:

intercept: setName
intercept: getName
null
intercept: foo
foo

上面的例子中,我们既通过setSuperClass(Foo.class)设置了动态代理类的父类是 Foo ,同时又通过setInterfaces(new Class[] {FooInterface.class})设置了动态代理类实现的接口。我们在Callback的实现中,调用setName()方法的时候,使用的是内部的foo对象,而getName()的时候调用的的是methodProxy.invokeSuper(),因为调用的不是同一个对象,所以getName()获取的值是null。

这个例子中,我们使用了methodProxy.invoke()方法,而不是上面 InvocationHandler 例子中的method.invoke()InvocationHandler 中的method.invoke()是运用反射的方式来实现方法调用的,而CGLIB的methodProxy.invoke()是通过另外的FastClass的方式实现的,具体的细节由于篇幅限制,不在这里展开。

同样的,Enhancer类也提供了一个静态方法来简化创建动态代理的过程。

public static Object create(Class superclass, Class[] interfaces, Callback callback);

多个Callback对象

CGLIB支持同时定义多个 Callback 对象。Enhancer 类的create方法可以传递一个 Callback 数组,当指定多个 Callback 对象的时候,需要通过 CallbackFilter 类来分派不同的方法到不同的 Callback 对象。

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);
}

CGLIB提供了一个Callback对象过滤器:CallbackFilter ,该类提供了一个accept()方法,该方法返回的是提供的 Callback 数组的下标。通过 CallbackFilter 可以将拦截的请求分派到不同的 Callback 对象中。

// Foo.java
public class Foo {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

// Main.java
public class Main {
    public static void main(String ...args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Foo.class);
        enhancer.setCallbacks(new Callback[] {new CallbackImpl1(), new CallbackImpl2()});
        enhancer.setCallbackFilter(new CallbackFilterImpl());
        Foo fooProxy = (Foo) enhancer.create();
        fooProxy.setName("name");
        System.out.println(fooProxy.getName());
    }

    private static class CallbackFilterImpl implements CallbackFilter {
        @Override
        public int accept(Method method) {
            if (method.getName().equals("setName")) {
                return 0;
            } else {
                return 1;
            }
        }
    }

    private static class CallbackImpl1 implements MethodInterceptor {
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("intercept: " + method.getName() + " in: " + getClass().getName());
            return methodProxy.invokeSuper(proxy, args);
        }
    }

    private static class CallbackImpl2 implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("intercept: " + method.getName() + " in: " + getClass().getName());
            return proxy.invokeSuper(obj, args);
        }
    }
}

在上面的例子中,我们用 CallbackFilterImpl 实现了一个Callback的过滤器,通过返回Callback对象的下标将拦截到的请求分派到不同的Callback实现类: CallbackImpl1CallbackImpl2 中。

public static Object create(Class superclass, Class[] interfaces, CallbackFilter filter, Callback[] callbacks);

CGLIB也提供了一个静态方法来创建支持Callback对象拦截器的动态代理对象。

动态代理Class对象

上面我们了解了如何通过 Enhancer 创建一个动态代理对象。有时候,我们处于性能或者延迟创建动态代理对象的考虑,我们需要创建一个动态代理类对象,然后将这个类对象缓存起来,等到需要的时候再通过这个类对象创建需要的动态代理类。

// Foo.java
public class Foo {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

public class Foo {
    public static void main(String ...args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Foo.class);
        enhancer.setCallbackTypes(new Class[] {CallbackImpl1.class, CallbackImpl2.class});
        enhancer.setCallbackFilter(new CallbackFilterImpl());

        Class clazz = enhancer.createClass();
        Enhancer.registerCallbacks(clazz, new Callback[] {new CallbackImpl1(), new CallbackImpl2()});

        try {
            Foo fooProxy = (Foo) clazz.newInstance();
            fooProxy.setName("name");
            System.out.println(fooProxy.getName());
        } catch (IllegalAccessException | InstantiationException ignore) {

        }
    }

    private static class CallbackFilterImpl implements CallbackFilter {
        @Override
        public int accept(Method method) {
            if (method.getName().equals("setName")) {
                return 0;
            } else {
                return 1;
            }
        }
    }

    private static class CallbackImpl1 implements MethodInterceptor {
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("intercept: " + method.getName() + " in: " + getClass().getName());
            return methodProxy.invokeSuper(proxy, args);
        }
    }

    private static class CallbackImpl2 implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("intercept: " + method.getName() + " in: " + getClass().getName());
            return proxy.invokeSuper(obj, args);
        }
    }
}

输出结果:

intercept: setName in: org.learn.agent.Foo$CallbackImpl1
intercept: getName in: org.learn.agent.Foo$CallbackImpl2
name

我们首先通过Enhancer enhancer = new Enhancer()创建一个Enhancer对象,然后和之前一样,我们对这个enhancer对象调用enhancer.setSuperClass()来设置需要被代理的类对象。然后通过enhancer.setCallbackTypes()设置所有拦截器的类型。这里我们设置了多个拦截器Callback,所以需要通过enhancer.setCallbackFilter()设置一个拦截器过滤器CallbackFilter来处理拦截请求的分派。如果设置的拦截器只有一个,那设置过滤器的这一步可以省略。然后我们通过调用enhancer.createClass()创建一个代理类的类对象。创建完类对象以后,我们还需要把拦截器对象通过registerCallbacks()注册到这个动态代理类对象上面。然后我们通过反射的方式,用这个动态代理类对象创建了一个动态代理对象。

需要注意的一点是,我们在上面的代码中,通过registerCallbacks()注册到动态代理类对象的拦截器,只会影响到注册这些拦截器的线程,如果这个动态代理类对象被别的线程使用,那么这些拦截器不会起作用。不过 Enhancer 类提供了另外一个方法registerStaticCallbacks(),这个方式注册的拦截器可以被所有线程共享。

CallbackHelper

当我们的拦截器 Callback 对象有多个的时候,我们创建一个 CallbackFilter 对象来处理拦截请求的分派。我们上面的例子中, CallbackFilterImpl 的实现中通过设置上下文中定义的拦截器 Callback 数组的下标来确定不同的拦截器对象。这种方式既容易出错,也比较繁琐。CGLIB提供了一个 CallbackHelper 帮助对象,它其实一个实现了 CallbackFilter 接口的抽象类型,使用者只要继承这个抽象类,然后实现 getCallback(Method)方法,通过入参提供的每个需要被拦截的方法,提供不同的Callback对象。利用这个方法,可以获取到所有需要使用到的拦截器,然后可以通过getCallbacks()获取所有的拦截器对象,也可以通过getCallbackTypes()获取到所有拦截器的类型,可以很方便得给Enhancer对象提供需要的参数,而对拦截器的定义统一放在这个帮助类中。

上面的多拦截器的例子,可以用 CallbackHelper 实现同样的功能。

public class Foo {
    public static void main(String ...args) {
        CallbackHelper callbackHelper = new CallbackHelperImpl(Foo.class, null);
        
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Foo.class);
        enhancer.setCallbackTypes(callbackHelper.getCallbackTypes());
        enhancer.setCallbackFilter(callbackHelper);

        Class clazz = enhancer.createClass();
        Enhancer.registerCallbacks(clazz, callbackHelper.getCallbacks());

        try {
            Foo fooProxy = (Foo) clazz.newInstance();
            fooProxy.setName("name");
            System.out.println(fooProxy.getName());
        } catch (IllegalAccessException | InstantiationException ignore) {

        }
    }
    
    private static class CallbackHelperImpl extends CallbackHelper {
        public CallbackHelperImpl(Class superclass, Class[] interfaces) {
            super(superclass, interfaces);
        }

        @Override
        protected Object getCallback(Method method) {
            if (method.getName().equals("setName")) {
                return new CallbackImpl1();
            } else {
                return new CallbackImpl2();
            }
        }
    }

    private static class CallbackImpl1 implements MethodInterceptor {
        @Override
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
            System.out.println("intercept: " + method.getName() + " in: " + getClass().getName());
            return methodProxy.invokeSuper(proxy, args);
        }
    }

    private static class CallbackImpl2 implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("intercept: " + method.getName() + " in: " + getClass().getName());
            return proxy.invokeSuper(obj, args);
        }
    }
}

Callback类型

除了我们上面介绍的 MethodInterceptorInvocationHandler 这两种类型的拦截器,CGLIB还提供了一些其他类型的拦截器,可以用于特定的场景。

MethodInterceptor

MethodInterceptor 是我们在上文介绍中使用的拦截器,这个拦截器是CGLIB中最通用的拦截器类型,它的作用比较简单,就是拦截方法调用,可以用于实现在AOP中的环绕增强方式。

/**
 * 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;

}

这个拦截器的使用我们在上文中已经介绍了很多了,这里就不再单独介绍了。

InvocationHandler

InvocationHandler 拦截器的作用和上面的 MethodInterceptor 一样,是一个通用的方法拦截器。唯一的不同是,这个拦截器类型是对JDK的动态代理实现 InvocationHandler 的替换,JDK的动态代理可以选择使用CGLIB中的这个 InvocationHandler 拦截器类型,也可以在Enhancer类中使用这个拦截器,实现和Proxy一样的功能。

/**
 * {@link java.lang.reflect.InvocationHandler} replacement (unavailable under JDK 1.2).
 * This callback type is primarily for use by the {@link Proxy} class but
 * may be used with {@link Enhancer} as well.
 * @author Neeme Praks <a href="mailto:neeme@apache.org">neeme@apache.org</a>
 * @version $Id: InvocationHandler.java,v 1.3 2004/06/24 21:15:20 herbyderby Exp $
 */
public interface InvocationHandler extends Callback {
    /**
     * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object)
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;

}

FixedValue

FixedValue 这个拦截器类型和它命名含义一样,会对所有的方法调用返回一个固定的值。这个拦截器只有一个loadObject()方法,在实现这个拦截器的时候,需要返回和被拦截的方法的返回值类型相兼容的类型。

/**
 * {@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;
}

FixedValue 拦截器使用也比较简单:

public class Foo {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

public class Main {
    public static void main(String ...args) {
        Foo fooProxy = (Foo) Enhancer.create(Foo.class, new FixedValueImpl());
        fooProxy.setName("new name");
        System.out.println(fooProxy.getName());
    }

    private static class FixedValueImpl implements FixedValue {
        @Override
        public Object loadObject() throws Exception {
            return "default";
        }
    }
}

输出结果:

default

可以看到,调用fooProxy.getName()的时候,由于 FixedValue 的作用,原先通过setName设置的值被覆盖了。

LazyLoader

/**
 * Lazy-loading {@link Enhancer} callback.
 */
public interface LazyLoader extends Callback {
    /**
     * Return the object which the original method invocation should be
     * dispatched. Called as soon as the first lazily-loaded method in
     * the enhanced instance is invoked. The same object is then used
     * for every future method call to the proxy instance.
     * @return an object that can invoke the method
     */
    Object loadObject() throws Exception;
}

LazyLoader 这个Callback的作用是实现延迟加载,LazyLoader接口中只包含了一个loadObject()方法,当第一次调用设置了这个拦截器的动态代理对象的时候,CGLIB会调用这个loadObject()方法获取方法调用实际需要被分派的对象,之后所有拦截到的方法调用都会分派给这个返回的对象。也就是说loadObject()方法只会被调用一次,后续所有的请求都会分派给这个返回的对象。

LazyLoader 不同于前面介绍的 MethodInterceptor ,在 MethodInterceptor 中处理的是方法调用的分派细节,可以控制调用实际对象的前后逻辑,而 LazyLoader 关注的是实际方法调用被分派的对象,不关心方法调用前后的控制。

public class Foo {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

public class Main {
    public static void main(String ...args) {
        Foo fooProxy = (Foo) Enhancer.create(Foo.class, new LazyLoaderImpl());
        fooProxy.setName("new name 1");
        fooProxy.setName("new name 2");
        System.out.println(fooProxy.getName());
    }

    private static class LazyLoaderImpl implements LazyLoader {
        @Override
        public Object loadObject() throws Exception {
            System.out.println("lazy load object");
            return new Foo();
        }
    }
}

输出结果:

lazy load object
new name 2

从结果中我们也可以看到,虽然我们调用了两次fooProxy.setName(),但是实际LazyLoader.loadObject()只执行了一次。loadObject()的返回值被CGLIB缓存了起来,下次所有拦截到的方法都分派给了这个对象。

Dispatcher

/**
 * Dispatching {@link Enhancer} callback. This is identical to the
 * {@link LazyLoader} interface but needs to be separate so that <code>Enhancer</code>
 * knows which type of code to generate.
 */
public interface Dispatcher extends Callback {
    /**
     * Return the object which the original method invocation should
     * be dispatched. This method is called for <b>every</b> method invocation.
     * @return an object that can invoke the method
     */
    Object loadObject() throws Exception;
}

DispatcherLazyLoader 类似,接口中只有一个loadObject()方法,唯一的区别是:每次方法调用, DispatcherloadObject()方法都会执行,并且返回一个新的对象,而 LazyLoader 只会执行一次loadObject()方法。

Dispatcher 可以用于请求的分派,实现类似于OO里面多态的特性,但是可以控制并决定实际分派的目标对象。

public class Foo {
    private String name;

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

public class Main {
    public static void main(String ...args) {
        Foo fooProxy = (Foo) Enhancer.create(Foo.class, new DispatcherImpl());
        fooProxy.setName("new name 1");
        fooProxy.setName("new name 2");
        System.out.println(fooProxy.getName());
    }

    private static class DispatcherImpl implements Dispatcher {
        @Override
        public Object loadObject() throws Exception {
            System.out.println("dispatch");
            return new Foo();
        }
    }
}

输出结果:

dispatch
dispatch
dispatch
null

可以看到,对代理对象的每次方法调用都会执行Dispatcher.loadObject()方法,而且从输出中可以看到,由于每次调用都是作用于新的对象,所以最后一次的getName()调用和之前的setName()调用分派的是不同的对象,导致最后输出的结果数null

ProxyRefDispatcher

/**
 * Dispatching {@link Enhancer} callback. This is the same as the
 * {@link Dispatcher} except for the addition of an argument
 * which references the proxy object.
 */
public interface ProxyRefDispatcher extends Callback {
    /**
     * Return the object which the original method invocation should
     * be dispatched. This method is called for <b>every</b> method invocation.
     * @param proxy a reference to the proxy (generated) object
     * @return an object that can invoke the method
     */
    Object loadObject(Object proxy) throws Exception;
}

ProxyRefDispatcher 这个拦截器类型实现的功能和 Dispatcher 类似,也是用于方法调用的分派。但是 ProxyRefDispatcher 这个拦截器类型定义了一个带参数的loadObject()方法,方法中的proxy参数是对代理对象的引用。

NoOp

这个拦截器类型和它的名字一样,什么也不做。所有的请求都会直接分派给被代理的对象。

/**
 * 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() { };
}

总结

本文介绍了如何利用CGLIB的net.sf.cglib.proxy包实现动态代理,介绍了Enhancer类和API的使用,最后对所有Callback类型的功能和特性逐个进行了介绍。动态代理只是CGLIB这个工具包提供了其中一项功能,除了本文介绍的动态代理,它还可以处理Bean对象和实现反射,下次我们介绍下如何利用CGLIB进行反射。

TOP