spring-projects/spring-framework · error · AopConfigException

DynamicIntroductionAdvice may only be added as part of Intro

Error message

DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor

What it means

Thrown by AdvisedSupport.addAdvice(int, Advice) when the supplied Advice is a DynamicIntroductionAdvice but does not implement IntroductionInfo. Dynamic introductions (which add new interfaces/behavior to a target) require an IntroductionAdvisor to describe the interfaces being introduced, because the framework cannot infer them from a bare DynamicIntroductionAdvice. Spring routes self-describing IntroductionInfo instances through a DefaultIntroductionAdvisor automatically, but rejects bare DynamicIntroductionAdvice to avoid creating an incomplete introduction.

Source

Thrown at spring-aop/src/main/java/org/springframework/aop/framework/AdvisedSupport.java:444

	public void addAdvice(Advice advice) throws AopConfigException {
		int pos = this.advisors.size();
		addAdvice(pos, advice);
	}

	/**
	 * Cannot add introductions this way unless the advice implements IntroductionInfo.
	 */
	@Override
	public void addAdvice(int pos, Advice advice) throws AopConfigException {
		Assert.notNull(advice, "Advice must not be null");
		if (advice instanceof IntroductionInfo introductionInfo) {
			// We don't need an IntroductionAdvisor for this kind of introduction:
			// It's fully self-describing.
			addAdvisor(pos, new DefaultIntroductionAdvisor(advice, introductionInfo));
		}
		else if (advice instanceof DynamicIntroductionAdvice) {
			// We need an IntroductionAdvisor for this kind of introduction.
			throw new AopConfigException("DynamicIntroductionAdvice may only be added as part of IntroductionAdvisor");
		}
		else {
			addAdvisor(pos, new DefaultPointcutAdvisor(advice));
		}
	}

	@Override
	public boolean removeAdvice(Advice advice) throws AopConfigException {
		int index = indexOf(advice);
		if (index == -1) {
			return false;
		}
		else {
			removeAdvisor(index);
			return true;
		}
	}

View on GitHub (pinned to e8729d0438)

Solutions

  1. Wrap the DynamicIntroductionAdvice in a DefaultIntroductionAdvisor: new DefaultIntroductionAdvisor(advice, IntroductionInfo-implementation).
  2. Have your advice class implement IntroductionInfo (or DelegatingIntroductionInterceptor) so addAdvice can auto-wrap it.
  3. Use addAdvisor(new DefaultIntroductionAdvisor(...)) directly instead of addAdvice for introductions.

Example fix

// before
DynamicIntroductionAdvice myIntro = ...;  // no IntroductionInfo
advised.addAdvice(myIntro);  // throws

// after
import org.springframework.aop.support.DefaultIntroductionAdvisor;
advised.addAdvisor(new DefaultIntroductionAdvisor(myIntro, MyInterface.class));
Defensive patterns

Strategy: type-guard

Type guard

static boolean needsIntroductionInfo(Advice advice) {
  return advice instanceof org.springframework.aop.DynamicIntroductionAdvice
      && !(advice instanceof org.springframework.aop.IntroductionInfo);
}

Prevention

When it happens

Trigger: Passing a custom DynamicIntroductionAdvice implementation that implements neither IntroductionInfo nor is wrapped in an IntroductionAdvisor; using an old advice type that mixes introduction semantics without metadata.

Common situations: Authoring a custom mixin/introduction advice and forgetting to implement IntroductionInfo or to wrap it in DefaultIntroductionAdvisor; porting advice from frameworks that bundle introduction differently.

Related errors


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