22

when trying to implement an Aspect, that is responsible for catching and logging a certain type of error, I initially thought this would be possible using the AfterThrowing advice. However it seems that his advice doesn't catch the exception, but just provides an additional entry point to do something with the exception.

The only advice which would also catch the exception in question would then be an AroundAdvice - either that or I did something wrong.

Can anyone assert that indeed if I want to catch the exception I have to use an AroundAdvice? The configuration I used follows:

@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());
  }
}

Note that in this example I caught all Exceptions, because it just is an example. I know its bad practice to just swallow all exceptions, but for my current use case I want one special type of exception to be just logged while avoiding duplicate logging logic.

whiskerz
  • 299
  • 1
  • 3
  • 7
  • You really wouldn't use both around advice and after throwing advice on the same joinpoint like this. Around advice is holistic and allows you to completely control execution. Since it isn't possible for an exception to be thrown out of your around advice (because of the try/catch block you have which does not re-throw the exception), the after throwing advice will never be applied. – Jonathan W Jul 16 '14 at 03:26

2 Answers2

25

The Spring reference doc says:

"After throwing advice runs when a matched method execution exits by throwing an exception"

By then it's too late to catch the exception as it has already been thrown and the method has exited. The approach you've taken with the @Around advice is the only way to actually catch the exception and deal with it before the method exits.

Shane Bell
  • 1,048
  • 8
  • 11
  • I don't actually think this is true. I believe that the handler join point is the way to catch the exception and deal with it before the method exits. Around advice will allow you to catch the exception after the method has thrown the exception and returned. Please see http://www.eclipse.org/aspectj/doc/next/progguide/language-joinPoints.html – Tim Hennekey May 18 '10 at 18:39
  • 1
    I know this is 4 years later, but this question just came up on a Google search. @TimHennekey, Shane is correct. After throwing advice cannot prevent the exception from being thrown (at least not with Spring AOP). I know because I just tried it (with spring-aop 3.2.9). If you want to catch the exception, that is when you would use around advice, because that would give you complete control of the execution. – Jonathan W Jul 16 '14 at 03:21
  • Actually, it is not only `@Around` that can catch thrown exception, `@AfterThrowing` can catch it as well. Please see example in another answer to this question. – fg78nc Feb 02 '20 at 17:44
3

Actually, it is possible to catch exception within AfterThrowing advice as well. I know it is a convoluted example, but it works.

@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");
    }
}

As you can see it is more about how to catch originally thrown exception and thus prevent its propagation back to the caller.

Working example on GitHub (check com.example.advices package)

fg78nc
  • 4,774
  • 3
  • 19
  • 32
  • it does not work in my example, the original exception keeps being propagated – Enrico Giurin Oct 25 '21 at 17:12
  • @EnricoGiurin Did you try to run GitHub example? – fg78nc Oct 26 '21 at 15:25
  • I've just tried to run mvn clean install and I got this error: [ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:2.2.4.RELEASE:repackage (repackage) on project qualifier-demo: Execution repackage of goal org.springframework.boot:spring-boot-maven-plugin:2.2.4.RELEASE:repackage failed: Unable to find a single main class from the following candidates [com.example.advices.AdvicesDemo, com.example.qualifierdemo.QualifierDemoApplication] – Enrico Giurin Oct 27 '21 at 15:22
  • Main class is there https://github.com/fg78nc/qualifier-test/blob/master/src/main/java/com/example/advices/AdvicesDemo.java#L48 Just clone and execute it. – fg78nc Oct 27 '21 at 16:22
  • ok your example works. But I have an issue on my code, which is a listener from MQ, it seems that in spite I handle the exception, in the way you propose, somehow the MQ is dealing how the transaction was rollbacked then keeps sending the same message – Enrico Giurin Oct 28 '21 at 11:33