问题
I am working with Spring Framework
4.3.3 in a web environment.
I have a @Controller
used for Web
requests through a Web Browser
that uses how dependency other @Controller
but for Rest
purposes. It latter mentioned uses a @Service
etc...
This approach about a 'Web' using a 'Rest' how a dependency is explained in Content Negotiation using Spring MVC for the Combining Data and Presentation Formats
section. Until here for development/testing and production works fine. It is a valuable approach.
Note The Rest
class is annotated with @Controller
because I work with ResponseEntity<?>
and @ResponseBody
.
The problem is with AOP
About its infrastructure I have:
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}
About the @Controller
s I have these two classes:
PersonaDeleteOneController
with:deleteOne(@PathVariable String id, Model model)
for@GetMapping
deleteOne(@PathVariable String id, RedirectAttributes redirectAttributes)
for@DeleteMapping
PersonaRestController
deleteOne(@PathVariable String id)
for@DeleteMapping
These two classes are declared within the same package named:
com.manuel.jordan.controller.persona
I have the following @Pointcut
:
@Pointcut(value=
"execution(* com.manuel.jordan.controller.*.*Controller.deleteOne(String, ..))
&& args(id) && target(object)")
public void deleteOnePointcut(String id, Object object){}
That @Pointcut
is used for the following advice:
@Before(value="ControllerPointcut.deleteOnePointcut(id, object)")
public void beforeAdviceDeleteOne(String id, Object object){
logger.info("beforeAdviceDeleteOne - @Controller: {} - Method: deleteOne - id: {}", object.getClass().getSimpleName(), id);
}
When I execute the Rest
tests I can confirm through AOP + logging
that prints the following pattern:
@Controller
(Rest) ->@Service
->@Repository
Until here all work how is expected
When I execute the Web
tests I can confirm through AOP + logging
that prints the following pattern:
@Controller
(Rest) ->@Service
->@Repository
What I need or expect is the following:
@Controller
(Web) ->@Controller
(Rest) ->@Service
->@Repository
What is wrong or missing?. The deleteOne
signatures are not ambiguous about their parameters.
Same case for Production.
Alpha
Here the controllers:
@Controller
@RequestMapping(value="/personas")
public class PersonaDeleteOneController {
private final PersonaRestController personaRestController;
@Autowired
public PersonaDeleteOneController(PersonaRestController personaRestController){
this.personaRestController = personaRestController;
}
@GetMapping(value="/delete/{id}",
produces=MediaType.TEXT_HTML_VALUE)
public String deleteOne(@PathVariable String id, Model model){
model.addAttribute(personaRestController.findOneById(id));
model.addAttribute("root", "/personas/delete");
return "persona/deleteOne";
}
@DeleteMapping(value="/delete/{id}",
produces=MediaType.TEXT_HTML_VALUE)
public String deleteOne(@PathVariable String id, RedirectAttributes redirectAttributes){
personaRestController.deleteOne(id);
redirectAttributes.addFlashAttribute("message", "process.successful");
return "redirect:/message";
}
}
And
@Controller
@RequestMapping(value="/personas")
public class PersonaRestController {
private final PersonaService personaService;
@Autowired
public PersonaRestController(PersonaService personaService){
this.personaService = personaService;
}
@DeleteMapping(value="/{id}")
public ResponseEntity<Void> deleteOne(@PathVariable String id){
personaService.deleteOne(id);
return ResponseEntity.noContent().build();
}
....
How you can see I don't use this.
to execute the method invocations.
回答1:
Seems that the problem is in you pointcut
definition. You may notice that your advice method is performed only for methods with one parameter, so this is due to the fact that you have specified args(id)
in the pointcut declaration. It must work as you expect if you remove args(id)
, but in this case some workaround must be used to expose parameter value.
I think that this is strange behavior form AspectJ because constructions like execution(* *.*(String, ..)) && args(arg) && target(t))
has clear semantic sense to capture all the methods with String
first parameter and expose it to args
. It can be a bug or feature, at least, to AspectJ developers.
To get what you want, you can use a workaround with joinPoint.getArgs()
inside advice method like this:
@Pointcut(value=
"execution(* com.manuel.jordan.controller.*.*Controller.deleteOne(..)) && target(object)")
public void deleteOnePointcut(Object object){}
@Before(value="ControllerPointcut.deleteOnePointcut(object)")
public void beforeAdviceDeleteOne(JoinPoint jp, Object object){
Object id = jp.getArgs()[0];
logger.info("beforeAdviceDeleteOne - @Controller: {} - Method: deleteOne - id: {}", object.getClass().getSimpleName(), id);
}
来源:https://stackoverflow.com/questions/39708410/spring-mvc-and-aop-pointcuts-only-apply-for-rest-controllers-and-not-for-commo