Spring MVC 和 AOP:@Pointcuts 仅适用于 Rest 控制器,不适用于常见的 Web 控制器 [英] Spring MVC and AOP: @Pointcuts only apply for Rest Controllers and not for common Web Controllers
问题描述
我正在网络环境中使用 Spring Framework
4.3.3.
I am working with Spring Framework
4.3.3 in a web environment.
我有一个 @Controller
用于通过 Web 浏览器
的 Web
请求,它使用其他 @Controller
但用于 Rest
目的.后者提到使用 @Service
等...
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...
关于使用Rest"的Web"的这种方法如何在 使用 Spring MVC 的内容协商用于 组合数据和表示格式
部分.直到这里用于开发/测试和生产工作正常.这是一种有价值的方法.
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.
注意 Rest
类用 @Controller
注释,因为我使用 ResponseEntity>
和@ResponseBody
.
Note The Rest
class is annotated with @Controller
because I work with ResponseEntity<?>
and @ResponseBody
.
问题在于AOP
关于我拥有的基础设施:
About its infrastructure I have:
@Configuration
@EnableAspectJAutoProxy
public class AopConfig {
}
关于 @Controller
s 我有这两个类:
About the @Controller
s I have these two classes:
PersonaDeleteOneController
带有:deleteOne(@PathVariable String id, Model model)
for@GetMapping
deleteOne(@PathVariable String id, RedirectAttributes redirectAttributes)
for@DeleteMapping
PersonaDeleteOneController
with:deleteOne(@PathVariable String id, Model model)
for@GetMapping
deleteOne(@PathVariable String id, RedirectAttributes redirectAttributes)
for@DeleteMapping
deleteOne(@PathVariable String id)
for@DeleteMapping
这两个类在同一个包中声明:
These two classes are declared within the same package named:
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
确认:When I execute the
Rest
tests I can confirm throughAOP + logging
that prints the following pattern:@Controller
(Rest) ->@Service
->@Repository
@Controller
(Rest) ->@Service
->@Repository
直到这里一切都按预期工作
Until here all work how is expected
当我执行
Web
测试时,我可以通过打印以下模式的AOP + logging
确认:When I execute the
Web
tests I can confirm throughAOP + logging
that prints the following pattern:@Controller
(Rest) ->@Service
->@Repository
@Controller
(Rest) ->@Service
->@Repository
我需要或期望的是:
@Controller
(Web) ->@Controller
(Rest) ->@Service
->@Repository
@Controller
(Web) ->@Controller
(Rest) ->@Service
->@Repository
出了什么问题或遗漏了什么?
deleteOne
签名对其参数没有歧义.What is wrong or missing?. The
deleteOne
signatures are not ambiguous about their parameters.生产情况相同.
阿尔法
这里是控制器:
@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.
来执行方法调用.How you can see I don't use
this.
to execute the method invocations.推荐答案
看来问题出在你的
pointcut
定义上.您可能会注意到,您的通知方法仅对具有一个参数的方法执行,这是因为您在切入点声明中指定了args(id)
.如果删除args(id)
,它必须按预期工作,但在这种情况下,必须使用一些变通方法来公开参数值.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 specifiedargs(id)
in the pointcut declaration. It must work as you expect if you removeargs(id)
, but in this case some workaround must be used to expose parameter value.我认为这是 AspectJ 的奇怪行为,因为像
execution(* *.*(String, ..)) && 这样的结构args(arg) &&target(t))
具有清晰的语义,可以捕获所有带有String
第一个参数的方法,并将其暴露给args
.至少对于 AspectJ 开发人员来说,它可能是一个错误或功能.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 withString
first parameter and expose it toargs
. It can be a bug or feature, at least, to AspectJ developers.为了得到你想要的东西,你可以使用一个带有
joinPoint.getArgs()
内部通知方法的解决方法,如下所示: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); }
这篇关于Spring MVC 和 AOP:@Pointcuts 仅适用于 Rest 控制器,不适用于常见的 Web 控制器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!