我正在使用:
Spring Framework
4.3.3AspectJ
1.8.9
我有以下正常过程:
@Controller
->@Service
->@Repository
我有以下关于AOP
的配对:
PersonaServicePointcut
PersonaServiceAspect
场景如下:
@Service
类有一些方法,如:delete
、save
、update
和findOneById
。它们在同一类中一起声明为。
对于诸如delete
和update
到AOP
之类的方法,我使用@Before
或@Around
建议来调用findOneById
方法。
原因是如果实体不存在,执行delete
或update
方法(考虑Rest场景(是没有意义的。因此,通过该advice
,必须抛出异常,比如exceptionA,它必须在@ControllerAdvice
中处理
实际上,save
方法也采用了相同的方法。因此,在执行之前,执行save
方法其他@Before
或@Around
建议,再次调用findOneById
方法。如果实体已经存在,则必须抛出异常,比如exceptionB,它必须在@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(。
似乎是多余的通过针对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建议,但如果其调用是从
的其他建议中完成的,则从不执行CCD_ 46类。
我用!execution
和!within
组合尝试了很多方法,但都没有结果。
即使当我只创建了一个切入点时,也会截取所有@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 find建议看起来是这样的
@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 answer中所述,但您应该注意,在这种情况下,堆栈中低于建议执行的所有beforeFindOneByIdAdvice
连接点也将被排除。因此,这可能会在未来给你带来一些意想不到的结果。
您可以在"面向方面编程-什么是';cflow';?"答案中看到cflow
是如何工作的。