From fa21dffcf8f8500b0712a998740b63a17e14ac05 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 29 Oct 2024 23:01:16 +0100 Subject: [PATCH] Restore traditional AspectJ behavior through "spring.aop.ajc.ignore=true" Closes gh-33704 --- .../modules/ROOT/pages/appendix.adoc | 14 +++++--- .../AbstractAspectJAdvisorFactory.java | 32 ++++++++++++++++++- 2 files changed, 40 insertions(+), 6 deletions(-) diff --git a/framework-docs/modules/ROOT/pages/appendix.adoc b/framework-docs/modules/ROOT/pages/appendix.adoc index eb046cc2754..896453d8e91 100644 --- a/framework-docs/modules/ROOT/pages/appendix.adoc +++ b/framework-docs/modules/ROOT/pages/appendix.adoc @@ -23,6 +23,12 @@ The following table lists all currently supported Spring properties. |=== | Name | Description +| `spring.aop.ajc.ignore` +| Instructs Spring to ignore ajc-compiled aspects for Spring AOP proxying, restoring traditional +Spring behavior for scenarios where both weaving and AspectJ auto-proxying are enabled. See +{spring-framework-api}++/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.html#IGNORE_AJC_PROPERTY_NAME++[`AbstractAspectJAdvisorFactory`] +for details. + | `spring.aot.enabled` | Indicates the application should run with AOT generated artifacts. See xref:core/aot.adoc[Ahead of Time Optimizations] and @@ -32,7 +38,7 @@ for details. | `spring.beaninfo.ignore` | Instructs Spring to use the `Introspector.IGNORE_ALL_BEANINFO` mode when calling the JavaBeans `Introspector`. See -{spring-framework-api}++/beans/StandardBeanInfoFactory.html#IGNORE_BEANINFO_PROPERTY_NAME++[`CachedIntrospectionResults`] +{spring-framework-api}++/beans/StandardBeanInfoFactory.html#IGNORE_BEANINFO_PROPERTY_NAME++[`StandardBeanInfoFactory`] for details. | `spring.cache.reactivestreams.ignore` @@ -49,15 +55,13 @@ for details. | `spring.context.checkpoint` | Property that specifies a common context checkpoint. See -xref:integration/checkpoint-restore.adoc#_automatic_checkpointrestore_at_startup[Automatic -checkpoint/restore at startup] and +xref:integration/checkpoint-restore.adoc#_automatic_checkpointrestore_at_startup[Automatic checkpoint/restore at startup] and {spring-framework-api}++/context/support/DefaultLifecycleProcessor.html#CHECKPOINT_PROPERTY_NAME++[`DefaultLifecycleProcessor`] for details. | `spring.context.exit` | Property for terminating the JVM when the context reaches a specific phase. See -xref:integration/checkpoint-restore.adoc#_automatic_checkpointrestore_at_startup[Automatic -checkpoint/restore at startup] and +xref:integration/checkpoint-restore.adoc#_automatic_checkpointrestore_at_startup[Automatic checkpoint/restore at startup] and {spring-framework-api}++/context/support/DefaultLifecycleProcessor.html#EXIT_PROPERTY_NAME++[`DefaultLifecycleProcessor`] for details. diff --git a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java index 6f0eef82070..118d2325451 100644 --- a/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java +++ b/spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AbstractAspectJAdvisorFactory.java @@ -18,6 +18,7 @@ package org.springframework.aop.aspectj.annotation; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Map; import java.util.StringTokenizer; @@ -37,6 +38,7 @@ import org.aspectj.lang.reflect.PerClauseKind; import org.springframework.aop.framework.AopConfigException; import org.springframework.core.ParameterNameDiscoverer; +import org.springframework.core.SpringProperties; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.lang.Nullable; @@ -58,6 +60,23 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac private static final Class[] ASPECTJ_ANNOTATION_CLASSES = new Class[] { Pointcut.class, Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class}; + private static final String AJC_MAGIC = "ajc$"; + + /** + * System property that instructs Spring to ignore ajc-compiled aspects + * for Spring AOP proxying, restoring traditional Spring behavior for + * scenarios where both weaving and AspectJ auto-proxying are enabled. + *

The default is "false". Consider switching this to "true" if you + * encounter double execution of your aspects in a given build setup. + * Note that we recommend restructuring your AspectJ configuration to + * avoid such double exposure of an AspectJ aspect to begin with. + * @since 6.1.15 + */ + public static final String IGNORE_AJC_PROPERTY_NAME = "spring.aop.ajc.ignore"; + + private static final boolean shouldIgnoreAjcCompiledAspects = + SpringProperties.getFlag(IGNORE_AJC_PROPERTY_NAME); + /** Logger available to subclasses. */ protected final Log logger = LogFactory.getLog(getClass()); @@ -67,7 +86,8 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac @Override public boolean isAspect(Class clazz) { - return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null); + return (AnnotationUtils.findAnnotation(clazz, Aspect.class) != null && + (!shouldIgnoreAjcCompiledAspects || !compiledByAjc(clazz))); } @Override @@ -114,6 +134,16 @@ public abstract class AbstractAspectJAdvisorFactory implements AspectJAdvisorFac } } + private static boolean compiledByAjc(Class clazz) { + for (Field field : clazz.getDeclaredFields()) { + System.out.println(clazz + ": " + field.getName()); + if (field.getName().startsWith(AJC_MAGIC)) { + return true; + } + } + return false; + } + /** * Enum for AspectJ annotation types.