I am working with
- Spring Framework 4.3.3
- AspectJ 1.8.9
I have two @Controllers
one for mvc
and other for rest
. Each one uses how a dependency a @Service
.
I have the following Rest method
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();
}
Observe for the second parameter I use @Validated
I have the following Pointcut:
@Pointcut(value=
"execution(* com.manuel.jordan.controller.persona.PersonaRestController.updateOne(..))")
public void updateOnePointcut(){}
And the following 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());
....
}
Through Spring MVC Test
When I send valid data:
Such as:
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);
Through Gradle
reports I can confirm that the @Around
advice works how is expected. It just watching the Beginning aroundAdviceUpdateOne - Class ....
text in the report and the rest of the advice body works how is expected.
The problem is when I send invalid data to be tested through @Validated
Using the two ways shown above but just changing personaValid
by personaInvalid
(null fields, breaking the min and max boundaries, etc) I did realize the @Around
advice always is ignored. It confirming that Beginning aroundAdviceUpdateOne - Class ....
never appears.
Note: even when the @Around
advice does not work, the validation process happens. I mean, there are none exception. The @Test
method pass.
Here some considerations.
- I have the same behaviour with
@Before
advice. - I am assuming before the execution of the
PersonaRestController.updateOne
method the@Around
(even@Before
) advice must do its work. It without matter the data is valid or not. - I have this scenario totally valid when I send data either valid or invalid to a non- rest controller
That method is:
@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){
The same @Around
advice works, just with the following variation (note the ||
):
@Around(value="PersonaRestControllerPointcut.updateOnePointcut() || PersonaControllerPointcut.updateOnePointcut()")
@Transactional(noRollbackFor={some exceptions })
public Object aroundAdviceUpdateOne(JoinPoint proceedingJoinPoint) throws Throwable {
Update 01
Therefore 4 scenarios where the @Around
advice should work:
- non rest method with valid data (works)
- non rest method with invalid data (works)
- rest method with valid data (works)
- rest method with invalid data (fail or ignored -
@Around
does not execute)
Again: the @Validated
works how is expected for rest and non rest methods. The problem is about the @Around
advice, it does not work when the Rest
method is executed just when invalid data is sent. What is wrong? or missing?
Update 02
@Validated
is:
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
It from its API
Seems is not an AOP
annotation. Is just a Spring annotation. My @Pointcut
does not care or check about annotations. It uses updateOne(..)
Update 03
Even adding
@Aspect
@Component
@Transactional
@Order(0)
class PersonaRestControllerAspect {
Does not work how is expected.
Update 04
I have
@EnableWebMvc
@Configuration
public class WebMvcConfig extends WebMvcConfigurerAdapter {
...
@Override
public Validator getValidator() {
return validatorConfig.localValidatorFactoryBean();
}
...
Where the Validator
comes from:
@Bean
public LocalValidatorFactoryBean localValidatorFactoryBean(){
LocalValidatorFactoryBean localValidatorFactoryBean = new LocalValidatorFactoryBean();
localValidatorFactoryBean.setValidationMessageSource(rrbms);
return localValidatorFactoryBean;
}
That rrbms
is a ReloadableResourceBundleMessageSource
instance that load many .properties
files, one of them is "classpath:/com/manuel/jordan/validation/validation"
.