设计模式——代理模式之CGLib实现分析JDK1.8(二)

上篇博客介绍了JDK当中提供的动态代理实现方式,这篇主要是来介绍,通过CGLib工具实现的动态代理。

首先在maven项目当中添加jar包依赖

 <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.1</version>
        </dependency>

接下来我们可以直接编写具体的实现类,不需要提供接口

public class HelloServiceImpl {
    public void sayHello() {
        System.out.println("hello eakonzhao");
    }
}

接下来编写自定义的代理类,需要继承MethodInterceptor

public class HelloMethodInterceptor implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("Before:" + method.getName());
        Object object = methodProxy.invokeSuper(o,objects);
        System.out.println("After:" + method.getName());
        return object;
    }
}

测试类

public class Client {
    public static void main(String[] args) {
      // 设置动态代理生成的class文件路径
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "C:\\Users\\Administrator\\Desktop\\");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(HelloServiceImpl.class);
        enhancer.setCallback(new HelloMethodInterceptor());
        HelloServiceImpl helloService = (HelloServiceImpl)enhancer.create();
        helloService.sayHello();
    }
}

执行结果

Before:sayHello
hello eakonzhao
After:sayHello

我们发现这个CGLib动态代理生成了三个class文件

这个类继承了我们前面设置的HelloServiceImpl类,实现了Factory接口

我们主要看HelloServiceImpl$$EnchancerByCGLIB$$这个类,这个类当中生成了一个final的类型的sayHello方法,还有一个CGLIB$sayHello$0

    public final void sayHello() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if(this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if(var10000 != null) {
            var10000.intercept(this, CGLIB$sayHello$0$Method, CGLIB$emptyArgs, CGLIB$sayHello$0$Proxy);
        } else {
            super.sayHello();
        }
    }


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

这里的this.CGLIB$CALLBACK_0就是我们传递进去的HelloMethodInterceptor对象,如果为null的话,可以看到是直接调用父类的sayHello方法,否则就调用HelloMethodInterceptor当中的intercept方法。

当我们调用自定义的HelloMethodInterceptor方法的intercept时,会执行我们添加进去的逻辑,调用MethodProxy.invokeSuper方法,来调用我们自己类的实现方法,接下来看MethodProxy当中invokeSuper的实现

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

我们来看下init方法

 private void init() {
        if(this.fastClassInfo == null) {
            Object var1 = this.initLock;
            synchronized(this.initLock) {
                if(this.fastClassInfo == null) {
                    MethodProxy.CreateInfo ci = this.createInfo;
                    MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
                    fci.f1 = helper(ci, ci.c1);
                    fci.f2 = helper(ci, ci.c2);
                    fci.i1 = fci.f1.getIndex(this.sig1);
                    fci.i2 = fci.f2.getIndex(this.sig2);
                    this.fastClassInfo = fci;
                    this.createInfo = null;
                }
            }
        }

    }

这一步主要是完成对FastClassInfo的初始化操作,关于FastClassInfo,其结构如下

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

        private FastClassInfo() {
        }
    }

f1主要保存的是代理对象的类,即HelloMethodInterceptor对象,f2是被代理对象的类信息即HelloServiceImpl,i1是代理类方法签名索引,i2是被代理类方法签名索引

FastClass的实现类就是我们上面截图当中生成的

 public int getIndex(Signature var1) {
        String var10000 = var1.toString();
        switch(var10000.hashCode()) {
        case -1725733088:
            if(var10000.equals("getClass()Ljava/lang/Class;")) {
                return 7;
            }
            break;
        case -1026001249:
            if(var10000.equals("wait(JI)V")) {
                return 2;
            }
            break;
        case 243996900:
            if(var10000.equals("wait(J)V")) {
                return 3;
            }
            break;
        case 946854621:
            if(var10000.equals("notifyAll()V")) {
                return 9;
            }
            break;
        case 1116248544:
            if(var10000.equals("wait()V")) {
                return 1;
            }
            break;
        case 1535311470:
            if(var10000.equals("sayHello()V")) {
                return 0;
            }
            break;
        case 1826985398:
            if(var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return 4;
            }
            break;
        case 1902039948:
            if(var10000.equals("notify()V")) {
                return 8;
            }
            break;
        case 1913648695:
            if(var10000.equals("toString()Ljava/lang/String;")) {
                return 5;
            }
            break;
        case 1984935277:
            if(var10000.equals("hashCode()I")) {
                return 6;
            }
        }

        return -1;
    }

生成的getIndex方法,根据方法签名的hash值来返回索引,sayHello方法返回索引为0,

在进行方法调用时,根据索引调用相应的方法。CGLib是在invoke时,会生成FastClass对象,我们在调用的时候,其实调用的是已经生成好的方法,

可以看到其实调用的被代理类的方法,即CGLIB$sayHello$0 方法,而fci.f1对象的其实是HelloServiceImpl$$EnchancerByCGLIB$$当中的 sayHello方法。

对比CGLib和JDK当中的动态代理

JDK动态代理:

1、被代理对象必须实现一个接口,而CGLib则不需要,可以是单独一个类,也可以是一个接口的实现

2、JDK动态代理生成的文件比较简单,而CGLib生成的字节码文件更复杂,

3、在方法调用上,JDK采用反射的方式进行方法调用,而CGLib是直接调用生成好的方法,执行效率更高。

猜你喜欢

转载自blog.csdn.net/summerZBH123/article/details/81285150