使用cglib创建Java代理以及调用的结果分析

KevinQ 发表于 2022/04/21 20:36:10 2022/04/21
【摘要】 问题起源在最近的工作中,AOP如何实现的问题一直困扰着笔者,奈何工作繁重,一直没有时间学习了解这一块。这一次,趁着工作上使用shiro的风,顺着shiro注解的使用过程,我们来逐步了解了注解,自定义注解的实现,Java反射,以及在上一篇文章《从使用Java代理的极简代码看Java代理逻辑》中,我们学习了Java的代理。顺着Java代理的学习路线,我们了解到有一个非常遍历的工具包:cglib...

问题起源

在最近的工作中,AOP如何实现的问题一直困扰着笔者,奈何工作繁重,一直没有时间学习了解这一块。

这一次,趁着工作上使用shiro的风,顺着shiro注解的使用过程,我们来逐步了解了注解,自定义注解的实现,Java反射,以及在上一篇文章《从使用Java代理的极简代码看Java代理逻辑》中,我们学习了Java的代理。顺着Java代理的学习路线,我们了解到有一个非常遍历的工具包:cglib。

cglib的基础原理

对于cglib的基础原理,我们暂不做深究,但是可以大概概况为:使用字节码处理框架ASM,动态地生成所要代理类的一个子类,子类中重写类的所有非final方法,从而在子类中拦截父类方法,插入相关逻辑代码。

cglib的应用

我们以java的maven项目为例,来看看如何使用cglib的包。

cglib的mvn包分为两个:cglib与cglib-nodep,我们可以在mvn仓库中搜索到,cglib-nodep内部包含asm的类,不需要再额外引入,通常我们使用这个包,使用方式如下:

<!-- https://mvnrepository.com/artifact/cglib/cglib-nodep -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib-nodep</artifactId>
    <version>3.3.0</version>
    <!-- <scope>test</scope> -->
</dependency>

PS: 这里被官方的maven坑了一下,scope为test,怎么着都引用不到包中的接口与类。

另外,我们惊喜的发现jfinal中也使用了cglib包,相信我们距离jfinal拦截器的实现又接近了一大步。

image.png

实现过程

如下,我们有想要被代理的类

package com.qw.cglib;

public class Writer {
    String name;

    public Writer(String name) {
        this.name = name;
    }

    public void doWork() {
        System.out.println(name + "在工作");
    }
}

方法拦截器类,实现cglib中的接口MethodInterceptor

package com.qw.cglib;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class WriterInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("方法调用前");
        Object result = methodProxy.invokeSuper(o, objects);
        System.out.println("方法调用后");
        System.out.println(result);
        return result;
    }
}

其中有一点,需要我们观测一下,我们想要监测的方法是doWork,它的返回值是void,我们观察一下result的输出结果。

测试类:

package com.qw.cglib;

import net.sf.cglib.proxy.Enhancer;

public class TestWriter {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(Writer.class);
        enhancer.setCallback(new WriterInterceptor());
        Writer writer = (Writer) enhancer.create(new Class[]{String.class}, new String[]{"洛叶飘"});
        writer.doWork();
    }
}

执行结果:

方法调用前
洛叶飘在工作
方法调用后
null

成功的完成了对方法的监控,并插入了相关代码。

注意invokeSuper的返回值

其中,不难注意到,void返回值的打印结果是null。那么,如果返回值为null的,其反射调用结果是多少呢?

我们修改一下doWork方法:

public String doWork() {
        System.out.println(name + "在工作");
        return null;
    }

其他部分代码不变,重新执行,结果如下:

方法调用前
洛叶飘在工作
方法调用后
null

如此,即无论返回值为void,或者实际返回的null,反射方法调用的返回结果均为null

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区),文章链接,文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:cloudbbs@huaweicloud.com进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。