3

我正在与:

  • Spring Framework4.3.3
  • AspectJ1.8.9

我有以下正常过程:

  • @Controller-> @Service->@Repository

我有以下一对AOP

  • PersonaServicePointcut
    • PersonaServiceAspect

场景如下:

该类有一些方法,@Service例如: delete、和。它们在同一个类中一起声明。saveupdatefindOneById

对于诸如deleteupdate通过之类的方法,AOP我使用一个@Before@Around建议来调用该findOneById方法。

原因是如果实体不存在,执行deleteor方法(考虑 Rest 场景)是没有意义的。update因此,advice必须抛出异常,比如说 Exception A,它必须在@ControllerAdvice

该方法实际上采用了相同的save方法。因此,在执行save方法之前,其他 @Before@Around通知再次调用该findOneById方法。如果实体已经存在,则必须抛出异常,比如说 Exception B,它必须在@ControllerAdvice

请注意,我有 3 个点/3 个使用该findOneById方法的建议。它检查是否存在实体。

例如:

    @Pointcut(value=
    "execution(* mypackage.PersonaServiceImpl.saveOne(otherpackage.Persona)) 
    && args(persona)")
    public void saveOnePointcut(Persona persona){}

    @Pointcut(value=
    "execution(*  
    mypackage.PersonaServiceImpl.updateOne(otherpackage.Persona)) 
    && args(persona)")
    public void updateOnePointcut(Persona persona){}

    @Pointcut(value="execution(*  
    mypackage.PersonaServiceImpl.deleteOne(String)) && args(id)")
    public void deleteOnePointcut(String id){}

同样:这 3 个建议使用或执行findOneById方法。

问题是当我添加一个新的切入点时,例如:

@Pointcut(value="execution(*    
mypackage.PersonaServiceImpl.findOneById(String)) 
&& args(id)")
public void findOneByIdPointcut(String id){}

我创建了这个切入点来检查一个实体是否已经存在,如果它不存在,它必须抛出一个类型的异常C(它适用于经典的 404)。

通过对方法本身的or建议执行findOneById方法似乎是多余的。但我需要这个来达到目的,也需要创建C类型的例外。它必须由某些人处理@Before@AroundfindOneByIdloggingaudit@ControllerAdvice

问题是当其他方法的建议被delete/update/save执行时(记住他们也调用并执行findOneById方法)我findOneByIdPointcut不必要地执行了。

我需要更改切入点声明以指示如下内容:

@Pointcut(Alpha)
public void findOneByIdPointcut(String id){}

在哪里Alpha

执行@Service'sfindOneById方法的 before/around 建议, 如果它的调用是从类的其他建议中完成的,则永远不会
PersonaServiceAspect

我尝试了很多方法!execution!within组合,但没有结果。

即使我只创建了一个切入点,它使用其各自独特的建议拦截 所有's 方法,并且通过参数我能够检查调用了什么方法,然后执行相应的控制。但是这种行为再次发生。@Service@AroundProceedingJoinPoint proceedingJoinPoint

这意味着,通过以下方式:

@Around("PersonaServicePointcut.anyMethodPointcut()")
    public Object aroundAdviceAnyMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{

anyMethodPointcut在哪里execution(* mypackage.PersonaServiceImpl.*(..))

有可能实现这种方法吗?如何?

谢谢。

4

2 回答 2

2

您可以在切入点表达式的控制流中排除切入点

!cflow(<pointcut>)

在您的示例中,您希望排除执行在findOneById您自己的建议的控制流内的那些执行。如果您的建议应用于切入点表达式

@Pointcut("saveOnePointcut() || updateOnePointcut() || deleteOnePointcut()")
public void combinedPointcut() {}

你可以排除它:

!cflow(combinedPointcut())

或者简单地从所有建议执行的控制流中排除所有控制流,您可以使用:

!cflow(adviceexecution())

根据组合的切入点表达式,您的周围查找建议将如下所示:

@Around("findOneByIdPointcut() && !cflow(combinedPointcut())")
public void aroundFind() {
    ....
}

请注意,您不能在示例中组合切入点表达式,因为它们是绑定切入点参数。您需要删除这些&& args(...)部分并将它们直接移动到建议切入点表达式。您不能将绑定切入点表达式args(paramName)||(or) 运算符结合使用,因此您需要为这些情况创建单独的建议。如果您愿意,您仍然可以将这些建议的大部分工作委托给单个方法。在此处查看此类委托的示例。

于 2016-10-03T12:13:45.633 回答
1

如果我理解正确,您希望findOneByIdPointcut不在您的建议中调用,但应在所有其他情况下触发。

一种方法是与切入点call结合使用。将连接点向上移动到调用者方法,并将排除来自目标连接点的建议。withincallwithin

因此,您的方面代码可能如下所示:

public class ControllerAspect {

    ...

    @Pointcut(value = "execution(* PersonaServiceImpl.deleteOne(String)) && args(id)")
    public void deleteOnePointcut(String id)
    {
    }

    @Pointcut(value = "call(* PersonaServiceImpl.findOneById(String)) && args(id)")
    public void findOneByIdPointcut(String id)
    {
    }

    @Before(value = "findOneByIdPointcut(id) && !within(ControllerAspect)")
    public void beforeFindOneByIdAdvice(String id)
    {
       //do some logic here
    }


    @Before(value = "deleteOnePointcut(id)")
    public void beforeDeleteOneAdvice(String id)
    {
        Persona persona = personaService.findOneById(id);
        //check that persona is not null
    }
}

另一种方法是使用cflow@Nándor Előd Fekete 回答中描述的切入点,但您应该注意,在这种情况下beforeFindOneByIdAdvice,堆栈中建议执行下方的所有连接点也将被排除。因此,这可能会在将来为您带来一些意想不到的结果。

您可以cflow在“面向方面的编程 - 什么是 'cflow'? ”答案中看到其工作原理。

于 2016-10-04T02:58:54.830 回答