spring-projects/spring-framework · error · AopConfigException

PerClause {ajType.getPerClause().getKind()} not supported by

Error message

PerClause {ajType.getPerClause().getKind()} not supported by Spring AOP for {aspectClass}

What it means

Thrown as an AopConfigException from the AspectMetadata constructor's switch default branch when the per-clause kind is not SINGLETON, PERTARGET, PERTHIS, or PERTYPEWITHIN. The remaining AspectJ per-clause kinds are PERCFLOW and PERCFLOWBELOW, which Spring AOP cannot implement with proxies. This is the metadata-layer companion to the AbstractAspectJAdvisorFactory.validate() checks (errors 41/42).

Source

Thrown at spring-aop/src/main/java/org/springframework/aop/aspectj/annotation/AspectMetadata.java:118

		this.aspectClass = ajType.getJavaClass();
		this.ajType = ajType;

		switch (this.ajType.getPerClause().getKind()) {
			case SINGLETON -> {
				this.perClausePointcut = Pointcut.TRUE;
			}
			case PERTARGET, PERTHIS -> {
				AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut();
				ajexp.setLocation(aspectClass.getName());
				ajexp.setExpression(findPerClause(aspectClass));
				ajexp.setPointcutDeclarationScope(aspectClass);
				this.perClausePointcut = ajexp;
			}
			case PERTYPEWITHIN -> {
				// Works with a type pattern
				this.perClausePointcut = new ComposablePointcut(new TypePatternClassFilter(findPerClause(aspectClass)));
			}
			default -> throw new AopConfigException(
					"PerClause " + ajType.getPerClause().getKind() + " not supported by Spring AOP for " + aspectClass);
		}
	}

	/**
	 * Extract contents from String of form {@code pertarget(contents)}.
	 */
	private String findPerClause(Class<?> aspectClass) {
		Aspect ann = aspectClass.getAnnotation(Aspect.class);
		if (ann == null) {
			return "";
		}
		String value = ann.value();
		int beginIndex = value.indexOf('(');
		if (beginIndex < 0) {
			return "";
		}
		return value.substring(beginIndex + 1, value.length() - 1);

View on GitHub (pinned to 69bf83ad71)

Solutions

  1. Switch the aspect to a singleton per-clause (plain @Aspect) or to perthis/pertarget/pertypewithin if Spring-supported per-object semantics are needed.
  2. Use AspectJ compile-time or load-time weaving for aspects that genuinely require percflow/percflowbelow.
  3. Simulate control-flow-scoped state with a ThreadLocal in a singleton aspect.

Example fix

// before
@Aspect("percflow(execution(* com.example.Service.*(..)))")
public class FlowScopedAspect { ... }
// after
@Aspect
public class FlowScopedAspect {
    private final ThreadLocal<Integer> depth = ThreadLocal.withInitial(() -> 0);
    @Around("execution(* com.example.Service.*(..))")
    public Object track(ProceedingJoinPoint pjp) throws Throwable { ... }
}
Defensive patterns

Strategy: validation

Validate before calling

import org.aspectj.lang.reflect.AjTypeSystem;
import org.aspectj.lang.reflect.PerClauseKind;
import java.util.EnumSet;

public static boolean isSupportedPerClause(Class<?> aspectClass) {
    var kind = AjTypeSystem.getAjType(aspectClass).getPerClause().getKind();
    return EnumSet.of(PerClauseKind.SINGLETON, PerClauseKind.PERTARGET,
                      PerClauseKind.PERTHIS, PerClauseKind.PERTYPEWITHIN).contains(kind);
}

// before new AspectMetadata(clazz, name):
if (!isSupportedPerClause(clazz)) {
    throw new IllegalStateException("Aspect per-clause not supported by Spring AOP: " + clazz);
}

Type guard

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

public static boolean isSpringSupportedPerClause(Class<?> c) {
    var k = AjTypeSystem.getAjType(c).getPerClause().getKind();
    return k != PerClauseKind.PERCFLOW && k != PerClauseKind.PERCFLOWBELOW;
}

Try / catch

try {
    AspectMetadata md = new AspectMetadata(clazz, name);
} catch (AopConfigException ex) {
    if (ex.getMessage().contains("not supported by Spring AOP")) {
        // switch per-clause to singleton or use AspectJ weaving
    } else throw ex;
}

Prevention

When it happens

Trigger: Constructing AspectMetadata for a class annotated @Aspect("percflow(...)") or @Aspect("percflowbelow(...)"); indirectly when BeanFactoryAspectJAdvisorsBuilder or AspectJProxyFactory builds metadata for such a class.

Common situations: Using an AspectJ-native aspect with control-flow-scoped instantiation in a Spring AOP context; migrating from AspectJ weaving to Spring AOP without rewriting the per-clause.

Related errors


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