spring-projects/spring-framework · error · UnsupportedOperationException
An advice method can never be a constructor
Error message
An advice method can never be a constructor
What it means
Thrown by AspectJAdviceParameterNameDiscoverer.getParameterNames(Constructor) (line 288-291) when raiseExceptions is true and the discoverer is asked for the parameter names of a Constructor rather than a Method. Advice in Spring is always a Method, never a Constructor; the discoverer is only meaningful for methods, so it refuses the constructor call (returning null normally, throwing when raiseExceptions).
Solutions
- Do not use AspectJAdviceParameterNameDiscoverer for constructor parameter discovery — use a plain DefaultParameterNameDiscoverer or StandardReflectionParameterNameDiscoverer.
- Keep raiseExceptions=false (default) if the discoverer participates in a general chain.
- Separate the discoverer instance used for advice binding from the one used for general reflection.
Example fix
// before — advice discoverer (raiseExceptions=true) reused for constructors DefaultParameterNameDiscoverer d = new DefaultParameterNameDiscoverer(); AspectJAdviceParameterNameDiscoverer aj = new AspectJAdviceParameterNameDiscoverer(expr); aj.setRaiseExceptions(true); d.addDiscoverer(aj); String[] names = d.getParameterNames(MyClass.class.getConstructors()[0]); // throws // after — separate discoverer for constructor reflection DefaultParameterNameDiscoverer ctorDiscoverer = new DefaultParameterNameDiscoverer(); String[] names = ctorDiscoverer.getParameterNames(MyClass.class.getConstructors()[0]);
Defensive patterns
Strategy: validation
Validate before calling
import java.lang.reflect.Constructor;
import org.springframework.aop.aspectj.AspectJAdviceParameterNameDiscoverer;
// Do not route constructor queries through an advice discoverer with raiseExceptions=true.
String[] safeGetCtorParamNames(Constructor<?> ctor,
AspectJAdviceParameterNameDiscoverer adviceDiscoverer) {
if (adviceDiscoverer != null) {
// use a separate plain discoverer for constructors
return new org.springframework.core.DefaultParameterNameDiscoverer().getParameterNames(ctor);
}
return adviceDiscoverer.getParameterNames(ctor);
} Type guard
boolean isMethodDiscoveryTarget(Class<?> member) {
return member == java.lang.reflect.Method.class;
} Try / catch
try {
String[] names = discoverer.getParameterNames(constructor);
} catch (UnsupportedOperationException ex) {
if (ex.getMessage().contains("advice method can never be a constructor")) {
// switch to a plain DefaultParameterNameDiscoverer for constructors
} else { throw ex; }
} Prevention
- Use a dedicated DefaultParameterNameDiscoverer for general constructor reflection.
- Keep the advice discoverer (raiseExceptions=true) scoped to advice binding only.
- Never set raiseExceptions=true on a discoverer reused for non-advice purposes.
When it happens
Trigger: A ParameterNameDiscoverer chain that includes AspectJAdviceParameterNameDiscoverer is invoked with a Constructor — e.g. DefaultParameterNameDiscoverer.getParameterNames(ctor) where the advice discoverer is in the chain and raiseExceptions was set to true. This is almost always a misconfiguration or misuse rather than a real aspect scenario.
Common situations: Reusing an AspectJAdviceParameterNameDiscoverer instance (or a DefaultParameterNameDiscoverer that has one added) for general constructor parameter-name discovery, while raiseExceptions=true. Should not occur in normal aspect processing.
Related errors
- Binding of throwing parameter
- Binding of throwing parameter
- Failed to bind all argument names
- Failed to invoke aspect constructor
- Not enough arguments in method to satisfy binding of…
AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09).
Data as JSON: /api/errors/c70309fe063963dd.
Report an issue: GitHub.
Appendix: source
Thrown at spring-aop/src/main/java/org/springframework/aop/aspectj/AspectJAdviceParameterNameDiscoverer.java:290
this.numberOfRemainingUnboundArguments + " argument(s) could not be bound");
}
else {
// convention for failing is to return null, allowing participation in a chain of responsibility
return null;
}
}
}
/**
* An advice method can never be a constructor in Spring.
* @return {@code null}
* @throws UnsupportedOperationException if
* {@link #setRaiseExceptions(boolean) raiseExceptions} has been set to {@code true}
*/
@Override
public String @Nullable [] getParameterNames(Constructor<?> ctor) {
if (this.raiseExceptions) {
throw new UnsupportedOperationException("An advice method can never be a constructor");
}
else {
// we return null rather than throw an exception so that we behave well
// in a chain-of-responsibility.
return null;
}
}
private void bindParameterName(int index, @Nullable String name) {
this.parameterNameBindings[index] = name;
this.numberOfRemainingUnboundArguments--;
}
/**
* If the first parameter is of type JoinPoint or ProceedingJoinPoint, bind "thisJoinPoint" as
* parameter name and return true, else return false.
*/View on GitHub (pinned to 69bf83ad71)