Spring MVC 和 AOP:@Pointcuts 仅适用于 Rest 控制器,不适用于常见的 Web 控制器 [英] Spring MVC and AOP: @Pointcuts only apply for Rest Controllers and not for common Web Controllers

查看:41
本文介绍了Spring MVC 和 AOP:@Pointcuts 仅适用于 Rest 控制器,不适用于常见的 Web 控制器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在网络环境中使用 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 {

}

关于 @Controllers 我有这两个类:

About the @Controllers 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 through AOP + 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 through AOP + 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 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.

      我认为这是 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 with String first parameter and expose it to args. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆