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
- Switch the aspect to a singleton per-clause (plain @Aspect) or to perthis/pertarget/pertypewithin if Spring-supported per-object semantics are needed.
- Use AspectJ compile-time or load-time weaving for aspects that genuinely require percflow/percflowbelow.
- 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
- Restrict aspect per-clauses to singleton/perthis/pertarget/pertypewithin in Spring AOP modules.
- Route control-flow-scoped aspects to an AspectJ-weaving module.
- Add a startup check listing each aspect's per-clause kind.
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
- {aspectClass.getName()} uses percflow instantiation model: T
- {aspectClass.getName()} uses percflowbelow instantiation mod
- Bean with name '{beanName}' is a singleton, but aspect insta
- 'argumentNames' property of AbstractAspectJAdvice contains a
- Only afterReturning advice can be used to bind a return valu
AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09).
Data as JSON: /api/errors/1a1be605e345f924.
Report an issue: GitHub.