DengQN·一个普通程序员;
简单aop模块(cglib实现)
2018-09-30 17:00 60
#方法#实现#类#增强#切面#类型#代码#接口#切点

使用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

  • 注解解析

主要的注解有两个:AspectPointCut,一个是用于标记TargetClass要应用什么切面,一个是用于标记方法的切点类型(before、after..)

demo

gitee demo