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

问题描述

我正在Web环境中使用Spring Framework 4.3.3.

I am working with Spring Framework 4.3.3 in a web environment.

我有一个@Controller用于通过Web BrowserWeb请求,该请求使用其他@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'的方法,如何在进行内容协商.直到这里进行开发/测试和生产工作正常.这是一种有价值的方法.

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

关于我的基础架构,

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {

}

关于@Controller,我有以下两个类:

About the @Controllers I have these two classes:

  • PersonaDeleteOneController具有:
    • deleteOne(@PathVariable String id, Model model)@GetMapping
    • deleteOne(@PathVariable String id, RedirectAttributes redirectAttributes)@DeleteMapping
    • PersonaDeleteOneController with:
      • deleteOne(@PathVariable String id, Model model) for @GetMapping
      • deleteOne(@PathVariable String id, RedirectAttributes redirectAttributes) for @DeleteMapping
      • deleteOne(@PathVariable String id)@DeleteMapping

      这两个类在同一个名为

      • 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

      直到这里一切正常

      当我执行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

      我需要或期望的是以下内容:

      What I need or expect is the following:

      • @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.

      生产情况相同.

      Alpha

      这里的控制器:

      @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控制器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-12 23:43