Как заставить Spring @ControllerAdvice работать с другими настраиваемыми Spring @Aspect? - PullRequest
1 голос
/ 11 января 2020

У меня есть специальный регистратор, использующий @Aspect, который я sh всегда запускает в последний раз, так что независимо от того, какой ответ возвращается контроллером, он будет записан в базу данных (поэтому я поставил @Order(1) на этот аспект). И я также написал обработчик ошибок, используя @ControllerAdvice, который обрабатывает все непредвиденные исключения и возвращает 500 с настраиваемым телом ответа, и я хочу, чтобы это также регистрировалось регистратором, поэтому я поместил в него @Order(2), однако, похоже, что размещение аннотации @Order не организует порядок между Spring Aspect и Spring ControllerAdvice, так как я могу позволить своему обработчику ошибок всегда работать перед регистратором? (конечно, не обращая мой обработчик ошибок к другому Spring Aspect)

1 Ответ

1 голос
/ 11 января 2020

Я искал и устранял твою главную проблему. Я не нашел ответа на ваш вопрос о @Order, но я поделюсь с вами своими мыслями (вторая часть этого поста)

Рассматривали ли вы вопрос об использовании Interceptor?

Они обеспечивают способ регистрации действий перед входом в контроллер и после выполнения @ControllerAdvice, если таковой имеется.

@Component
public class WebInterceptor extends HandlerInterceptorAdapter {

    private Logger logger = LoggerFactory.getLogger(WebInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.error("WebInterceptor prehandle is now logged");
        return super.preHandle(request, response, handler);
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        logger.error("WebInterceptor after completion is now logged");
        super.afterCompletion(request, response, handler, ex);
    }
}

Чтобы активировать этот перехватчик, вам необходимо создать новый @Configuration класс, реализующий WebMvcConfigurer:

@Configuration
public class AppConfig implements WebMvcConfigurer {

    @Autowired
    private WebInterceptor webInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(webInterceptor).addPathPatterns("/user");
    }
}

Я создал простые Controller и ControllerAdvice для теста:

@RestController
public class WebController {
    @GetMapping("/user")
    public String user() {
        throw new IllegalArgumentException("my bad");
    }
}


@RestControllerAdvice
@Order(2)
public class WebAdvice {

    private Logger logger = LoggerFactory.getLogger(WebAdvice.class);

    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public void handle(RuntimeException e) {
        logger.error("ControllerAdvice is now logged");
    }
}

И результат:

Дескриптор WebAdvice теперь зарегистрирован WebInterceptor после завершения теперь регистрируется

Вы также можете посмотреть это, если вам нужно регистрировать запросы: https://www.baeldung.com/spring-http-logging

Вот что я Вы узнали о Order, Aspect и ControllerAdvice

Вы правы, между @ControllerAdvice и @Aspect нет прецедента, ну вроде ...

Я не смог выяснить, почему (пока нет), но когда @Aspect указывает на метод @Controller, то смещение точки будет выполнено до @ControllerAdvice, независимо от установленного вами порядка

@Aspect
@Component
@Order(1)
public class MyAspect {

    private Logger logger = LoggerFactory.getLogger(MyAspect.class);

    @After("execution(* WebController.user(..))")
    public void afterLog() {
        logger.error("Aspect is now logged");
    }
}

@RestControllerAdvice
@Order(2)
public class WebAdvice {

    private Logger logger = LoggerFactory.getLogger(WebAdvice.class);

    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public void handle(RuntimeException e) {
        logger.error("ControllerAdvice is now logged");
    }
}

Если вы Get http://localhost:8080/user результат будет:

Аспект теперь зарегистрирован ControllerAdvice теперь регистрируется

Но я нашел кое-что интересное: если вы установите pointcut @After @ControllerAdvice, то у вас будет аспект, выполняемый после controllerAdvice:

@Aspect
@Component
@Order(1)
public class MyAspect {

    private Logger logger = LoggerFactory.getLogger(MyAspect.class);

    @After("execution(* WebAdvice.handle(..)) || execution(* WebController.user(..))")
    public void afterLog() {
        logger.error("Aspect is now logged");
    }
}

Выполняется 2 раза. Один за контроллером, а другой после совета.

Аспект теперь зарегистрирован ControllerAdvice теперь зарегистрирован Аспект теперь зарегистрирован

Возможно, это может соответствовать вашим потребностям, но я не считаю это элегантным решением ...

То, что еще нужно решить:

  • Найти способ иметь приоритет над Aspect и ControllerAdvice

Надеюсь, это все равно поможет. Если я найду что-нибудь получше, я дам вам знать.

...