Spring MVC 测试和 AOP:周围的建议没有按照 rest 控制器的预期执行(仅针对无效数据)

Spring MVC Test & AOP: Around advice is not executed how is expected for rest controller (just for invalid data)

我正在与

合作

我有两个 @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(value=
"execution(* com.manuel.jordan.controller.persona.PersonaRestController.updateOne(..))")
public void updateOnePointcut(){}

以及以下 Around Advice:

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

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 .... never 出现。

注意:即使 @Around 建议不起作用,验证过程也会发生。我的意思是,有 none 个例外。 @Test方法通过。

这里有一些注意事项。

那个方法是:

@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

因此 @Around 建议应该起作用的 4 种情况:

同样:@Validated 对休息和非休息方法的预期工作方式。 问题是关于@Around建议的,它不起作用 Rest方法只是在无效数据时执行已发送。怎么了?还是失踪了?

更新 02

@Validated 是:

Variant of JSR-303's Valid, supporting the specification of validation groups. Designed for convenient use with Spring's JSR-303 support but not JSR-303 specific

它来自它的 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 是一个加载许多 .properties 文件的 ReloadableResourceBundleMessageSource 实例,其中之一是 "classpath:/com/manuel/jordan/validation/validation".

这是预期的行为。如果您使用 @Validated 而没有附带 BindingResult 参数,则永远不会调用该方法。如果数据无效,则会出现异常。毕竟这就是验证的全部意义所在。如果不调用该方法,那么你的建议也不会被执行。