spring-projects/spring-framework · error · IllegalArgumentException

Specified class [ ] must be an interface

Error message

Specified class [{ifc.getName()}] must be an interface

What it means

Thrown by DefaultIntroductionAdvisor.addInterface (line 100-103) when the supplied Class is not actually an interface (ifc.isInterface() returns false). Introductions work by making the proxy implement additional interfaces, so passing a concrete class or enum type is a misuse and rejected with an IllegalArgumentException naming the offending class.

Solutions

  1. Pass an interface type: addInterface(Foo.class) where Foo is an interface, not FooImpl.
  2. If the type is a class, extract or define an interface it should implement and introduce that interface.
  3. Double-check the class literal is the interface reference, not the implementation class.

Example fix

// before
advisor.addInterface(MyServiceImpl.class); // concrete class

// after
advisor.addInterface(MyService.class); // interface
Defensive patterns

Strategy: type-guard

Validate before calling

if (!ifc.isInterface()) {
    throw new IllegalArgumentException(ifc.getName() + " is not an interface");
}
advisor.addInterface(ifc);

Type guard

static boolean isIntroductionInterface(Class<?> c) {
    return c != null && c.isInterface();
}

Prevention

When it happens

Trigger: Calling addInterface(SomeConcreteClass.class), or constructing DefaultIntroductionAdvisor with a Class argument that is a class rather than an interface. Programmatically adding an introduction with a class reference where an interface was expected.

Common situations: Passing an implementation class instead of its interface (e.g. FooImpl.class instead of Foo.class). Copy-paste from a place that used a class type. Generic Class<?> parameter hiding the interface requirement until runtime.

Related errors


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

Appendix: 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 69bf83ad71)