Search code examples
spring-bootaopaspectjspring-aop

Check in Spring AOP if method is called by another annotated method


I use in my Spring boot project aspect that is fired on every public method in annotated class:

@Aspect
@Component
public class DeletedAwareAspect {
    @Before("@within(com.example.DeleteAware)")
    public void aroundExecution(JoinPoint pjp) throws Throwable {
        //... some logic before
    }

    @After("@within(com.example.DeleteAware)")
    public void cleanUp(JoinPoint pjp) throws Throwable {
       //... some logic after
    }
}

Usage of that aspect is below:

@DeleteAware
@Service
public class MyService {
    public void foo() {}
}
@DeleteAware
@Service
public class MyAnotherService {
    @Autowired
    private MyService service;

    public void anotherFoo() {}

    public void VERY_IMPORTANT_METHOD() {
        service.foo();
    }
}

MyService.foo() and MyAnotherService.anotherFoo() works as expected. But here is the problem - if method wrapped by aspect is called by another aspected method (like VERY_IMPORTANT_METHOD()), I dont want to fire aspect twice, but only once. How to check from Aspect whether method is called inside another aspected method?


Solution

  • Like I said, if you would switch to native AspectJ you could use percflow() aspect instantiation and/or cflow() pointcuts, but I will not explain that here in detail because you are looking for a Spring AOP solution. Like R.G said, it is actually quite simple: Use a thread-local counter for the aspect nesting level and only do something (e.g. deleting something in the after advice) if the counter is zero.

    @Aspect
    @Component
    public class DeletedAwareAspect {
      private ThreadLocal<Integer> nestingLevel = ThreadLocal.withInitial(() -> 0);
    
      @Before("@within(com.example.DeleteAware)")
      public void aroundExecution(JoinPoint pjp) {
        int level = nestingLevel.get() + 1;
        nestingLevel.set(level);
        System.out.println("BEFORE " + pjp + " -> " + level);
      }
    
      @After("@within(com.example.DeleteAware)")
      public void cleanUp(JoinPoint pjp) {
        int level = nestingLevel.get() - 1;
        nestingLevel.set(level);
        System.out.println("AFTER " + pjp + " -> " + level);
        if (level == 0)
          System.out.println("Deleting something");
      }
    }