Spring AOP:如何排除由于其他人执行而导致的不必要的@Pointcut(@Around advice)执行@Pointcuts/advices
Spring AOP: How exclude an unnecessary @Pointcut (@Around advice) execution due the execution from others @Pointcuts/advices
我正在与:
Spring Framework
4.3.3
AspectJ
1.8.9
我有以下正常流程:
@Controller
-> @Service
-> @Repository
关于 AOP
我有以下一对:
PersonaServicePointcut
PersonaServiceAspect
场景如下:
@Service
class有一些方法如:delete
、save
、update
和findOneById
。它们在同一个 class.
中一起 声明
对于 delete
和 update
到 AOP
等方法,我使用 @Before
或 @Around
建议来调用 findOneById
方法。
原因是没有意义 执行 delete
或 update
方法(考虑 Rest 场景)如果实体不存在。因此,通过 advice
必须抛出异常,假设异常 A,它必须在 @ControllerAdvice
中处理
实际上应用了与 save
方法相同的方法。因此,在 execute save
方法 other @Before
or @Around
advice 调用 findOneById
方法。如果实体已经存在,则必须抛出异常,假设异常 B,它必须在 @ControllerAdvice
中处理
观察我有 3 个 points/3advices 使用 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
类型的异常(它用于 classic 404)。
似乎多余 执行 findOneById
方法通过 @Before
或 @Around
对 findOneById
方法本身的建议。但是我需要这个来达到 logging
和 audit
的目的,并创建 C 类型的异常。它必须由一些 @ControllerAdvice
处理
问题 是在执行 delete/update/save
方法的其他建议时(记住他们调用并执行 findOneById
方法)我的 findOneByIdPointcut
被执行 不必要地 。
我需要更改切入点声明以指示如下内容:
@Pointcut(Alpha)
public void findOneByIdPointcut(String id){}
其中 Alpha
是:
为@Service
的findOneById
方法执行before/around建议,但是从不 如果它的调用是根据
的其他建议完成的
PersonaServiceAspect
class.
我尝试了很多 !execution
和 !within
组合的方法,但没有结果。
Even 当我只创建 one 切入点时 拦截 所有 @Service
的方法及其各自独特的 @Around
建议,并通过 ProceedingJoinPoint proceedingJoinPoint
参数我能够检查调用了哪个方法,然后执行相应的控制。但这种行为再次发生。
意思是,通过以下:
@Around("PersonaServicePointcut.anyMethodPointcut()")
public Object aroundAdviceAnyMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
其中 anyMethodPointcut
是 execution(* mypackage.PersonaServiceImpl.*(..))
是否可以实现这种方法?怎么样?
谢谢。
您可以使用
在切入点表达式的控制流中排除切入点
!cflow(<pointcut>)
在您的示例中,您想排除 findOneById
的那些执行,其中执行在您自己的建议的控制流中。如果你的建议应用于切入点表达式
@Pointcut("saveOnePointcut() || updateOnePointcut() || deleteOnePointcut()")
public void combinedPointcut() {}
你可以排除它:
!cflow(combinedPointcut())
或者简单地从所有通知执行的控制流中排除所有控制流,您可以使用:
!cflow(adviceexecution())
基于组合的切入点表达式,您的周围查找建议如下所示:
@Around("findOneByIdPointcut() && !cflow(combinedPointcut())")
public void aroundFind() {
....
}
请注意,您不能在示例中组合切入点表达式,因为它们绑定切入点参数。您需要删除 && args(...)
部分并将它们直接移动到通知切入点表达式。您不能将 args(paramName)
等绑定切入点表达式与 ||
(或)运算符结合使用,因此您需要为这些情况创建单独的建议。如果愿意,您仍然可以将这些建议中的大部分工作委托给一个方法。请参阅此类委托的示例 。
如果我理解正确,你希望 findOneByIdPointcut
不会在你的建议中调用,但应该在所有其他情况下触发。
一种方法是将 call
与 within
切入点结合使用。 call
会将连接点向上移动到调用方方法,within
将从目标连接点排除通知。
因此您的方面代码可能如下所示:
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
连接点都将被排除也。所以这可能会给你带来一些意想不到的结果。
您可以在“Aspect oriented programming - what is 'cflow'?”答案中查看 cflow
的工作原理。
我正在与:
Spring Framework
4.3.3AspectJ
1.8.9
我有以下正常流程:
@Controller
->@Service
->@Repository
关于 AOP
我有以下一对:
PersonaServicePointcut
PersonaServiceAspect
场景如下:
@Service
class有一些方法如:delete
、save
、update
和findOneById
。它们在同一个 class.
对于 delete
和 update
到 AOP
等方法,我使用 @Before
或 @Around
建议来调用 findOneById
方法。
原因是没有意义 执行 delete
或 update
方法(考虑 Rest 场景)如果实体不存在。因此,通过 advice
必须抛出异常,假设异常 A,它必须在 @ControllerAdvice
实际上应用了与 save
方法相同的方法。因此,在 execute save
方法 other @Before
or @Around
advice 调用 findOneById
方法。如果实体已经存在,则必须抛出异常,假设异常 B,它必须在 @ControllerAdvice
观察我有 3 个 points/3advices 使用 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
类型的异常(它用于 classic 404)。
似乎多余 执行 findOneById
方法通过 @Before
或 @Around
对 findOneById
方法本身的建议。但是我需要这个来达到 logging
和 audit
的目的,并创建 C 类型的异常。它必须由一些 @ControllerAdvice
问题 是在执行 delete/update/save
方法的其他建议时(记住他们调用并执行 findOneById
方法)我的 findOneByIdPointcut
被执行 不必要地 。
我需要更改切入点声明以指示如下内容:
@Pointcut(Alpha)
public void findOneByIdPointcut(String id){}
其中 Alpha
是:
为@Service
的findOneById
方法执行before/around建议,但是从不 如果它的调用是根据
的其他建议完成的
PersonaServiceAspect
class.
我尝试了很多 !execution
和 !within
组合的方法,但没有结果。
Even 当我只创建 one 切入点时 拦截 所有 @Service
的方法及其各自独特的 @Around
建议,并通过 ProceedingJoinPoint proceedingJoinPoint
参数我能够检查调用了哪个方法,然后执行相应的控制。但这种行为再次发生。
意思是,通过以下:
@Around("PersonaServicePointcut.anyMethodPointcut()")
public Object aroundAdviceAnyMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
其中 anyMethodPointcut
是 execution(* mypackage.PersonaServiceImpl.*(..))
是否可以实现这种方法?怎么样?
谢谢。
您可以使用
在切入点表达式的控制流中排除切入点!cflow(<pointcut>)
在您的示例中,您想排除 findOneById
的那些执行,其中执行在您自己的建议的控制流中。如果你的建议应用于切入点表达式
@Pointcut("saveOnePointcut() || updateOnePointcut() || deleteOnePointcut()")
public void combinedPointcut() {}
你可以排除它:
!cflow(combinedPointcut())
或者简单地从所有通知执行的控制流中排除所有控制流,您可以使用:
!cflow(adviceexecution())
基于组合的切入点表达式,您的周围查找建议如下所示:
@Around("findOneByIdPointcut() && !cflow(combinedPointcut())")
public void aroundFind() {
....
}
请注意,您不能在示例中组合切入点表达式,因为它们绑定切入点参数。您需要删除 && args(...)
部分并将它们直接移动到通知切入点表达式。您不能将 args(paramName)
等绑定切入点表达式与 ||
(或)运算符结合使用,因此您需要为这些情况创建单独的建议。如果愿意,您仍然可以将这些建议中的大部分工作委托给一个方法。请参阅此类委托的示例
如果我理解正确,你希望 findOneByIdPointcut
不会在你的建议中调用,但应该在所有其他情况下触发。
一种方法是将 call
与 within
切入点结合使用。 call
会将连接点向上移动到调用方方法,within
将从目标连接点排除通知。
因此您的方面代码可能如下所示:
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
连接点都将被排除也。所以这可能会给你带来一些意想不到的结果。
您可以在“Aspect oriented programming - what is 'cflow'?”答案中查看 cflow
的工作原理。