专业的编程技术博客社区

网站首页 > 博客文章 正文

动态代理之Cglib代理CASE篇(cglib动态代理应用业务场景)

baijin 2024-09-04 02:03:21 博客文章 9 ℃ 0 评论

CGLib (Code Generation Library) 是一个强大的、高性能、高质量的 Code 生成类库(代码生成包)。它可以在运行期扩展 Java 类与实现 Java 接口。

静态代理JDK动态代理模式都是要求目标对象是实现一个接口的目标对象,但是有时候目标对象只是一个单独的对象,并没有实现任何的接口,这个时候就可以使用以目标对象子类的方式类实现代理,这种方法就叫做:Cglib代理

Cglib代理,也叫作子类代理,它是在内存中构建一个子类对象从而实现对目标对象功能的扩展.

JDK的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口,如果想代理没有实现接口的类,就可以使用Cglib实现.

CGLib 比 Java 的 java.lang.reflect.Proxy 类更强的在于它不仅可以接管接口类的方法,还可以接管普通类的方法。

CGLIB的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。但不鼓励大家直接使用ASM框架,因为对底层技术要求比较高。


CASE需求: 机构报表任务统计,执行完成前置和后置增加打印日志,任务完成时间统计

类图


CglibProxyFactory

cglib代理构造工厂:

public final class CglibProxyFactory {

    private CglibProxyFactory() {
    }

    public static <T> T getProxyInstance(T target, MethodInterceptor interceptor) {
        Class<?> targetClass = target.getClass();
        Enhancer enhancer = new Enhancer();
        //设置类加载器
        enhancer.setClassLoader(targetClass.getClassLoader());
        //设置要代理的目标类,以扩展功能
        enhancer.setSuperclass(targetClass);

        // 设置单一回调对象,在回调中拦截对目标方法的调用
        enhancer.setCallback(interceptor);

        return (T) enhancer.create();
    }
}

资源任务

public interface ResourceJob<T> {
    String execute(T t) throws Exception;
}

真实主题

public class OrganizationIntervalReportStatisticJob implements ResourceJob<String> {

    @Override
    public String execute(String jobExecutionParam) throws Exception {
        String message = String.format("org interval report job run arg:[%s]", jobExecutionParam);
        System.out.println(message);
        int time = new Random().nextInt(10) + 1;
        Thread.sleep(time * 1000);

        return Boolean.toString(true);
    }

    protected String executeTask(String jobExecutionParam) throws Exception {
        String message = String.format("org interval report job run arg:[%s]", jobExecutionParam);
        System.out.println(message);
        int time = new Random().nextInt(10) + 1;
        Thread.sleep(time * 1000);

        return Boolean.toString(true);
    }

    public static String start(String jobExecutionParam) throws Exception {
        String message = String.format("org interval report job run arg:[%s]", jobExecutionParam);
        System.out.println(message);
        int time = new Random().nextInt(10) + 1;
        Thread.sleep(time * 1000);

        return Boolean.toString(true);
    }
}

ReportStatisticJobLogInterceptor

public class ReportStatisticJobLogInterceptor implements MethodInterceptor {

    private Object target;

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

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        long startTime = System.currentTimeMillis();
        System.out.println("the task start run");
        Object result = method.invoke(target, args);
        //        Object result = proxy.invokeSuper(obj, args);
        long stopTime = System.currentTimeMillis();
        System.out.println("the task end run");
        System.out.println("task run time ::" + (stopTime - startTime) + "毫秒!");
        return result;
    }
}

测试类

public class CglibProxyTest {

    public static void main(String[] args) throws Exception {

        OrganizationIntervalReportStatisticJob job = new OrganizationIntervalReportStatisticJob();

        OrganizationIntervalReportStatisticJob proxyInstance = CglibProxyFactory.getProxyInstance(job, new ReportStatisticJobLogInterceptor(job));

        String result = proxyInstance.execute("taskId=123");

        System.out.println("resource task run result:" + result);

        System.out.println("----------static test------------------");
        OrganizationIntervalReportStatisticJob.start("taskId=456");

        System.out.println("----------protected test------------------");
        proxyInstance.executeTask("taskId=789");
    }
}


执行结果:


分析:

Cglib实现动态代理的步骤也不是很麻烦,先创建一个类实现MethodInterceptor接口,重写intercept方法,在intercep中可以截获委托类的可代理的方法。


特点:

  • 内存中动态的构建目标类的子类以达到代理的目的

局限性

    • 目标类中方法如果有final/static,则不会被MethodInterceptor 拦截
    • 目标类不能被final修饰,否则报异常(final修饰的类不能被继承)

Cglib动态代理与JDK动态代理对比:

相同的原理:

运行时动态生成代理对象,即通过编写代理的class文件,并将拦截的方法写入到代理的class中,在原有方法调用前,存在拦截器,则先调用拦截器的方法

  区别:

1)JDK动态代理是Java原生支持的,不需要任何外部依赖,利用的是接口来实现的代理,目标类必须也只能实现某个或者某些接口;

2)Cglib则是利用继承关系,利用asm在运行时动态生成委托类的子类,从而实现对委托类的代理。因此不依赖接口

3)Cglib由于是利用继承关系来实现代理的,无论目标对象有没有实现接口都可以代理,但也因此无法代理被final修饰的类以及被final修饰的方法。

4)Cglib一般来说效率要比JDK动态代理效率更高,可以实现的代理也更为强大


应用

Hibernate 用它来实现 PO(ersistent Object 持久化对象) 字节码的动态生成

Spring AOP用它提供方法的interception(拦截)

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表