Java 学习笔记

所有文章
📖 最新文章 transmittable-thread-local 库 JOOR 反射库 Lombok 库:为你减少样板代码 Slf4j 日志组件的使用 加速maven、gradle依赖下载
📖 Java 基础 安装 第一个程序 使用 UTF-8 编写代码 空值 null 正则表达式 线程 日期/时间 匿名类 枚举 ThreadLocal 线程本地变量 动态代理 jar 命令

Java : 使用 cglib 实现动态代理


cglib 项目地址:https://github.com/cglib/cglib

Java自带的动态代理不同,cgilib 动态代理没有必须使用接口的限制。也就是,如果一个类没有实现任何接口,cglib 支持对其生成动态代理。当然,如果一个类实现了一些接口,cglib 也支持对其生成动态代理。cglib 的代理机制基于继承。

要使用 cglib,需要引入依赖。如果使用 gradle 构建项目,配置依赖如下:

compile group: 'cglib', name: 'cglib', version: '3.2.10'

入门

Hello 类是要被代理的类:

package demo01;

public class Hello {

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

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

}

方法执行的拦截器类,用于实现动态代理:

package demo01;

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

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.printf("发起调用. obj: %s, method: %s, proxy: %s\n",
                obj.getClass().getCanonicalName(),
                method.getName(),
                proxy.getClass().getCanonicalName()
        );
        return proxy.invokeSuper(obj, args);
    }

    // 该函数用于生成代理
    public static Object createProxy(Class cls) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(cls);
        enhancer.setCallback(new CustomInterceptor());
        return enhancer.create();
    }

}

测试代理效果:

package demo01;

public class Main {

    public static void main(String[] args) {
        Hello hello = (Hello) CustomInterceptor.createProxy(Hello.class);
        hello.hello();
    }

}

运行结果:

发起调用. obj: demo01.Hello$$EnhancerByCGLIB$$564008dc, method: hello, proxy: net.sf.cglib.proxy.MethodProxy
hello

MethodInterceptor 是方法级别的代理

上面的示例中,CustomInterceptor 继承自 MethodInterceptor 。MethodInterceptor 实现的是方法级别的代理。当被代理类进行自调用时,依然会执行代理逻辑。

在 Hello 类的 hello 方法中增加自调用:

public class Hello {

    public void hello() {
        System.out.println("hello");
        hi(); // 调用 hi
    }

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

}

运行下面的测试代码:

public class Main {

    public static void main(String[] args) {
        Hello hello = (Hello) CustomInterceptor.createProxy(Hello.class);
        hello.hello();
    }

}

结果如下:

发起调用. obj: demo02.Hello$$EnhancerByCGLIB$$263b7947, method: hello, proxy: net.sf.cglib.proxy.MethodProxy
hello
发起调用. obj: demo02.Hello$$EnhancerByCGLIB$$263b7947, method: hi, proxy: net.sf.cglib.proxy.MethodProxy
hi

使用 InvocationHandler 构造类级别的代理

有时候,我们并不想自调用的时候走代理逻辑,那么,可以使用 InvocationHandler 构造类级别的代理。

Java本身的动态代理中也涉及一个类,名字也是 InvocationHandler 。不过它的全路径是 java.lang.reflect.InvocationHandler。 cglib 中的全路径是 net.sf.cglib.proxy.InvocationHandler。

我们看下如何使用:

package demo03;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.InvocationHandler;

import java.lang.reflect.Method;

public class CustomInterceptor implements InvocationHandler {

    private Object target;

    public CustomInterceptor(Object target) {
        this.target = target;
    }

    public static Object createProxy(Object target) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(target.getClass());
        enhancer.setCallback(new CustomInterceptor(target));
        return enhancer.create();
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.printf("发起调用. method: %s, proxy: %s\n",
                method.getName(),
                proxy.getClass().getCanonicalName()
        );

        return method.invoke(target, args);
    }
}

假如被代理类代码是:

package demo03;

public class Hello {

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

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

}

测试代理效果:

package demo03;

public class Main {

    public static void main(String[] args) {
        Hello hello = (Hello) CustomInterceptor.createProxy(new Hello());
        hello.hello();
    }

}

运行结果如下:

发起调用. method: hello, proxy: demo03.Hello$$EnhancerByCGLIB$$8913a7da
hello
hi

可以看到,输出 hi 时,没有输出 CustomInterceptor 中那些额外内容。


( 本文完 )

文章目录