我正在使用:
Spring Framework
4.3.3 AspectJ
1.8.9 我有以下正常过程:
@Controller
- > @Service
- > @Repository
我有以下关于AOP
的对:
PersonaServicePointcut
PersonaServiceAspect
方案如下:
@Service
类有一些方法,例如:delete
,save
,update
和findOneById
。它们在同一个类中声明为在一起。
对于delete
和update
到AOP
等方法,我使用@Before
或@Around
建议来调用findOneById
方法。
原因是,如果实体不存在,执行 delete
或update
方法(考虑Rest方案)是没有意义的。因此,通过advice
必须抛出异常,假设异常 A ,必须在@ControllerAdvice
实际上,save
方法采用了相同的方法。因此,在执行之前,save
方法其他 @Before
或@Around
建议会再次调用findOneById
方法。如果实体已经存在,则必须抛出异常,假设异常 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){}
再次:这三个建议使用或执行 findOneById
方法。
问题是当我添加一个新的切入点时,例如:
@Pointcut(value="execution(*
mypackage.PersonaServiceImpl.findOneById(String))
&& args(id)")
public void findOneByIdPointcut(String id){}
我已创建此切入点以检查实体是否已存在,如果它不存在,则必须抛出类型为C
的异常(对于经典404)。
通过findOneById
或@Before
方法本身的@Around
或findOneById
建议,似乎多余的执行 {/ 1}}方法。但我需要logging
和audit
目的,并且还要创建 C 类型的例外。它必须由某些@ControllerAdvice
问题是指执行delete/update/save
方法的其他建议时(记住他们调用执行 findOneById
方法) findOneByIdPointcut
执行不必要。
我需要更改切入点声明以表示类似这样的内容:
@Pointcut(Alpha)
public void findOneByIdPointcut(String id){}
Alpha
的位置:
执行@Service
的{{1}}方法的前/后建议,但 从不,如果其他人已完成此项通话来自的建议
findOneById
上课。
我已经尝试了多种使用PersonaServiceAspect
和!execution
组合的方式,但没有结果。
甚至当我创建一个切入点拦截 所有 !within
时方法及其各自唯一的@Service
建议,并通过@Around
参数,我能够检查调用的方法,然后执行相应的控制。但这种行为再次发生。
这意味着,通过以下方式:
ProceedingJoinPoint proceedingJoinPoint
其中@Around("PersonaServicePointcut.anyMethodPointcut()")
public Object aroundAdviceAnyMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
为anyMethodPointcut
有可能完成这种方法吗?怎么样?
感谢。
答案 0 :(得分:1)
您可以使用
在切入点表达式的控制流中排除切入点!cflow(<pointcut>)
在您的示例中,您希望排除执行在您自己的建议的控制流中的findOneById
的执行。如果您的建议适用于切入点表达式
@Pointcut("saveOnePointcut() || updateOnePointcut() || deleteOnePointcut()")
public void combinedPointcut() {}
你可以用以下内容排除:
!cflow(combinedPointcut())
或者只是从所有通知执行的控制流中排除所有控制流,您可以使用:
!cflow(adviceexecution())
基于组合切入点表达式,您的周围查找建议将如下所示:
@Around("findOneByIdPointcut() && !cflow(combinedPointcut())")
public void aroundFind() {
....
}
请注意,您无法在示例中组合切入点表达式,因为它们是绑定切入点参数。您需要删除&& args(...)
部分并将它们直接移动到建议切入点表达式。您无法将args(paramName)
之类的绑定切入点表达式与||
(或)运算符组合在一起,因此您需要为这些情况创建单独的建议。如果您愿意,您仍然可以将这些建议中的大部分工作委托给单个方法。请参阅此类委派的示例here。
答案 1 :(得分:1)
如果我理解正确,您希望在您的建议中不调用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
}
}
另一种方法是使用@NándorElődFekete回答中描述的cflow
切入点,但是你应该注意,在这种情况下,堆栈中建议执行的所有beforeFindOneByIdAdvice
连接点也将被排除。所以这对你来说会有一些意想不到的结果。
您可以在“Aspect oriented programming - what is 'cflow'?”答案中了解cflow
的工作原理。