Affects: 6.1.7-6.1.13


This might be a special case of #32970, except it's not fixed yet. We have both aspectj-maven-plugin and @Aspect.

Demo code (short): - double-aspect.tar.gz - mvn test will run a failing test. - mvn test -Dspring-framework.version=6.1.6 will pass the test.

We have this pattern where we use an Aspect interceptor to replace exceptions thrown by some library with application-specific exceptions. This worked fine until Spring Framework 6.1.7, and since then, the interceptor is triggered (weaved?) twice. The interceptor doesn't expect the exception it's provided, so it throws an exception nobody expects (In the demo, it just wraps the exception with a new one, and the test checks for getCause().getMessage()).

Comment From: jhoeller

The ajc-compiled check is only reliable for aspect classes, not for target classes being weaved. We'll try to revisit this but I'm not sure this will ever be 100% reliable since we are checking for internal ajc markers there. Recent AspectJ recommendations for reusable aspects do not differentiate between the usage model anymore, suggesting ajc-pre-compiled aspects to be usable everywhere, which triggered our change where Spring AOP does not exclude ajc-compiled aspects for proxying anymore.

Conceptually, the root of this problem is the exposure of the aspect class as a Spring bean. With Spring auto-proxying being active (which it is by default in Boot), Spring AOP will by design consider any such aspect bean for regular AOP proxying. If an aspect is exclusively meant to be used with ajc compilation (as performed by the AspectJ Maven plugin), you either need to turn off Boot's auto-proxying auto-configuration - or not expose the AspectJ-managed aspect as a Spring bean to begin with.

With ajc, an aspect is an ajc-managed singleton instance anyway, not a real Spring bean with a Spring-managed lifecycle. So I assume that the only reason for bean exposure is autowiring your interceptor. Consider revising your interceptor setup along the following lines, with the interceptor setting itself into the ajc-managed aspect instance, and no @Bean AspectInjector method at all anymore. This avoids the mixture of models and lifecycles and will work reliably with any Spring version:

    @Bean
    public Interceptor interceptor() {
        Interceptor interceptor = new Interceptor();
        Aspects.aspectOf(AspectInjector.class).interceptor = interceptor;
        return interceptor;
    }

Comment From: jhoeller

On review, there isn't much we can do to prevent this. Hard-ignoring compiled AspectJ aspects would be a much worse outcome - since a simple build change following AspectJ recommendations for an existing aspect could suddenly make it ignored in Spring AOP - which is why we had to do this. Imagine the effect of remaining unnoticed for access control aspects etc. Overall, sorry for the impact mid-line but strategically it was unavoidable to get rid of this age-old awkward ignoring that we had there before.

As stated above, please configure your AspectJ-managed aspect instance directly rather than exposing it as a Spring bean if it is only meant to be used with AspectJ weaving. Once you expose it as a Spring-managed bean, Spring AOP may consider it for auto-proxying. The latter is an opt-in feature in the core Spring Framework but for better or for worse it is auto-configured in Spring Boot when AspectJ is present on the classpath. You can turn off that particular AOP auto-config in Boot but I'd rather recommend not exposing such an AspectJ aspect as a bean to begin with, as shown above, which leads to correct behavior in all scenarios.

Comment From: jhoeller

On reconsideration, we can quite easily provide a "spring.aop.ajc.ignore=true" property to set as a JVM system property or in a spring.properties file in the root of the classpath, similar to other such properties that exist in various places already (see https://docs.spring.io/spring-framework/reference/appendix.html). I'll reopen this issue for such an escape hatch.

Comment From: jhoeller

I've tested the approach above, and it works fine with the provided sample application: Simply add a spring.properties file (with the content spring.aop.ajc.ignore=true as a single line) next to application.properties, and the test passes fine without any further changes then. We'll ship this in Spring Framework 6.1.15 in mid November; it will be available in the next 6.1.15-SNAPSHOT for early testing.

Comment From: aviv-amdocs

Great, thanks!

Please also port this flag to the 6.0 branch.