spring-projects/spring-framework · error · IllegalArgumentException
Specified class [{ifc.getName()}] must be an interface
Error message
Specified class [{ifc.getName()}] must be an interface What it means
DefaultIntroductionAdvisor.addInterface throws IllegalArgumentException when the Class passed is not an interface. Spring AOP introductions can only add interfaces to a proxy (the proxy then implements them and dispatches to the advice); introducing a concrete class has no meaning and is rejected.
Source
Thrown at spring-aop/src/main/java/org/springframework/aop/support/DefaultIntroductionAdvisor.java:102
* Create a DefaultIntroductionAdvisor for the given advice.
* @param advice the Advice to apply
* @param ifc the interface to introduce
*/
public DefaultIntroductionAdvisor(DynamicIntroductionAdvice advice, Class<?> ifc) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
addInterface(ifc);
}
/**
* Add the specified interface to the list of interfaces to introduce.
* @param ifc the interface to introduce
*/
public void addInterface(Class<?> ifc) {
Assert.notNull(ifc, "Interface must not be null");
if (!ifc.isInterface()) {
throw new IllegalArgumentException("Specified class [" + ifc.getName() + "] must be an interface");
}
this.interfaces.add(ifc);
}
@Override
public Class<?>[] getInterfaces() {
return ClassUtils.toClassArray(this.interfaces);
}
@Override
public void validateInterfaces() throws IllegalArgumentException {
for (Class<?> ifc : this.interfaces) {
if (this.advice instanceof DynamicIntroductionAdvice dynamicIntroductionAdvice &&
!dynamicIntroductionAdvice.implementsInterface(ifc)) {
throw new IllegalArgumentException("DynamicIntroductionAdvice [" + this.advice + "] " +
"does not implement interface [" + ifc.getName() + "] specified for introduction");
}
}View on GitHub (pinned to e8729d0438)
Solutions
- Pass the interface type (e.g. Lockable.class), not the implementation class.
- Guard with Class.isInterface() before calling addInterface.
- Use DelegatePerTargetObjectIntroductionInterceptor(implType, interfaceType) keeping the two distinct.
Example fix
// before advisor.addInterface(LockableImpl.class); // concrete class -> error 117 // after advisor.addInterface(Lockable.class); // the interface
Defensive patterns
Strategy: type-guard
Validate before calling
if (!ifc.isInterface()) {
throw new IllegalArgumentException(ifc.getName() + " is not an interface");
} Type guard
boolean isIntroductionInterface(Class<?> ifc) {
return ifc != null && ifc.isInterface();
} Prevention
- Always pass the interface, not its implementation, to addInterface.
- Keep DelegatePerTargetObjectIntroductionInterceptor's defaultImplType/interfaceType distinct.
- Add a type guard in any code that wires introductions dynamically.
When it happens
Trigger: Calling advisor.addInterface(MyImpl.class) or new DefaultIntroductionAdvisor(advice, MyImpl.class) where MyImpl is a concrete class rather than an interface.
Common situations: Confusing the mixin implementation class with the interface it implements; passing defaultImplType where interfaceType is expected; refactoring an interface into a class.
Related errors
- DynamicIntroductionAdvice [{advice}] does not implement inte
- 'defaultImpl' attribute must be set on DeclareParents
- DynamicIntroductionAdvice may only be added as part of Intro
- IntroductionInfo defines no interfaces to introduce: {introd
- Cannot create default implementation for '{interfaceType.get
AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04).
Data as JSON: /data/errors/f979ee65dba2eb13.json.
Report an issue: GitHub.