2017-03-11 28 views
0

私はSpring BootとSpring Initializrを使って簡単なWebアプリケーションを作成し、@Aspect@Aroundというアドバイスを書こうとしました。Spring AOP:aspect @Aroundが動作しない

私はコントローラに私のカスタム注釈@RetryOnFailureを追加するエンドポイント法 - それは動作しますが、私はコントローラにこの注釈を追加するコントローラのエンドポイントで実行されることを、この方法 - それは動作しません。私はそのような行動の理由を理解するのに多くの時間を費やしますが、何の結果もありません。だから助けてください。 https://github.com/zalizko/spring-aop-playground

@Aspect 
@Component 
public final class MethodRepeater { 

    @Around("execution(* *(..)) && @annotation(RetryOnFailure)") 
    public Object wrap(final ProceedingJoinPoint joinPoint) throws Throwable { 
     // code is here 
    } 
} 

だから、私の目標は、ということです:

プロジェクトはここにある

@RequestMapping 
public String index() { 
    inTry(); 
    return "OK"; 
} 


@RetryOnFailure(attempts = 3, delay = 2, unit = TimeUnit.SECONDS) 
public void inTry() { 
    throw new RuntimeException("Exception in try " + ++counter); 
} 
+0

'inTry()'は常に例外をスローします。それは意味をなさないでしょうか? – kriegaex

+0

これは単なる例です。私は必要な場所で実際のプロジェクトを持っています。外部リソースが利用できない場合、「再試行」機能を実装する必要があります。 –

答えて

3

あなたは典型的なSpring AOPの初心者の間違いを犯しました。プロキシメソッドが外部から呼び出された場合にのみ、プロキシベースのAOPが機能することを忘れました。this(プロキシを避ける)ではなく、しかし、内部コールinTry()this.inTry()と同じです。このように、側面はinTryのトリガーはありません、あなたは、このようにあなたのコードを再配置する必要があります。私も少しように避け反射

  • に様相を変えての注釈をバインド
    package spring.aop; 
    
    import org.springframework.web.bind.annotation.RequestMapping; 
    import org.springframework.web.bind.annotation.RestController; 
    
    import java.util.concurrent.TimeUnit; 
    
    @RestController("/") 
    public class HomeController { 
    
        static int counter = 0; 
    
        @RequestMapping 
        @RetryOnFailure(attempts = 3, delay = 2, unit = TimeUnit.SECONDS) 
        public String index() { 
         throw new RuntimeException("Exception in try " + ++counter); 
        } 
    } 
    

    アドバイスパラメータを直接@annotation()経由で、
  • アドバイスがトリガされたときにジョインポイントをログに記録し、
  • は、試し#3で "OK"を返します。
package spring.aop; 

import org.aspectj.lang.ProceedingJoinPoint; 
import org.aspectj.lang.annotation.Around; 
import org.aspectj.lang.annotation.Aspect; 
import org.springframework.stereotype.Component; 

@Aspect 
@Component 
public final class MethodRepeater { 

    @Around("execution(* spring.aop..*(..)) && @annotation(retryOnFailure)") 
    public Object wrap(final ProceedingJoinPoint joinPoint, RetryOnFailure retryOnFailure) throws Throwable { 
     System.out.println(joinPoint); 
     return proceed(joinPoint, retryOnFailure); 
    } 

    private Object proceed(ProceedingJoinPoint joinPoint, RetryOnFailure retryOnFailure) throws Throwable { 
     int attempt = 1; 
     while (true) { 
      try { 
       return joinPoint.proceed(); 
      } catch (final Throwable ex) { 
       System.out.println("Try #" + attempt + " failed: " + ex); 
       if (++attempt >= retryOnFailure.attempts()) 
        return "OK"; 
       if (retryOnFailure.delay() > 0L) 
        retryOnFailure.unit().sleep(retryOnFailure.delay()); 
      } 
     } 
    } 
} 

今では動作し、コンソールログは言う:

execution(String spring.aop.HomeController.index()) 
Try #1 failed: java.lang.RuntimeException: Exception in try 1 
Try #2 failed: java.lang.RuntimeException: Exception in try 2 
+0

ありがとうございました。はい、あなたは正しいです。質問のポイントは「なぜですか?」と、あなたはそれをうまく説明しました。 –

0

私は同様の問題を持っていたし、私が使用してそれを解決するためにAspectJの管理:

https://github.com/mdanetzky/tour-of-heroes-java

また、私には時間がかかりました帽子私のIDEAは、適切な側面を再構築していないので、いくつかのより徹底的な対策を試す前に、プロジェクトをきれいにする/再構築する価値があるかもしれません。

+0

なので、欠けているaspectj-maven-pluginの問題ですか? –

+0

aspectj-maven-pluginを追加しても動作します。あなたの答えに感謝しますが、コンパイル時には、私は純粋なspring-aopを実装しようとしています - ランタイム製織。 https://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-introduction-defn –

+0

いいえ、それは解決策ではありません。 AspectJ Maven Pluginは、AspectJコンパイル時またはそれ以降の読み込み時に使用するためのものです。プロキシベースのSpring AOPでは絶対に必要ではありません。あなたのケースでは、Spring AOPからAspectJに暗黙的に切り替えただけで、本当の問題を回避するだけで解決できました。 – kriegaex

関連する問題