AOP学习之一 -- CGLIB使用介绍

AOP学习之一 – CGLIB使用介绍

前言

spring-aop是基于cglib实现的(spring本身基于cglib做了一些修改,但是使用方法类似),所以在学习aop的过程中,看到最后,是绕不过cglib的,如果不熟悉cglib的使用,可能会比较难理解其中的一些逻辑实现,本文就简单介绍下cglib的使用。

CGLIB

CGLIB简单来讲,就是围绕Callback接口而来,更具体的是MethodInterceptor接口,代理类的每个函数都会调用该接口的intercept函数,然后由intercept负责具体的代理任务,也就是你需要嵌入的工作都扔在这个函数去做,可见MethodInterceptor针对的是类级别的代理;若需要针对函数级别的代理,需要使用CallbackFilter接口,使用该接口可以将每个函数精确的映射到对应的Callback,因此你需要针对函数设置对应的Callback。

框图参见附录1.

Enhancer

cglib最核心的类非Enhancer莫属,Enhancer类负责创建代理实例,相当于cglib的入口,使用它就可以轻松创建各种代理:

public Object createProxy(Class targetClass) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(targetClass);
        enhancer.setCallback(NoOp.INSTANCE);
        return enhancer.create();
}

上面就是一个最简单的示例,实际使用中换掉Callback就可以了。

示例

我们选择一个常见的场景–日志监控,对需要监控的类的每个调用函数都打印调用信息。

1 目标类

public class OperationService {
    public int add(int x) {
    	System.out.println(x + 1);
        return x + 1;
    }

    public int minus(int x) {
        System.out.println(x - 1);
        return x - 1;
    }
}

2 日志接口和类

public interface LogInterface {
    void log(String msg);
}

public class LogImpl implements LogInterface {
    @Override
    public void log(String msg) {
        System.out.println(msg);
    }
}

3 MethodInterceptor

为使用Enhancer,必须创建一个MethodInterceptor实现类,在该类中实现函数调用的监控:

public class LogInterceptor implements MethodInterceptor {

    private LogInterface logService;

    public LogInterceptor(LogInterface logger) {
        logService = logger;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        String msg = String.format("func %s invoked, args: %s", method.getName(), args[0]);
        logService.log(msg);
        return methodProxy.invokeSuper(o, args);
    }
}

同时,我们修改下createProxy的实现:

public static Object createProxy(Class targetClass) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(targetClass);
    enhancer.setCallback(new LogInterceptor(new LogImpl()));
    return enhancer.create();
}

main函数:

public static void main(String[] args) {
    OperationService service = (OperationService) MyProxyFactory.createProxy(OperationService.class);
    service.add(1);
    service.minus(1);
}

输出:
func add invoked, args: 1
2
func minus invoked, args: 1
0

可见,目标类OperationService中的每个函数调用都被打印出来。

4 CallbackFilter

cglib中的CallbackFilter机制的使用,需要使用setCallbacks设置Callback数组,而为了达到分发的目的,CallbackFilter的accept函数会返回对应的Callback数组的下标。

假如我们需要为minus函数设置一个不同的打印格式,则首先需要引入一个不同的MethodInterceptor:

public class LogInterceptor1 implements MethodInterceptor {
    private LogInterface logService;

    public LogInterceptor1(LogInterface logger) {
        logService = logger;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        String msg = String.format("[LogInterceptor1] func %s invoked, args: %s", method.getName(), args[0]);
        logService.log(msg);
        return methodProxy.invokeSuper(o, args);
    }
}

设置映射关系:

public class LogFilter implements CallbackFilter {
    @Override
    public int accept(Method method) {
        if (method.getName().equals("add")) {
            return 0;  // add函数对应的Callback的下标
        } else {
            return 1; // minus函数对应的Callback的下标
        }
    }
}

再改变代理的创建方式:

public static Object createProxy(Class targetClass) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(targetClass);
    enhancer.setCallbacks(new Callback[] {new LogInterceptor(new LogImpl()),
            new LogInterceptor1(new LogImpl())});
    enhancer.setCallbackFilter(new LogFilter());
    return enhancer.create();
}

输出:
func add invoked, args: 1
2
[LogInterceptor1] func minus invoked, args: 1
0

可见,两个函数的打印格式不同。

5 LazyLoader和Dispatcher

前面讲到cglib主要是围绕Callback而来,而Callback其实只是个标记接口(空接口),为啥不直接用MethodInterceptor接口呢?可能就是考虑到更大的扩展性,比如某些情况下,直接调用目标对象,而不做额外的代理工作,cglib提供了LazyLoader和Dispatcher两个接口:

LazyLoader: 在loadObject之后,缓存该对象,后续不在调用loadObject获取目标对象。

Dispatcher:每次都调用loadObject获取目标对象。

Dispather示例:

public class StaticDispatcher implements Dispatcher {
    private Object target;

    public StaticDispatcher(Object o) {
        target = o;
    }

    @Override
    public Object loadObject() throws Exception {
        System.out.println("StaticDispatcher");
        return target;
    }
}

改变代理创建方式:

public static Object createProxy(Object target) {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(target.getClass());
    enhancer.setCallbacks(new Callback[] {new StaticDispatcher(target),
            new LogInterceptor1(new LogImpl())});
    enhancer.setCallbackFilter(new LogFilter());
    return enhancer.create();
}

注意参数变为对象实例。

public static void main(String[] args) {
    OperationService service = (OperationService) MyProxyFactory.createProxy(new OperationService());
    service.add(1);
    service.minus(1);
    service.add(1);
}

输出:
StaticDispatcher
2
[LogInterceptor1] func minus invoked, args: 1
0
StaticDispatcher
2

可见,每次调用add,loadObject都会被调用。

而使用LazyLoader之后:

public class LazyDispatcher implements LazyLoader {
    private Object target;

    public LazyDispatcher(Object o) {
        target = o;
    }

    @Override
    public Object loadObject() throws Exception {
        System.out.println("LazyDispatcher");
        return target;
    }
}

输出:
LazyDispatcher
2
[LogInterceptor1] func minus invoked, args: 1
0
2

可见,第二次调用add时,loadObject并未被调用。

总结

cglib使用还是很方便的,代码编写也挺简洁,但是个人不太喜欢CallbackFilter这种分发方式。。。有时间再研究下cglib的实现,以及spring对cglib的改造。

附录

  1. CGLIB极简文档
  2. CGLIB手册
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值