【问题标题】:Spring MVC and AOP: @Pointcuts only apply for Rest Controllers and not for common Web ControllersSpring MVC 和 AOP:@Pointcuts 仅适用于 Rest Controller,不适用于常见的 Web Controller
【发布时间】:2016-09-26 16:59:48
【问题描述】:

我在网络环境中使用Spring Framework 4.3.3。

我有一个@Controller 用于通过Web Browser 请求Web,该Web Browser 使用其他@Controller 的依赖关系,但用于Rest 目的。后者提到使用@Service 等...

这种关于使用“Rest”的“Web”的方法如何在Content Negotiation using Spring MVC 中为Combining Data and Presentation Formats 部分解释依赖关系。直到这里进行开发/测试和生产工作正常。这是一种有价值的方法。

注意Rest 类使用@Controller 进行注释,因为我使用的是ResponseEntity<?>@ResponseBody

问题在于AOP

关于我拥有的基础设施:

@Configuration
@EnableAspectJAutoProxy
public class AopConfig {

}

关于@Controllers 我有这两个类:

  • PersonaDeleteOneController 与:
    • deleteOne(@PathVariable String id, Model model)@GetMapping
    • deleteOne(@PathVariable String id, RedirectAttributes redirectAttributes)@DeleteMapping
  • PersonaRestController
    • 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 进行确认:

  • @Controller (休息) -> @Service -> @Repository

到这里为止,所有工作都按预期进行

当我执行Web 测试时,我可以通过打印以下模式的AOP + logging 进行确认:

  • @Controller (休息) -> @Service -> @Repository

我需要或期望的是:

  • @Controller (Web) -> @Controller (休息) -> @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. 来执行方法调用。

【问题讨论】:

  • 能否提供@Controller类方法调用的代码示例?如果您尝试执行 this.deleteOne(...) 之类的操作,则可能会出现问题。
  • 完成,请参阅Alpha 部分。谢谢。

标签: spring-mvc aop spring-aop spring-aspects


【解决方案1】:

似乎问题出在您pointcut 的定义中。您可能会注意到您的通知方法仅对具有一个参数的方法执行,因此这是由于您在切入点声明中指定了args(id)。如果您删除 args(id),它必须按预期工作,但在这种情况下,必须使用一些变通方法来公开参数值。

我认为这是 AspectJ 的奇怪行为,因为像 execution(* *.*(String, ..)) &amp;&amp; args(arg) &amp;&amp; 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);
}

【讨论】:

  • 有效。非常感谢。这是funnytricky 案例。我希望一些AspectJ 开发人员阅读这篇文章。如果它真的是一个错误,那么应该提出一个问题。
  • 顺便说一句,对于其他 @Pointcut,对于包含 (String, ..) 的其他表达式(不适用于这种情况),我可以拥有 args(id, ..)。我认为不可能在args() 部分中使用..
猜你喜欢
  • 1970-01-01
  • 2020-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-27
相关资源
最近更新 更多