我的工作是:
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必须抛出异常,让我们说异常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方法似乎是多余的。但是,为了logging和audit的目的,我需要这样做,并创建C类型的例外。它必须由某个@ControllerAdvice来处理
问题是当其他对delete/update/save方法的建议被执行时(记住,它们也调用和执行findOneById方法),因此不必要地执行了my findOneByIdPointcut。
我需要更改切入点声明,以表示如下所示:
@Pointcut(Alpha)
public void findOneByIdPointcut(String id){}其中Alpha是:
为@Service的findOneById方法执行“前”/“环绕”通知,但是如果它的调用是从
PersonaServiceAspect类。
我在!execution和!within组合中尝试过许多方法,但没有结果。
甚至--当我创建了只截取all @Service's方法及其各自唯一的@Around建议的one Pointcut,并且通过ProceedingJoinPoint proceedingJoinPoint参数,我能够检查调用了什么方法,然后执行相应的控件。但这种行为再次发生。
这意味着,通过以下方式:
@Around("PersonaServicePointcut.anyMethodPointcut()")
public Object aroundAdviceAnyMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{其中anyMethodPointcut是execution(* mypackage.PersonaServiceImpl.*(..))
是否有可能实现这一方法?多么?
谢谢。
发布于 2016-10-03 12:13:45
可以将切入点排除在切入点表达式的控制流中。
!cflow(<pointcut>)在您的示例中,您希望排除执行在您自己的通知的控制流中的findOneById的那些执行。如果您的建议被应用于切入点表达式
@Pointcut("saveOnePointcut() || updateOnePointcut() || deleteOnePointcut()")
public void combinedPointcut() {}你可以通过以下方式排除这一点:
!cflow(combinedPointcut())或者简单地从所有通知执行的控制流中排除所有控制流,您可以使用:
!cflow(adviceexecution())根据组合的切入点表达式,您的find建议如下所示:
@Around("findOneByIdPointcut() && !cflow(combinedPointcut())")
public void aroundFind() {
....
}请注意,不能在示例中组合切入点表达式,因为它们是绑定切入点参数的。您需要删除&& args(...)部件,并将它们直接移动到建议切入点表达式。不能将绑定切入点表达式(如args(paramName) )与|| (或)运算符组合在一起,因此需要为这些情况创建单独的建议。如果愿意,仍然可以将这些建议中的大部分工作委托给单个方法。请参阅此类委托here的示例。
发布于 2016-10-04 02:58:54
如果我正确理解,您希望在您的通知中不调用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切入点,如@ndorőd Fekete答案中所述,但您应该注意,在这种情况下,堆栈中通知执行下面的所有beforeFindOneByIdAdvice连接点也将被排除在外。因此,这可能会有一些意想不到的结果,你在未来。
您可以看到cflow在"Aspect oriented programming - what is 'cflow'?“答案中是如何工作的。
https://stackoverflow.com/questions/39821938
复制相似问题