使用 cglib 实现简单的 aop 模块
around 还有些问题。
jar 包#
需要 jar 包cglib-nodep-2.1_3
, 或者 maven 加上
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.4</version>
</dependency>
版本视情况改变。
目标类#
假设有这么个目标类型
public class TestTarget {
public void out(String a) {
System.out.println("Target out."+a);
}
}
TestTarget 类里面有个out
方法,我们希望在out
方法执行的时候打 log。
增强器#
增强器 Enhancer 可以增强原有的方法,并返回一个新的对象。
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TestTarget.class);
enhancer.setCallback(callback);
代码创建一个 enhancer,然后把一个Class<TestTarget>
对象传进去,enhancer 对它干了什么,暂时不用理。
随后传入一个 callback,callback 是这样的:
public class BeforeCallBack implements MethodInterceptor {
public BeforeCallBack() {
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
// do something here.
return methodProxy.invokeSuper(o, objects);
}
}
BeforeCallBack
实现了MethodInterceptor
接口。这里才是真正的增强,在 return 原有的方法之前,执行一些东西(这里是 before 切点,也可以改成 after、around)
效果:
此处匿名内部类实现的接口并替换成 lambda。
public class MainClass {
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TestTarget.class);
enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
System.out.println("before.");
return methodProxy.invokeSuper(o,objects);
});
TestTarget testTarget = (TestTarget) enhancer.create();
testTarget.out();
}
}
class TestTarget{
public void out(){
System.out.println("out. ");
}
}
before.
out.
通用化#
- 切点
在增强的时候,我们并没有指定是哪一个方法被增强,所以类里面的所有方法都会被起作用。
cglib 提供了CallbackFilter
来区分什么方法应该使用什么 Callback(before、after..etc)来处理。
用来标记方法类型的,注解@interface
无疑是最合适的了。
- 切面
我们在拦截了方法之后,在它返回前后进行一些操作,在intercept
方法里边写上我们要有的操作。
也就是说,每有一个 Callback 的需求,我们都要手动的实现接口并且加上业务代码。那么我们应该把业务代码抽取出来。
把业务抽象成一个切面,比如打 log,我们有个 LogAspect, 继承于 AbstractAspect,届时,只要在intercept
跑抽象的方法就可以了。
比如:
public class BeforeCallBack implements MethodInterceptor {
Class<? extends AbstractAspect> aspect;
public BeforeCallBack(Class<? extends AbstractAspect> aspect) {
this.aspect = aspect;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
AbstractAspect instance = aspect.newInstance();
instance.doBefore();
return methodProxy.invokeSuper(o, objects);
}
}
不管实际上的切面是什么,他都是AbstractAspect
。
- 注解解析
主要的注解有两个:Aspect
、PointCut
, 一个是用于标记 TargetClass 要应用什么切面,一个是用于标记方法的切点类型(before、after..)