banner
RustyNail

RustyNail

coder. 【blog】https://rustynail.me 【nostr】wss://ts.relays.world/ wss://relays.world/nostr

简单aop模块(cglib实现)

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

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。