spring-projects/spring-framework · error · AopConfigException
Unknown advisor type {}; can only include Advisor or Advice
Error message
Unknown advisor type {}; can only include Advisor or Advice type beans in interceptorNames chain except for last entry which may also be target instance or TargetSource What it means
Thrown as AopConfigException (wrapping UnknownAdviceTypeException) by ProxyFactoryBean.namedBeanToAdvisor when an entry in interceptorNames points to a bean that is neither an Advice nor an Advisor, AND it is not allowed to be the target (i.e., targetName/targetSource was already set, or it is not the last entry). Spring expected every non-final name to be an Advice/Advisor.
Source
Thrown at spring-aop/src/main/java/org/springframework/aop/framework/ProxyFactoryBean.java:558
logger.debug("Refreshing target with name '" + this.targetName + "'");
}
Object target = this.beanFactory.getBean(this.targetName);
return (target instanceof TargetSource targetSource ? targetSource : new SingletonTargetSource(target));
}
}
/**
* Convert the following object sourced from calling getBean() on a name in the
* interceptorNames array to an Advisor or TargetSource.
*/
private Advisor namedBeanToAdvisor(Object next) {
try {
return this.advisorAdapterRegistry.wrap(next);
}
catch (UnknownAdviceTypeException ex) {
// We expected this to be an Advisor or Advice,
// but it wasn't. This is a configuration error.
throw new AopConfigException("Unknown advisor type " + next.getClass() +
"; can only include Advisor or Advice type beans in interceptorNames chain " +
"except for last entry which may also be target instance or TargetSource", ex);
}
}
/**
* Blow away and recache singleton on an advice change.
*/
@Override
protected void adviceChanged() {
super.adviceChanged();
if (this.singleton) {
logger.debug("Advice has changed; re-caching singleton instance");
synchronized (this) {
this.singletonInstance = null;
}
}
}View on GitHub (pinned to 69bf83ad71)
Solutions
- Move the target bean out of interceptorNames and use the targetName/target property instead.
- Ensure every entry in interceptorNames (except optionally the last) is a bean of type Advice or Advisor.
- If the last entry is meant to be the target, do NOT also set targetName/targetSource (the factory only allows one target style).
- Check bean type with beanFactory.getType(name) — it must be assignable to Advice or Advisor (or be the legitimate final target).
Example fix
<!-- before --> <bean id="p" class="...ProxyFactoryBean"> <property name="targetName" value="svc"/> <property name="interceptorNames"><list><value>svc</value><value>loggingAdvice</value></list></property> </bean> <!-- after --> <bean id="p" class="...ProxyFactoryBean"> <property name="targetName" value="svc"/> <property name="interceptorNames"><list><value>loggingAdvice</value></list></property> </bean>
Defensive patterns
Strategy: validation
Validate before calling
// Verify each non-final interceptor name is Advice/Advisor
BeanFactory bf = ...;
String[] names = pfb.getInterceptorNames();
for (int i = 0; i < names.length; i++) {
Class<?> t = bf.getType(names[i]);
boolean isAdvice = t != null && (Advice.class.isAssignableFrom(t) || Advisor.class.isAssignableFrom(t));
if (!isAdvice && i < names.length - 1) throw new IllegalStateException(names[i] + " is not Advice/Advisor");
} Type guard
static boolean isAdviceOrAdvisorBean(BeanFactory bf, String name) {
Class<?> t = bf.getType(name);
return t != null && (Advice.class.isAssignableFrom(t) || Advisor.class.isAssignableFrom(t));
} Try / catch
try { Object p = pfb.getObject(); }
catch (AopConfigException e) {
if (e.getMessage().contains("Unknown advisor type")) { /* move target out of interceptorNames */ }
throw e;
} Prevention
- Use targetName/target property for the target; reserve interceptorNames for Advice/Advisor beans.
- Run beanFactory.getType(name) assertions in a smoke test of AOP configuration.
When it happens
Trigger: interceptorNames references a plain service/POJO bean in a position that is not the final one, or the target is already set via targetName so even the last entry must be an Advice/Advisor. The advisorAdapterRegistry.wrap call at line 553 throws UnknownAdviceTypeException, rethrown here as AopConfigException with the explanatory message.
Common situations: Listing the target bean in the middle of interceptorNames by mistake; using a target bean name that is shared with another non-advice bean; renaming an advice bean and forgetting to update interceptorNames; mixing targetName property with a target-as-last-entry style (now the last entry is treated as advice and fails).
Related errors
- Cannot determine target class for proxy
- No BeanFactory available anymore (probably due to serializat
- Target required after globals
- Can only use global advisors or interceptors with a Listable
- No BeanFactory available anymore (probably due to serializat
AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09).
Data as JSON: /api/errors/12d09946289e287b.
Report an issue: GitHub.