Spring AOP – Pointcut applying to method only when joinPoint.proceed is invoked from a lambda

I reproduced your problem and also compared a similar setup in plain Java + AspectJ (i.e. without Spring or Spring AOP, only using two Spring annotations used in aspect pointcuts). There the problem does not occur. It is something specific to Spring AOP, that much is sure.

Now Spring uses AspectJ’s pointcut matching in combination with its own AOP framework based on proxies and delegation. Somewhere in there this edge case must mess up the status of Spring aspect matching, causing the behaviour you see. I have not debugged into it so far, but from what I see now I would suggest to create an issue and see what the maintainers say about it.

Here is my AspectJ MCVE proving that the problem does not occur there. BTW, I had to rename the package aspect to aop because in AspectJ aspect is a reserved keyword. But I also renamed it in the Spring project in order to make sure it is unrelated to the problem at hand, and it is unrelated.

package aop.mcve;

import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {
  public void controllerMethod() {}
}
package aop.mcve;

import org.springframework.stereotype.Service;

@Service
public class MyService {
  public Object delegateTo(MyAspect.Callable<?> callable) throws Throwable {
    return callable.call();
  }

  public void serviceMethod() {}
}
package aop.mcve;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class MyAspect {
  private final MyService myService = new MyService();

  @Pointcut("within(aop.mcve..*) && !within(MyAspect) && execution(* *(..))")
  public void inDomain() {}

  @Pointcut("@target(org.springframework.stereotype.Service)")
  public void inService() {}

  @Pointcut("execution(* aop.mcve.MyService.*(..))")
  public void inMyService() {}

  @Pointcut("@target(org.springframework.web.bind.annotation.RestController)")
  public void inController() {}

  @Around("inDomain() && inController()")
  public Object aroundController(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("aroundController -> " + joinPoint);
    return myService.delegateTo(joinPoint::proceed);
  }

  @Around("inDomain() && inService() && !inMyService()")
  public Object aroundService(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("aroundService -> " + joinPoint);
    System.out.println("You should never see this message!");
    return joinPoint.proceed();
  }

  public interface Callable<T> {
    T call() throws Throwable;
  }
}
package aop.mcve;

public class AspectMcveApplication {
  public static void main(String[] args) throws Throwable {
    new MyService().serviceMethod();
    new MyController().controllerMethod();
  }
}

The console log:

aroundController -> execution(void aop.mcve.MyController.controllerMethod())

As you can see, the advice method aroundService(..) does not get triggered like in Spring AOP.


Update: I modified your MCVE in order to make it runnable with both Spring AOP and AspectJ, it automatically detects AspectJ’s load-time weaver when active. I sent you this pull request.

CLICK HERE to find out more related problems solutions.

Leave a Comment

Your email address will not be published.

Scroll to Top