Spring MVC和AOP:@Pointcuts只适用于Rest Controllers,不适用于普通的Web Controllers
Spring MVC and AOP: @Pointcuts only apply for Rest Controllers and not for common Web Controllers
我在 Web 环境中使用 Spring Framework
4.3.3。
我有一个 @Controller
通过 Web Browser
用于 Web
请求,它使用其他 @Controller
的依赖性但用于 Rest
目的。它后来提到使用 @Service
等...
这种方法是关于 'Web' 使用 'Rest' 如何在 Content Negotiation using Spring MVC 中解释 Combining Data and Presentation Formats
部分的依赖关系。到这里 development/testing 并且生产工作正常。这是一种有价值的方法。
注意 Rest
class 被注释为 @Controller
因为我使用 ResponseEntity<?>
和 @ResponseBody
.
问题出在 AOP
关于它的基础设施我有:
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}
关于 @Controller
我有这两个 class:
PersonaDeleteOneController
有:
deleteOne(@PathVariable String id, Model model)
对于 @GetMapping
deleteOne(@PathVariable String id, RedirectAttributes redirectAttributes)
对于 @DeleteMapping
PersonaRestController
deleteOne(@PathVariable String id)
对于 @DeleteMapping
这两个 class 是在同一个包中声明的:
com.manuel.jordan.controller.persona
我有以下 @Pointcut
:
@Pointcut(value=
"execution(* com.manuel.jordan.controller.*.*Controller.deleteOne(String, ..))
&& args(id) && target(object)")
public void deleteOnePointcut(String id, Object object){}
@Pointcut
用于以下建议:
@Before(value="ControllerPointcut.deleteOnePointcut(id, object)")
public void beforeAdviceDeleteOne(String id, Object object){
logger.info("beforeAdviceDeleteOne - @Controller: {} - Method: deleteOne - id: {}", object.getClass().getSimpleName(), id);
}
当我执行 Rest
测试时,我可以通过 AOP + logging
确认打印出以下模式:
@Controller
(休息) -> @Service
-> @Repository
到这里为止所有的工作都是如何预期的
当我执行 Web
测试时,我可以通过 AOP + logging
确认打印出以下模式:
@Controller
(休息) -> @Service
-> @Repository
我需要或期待的是:
@Controller
(Web) -> @Controller
(Rest) -> @Service
-> @Repository
有什么错误或缺失? deleteOne
签名对其参数没有歧义。
相同的生产案例。
阿尔法
这里是控制器:
@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";
}
}
和
@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();
}
....
您怎么看我没有使用 this.
来执行方法调用。
看来问题出在你pointcut
的定义上。您可能会注意到您的建议方法仅针对具有一个参数的方法执行,因此这是由于您在切入点声明中指定了 args(id)
。如果删除 args(id)
,它必须按预期工作,但在这种情况下,必须使用一些变通方法来公开参数值。
我认为这是 AspectJ 的奇怪行为,因为像 execution(* *.*(String, ..)) && args(arg) && target(t))
这样的结构具有清晰的语义,可以捕获第一个参数为 String
的所有方法并将其公开给 args
。至少对于 AspectJ 开发人员而言,它可能是错误或功能。
要得到你想要的,你可以使用 joinPoint.getArgs()
内部建议方法的变通方法,如下所示:
@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);
}
我在 Web 环境中使用 Spring Framework
4.3.3。
我有一个 @Controller
通过 Web Browser
用于 Web
请求,它使用其他 @Controller
的依赖性但用于 Rest
目的。它后来提到使用 @Service
等...
这种方法是关于 'Web' 使用 'Rest' 如何在 Content Negotiation using Spring MVC 中解释 Combining Data and Presentation Formats
部分的依赖关系。到这里 development/testing 并且生产工作正常。这是一种有价值的方法。
注意 Rest
class 被注释为 @Controller
因为我使用 ResponseEntity<?>
和 @ResponseBody
.
问题出在 AOP
关于它的基础设施我有:
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}
关于 @Controller
我有这两个 class:
PersonaDeleteOneController
有:deleteOne(@PathVariable String id, Model model)
对于@GetMapping
deleteOne(@PathVariable String id, RedirectAttributes redirectAttributes)
对于@DeleteMapping
PersonaRestController
deleteOne(@PathVariable String id)
对于@DeleteMapping
这两个 class 是在同一个包中声明的:
com.manuel.jordan.controller.persona
我有以下 @Pointcut
:
@Pointcut(value=
"execution(* com.manuel.jordan.controller.*.*Controller.deleteOne(String, ..))
&& args(id) && target(object)")
public void deleteOnePointcut(String id, Object object){}
@Pointcut
用于以下建议:
@Before(value="ControllerPointcut.deleteOnePointcut(id, object)")
public void beforeAdviceDeleteOne(String id, Object object){
logger.info("beforeAdviceDeleteOne - @Controller: {} - Method: deleteOne - id: {}", object.getClass().getSimpleName(), id);
}
当我执行 Rest
测试时,我可以通过 AOP + logging
确认打印出以下模式:
@Controller
(休息) ->@Service
->@Repository
到这里为止所有的工作都是如何预期的
当我执行 Web
测试时,我可以通过 AOP + logging
确认打印出以下模式:
@Controller
(休息) ->@Service
->@Repository
我需要或期待的是:
@Controller
(Web) ->@Controller
(Rest) ->@Service
->@Repository
有什么错误或缺失? deleteOne
签名对其参数没有歧义。
相同的生产案例。
阿尔法
这里是控制器:
@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";
}
}
和
@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();
}
....
您怎么看我没有使用 this.
来执行方法调用。
看来问题出在你pointcut
的定义上。您可能会注意到您的建议方法仅针对具有一个参数的方法执行,因此这是由于您在切入点声明中指定了 args(id)
。如果删除 args(id)
,它必须按预期工作,但在这种情况下,必须使用一些变通方法来公开参数值。
我认为这是 AspectJ 的奇怪行为,因为像 execution(* *.*(String, ..)) && args(arg) && target(t))
这样的结构具有清晰的语义,可以捕获第一个参数为 String
的所有方法并将其公开给 args
。至少对于 AspectJ 开发人员而言,它可能是错误或功能。
要得到你想要的,你可以使用 joinPoint.getArgs()
内部建议方法的变通方法,如下所示:
@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);
}