首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Spring :如何排除不必要的@Pointcuts(@ from通知)执行,因为执行来自其他@Pointcuts/建议

Spring :如何排除不必要的@Pointcuts(@ from通知)执行,因为执行来自其他@Pointcuts/建议
EN

Stack Overflow用户
提问于 2016-10-02 21:36:11
回答 2查看 2.9K关注 0票数 3

我的工作是:

  • Spring Framework 4.3.3
  • AspectJ 1.8.9

我有以下正常程序:

  • @Controller -> @Service -> @Repository

我有关于AOP的下面一对

  • PersonaServicePointcut
    • PersonaServiceAspect

设想如下:

@Service类有一些方法,如:deletesaveupdatefindOneById。它们一起被声明为在同一个类中。

对于deleteupdate等通过AOP的方法,我使用@Before@Around通知来调用findOneById方法。

原因是如果实体不存在,执行deleteupdate方法(考虑Rest方案)就没有意义。因此,通过该advice必须抛出异常,让我们说异常A,它必须在@ControllerAdvice中处理

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

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

例如:

代码语言:javascript
复制
    @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){}

再次:这三个建议使用或执行findOneById方法。

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

代码语言:javascript
复制
@Pointcut(value="execution(*    
mypackage.PersonaServiceImpl.findOneById(String)) 
&& args(id)")
public void findOneByIdPointcut(String id){}

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

通过findOneById方法本身的@Before@Around通知执行findOneById方法似乎是多余的。但是,为了loggingaudit的目的,我需要这样做,并创建C类型的例外。它必须由某个@ControllerAdvice来处理

问题是当其他对delete/update/save方法的建议被执行时(记住,它们也调用和执行findOneById方法),因此不必要地执行了my findOneByIdPointcut

我需要更改切入点声明,以表示如下所示:

代码语言:javascript
复制
@Pointcut(Alpha)
public void findOneByIdPointcut(String id){}

其中Alpha是:

@ServicefindOneById方法执行“前”/“环绕”通知,但是如果它的调用是从

PersonaServiceAspect类。

我在!execution!within组合中尝试过许多方法,但没有结果。

甚至--当我创建了只截取all @Service's方法及其各自唯一的@Around建议的one Pointcut,并且通过ProceedingJoinPoint proceedingJoinPoint参数,我能够检查调用了什么方法,然后执行相应的控件。但这种行为再次发生。

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

代码语言:javascript
复制
@Around("PersonaServicePointcut.anyMethodPointcut()")
    public Object aroundAdviceAnyMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{

其中anyMethodPointcutexecution(* mypackage.PersonaServiceImpl.*(..))

是否有可能实现这一方法?多么?

谢谢。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-10-03 12:13:45

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

代码语言:javascript
复制
!cflow(<pointcut>)

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

代码语言:javascript
复制
@Pointcut("saveOnePointcut() || updateOnePointcut() || deleteOnePointcut()")
public void combinedPointcut() {}

你可以通过以下方式排除这一点:

代码语言:javascript
复制
!cflow(combinedPointcut())

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

代码语言:javascript
复制
!cflow(adviceexecution())

根据组合的切入点表达式,您的find建议如下所示:

代码语言:javascript
复制
@Around("findOneByIdPointcut() && !cflow(combinedPointcut())")
public void aroundFind() {
    ....
}

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

票数 2
EN

Stack Overflow用户

发布于 2016-10-04 02:58:54

如果我正确理解,您希望在您的通知中不调用findOneByIdPointcut,而是应该在所有其他情况下触发它。

这样做的一种方法是结合使用callwithin切入点。call将连接点向上移动到调用方方法,而within将从目标连接点中排除通知。

所以您的方面代码可能如下所示:

代码语言:javascript
复制
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切入点,如@ndorőd Fekete答案中所述,但您应该注意,在这种情况下,堆栈中通知执行下面的所有beforeFindOneByIdAdvice连接点也将被排除在外。因此,这可能会有一些意想不到的结果,你在未来。

您可以看到cflow在"Aspect oriented programming - what is 'cflow'?“答案中是如何工作的。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/39821938

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档