Spring MVC Test & AOP: Around advice не выполняется так, как ожидается для контроллера покоя (только для недопустимых данных)

Я работаю с

  • Spring Framework 4.3.3
  • AspectJ 1.8.9

У меня есть два @Controllers, один для mvc, а другой для rest. Каждый использует как зависимость @Service.

У меня есть следующий метод отдыха

package com.manuel.jordan.controller.persona;

@Controller
@RequestMapping(value="/personas")
public class PersonaRestController {

    @PutMapping(value="/{id}", consumes={MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_UTF8_VALUE})
    public ResponseEntity<Void> updateOne(@PathVariable String id, @Validated @RequestBody Persona persona){
       persona = personaService.updateOne(persona);
       return ResponseEntity.noContent().build();
    }

Обратите внимание на второй параметр, который я использую @Validated

У меня есть следующий Pointcut:

@Pointcut(value=
"execution(* com.manuel.jordan.controller.persona.PersonaRestController.updateOne(..))")
public void updateOnePointcut(){}

И следующий совет Around:

@Around(value="PersonaRestControllerPointcut.updateOnePointcut()")
@Transactional(noRollbackFor={Some exceptions})
public Object aroundAdviceUpdateOne(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

   logger.info("Beginning aroundAdviceUpdateOne - Class: {}, Method: {}",   
   proceedingJoinPoint.getTarget().getClass().getSimpleName(),
   proceedingJoinPoint.getSignature().getName());

   ....

}

Через Spring MVC Test

Когда я отправляю действительные данные:

Такие как:

resultActions = mockMvc.perform(put(uri).contentType(MediaType.APPLICATION_JSON_UTF8)
                        .accept(MediaType.APPLICATION_JSON_UTF8)
                        .header("Accept-Language", locale.toString())
.content(JsonTransformerSupport.objectToJson(personaValid))).andDo(print());

or by

RequestEntity<Persona> requestEntity = RequestEntity.put(uri).contentType(MediaType.APPLICATION_JSON_UTF8)                                                                             
 .accept(MediaType.APPLICATION_JSON_UTF8)
 .header("Accept-Language", locale.toString())                                                                          
 .body(personaValid);

Посредством Gradle отчетов я могу подтвердить, что совет @Around работает так, как ожидалось. Он просто наблюдает Beginning aroundAdviceUpdateOne - Class .... текст в отчете, а остальная часть совета работает так, как ожидается.

Проблема возникает, когда я отправляю недействительные данные для проверки через @Validated

Используя два способа, показанных выше, но просто изменив personaValid на personaInvalid (пустые поля, нарушение минимальных и максимальных границ и т. Д.), Я понял, что совет @Around всегда игнорируется. Это подтверждает, что Beginning aroundAdviceUpdateOne - Class .... никогда не появляется.

Примечание: даже если совет @Around не работает, выполняется процесс проверки. То есть нет никаких исключений. Метод @Test проходит.

Вот некоторые соображения.

  • У меня такое же поведение с советом @Before.
  • Я предполагаю, что до выполнения метода PersonaRestController.updateOne @Around (даже @Before) совет должен сделать свою работу. Неважно, верны ли данные или нет.
  • У меня этот сценарий полностью действителен, когда я отправляю данные, действительные или недействительные, на не контроллер отдыха.

Этот метод:

@PutMapping(value="/update/{id}",
            consumes=MediaType.APPLICATION_FORM_URLENCODED_VALUE,
            produces=MediaType.TEXT_HTML_VALUE)
public String updateOne(@PathVariable String id,
             @Validated @ModelAttribute Persona persona,
             BindingResult result,
             RedirectAttributes redirectAttributes){

Тот же @Around совет работает, только со следующим вариантом (обратите внимание на ||):

@Around(value="PersonaRestControllerPointcut.updateOnePointcut() || PersonaControllerPointcut.updateOnePointcut()")
@Transactional(noRollbackFor={some exceptions })
public Object aroundAdviceUpdateOne(JoinPoint proceedingJoinPoint) throws Throwable {

Обновление 01

Поэтому 4 сценария, в которых совет @Around должен работать:

  • метод без отдыха с действительными данными (работает)
  • метод без отдыха с недопустимыми данными (работает)
  • метод отдыха с достоверными данными (работает)
  • метод rest с недопустимыми данными (сбой или игнорирование - @Around не выполняется)

Опять же: @Validated работает так, как ожидается для методов отдыха и без отдыха. Проблема связана с советом @Around, он не работает когда метод Rest выполняется только тогда, когда отправляются недопустимые данные. Что не так? или пропал без вести?

Обновление 02

@Validated is:

Вариант Valid JSR-303, поддерживающий спецификацию групп валидации. Разработан для удобного использования с поддержкой Spring JSR-303, но не для JSR-303.

Это из его API

Кажется, это не AOP аннотация. Это просто аннотация Spring. Мой @Pointcut не заботится об аннотациях и не проверяет их. Он использует updateOne(..)

Обновление 03

Даже добавив

@Aspect
@Component
@Transactional
@Order(0)
class PersonaRestControllerAspect {

Не работает так, как ожидается.

Обновление 04

у меня есть

@EnableWebMvc
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {

...

    @Override
    public Validator getValidator() {
        return validatorConfig.localValidatorFactoryBean();
    }

...

Откуда взялся Validator:

@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean(){
    LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
    localValidatorFactoryBean.setValidationMessageSource(rrbms);
    return localValidatorFactoryBean;
}

Этот rrbms экземпляр ReloadableResourceBundleMessageSource, который загружает много .properties файлов, один из них "classpath:/com/manuel/jordan/validation/validation".


person Manuel Jordan    schedule 06.10.2016    source источник
comment
Можете ли вы предоставить конфигурацию, которая использовалась для обработки @Validated аннотаций?   -  person Sergey Bespalov    schedule 07.10.2016
comment
Пожалуйста, уточните, какую конфигурацию вы имеете в виду? Помните, что метод Rest вызывается, если я отправляю действительные или недействительные данные, @Validated работает так, как ожидалось. Проблема в совете @Around. Большая часть конфигурации описана здесь: stackoverflow.com/a/39842293/3665178   -  person Manuel Jordan    schedule 07.10.2016
comment
См. Раздел Update 01.   -  person Manuel Jordan    schedule 07.10.2016
comment
Я имею в виду, как работает аннотация @Validated? Это тоже АОП?   -  person Sergey Bespalov    schedule 07.10.2016
comment
См. Раздел Update 02 (перечитайте раздел Update 01, на всякий случай тоже)   -  person Manuel Jordan    schedule 07.10.2016
comment
это похоже на АОП, потому что MethodVost вроде бы уже используется. А если это так, то вам просто нужно определить порядок выполнения ваших советов.   -  person Sergey Bespalov    schedule 07.10.2016
comment
Я попытаюсь добавить @Order(0) для моего @Aspect, кажется, вы правы, потому что все суперклассы этого MethodValidationPostProcessor принадлежат их собственным пакетам, и все они содержат термин aop.   -  person Manuel Jordan    schedule 07.10.2016
comment
См. Раздел Update 03. Я не знаю, что делать ..   -  person Manuel Jordan    schedule 09.10.2016
comment
Я постараюсь найти ответ, но сначала нужно повторить настройку валидации.   -  person Sergey Bespalov    schedule 10.10.2016
comment
См. Раздел Update 04. Спасибо   -  person Manuel Jordan    schedule 10.10.2016
comment
Другой ответ (ответ правильный)   -  person Manuel Jordan    schedule 10.10.2016


Ответы (1)


Это ожидаемое поведение. Если вы используете @Validated без сопутствующего параметра BindingResult, метод никогда не вызывается. Вы получите исключение, если данные недействительны. В конце концов, в этом весь смысл валидации. А если метод не вызывается, то и ваш совет не будет выполнен.

person a better oliver    schedule 07.10.2016
comment
Метод Rest вызывается, если я отправляю действительные или недействительные данные, @Validated работает так, как ожидалось. Проблема в совете @Around. - person Manuel Jordan; 07.10.2016
comment
См. Раздел Update 01. - person Manuel Jordan; 07.10.2016
comment
@ManuelJordan Без параметра BindingResult метод не вызывается (должен быть), если данные недействительны. В этом нет никакого смысла. Если в вашем случае он действительно вызывается, то это проблема. - person a better oliver; 10.10.2016
comment
Ваше решение работает. Действительно задавался вопросом об этом. Я осознал эту ситуацию, когда добавил компоненты @Pointcut и @Around. Как я сказал, процесс проверки в методе rest updateOne работает так, как ожидается, с AspectJ и без него (у меня много @Test, все методы проходят). Я думал, что BindingResult был обязательным только для методов без -rest. Вот действительно интересно, как BindingResult повлиял на @Around совет - person Manuel Jordan; 10.10.2016
comment
Как следствие, в остальном методе в добавленном блоке if(result.hasErrors()){ я должен вручную создать объект MethodArgumentNotValidException. Конечно, мой @ControllerAdvice с @ExceptionHandler сделает остальную работу. - person Manuel Jordan; 10.10.2016