代理模式三:CGLib动态代理

回顾:

上一篇代理模式二:Java动态代理介绍了JDK动态代理,使用Proxy.newProxyInstance生成代理类对象,使用InvocationHandler接口定义回调,但是存在一个局限性,JDK动态代理不能代理没有实现任何接口的类。CGLib可以。先看看CGLib怎么用的。

1、定义一个类,不实现任何接口,如下:

package aop.demo4;

public class GreetingImpl {

    public void sayHello(String name) {
        System.out.println("Hello! " + name);
    }
}

假如现在有个需求,想要在sayHello方法前后做些事,比如打印日志。但是不改动GreetingImpl.java本身

2、定义拦截器,实现MethodInterceptor接口,重写拦截方法intercept

public class CGLibDynamicProxy implements MethodInterceptor 
@Override
public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
    before();
    Object result = proxy.invokeSuper(target, args);
    after();
    return result;
}

3、调用Enhancer.create()方法创建代理对象

public  T getProxy(Class cls) {
    return (T) Enhancer.create(cls, this);
}

2、3两步的完整代码如下:

package aop.demo4;

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 CGLibDynamicProxy implements MethodInterceptor {

    private static CGLibDynamicProxy instance = new CGLibDynamicProxy();

    private CGLibDynamicProxy() {
    }

    public static CGLibDynamicProxy getInstance() {
        return instance;
    }

    @SuppressWarnings("unchecked")
    public  T getProxy(Class cls) {
        return (T) Enhancer.create(cls, this);
    }

    @Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        before();
        Object result = proxy.invokeSuper(target, args);
        after();
        return result;
    }

    private void before() {
        System.out.println("Before");
    }

    private void after() {
        System.out.println("After");
    }
}

4、客户端调用方式

package aop.demo4;

import aop.Greeting;

/**
 * 4. CGLib 动态代理
 */
public class Client {

    public static void main(String[] args) {
        GreetingImpl greeting = CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class);
        greeting.sayHello("Jack");
    }
}

5、执行结果

Before
Hello! Jack
After

下面从源码了解CGLib代理普通类(没有实现接口的类)的特性,看看它是怎么做到的,有没有可以值得我们工作中借鉴的地方。

1、 创建代理类对象。入口是Enhancer.create(cls, this)。

/**
     * Helper method to create an intercepted object.
     * For finer control over the generated instance, use a new instance of Enhancer
     * instead of this static method.
     * @param type class to extend or interface to implement
     * @param callback the callback to use for all methods
     */
    public static Object create(Class type, Callback callback) {
        Enhancer e = new Enhancer();
        e.setSuperclass(type);
        e.setCallback(callback);
        return e.create();
    }

从以上代码大概知道,Enhancer这个类是我们通过CGLib创建代理类对象的关键类,它把我们的真实业务类设置到SuperClass属性中,即作为代理类的父类来用,还设置了我们实现的MethodInterceptor 接口的类作为回调方法来用。

通过阅读Enhancer类的注释了解到:

(1)、Enhancer根据我们传入的真实业务类产生子类

(2)、Enhancer生成的子类覆盖父类的非final方法

(3)、Enhancer生成的子类中有钩子方法,用于调用用户定义的拦截器实现

(4)、MethodInterceptor这种回调(拦截器)在AOP术语中就是环绕增强(around advice),可以在调用真实业务类方法的前后增强,或者修改参数。

(5)、MethodInterceptor这种回调方式,是比较重的,生成的代理类,会为真实业务类(委托类)每一个非final方法增强,试着调用hashCode()方法。

public class Client {

    public static void main(String[] args) {
        GreetingImpl greeting = CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class);
        greeting.sayHello("Jack");

        greeting.hashCode();
    }
}

执行结果:

Before
Hello! Jack
After
Before
After

从输出发现hashCode也被增强了。

CGLib默认提供了一些其他回调方式:

private static final CallbackInfo[] CALLBACKS = {
        new CallbackInfo(NoOp.class, NoOpGenerator.INSTANCE),
        new CallbackInfo(MethodInterceptor.class, MethodInterceptorGenerator.INSTANCE),
        new CallbackInfo(InvocationHandler.class, InvocationHandlerGenerator.INSTANCE),
        new CallbackInfo(LazyLoader.class, LazyLoaderGenerator.INSTANCE),
        new CallbackInfo(Dispatcher.class, DispatcherGenerator.INSTANCE),
        new CallbackInfo(FixedValue.class, FixedValueGenerator.INSTANCE),
        new CallbackInfo(ProxyRefDispatcher.class, DispatcherGenerator.PROXY_REF_INSTANCE),
    };

LazyLoader可以提高性能,使用懒加载方式生成代理类;

还可以使用CallbackFilter选择要增强的方法。

先留个疑问,是否可以只增强指定方法?Spring AOP是怎么做到只拦截指定方法的?

2、字节码生成入口从AbstractClassGenerator.create方法开始

byte[] b = strategy.generate(this);
public byte[] generate(ClassGenerator cg) throws Exception {
        ClassWriter cw = getClassWriter();
        transform(cg).generateClass(cw);
        return transform(cw.toByteArray());
    }

transform(cg).generateClass(cw)会调用具体的生成字节码的实现类去生成字节码,比如我们的Enhancer中的generateClass方法。

回过头来看,Enhancer本身就是AbstractClassGenerator的子类

public class Enhancer extends AbstractClassGenerator

Enhancer中具体的生成字节码的过程有兴趣可以去看一下。

3、加载生成的代理类

String className = ClassNameReader.getClassName(new ClassReader(b));
getClassNameCache(loader).add(className);
gen = ReflectUtils.defineClass(className, b, loader);

4、在Enhancer中实例化代理类

private Object createUsingReflection(Class type) {
        setThreadCallbacks(type, callbacks);
        try{
        
        if (argumentTypes != null) {
        	
             return ReflectUtils.newInstance(type, argumentTypes, arguments);
             
        } else {
        	
            return ReflectUtils.newInstance(type);
            
        }
        }finally{
         // clear thread callbacks to allow them to be gc'd
         setThreadCallbacks(type, null);
        }
    }

CGLib动态代理内部细节:

为了更直观的看看生成的代理类内部的方法,反编译看一下:

修改Client.java,将代理类输出出来:

public class Client {
    public static final String DEFAULT_DEBUG_LOACATION = System.getProperty("user.home") +
            System.getProperty("file.separator") + "cglib-debug";

    public static void main(String[] args) {
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,DEFAULT_DEBUG_LOACATION);

        GreetingImpl greeting = CGLibDynamicProxy.getInstance().getProxy(GreetingImpl.class);
        greeting.sayHello("Jack");
    }
}

在用户目录下找到生成的文件:

用反编译工具procyon 查看代理类实现

java -jar /e/procyon-decompiler-0.5.30.jar ./GreetingImpl\$\$EnhancerByCGLIB\$\$315deeb6.class > 1.java

反编译后的代理类:

//继承GreetingImpl,实现Factory
public class GreetingImpl$$EnhancerByCGLIB$$315deeb6 extends GreetingImpl implements Factory
{
    private boolean CGLIB$BOUND;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static final Method CGLIB$sayHello$0$Method;
    private static final MethodProxy CGLIB$sayHello$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$finalize$1$Method;
    private static final MethodProxy CGLIB$finalize$1$Proxy;
    private static final Method CGLIB$equals$2$Method;
    private static final MethodProxy CGLIB$equals$2$Proxy;
    private static final Method CGLIB$toString$3$Method;
    private static final MethodProxy CGLIB$toString$3$Proxy;
    private static final Method CGLIB$hashCode$4$Method;
    private static final MethodProxy CGLIB$hashCode$4$Proxy;
    private static final Method CGLIB$clone$5$Method;
    private static final MethodProxy CGLIB$clone$5$Proxy;
    
    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        final Class forName = Class.forName("aop.demo4.GreetingImpl$$EnhancerByCGLIB$$315deeb6");
        final Class forName2;
        final Method[] methods = ReflectUtils.findMethods(new String[] { "finalize", "()V", "equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;" }, (forName2 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$finalize$1$Method = methods[0];
        CGLIB$finalize$1$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()V", "finalize", "CGLIB$finalize$1");
        CGLIB$equals$2$Method = methods[1];
        CGLIB$equals$2$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$2");
        CGLIB$toString$3$Method = methods[2];
        CGLIB$toString$3$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()Ljava/lang/String;", "toString", "CGLIB$toString$3");
        CGLIB$hashCode$4$Method = methods[3];
        CGLIB$hashCode$4$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()I", "hashCode", "CGLIB$hashCode$4");
        CGLIB$clone$5$Method = methods[4];
        CGLIB$clone$5$Proxy = MethodProxy.create((Class)forName2, (Class)forName, "()Ljava/lang/Object;", "clone", "CGLIB$clone$5");
        final Class forName3;
        CGLIB$sayHello$0$Method = ReflectUtils.findMethods(new String[] { "sayHello", "(Ljava/lang/String;)V" }, (forName3 = Class.forName("aop.demo4.GreetingImpl")).getDeclaredMethods())[0];
        CGLIB$sayHello$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "(Ljava/lang/String;)V", "sayHello", "CGLIB$sayHello$0");
    }
    
    final void CGLIB$sayHello$0(final String s) {
        super.sayHello(s);
    }
    
    public final void sayHello(final String s) {
        MethodInterceptor cglib$CALLBACK_2;
        MethodInterceptor cglib$CALLBACK_0;
        if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
			//greetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])o2)[0];
			//cglib$CALLBACK_2被赋值为拦截器
            CGLIB$BIND_CALLBACKS(this);
            cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
        }
        if (cglib$CALLBACK_0 != null) {
            cglib$CALLBACK_2.intercept((Object)this, GreetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$sayHello$0$Method, new Object[] { s }, GreetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$sayHello$0$Proxy);
            return;
        }
        super.sayHello(s);
    }
//...省略代码

}

这个代理类继承了真实业务类GreetingImpl,也就是说是真实业务类的子类,并且实现了Factory接口。

默认所有代理类都会实现Factory接口,除非设置了Enhancer.setUseFactory(false)。

这个接口可以改变已经生成的代理对象的回调,也提供了newInstance方法来实例化代理类对象。

 

看一下调用栈,然后分析一些调用细节。

代理模式三:CGLib动态代理_第1张图片

代理模式三:CGLib动态代理_第2张图片

调用细节:

1、开始调用代理类的sayHello方法

greeting.sayHello("Jack");
public final void sayHello(final String s) {
        MethodInterceptor cglib$CALLBACK_2;
        MethodInterceptor cglib$CALLBACK_0;
        if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
            //greetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])o2)[0];
            //cglib$CALLBACK_2被赋值为拦截器
            CGLIB$BIND_CALLBACKS(this);
            cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
        }
        if (cglib$CALLBACK_0 != null) {
            cglib$CALLBACK_2.intercept((Object)this, GreetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$sayHello$0$Method, new Object[] { s }, GreetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$sayHello$0$Proxy);
            return;
        }
        super.sayHello(s);
    }

上述代码,如果使用的是MethodInterceptor回调,就会调用实现了MethodInterceptor接口的拦截器的intercept方法。因为通过代理类的默认构造器CGLIB$BIND_CALLBACKS方法cglib$CALLBACK_2会被转换为拦截器的引用

public GreetingImpl$$EnhancerByCGLIB$$315deeb6() {
        CGLIB$BIND_CALLBACKS(this);
    }
private static final void CGLIB$BIND_CALLBACKS(final Object o) {
        final GreetingImpl$$EnhancerByCGLIB$$315deeb6 greetingImpl$$EnhancerByCGLIB$$315deeb6 = (GreetingImpl$$EnhancerByCGLIB$$315deeb6)o;
        if (!greetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$BOUND) {
            greetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$BOUND = true;
            Object o2;
            if ((o2 = GreetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$THREAD_CALLBACKS.get()) != null || (o2 = GreetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$STATIC_CALLBACKS) != null) {
                greetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])o2)[0];
            }
        }
    }

2、上面的CGLibDynamicProxy就是实现了MethodInterceptor,再看下它的intercept方法:

public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        before();
        Object result = proxy.invokeSuper(target, args);
        after();
        return result;
    }

传入四个参数:

Object target:生成的代理类的对象引用

Method method:真实业务类的方法引用

Object[] args:参数的数组引用

MethodProxy proxy:代理方法的对象

3、调用MethodProxy的invokeSuper方法

public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

这里有一个FastClassInfo类,是为了在调用代理类内部方法的时候,不需要再通过反射,而是通过方法索引的方式调用对应的方法。

FastClassInfo的数据结构中维持了两个FastClass引用f1,f2和两个索引i1,i2:

private static class FastClassInfo
    {
        FastClass f1;
        FastClass f2;
        int i1;
        int i2;
    }

FastClass类的数据结构也很简单,type维持是一个要包装的对象的引用。

private Class type;

FastClass.getIndex方法内部是对方法签名取hashcode,然后返回索引位置

 public int getIndex(final Signature signature) {
        final String string = signature.toString();
        switch (string.hashCode()) {
            case -1725733088: {
                if (string.equals("getClass()Ljava/lang/Class;")) {
                    return 7;
                }
                break;
            }
            case -1026001249: {
                if (string.equals("wait(JI)V")) {
                    return 2;
                }
                break;
            }
            case 243996900: {
                if (string.equals("wait(J)V")) {
                    return 3;
                }
                break;
            }
            case 771401912: {
                if (string.equals("sayHello(Ljava/lang/String;)V")) {
                    return 0;
                }
                break;
            }
//...省略代码

FastClass.invoke方法就是根据索引位置调用对应的方法。方法内容的细节继续看下面的4。

 

FastClassInfo类是在init()中初始化的。最终f1指向的是包装真实业务类(委托类)的FastClass子类,f2指向的是包装代理类的FastClass子类,i1指向代理类中sayHello方法的位置,i2指向CGLIB$sayHello$0方法在代理类中的位置。

4、调用包装了代理类的FastClass子类的invoke方法,根据索引找到要调用的代理类方法

public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {
        final GreetingImpl$$EnhancerByCGLIB$$315deeb6 greetingImpl$$EnhancerByCGLIB$$315deeb6 = (GreetingImpl$$EnhancerByCGLIB$$315deeb6)o;
        try {
            switch (n) {
                case 0: {
                    return new Boolean(greetingImpl$$EnhancerByCGLIB$$315deeb6.equals(array[0]));
                }
                case 1: {
                    return greetingImpl$$EnhancerByCGLIB$$315deeb6.toString();
                }
//...省略代码
                case 8: {
                    greetingImpl$$EnhancerByCGLIB$$315deeb6.sayHello((String)array[0]);
                    return null;
                }
//...省略代码
 case 14: {
                    greetingImpl$$EnhancerByCGLIB$$315deeb6.CGLIB$sayHello$0((String)array[0]);
                    return null;
                }

5、调用代理类中的CGLIB$sayHello$0方法,这个方法直接调用真实业务类的方法

final void CGLIB$sayHello$0(final String s) {
        super.sayHello(s);
    }

6、调用真实业务类方法

7、调用完真实业务类方法后,一步步出栈,回到我们实现的拦截器方法intercept,然后调用after方法,最后返回

@Override
    public Object intercept(Object target, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        before();
        Object result = proxy.invokeSuper(target, args);
        after();
        return result;
    }

 

这里有个疑问,为真实业务类生成的FastClass好像没有用上

细看CGLib生成的为真实业务类生成的FastClass,

public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {
        final GreetingImpl greetingImpl = (GreetingImpl)o;
        try {
            switch (n) {
                case 0: {
                    greetingImpl.sayHello((String)array[0]);
                    return null;
                }
//...省略代码
                case 7: {
                    return greetingImpl.getClass();
                }
            }
        }
//...省略代码

发现其实可以继承MethodProxy,自己实现invokeSuper,内部就直接调用CGLib生成的为真实业务类生成的FastClass

fci.f1.invoke(fci.i1, obj, args);

这样可以更快的调用真实业务类的方法。

 

可是为什么CGLib没有这样做呢?这个疑问有待思考

反编译工具:https://pan.baidu.com/s/1XD30RinqKxTy5uH104Q7Yg

 

 

你可能感兴趣的:(java,设计模式)