Issue
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.
Solution
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");
}
}
Answered By - akop
Answer Checked By - Gilberto Lyons (JavaFixing Admin)