web-dev-qa-db-ja.com

Spring AOP AfterThrowing vs. Around Advice

特定のタイプのエラーをキャッチしてログに記録するアスペクトを実装しようとしたとき、私は当初、AfterThrowingのアドバイスを使用してこれが可能であると考えました。ただし、彼のアドバイスは例外をキャッチしていないようですが、例外を処理するための追加のエントリポイントを提供しているだけです。

問題の例外をキャッチする唯一のアドバイスは、AroundAdviceです-それか私が何か間違ったことをしました。

例外をキャッチしたい場合は、AroundAdviceを使用する必要があると誰かが主張できますか?私が使用した構成は次のとおりです。

@Pointcut("execution(* test.simple.OtherService.print*(..))")
public void printOperation() {}

@AfterThrowing(pointcut="printOperation()", throwing="exception")
public void logException(Throwable exception) {
  System.out.println(exception.getMessage());
}

@Around("printOperation()")
public void swallowException(ProceedingJoinPoint pjp) throws Throwable {
  try {
    pjp.proceed();
  } catch (Throwable exception) {
    System.out.println(exception.getMessage());
  }
}

この例では、単なる例であるため、すべての例外をキャッチしたことに注意してください。すべての例外を飲み込むのは悪い習慣ですが、現在のユースケースでは、重複するログロジックを回避しながら、1つの特別なタイプの例外をログに記録する必要があります。

20
whiskerz

春のリファレンス ドキュメントによると:

「アドバイスをスローした後、一致したメソッドの実行が例外をスローして終了すると実行されます」

それまでに、例外はすでにスローされており、メソッドが終了しているため、例外をキャッチするには遅すぎます。 @Aroundアドバイスで採用したアプローチは、メソッドが終了する前に実際に例外をキャッチして処理する唯一の方法です。

19
Shane Bell

実際には、AfterThrowingアドバイス内でも例外をキャッチすることが可能です。複雑な例であることは知っていますが、機能します。

@Aspect
@Component
class MyAspect {

    @Autowired
    public Worker worker;

    @Pointcut(value = "execution(public * com.ex*..*.*(..))")
    public void matchingAll(){}

    @AfterThrowing(pointcut = "matchingAll()", throwing = "e")
    public void myAdvice(RuntimeException e){
        Thread.setDefaultUncaughtExceptionHandler((t, e1) -> 
                System.out.println("Caught " + e1.getMessage()));
        System.out.println("Worker returned " + worker.print());
    }
}

@Component
class Worker {

    public static int value = 0;

    public int print() {
        if (value++ == 0) {
            System.out.println("Throwing exception");
            throw new RuntimeException("Hello world");
        } else {
            return value;
        }
    }
}

@SpringBootApplication
@EnableAspectJAutoProxy
public class AdvicesDemo {

    public static void main(String[] args) {
        final ConfigurableApplicationContext applicationContext = SpringApplication.run(AdvicesDemo.class);
        final Worker worker = applicationContext.getBean(Worker.class);
        System.out.println("Worker returned " + worker.print());
        System.out.println("All done");
    }
}

ご覧のとおり、最初にスローされた例外をキャッチして、呼び出し元への伝播を防ぐ方法が重要です。

GitHub の作業例(com.example.advicesパッケージを確認してください)

0
fg78nc