cglib之设置回调过滤器

举报
KevinQ 发表于 2022/04/24 19:02:56 2022/04/24
【摘要】 问题缘起在学习开源框架jfinal的过程中,尤其是研究其拦截器的实现时,遇到AOP的问题,然后逐步引导着自己学习Java的动态代理,认识到一个强大的工具包:cglib。在上一篇文章《使用cglib创建Java代理以及调用的结果分析》中,我们学习了cglib创建Java代理的实现方式,通过Enhancer来创建监听对象,从而对方法进行拦截。如下为main方法中,对Writer类进行动态代理的...

问题缘起

在学习开源框架jfinal的过程中,尤其是研究其拦截器的实现时,遇到AOP的问题,然后逐步引导着自己学习Java的动态代理,认识到一个强大的工具包:cglib。

在上一篇文章《使用cglib创建Java代理以及调用的结果分析》中,我们学习了cglib创建Java代理的实现方式,通过Enhancer来创建监听对象,从而对方法进行拦截。

如下为main方法中,对Writer类进行动态代理的过程。其中WriterInterceptor实现了net.sf.cglib.proxy.MethodInterceptor接口

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();

在学习cglib的过程中,遇到一个新的概念:回调函数,以及回调过滤器。

回调

Java中也有回调,但是笔者个人碰到回调这个词,第一个想到的是javascript中的回调,将函数作为参数传递给另一个函数:如下所示:

function myDisplayer(some) {
  console.log(some)
}

function myCalculator(num1, num2, myCallback) {
  let sum = num1 + num2;
  myCallback(sum);
}

myCalculator(5, 5, myDisplayer);

而Java中的回调是怎么回事呢?

我们知道Java中参数不能传递函数,那么又是如何实现回调的呢?

如下代码是一种实现回调的逻辑:通过只有一个函数的接口,在对象中保存一个接口对象,在指定方法内部调用接口对象的方法,从而实现回调机制:

package com.qw.callback;

public interface CallBackIn {
    public void callBackMethod();
}
package com.qw.callback;

public class Test {
    private CallBackIn callBack;

    public void setCallBack(CallBackIn callBack) {
        this.callBack = callBack;
    }
    public void call() {
        callBack.callBackMethod();
    }

    public static void main(String[] args) {
        Test test = new Test();
        test.setCallBack(new CallBackIn() {
            @Override
            public void callBackMethod() {
                System.out.println("回调函数被执行啦...");
            }
        });
        test.call();
    }
}

执行结果:

回调函数被执行啦...

cglib的回调过滤

借助cglib,可以实现给某个类的所有方法或部分方法设置回调函数,可以实现修改函数返回结果,添加过滤器等功能。

一个实现样例如下:

有要被过滤的类Writer,他它有三个方法,如下:

package com.qw.cglib;

public class Writer {
    String name;

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

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

    public String rest() {
        System.out.println(name + "在休息");
        return name + "在休息";
    }

    public String eat() {
        System.out.println(name + "在吃饭");
        return name + "在吃饭";
    }
}

回调过滤器的实现,如下,通过实现net.sf.cglib.proxy.CallbackFilter接口,其中的accept方法可以指定设置类的过滤数组时,某个方法在过滤数组中的索引:

package com.qw.cglibfilter;

import net.sf.cglib.proxy.CallbackFilter;

import java.lang.reflect.Method;

public class TargetMethodCallbackFilter implements CallbackFilter {

    /**
     * @param method
     * @return 返回值为被代理类的各个方法在回调数组Callback[]中的位置索引
     */
    @Override
    public int accept(Method method) {
        System.out.println(method.getName());
        if (method.getName().equals("doWork")) {
            System.out.println("filter doWork == 0");
            return 0;
        }
        if (method.getName().equals("rest")) {
            System.out.println("filter rest == 1");
            return 1;
        }
        if (method.getName().equals("eat")) {
            System.out.println("filter eat == 2");
            return 2;
        }
        return 0;
    }
}

过滤器使用方式:

Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Writer.class);
CallbackFilter callbackFilter = new TargetMethodCallbackFilter();
Callback callback1 = new TargetInterceptor();
Callback noop = NoOp.INSTANCE;
Callback fixedValue = new TargetResultFixed();
Callback[] callbacks = new Callback[]{callback1, noop, fixedValue};
enhancer.setCallbacks(callbacks);
enhancer.setCallbackFilter(callbackFilter);
Writer writer = (Writer) enhancer.create(new Class[]{String.class}, new String[]{"洛叶飘"});
System.out.println("----------------函数调用doWork-----------------");
String workResult = writer.doWork();
System.out.println("----------------函数调用rest-------------------");
String restResult = writer.rest();
System.out.println("----------------函数调用eat--------------------");
String eatResult = writer.eat();
System.out.println("------------------函数返回值-------------------");
System.out.println(workResult);
System.out.println(restResult);
System.out.println(eatResult);

上述代码中,TargetInterceptor是我们上一篇文章中的方法拦截器类,这里不再赘述。NoOp类表明什么操作也不做(可以看看下面的运行结果来体会一下),TargetResultFixed实现了net.sf.cglib.proxy.FixedValue接口,可以修改函数的返回值。

我们先来看看运行结果:

doWork
filter doWork == 0
rest
filter rest == 1
eat
filter eat == 2
equals
toString
hashCode
clone
----------------函数调用doWork-----------------
调用前
洛叶飘在工作
调用后
调用结果:洛叶飘在工作
----------------函数调用rest-------------------
洛叶飘在休息
----------------函数调用eat--------------------
锁定结果
------------------函数返回值-------------------
洛叶飘在工作
洛叶飘在休息
什么都不干

代码补充

TargetResultFixed类的代码:

package com.qw.cglibfilter;

import net.sf.cglib.proxy.FixedValue;

public class TargetResultFixed implements FixedValue {

    @Override
    public Object loadObject() throws Exception {
        System.out.println("锁定结果");
        Object obj = "什么都不干";
        return obj;
    }
}
【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

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

全部回复

上滑加载中

设置昵称

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

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

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