Skip to content
Advertisement

Order between @ControllerAdvice and custom Aspect

tl;dr

How can I define the order of custom aspects and the @ControllerAdvice?

Detailed description

I want to decorate various methods (like normal REST-Calls or a @JmsListener) with MDC information on controller-level.

The idea is: I, as a developer, don’t have to think about the MDC data in my controller-methods.
I want extra MDC-information (e.g. the userId) and I can be sure, that information will be correctly removed.

my aspect

@Aspect
@Component
@Order(LOWEST_PRECEDENCE)
public class MdcSugar {

  @Pointcut("within(@MdcAwareController *)") // <- MdcAwareController is my annotation
  public void beanAnnotatedWithMdcAwareController() {
    // this pointcut identifies beans annotated with @MdcAwareController
  }

  @Pointcut("execution(public * *(..))")
  public void publicMethod() {
    // this pointcut identifies public methods
  }

  @Pointcut("publicMethod() && beanAnnotatedWithMdcAwareController()")
  public void publicMethodInsideMdcAwareController() {
    // this pointcut identifies public methods on beans annotated with @MdcAwareController
  }

  @Before("publicMethodInsideMdcAwareController()")
  public void addMdcSugar(JoinPoint joinPoint) {
    log.info("Entered");
    MDC.put("test", "test");
  }

  @After("publicMethodInsideMdcAwareController()")
  public void removeMdcSugar() {
    log.info("Leaved");
    MDC.clear();
  }
}

example controller

@RestController
@MdcAwareController // <- my annotation
public class SomeController {

   // doesn't matter if the method is a JmsListener or something else
   @GetMapping("/{id}")
   public String get(@PathVariable String id) {
      MDC.put("id", id);
      log.info("Got request");
      throw new RuntimeException("too bad ...");
   }
}

ControllerAdvice

@ControllerAdvice
@Order(HIGHEST_PRECEDENCE)
public class GeneralExceptionHandler extends ResponseEntityExceptionHandler {

  // some others cases with different handlers

  @ExceptionHandler(RuntimeException.class)
  protected ResponseEntity<HttpErrorResponseBody> handleUncaught(RuntimeException e) {
    log.error("Uncaught exception occurred", e);
    return new ResponseEntity<>(e.toString(), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
  }
}

Now I have a problem when a exception occurs. My aspect will always clear the MDC-context before the exception-handler jumps in. Log example:

Entered
[test=test][id=someString] Got request
[test=test][id=someString] Leaved
Uncaught exception occurred

But I want something like this:

Entered
[test=test][id=someString] Got request
[test=test][id=someString] Uncaught exception occurred
[test=test][id=someString] Leaved

I already tried to place @Order() on MdcSugar and GeneralExceptionHandler, but with no affect.

Advertisement

Answer

Sometimes the world is so easy – I only need to clear the MDC-data at the beginning.

@Aspect
@Component
public class MdcSugar {

  @Pointcut("within(@MdcAwareController *)") // <- MdcAwareController is my annotation
  public void beanAnnotatedWithMdcAwareController() {
    // this pointcut identifies beans annotated with @MdcAwareController
  }

  @Pointcut("execution(public * *(..))")
  public void publicMethod() {
    // this pointcut identifies public methods
  }

  @Pointcut("publicMethod() && beanAnnotatedWithMdcAwareController()")
  public void publicMethodInsideMdcAwareController() {
    // this pointcut identifies public methods on beans annotated with @MdcAwareController
  }

  @Before("publicMethodInsideMdcAwareController()")
  public void addMdcSugar(@Nullable JoinPoint joinPoint) {
    MDC.clear();
    log.info("Entered");
    MDC.put("test", "test");
  }
}
User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement