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的改造。