问题
I am working with:
Spring Framework
4.3.3AspectJ
1.8.9
I have the following normal process:
@Controller
->@Service
->@Repository
I have the following pair about AOP
:
PersonaServicePointcut
PersonaServiceAspect
The scenario is the following:
The @Service
class has some methods such as: delete
, save
, update
and findOneById
. They are declared together within the same class.
For the methods such as delete
and update
through AOP
I am using a @Before
or @Around
advice to call the findOneById
method.
The reason is that does not make sense execute the delete
or update
methods (consider Rest scenario) if the entity does not exist. Therefore through that advice
an exception must be throw, lets say Exception A, it must be handled in a @ControllerAdvice
Practically the same approach for the save
method has been applied. Therefore before to execute the save
method other @Before
or @Around
advice is executed calling the findOneById
method again. If the entity already exists an exception must be thrown, lets say Exception B, it must be handled in a @ControllerAdvice
Observe I have 3 points/3advices that use the findOneById
method. It to check if exists an entity or not.
For example:
@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){}
Again: these 3 advices use or execute the findOneById
method.
The problem is when I add a new pointcut such as:
@Pointcut(value="execution(*
mypackage.PersonaServiceImpl.findOneById(String))
&& args(id)")
public void findOneByIdPointcut(String id){}
I have created this pointcut to check if an entity already exists or not, if it does not exist it must throw an exception of type C
(it for the classic 404).
Seems redundant execute the findOneById
method through a @Before
or @Around
advice for the findOneById
method itself. But I need this to logging
and audit
purposes and to create the exception of type C too. It must be handle by some @ControllerAdvice
The problem is when the others advices for the delete/update/save
methods are executed (remember they call and execute the findOneById
method too) my findOneByIdPointcut
is executed unnecessarily.
I need change the pointcut declaration to indicate something like this:
@Pointcut(Alpha)
public void findOneByIdPointcut(String id){}
Where Alpha
is:
execute the before/around advice for the @Service
's findOneById
method, but never if its call has been done from the others advices from thePersonaServiceAspect
class.
I've tried many ways with !execution
and !within
combinations, but no results.
Even when I have created just one Pointcut that intercepts all the @Service
's methods with its respective unique @Around
advice, and through the ProceedingJoinPoint proceedingJoinPoint
parameter I am able to check what method has been called and then do the respective controls. But again this behaviour happens.
It means, through the following:
@Around("PersonaServicePointcut.anyMethodPointcut()")
public Object aroundAdviceAnyMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
where anyMethodPointcut
is execution(* mypackage.PersonaServiceImpl.*(..))
Is possible accomplish this approach? How?
Thanks.
回答1:
You can exclude pointcuts within the control flow of a pointcut expression with
!cflow(<pointcut>)
In your example, you want to exclude those executions of the findOneById
where the execution is inside the control flow of your own advice. If your advice is applied to the pointcut expression
@Pointcut("saveOnePointcut() || updateOnePointcut() || deleteOnePointcut()")
public void combinedPointcut() {}
you can exclude that with:
!cflow(combinedPointcut())
or to simply exclude all control flows from within the control flow of all advice executions, you could use:
!cflow(adviceexecution())
Your around find advice would look like this, based on the combined pointcut expression:
@Around("findOneByIdPointcut() && !cflow(combinedPointcut())")
public void aroundFind() {
....
}
Note that you can't combine the pointcut expressions in your example because they're binding pointcut arguments. You need to remove the && args(...)
parts and move them to the advice pointcut expressions directly. You can't combine binding pointcut expressions like args(paramName)
with the ||
(or) operator so you'll need to create separate advices for those cases. You can still delegate most of the work from those advices to a single method if you want to. See an example of this kind of delegation here.
回答2:
If I understand correctly you want the findOneByIdPointcut
is not invoked inside you advice but should triggered in all other cases.
One way to do this is to use call
in combination with within
pointcuts. call
will move join point up to caller method and within
will exclude advice from target join points.
So your aspect code may look like this:
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
}
}
Another way is to use cflow
pointcut like described in @Nándor Előd Fekete answer, but you should note that in this case all beforeFindOneByIdAdvice
join points below advice execution in the stack will be excluded too. So this can have some unexpected results for you in the future.
You can see how cflow
works in "Aspect oriented programming - what is 'cflow'?" answer.
来源:https://stackoverflow.com/questions/39821938/spring-aop-how-exclude-an-unnecessary-pointcut-around-advice-execution-due