spring-projects/spring-framework · error · IllegalArgumentException

Aspect class [{aspectClass.getName()}] does not define a sin

Error message

Aspect class [{aspectClass.getName()}] does not define a singleton aspect

What it means

Thrown as an IllegalArgumentException from AspectJProxyFactory.addAspect(Object aspectInstance). This overload requires the supplied aspect INSTANCE to be a singleton aspect (per-clause kind SINGLETON), because Spring reuses that single instance across all advised targets. If the aspect class declares perthis/pertarget/pertypewithin/percflow semantics, a single shared instance is semantically invalid, so Spring rejects it.

Source

Thrown at spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectJProxyFactory.java:96

	 */
	public AspectJProxyFactory(Class<?>... interfaces) {
		setInterfaces(interfaces);
	}


	/**
	 * Add the supplied aspect instance to the chain. The type of the aspect instance
	 * supplied must be a singleton aspect. True singleton lifecycle is not honored when
	 * using this method - the caller is responsible for managing the lifecycle of any
	 * aspects added in this way.
	 * @param aspectInstance the AspectJ aspect instance
	 */
	public void addAspect(Object aspectInstance) {
		Class<?> aspectClass = aspectInstance.getClass();
		String aspectName = aspectClass.getName();
		AspectMetadata am = createAspectMetadata(aspectClass, aspectName);
		if (am.getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON) {
			throw new IllegalArgumentException(
					"Aspect class [" + aspectClass.getName() + "] does not define a singleton aspect");
		}
		addAdvisorsFromAspectInstanceFactory(
				new SingletonMetadataAwareAspectInstanceFactory(aspectInstance, aspectName));
	}

	/**
	 * Add an aspect of the supplied type to the end of the advice chain.
	 * @param aspectClass the AspectJ aspect class
	 */
	public void addAspect(Class<?> aspectClass) {
		String aspectName = aspectClass.getName();
		AspectMetadata am = createAspectMetadata(aspectClass, aspectName);
		MetadataAwareAspectInstanceFactory instanceFactory = createAspectInstanceFactory(am, aspectClass, aspectName);
		addAdvisorsFromAspectInstanceFactory(instanceFactory);
	}

View on GitHub (pinned to 69bf83ad71)

Solutions

  1. Use the addAspect(Class) overload instead, which supports non-singleton aspects by creating independent instances via a factory.
  2. Re-annotate the aspect as a plain @Aspect (singleton) if per-object state is not actually required.
  3. Manage per-object state inside the singleton aspect using ThreadLocal or a keyed map rather than the perthis/pertarget model.

Example fix

// before
@Aspect("pertarget(execution(* com.example.Target.*(..)))")
public class PerTargetAspect { ... }

AspectJProxyFactory factory = new AspectJProxyFactory(target);
factory.addAspect(new PerTargetAspect()); // throws

// after
AspectJProxyFactory factory = new AspectJProxyFactory(target);
factory.addAspect(PerTargetAspect.class); // uses class-based overload
Defensive patterns

Strategy: validation

Validate before calling

import org.aspectj.lang.reflect.AjTypeSystem;
import org.aspectj.lang.reflect.PerClauseKind;

public static boolean isSingletonAspect(Class<?> aspectClass) {
    return AjTypeSystem.getAjType(aspectClass).getPerClause().getKind() == PerClauseKind.SINGLETON;
}

// before addAspect(instance):
if (!isSingletonAspect(instance.getClass())) {
    // use the class-based overload instead
    factory.addAspect(instance.getClass());
} else {
    factory.addAspect(instance);
}

Type guard

import org.aspectj.lang.reflect.AjTypeSystem;
import org.aspectj.lang.reflect.PerClauseKind;

public static boolean canAddAsInstance(Object aspectInstance) {
    return AjTypeSystem.getAjType(aspectInstance.getClass())
            .getPerClause().getKind() == PerClauseKind.SINGLETON;
}

Try / catch

try {
    factory.addAspect(aspectInstance);
} catch (IllegalArgumentException ex) {
    if (ex.getMessage().contains("does not define a singleton aspect")) {
        factory.addAspect(aspectInstance.getClass()); // fallback to class-based
    } else throw ex;
}

Prevention

When it happens

Trigger: Calling new AspectJProxyFactory(target).addAspect(myAspectInstance) where myAspectInstance's class is annotated @Aspect("perthis(...)"), @Aspect("pertarget(...)"), @Aspect("pertypewithin(...)"), or any non-singleton per-clause.

Common situations: Passing a pre-built aspect instance to AspectJProxyFactory when the aspect was designed for per-object instantiation; mixing programmatic proxy factory usage with aspects intended for container-managed prototype scope.

Related errors


AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09). Data as JSON: /api/errors/7e645d2ce9ff3311. Report an issue: GitHub.