spring-projects/spring-framework · error · IllegalArgumentException

Aspect class [{}] does not define a singleton aspect

Error message

Aspect class [{}] does not define a singleton aspect

What it means

Thrown by AspectJProxyFactory.addAspect(Object aspectInstance) when the supplied aspect instance's class does not declare a singleton per-clause (i.e. it is pertarget/perthis/pertypewithin/percflow/percflowbelow). addAspect(Object) requires a concrete singleton instance because the caller manages its lifecycle; non-singleton aspects must be added by class via addAspect(Class).

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 e8729d0438)

Solutions

  1. Switch to addAspect(MyAspect.class) so Spring creates per-target instances via a PrototypeAspectInstanceFactory.
  2. Change the aspect to singleton (remove the per-clause) if you want to keep passing an instance.
  3. Ensure the instance you pass is of a class annotated as a plain @Aspect (singleton).

Example fix

// before
factory.addAspect(new PerTargetAspect()); // @Aspect("pertarget(...)")
// after
factory.addAspect(PerTargetAspect.class);
Defensive patterns

Strategy: validation

Validate before calling

import org.aspectj.lang.reflect.*;

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

// before calling addAspect(instance):
if (!isSingletonAspect(instance.getClass())) {
  // use addAspect(Class) instead
  factory.addAspect(instance.getClass());
} else {
  factory.addAspect(instance);
}

Prevention

When it happens

Trigger: Calling new AspectJProxyFactory(target).addAspect(myAspectInstance) where myAspectInstance.getClass() is annotated @Aspect("pertarget(...)") or similar non-singleton per-clause.

Common situations: Passing a pre-built instance of a per-object aspect to AspectJProxyFactory. Confusing the two addAspect overloads — addAspect(Object) is singleton-only, addAspect(Class) supports non-singleton instantiation models.

Related errors


AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04). Data as JSON: /data/errors/3374365aab92c13e.json. Report an issue: GitHub.