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メソッドが実行される際にログを出力したいとします。

エンハンサー#

エンハンサー 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 {
        // ここで何かを実行します。
        return methodProxy.invokeSuper(o, objects);
    }
}

BeforeCallBackMethodInterceptorインターフェースを実装しています。ここで実際の強化が行われます。元のメソッドを返す前に、何かしらの処理を実行します(ここでは before ポイントカットですが、after や around に変更することもできます)。

効果

ここでは、匿名内部クラスを使用してインターフェースを実装し、ラムダ式に置き換えます。

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 など)を使用するかを区別することができます。

メソッドの種類をマークするために、アノテーション@interfaceが最適です。

  • アスペクト

メソッドをインターセプトした後、戻り値の前後でいくつかの操作を行います。interceptメソッドに必要な操作を記述します。

つまり、Callback の要件があるたびに、インターフェースを手動で実装し、ビジネスコードを追加する必要があります。そのため、ビジネスコードを抽出する必要があります。

ビジネスコードを切り出すために、ビジネスを抽象化する必要があります。例えば、ログを出力する場合、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です。

  • アノテーション解析

主要なアノテーションは 2 つあります:AspectPointCutAspectは TargetClass にどのアスペクトを適用するかを示すために使用され、PointCutはメソッドのポイントカットタイプ(before、after など)を示すために使用されます。

デモ#

gitee デモ

読み込み中...
文章は、創作者によって署名され、ブロックチェーンに安全に保存されています。