3

私は働いています:

  • Spring Framework4.3.3
  • AspectJ1.8.9

次の通常のプロセスがあります。

  • @Controller-> @Service->@Repository

について次のペアがありますAOP

  • PersonaServicePointcut
    • PersonaServiceAspect

シナリオは次のとおりです。

このクラスには、、 、@Serviceなどのメソッドが あります。それらは同じクラス内で一緒に宣言されます。deletesaveupdatefindOneById

deleteand updatethroughなどのメソッドについては、 orアドバイスをAOP使用してメソッドを呼び出しています。@Before@AroundfindOneById

その理由は、エンティティが存在しない場合にorメソッドを実行しても意味がないからです (Rest シナリオを検討してください)。したがって、例外をスローする必要があるため、 Exception Aとしましょう。deleteupdateadvice@ControllerAdvice

この方法については、実質的に同じアプローチsaveが適用されています。したがって、メソッドを実行する前に、メソッドを再度呼び出してsaveのメソッドまたはアドバイスを実行します。エンティティが既に存在する場合は、例外をスローする必要があります。たとえば、 Exception Bをスローする必要があります。 @Before@AroundfindOneById@ControllerAdvice

この方法を使用する 3 つのポイント/3 つのアドバイスがあることに注意してfindOneByIdください。エンティティが存在するかどうかを確認します。

例えば:

    @Pointcut(value=
    "execution(* mypackage.PersonaServiceImpl.saveOne(otherpackage.Persona)) 
    && args(persona)")
    public void saveOnePointcut(Persona persona){}

    @Pointcut(value=
    "execution(*  
    mypackage.PersonaServiceImpl.updateOne(otherpackage.Persona)) 
    && args(persona)")
    public void updateOnePointcut(Persona persona){}

    @Pointcut(value="execution(*  
    mypackage.PersonaServiceImpl.deleteOne(String)) && args(id)")
    public void deleteOnePointcut(String id){}

繰り返しますが、これら 3 つのアドバイスはメソッドを使用または実行findOneByIdます。

問題は、次のような新しいポイントカットを追加するときです。

@Pointcut(value="execution(*    
mypackage.PersonaServiceImpl.findOneById(String)) 
&& args(id)")
public void findOneByIdPointcut(String id){}

このポイントカットを作成して、エンティティが既に存在するかどうかを確認しました。存在しない場合は、タイプの例外をスローする必要がありますC(従来の 404 の場合)。

メソッド自体のまたはアドバイスを介してメソッドを実行するのは冗長なようです。しかし、これは目的とタイプ C の例外を作成するためにも必要です。一部の人が処理する必要がありますfindOneById@Before@AroundfindOneByIdloggingaudit@ControllerAdvice

問題は、メソッドの他のアドバイスが実行されるときです (メソッドも呼び出して実行することdelete/update/saveを思い出してください) my is execute unnecessarily .findOneByIdfindOneByIdPointcut

次のようなことを示すようにポイントカット宣言を変更する必要があります。

@Pointcut(Alpha)
public void findOneByIdPointcut(String id){}

どこAlphaにある:

@ServicefindOneByIdメソッドの before/around アドバイスを実行します 、その呼び出しがクラスの他のアドバイスから実行された場合は決して実行しません。
PersonaServiceAspect

!executionとの!within組み合わせで多くの方法を試しましたが、結果はありません。

のすべてのメソッドをそれぞれ固有のアドバイスでインターセプトするPointcut を1 つだけ作成した場合でも、パラメーターを使用して、どのメソッドが呼び出されたかを確認し、それぞれの制御を行うことができます。しかし、再びこの動作が発生します。 @Service@AroundProceedingJoinPoint proceedingJoinPoint

これは、次のことを意味します。

@Around("PersonaServicePointcut.anyMethodPointcut()")
    public Object aroundAdviceAnyMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{

どこanyMethodPointcutですかexecution(* mypackage.PersonaServiceImpl.*(..))

このアプローチを達成することは可能ですか?どのように?

ありがとう。

4

2 に答える 2

2

ポイントカット式の制御フロー内でポイントカットを除外するには、

!cflow(<pointcut>)

findOneByIdあなたの例では、実行が独自のアドバイスの制御フロー内にある実行を除外したいと考えています。あなたのアドバイスがポイントカット式に適用される場合

@Pointcut("saveOnePointcut() || updateOnePointcut() || deleteOnePointcut()")
public void combinedPointcut() {}

あなたはそれを除外することができます:

!cflow(combinedPointcut())

または、すべてのアドバイス実行の制御フロー内からすべての制御フローを単純に除外するには、次を使用できます。

!cflow(adviceexecution())

結合されたポイントカット式に基づいて、あなたの around find アドバイスは次のようになります。

@Around("findOneByIdPointcut() && !cflow(combinedPointcut())")
public void aroundFind() {
    ....
}

ポイントカット引数をバインドしているため、例のポイントカット式を結合できないことに注意してください。パーツを削除&& args(...)して、アドバイス ポイントカット式に直接移動する必要があります。args(paramName)(or) 演算子のようにバインディング ポイントカット式を組み合わせることはできない||ため、これらの場合には個別のアドバイスを作成する必要があります。必要に応じて、これらのアドバイスのほとんどの作業を 1 つのメソッドに委任することもできます。この種の委任の例については、こちらを参照してください

于 2016-10-03T12:13:45.633 に答える
1

私が正しく理解している場合は、findOneByIdPointcutアドバイス内で呼び出されるのではなく、他のすべての場合にトリガーされる必要があります。

これを行う 1 つの方法は、ポイントカットcallと組み合わせて使用​​することです。ジョインポイントを呼び出し元メソッドまで移動し、ターゲットジョインポイントからアドバイスを除外します。withincallwithin

したがって、アスペクトコードは次のようになります。

public class ControllerAspect {

    ...

    @Pointcut(value = "execution(* PersonaServiceImpl.deleteOne(String)) && args(id)")
    public void deleteOnePointcut(String id)
    {
    }

    @Pointcut(value = "call(* PersonaServiceImpl.findOneById(String)) && args(id)")
    public void findOneByIdPointcut(String id)
    {
    }

    @Before(value = "findOneByIdPointcut(id) && !within(ControllerAspect)")
    public void beforeFindOneByIdAdvice(String id)
    {
       //do some logic here
    }


    @Before(value = "deleteOnePointcut(id)")
    public void beforeDeleteOneAdvice(String id)
    {
        Persona persona = personaService.findOneById(id);
        //check that persona is not null
    }
}

別の方法はcflow、@Nándor Előd Fekete の回答で説明されているようにポイントカットを使用することですが、この場合beforeFindOneByIdAdvice、スタック内のアドバイス実行の下にあるすべての結合ポイントも除外されることに注意してください。したがって、これは将来、予期しない結果をもたらす可能性があります。

cflowアスペクト指向プログラミング - 「cflow」とは?」の回答でどのように機能するかがわかります。

于 2016-10-04T02:58:54.830 に答える