0

へい。

永続化レイヤーのロギング用に @Aspect を作成しています。

最初に、経験豊富な開発者にエラーを表示する可能性のあるコード ;)

/** Interface of the class to observe. */
public interface PersistenceService {

    public Serializable save(Serializable serializable);

    public List<Serializable> save(List<Serializable> list)
}

/** Actual class to observe. */
@Service
public class PersistenceService {

    @Autowired
    private SomeJpaRepository rep;

    public Serializable save(Serializable serializable) {
        return rep.save(serializable);
    }

    public List<Serializable> save(List<Serializable> list) {
        return rep.save(list);
    }
}

そしてここにアスペクト:

/** The Aspect. */
@Aspect
@Component
public class PersistenceService {

    /** A org.slf4j.Logger (using logback). */
    private final Logger logger = LoggerFactory.getLogger(getClass());

    /** Pointcut to define the classes to observe. */
    @Pointcut("within(de.mypckg.myproject.persistence.*.*)")
    public void inPersistanceLayer() {}

    /** Pointcut for the first save-method. */
    @Pointcut("execution(public * save(..)) && args(serializable)")
    public void saveOperation(Serializable serializable) {}

    /** Pointcut for the first save-method. */
    @Pointcut("execution(public * save(..)) && args(list)")
    public void saveOperation(List<Serializable> list) {}

    /** Method for the first save-method. */
    @Around("inPersistanceLayer() && saveOperation(serializable)")
    public List<Serializable> logSave(ProceedingJoinPoint joinPoint, Serializable serializable) throws Throwable {

        // log some stuff
        Object saved = joinPoint.proceed();
        // log somemore stuff
    }

    /** Method for the second save-method. */
    @Around("inPersistanceLayer() && saveOperation(list)")
    public List<Serializable> logSave(ProceedingJoinPoint joinPoint, List<Serializable> list) throws Throwable {

        // log some stuff
        Object saved = joinPoint.proceed();
        // log somemore stuff
    }
}

ポイントカット (およびそのメソッド) が 1 つしかない場合は機能しますが、2 つ目を追加すると、次の例外が発生します。

java.lang.IllegalArgumentException: warning no match for this type name: list [Xlint:invalidAbsoluteTypeName]

ポイントカットの順序を変更しました。常に 2 番目です。これを解決する方法についてのアイデアはありますか?

更新
質問を投稿したら、アイデアがありました。ポイントカットを次のように変更しました。

/** The Aspect. */
@Aspect
@Component
public class PersistenceService {

    /** A org.slf4j.Logger (using logback). */
    private final Logger logger = LoggerFactory.getLogger(getClass());

    /** Pointcut to define the classes to observe. */
    @Pointcut("within(de.mypckg.myproject.persistence.*.*)")
    public void inPersistanceLayer() {}

    /** Pointcut for the save-method. */
    @Pointcut("execution(public * save(..))")
    public void saveOperation() {}

    /** Pointcut for the serializable argument. */
    @Pointcut("args(serializable)")
    public void serializableArgument(Serializable serializable) {}

    /** Pointcut for the list argument. */
    @Pointcut("args(list)")
    public void listArgument(List<Serializable> list) {}

    /** Method for the first save-method. */
    @Around("inPersistanceLayer() && saveOperation() && serializableArgument(serializable)")
    public Object logSave(ProceedingJoinPoint joinPoint, Serializable serializable) throws Throwable {

        // log some stuff
        Object saved = joinPoint.proceed();
        // log somemore stuff
        return saved;
    }

    /** Method for the second save-method. */
    @Around("inPersistanceLayer() && saveOperation(list) && listArgument(list)")
    public Object logSave(ProceedingJoinPoint joinPoint, List<Serializable> list) throws Throwable {

        // log some stuff
        Object saved = joinPoint.proceed();
        // log somemore stuff
        return saved;
    }
}

例外はなくなりましたが、まだ小さな問題があります (解決するのははるかに簡単だと思います): ArrayList は Serializable を実装しているため、少なくとも ArrayList を使用する私のテスト ケースでは、両方のポイントカットが実行されます。
私はそれを調べて、見つけたものを投稿しますが、助けも大歓迎です;)

更新 2

kriegaex が指摘したコピペエラーを修正。ありがとう!

メソッド logSave(..) の戻り値の型は Object です。

アップデート 3

コードを 1 つのポイントカットと 1 つのメソッドだけを使用するように変更し、kriegaex が提案したように instanceof で確認しました。

/** The Aspect. */
@Aspect
@Component
public class PersistenceService {

    /** A org.slf4j.Logger (using logback). */
    private final Logger logger = LoggerFactory.getLogger(getClass());

    /** Pointcut to define the classes to observe. */
    @Pointcut("within(de.mypckg.myproject.persistence.*.*)")
    public void inPersistanceLayer() {}

    /** Pointcut for the save-method. */
    @Pointcut("execution(public * save(*)) && args(serializable)")
    public void saveOperation(Serializable serializable) {}

    /** Method for the first save-method. */
    @Around("inPersistanceLayer() && saveOperation() && serializableArgument(serializable)")
    public Serializable logSave(ProceedingJoinPoint joinPoint, Serializable serializable) throws Throwable {

        // log some stuff
        Serializable saved = (Serializable) joinPoint.proceed();

        if (saved instanceof List<?>) {
            List<?> savedList = (List<?>) saved;
            // log somemore stuff with a List
        } else {
            // log somemore stuff
        }
        return saved;
    }
}

なぜ他の方法でうまくいかなかったのか、今でも疑問に思っています。

4

2 に答える 2

1

次の 2 つのオプションがあります。

アプリケーションクラス

package de.scrum_master.aspectj.sample;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;

public class TestApp {
    public static void main(String[] args) {
        save(new HashSet<String>());
        List<Serializable> arg = new ArrayList<Serializable>();
        save(arg);
    }
    static Serializable save(Serializable arg) { return arg; }
    static List<Serializable> save(List<Serializable> arg) { return arg; }
}

側面

package de.scrum_master.aspectj.sample;

import java.io.Serializable;
import java.util.List;

public aspect TestAspect {
    pointcut saveOperation(Object arg) : execution(* save(*)) && args(arg);
    pointcut serializableArgument(Serializable serializable) : execution(* save(Serializable)) && args(serializable);
    pointcut listArgument(List<Serializable> list) : execution(* save(List<Serializable>)) && args(list);

    Object around(Object arg) : saveOperation(arg) {
        if (arg instanceof List)
            System.out.println("Global advice   [List]:         " + thisJoinPointStaticPart.getSignature());
        else
            System.out.println("Global advice   [Serializable]: " + thisJoinPointStaticPart.getSignature());
        return proceed(arg);
    }

    List<Serializable> around(List<Serializable> list) : listArgument(list) {
        System.out.println("Specific advice [List]:         " + thisJoinPointStaticPart.getSignature());
        return proceed(list);
    }

    Serializable around(Serializable serializable) : serializableArgument(serializable) {
        System.out.println("Specific advice [Serializable]: " + thisJoinPointStaticPart.getSignature());
        return proceed(serializable);
    }
}

ご覧のとおり、戻り値の型Objectと単純なポイントカットを使用する最初のアドバイスsaveOperation(Object arg)は、一般的なワンストップ ショッピング ソリューションです。他の 2 つのアドバイスは引数の型に固有のもので、それぞれ別のポイントカットを使用します。アプリケーション クラスを織り込んで実行すると、次の出力が得られます。

Global advice   [Serializable]: Serializable de.scrum_master.aspectj.sample.TestApp.save(Serializable)
Specific advice [Serializable]: Serializable de.scrum_master.aspectj.sample.TestApp.save(Serializable)
Global advice   [List]:         List de.scrum_master.aspectj.sample.TestApp.save(List)
Specific advice [List]:         List de.scrum_master.aspectj.sample.TestApp.save(List)

ポイントカットを使用したアドバイスserializableArgument(Serializable serializable)は、必要に応じて 1 回だけ実行されます。

于 2012-08-11T11:29:40.843 に答える
0

ポイントカットを次のように書く場合には違いがあります

args(java.io.Serializable)

また

execution(* *(java.io.Serializable))

前者は引数が実行時Serializableに一致する場合に一致し、後者は type の単一パラメータを宣言するメソッド シグネチャにのみ一致します。あなたの例では、任意の数のメソッドパラメーターが一致することを意味するパラメーターとして使用しています。Serializable..execution

Spring リファレンス ドキュメントのポイントカットの例を見てください。args特に、ディスカッションは興味深いものになると思います。


編集:

bindingexecutionにバリアントを使用することはできませんが、試したように2つを簡単に組み合わせることができます。&&

@Pointcut("execution(public * save(java.io.Serializable)) && args(serializable)")
public void saveOperation(Serializable serializable) {}

@Pointcut("execution(public * save(java.util.List)) && args(list)")
public void saveOperation(List<Serializable> list) {}
于 2012-08-11T12:36:40.723 に答える