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

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。