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:

这两个 class 是在同一个包中声明的:

我有以下 @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 确认打印出以下模式:

到这里为止所有的工作都是如何预期的

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

我需要或期待的是:

有什么错误或缺失? 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);
}