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

  1. Pass the interface type (e.g. Lockable.class), not the implementation class.
  2. Guard with Class.isInterface() before calling addInterface.
  3. 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

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


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