spring-projects/spring-framework · critical · FatalBeanException
ServiceLocatorFactoryBean needs to run in a BeanFactory…
Error message
ServiceLocatorFactoryBean needs to run in a BeanFactory that is a ListableBeanFactory
What it means
Thrown by ServiceLocatorFactoryBean.setBeanFactory() as a FatalBeanException when the enclosing BeanFactory does not implement ListableBeanFactory. ServiceLocatorFactoryBean resolves beans by type (getBean(Class)) when no id is passed, which requires the factory to support listing/iteration over bean definitions - a capability only ListableBeanFactory provides. A plain BeanFactory (e.g. a minimal programmatic DefaultListableBeanFactory subset) cannot satisfy this.
Solutions
- Run the bean inside a standard ApplicationContext (ClassPathXmlApplicationContext, GenericApplicationContext, AnnotationConfigApplicationContext) - their factories are ListableBeanFactory implementations.
- If constructing the factory manually, use DefaultListableBeanFactory instead of a non-listable implementation.
- Ensure no custom BeanFactory wrapper hides the ListableBeanFactory interface from the instanceof check.
Example fix
// before (broken): custom non-listable factory BeanFactory bf = new MyCustomBeanFactory(); new ServiceLocatorFactoryBean().setBeanFactory(bf); // throws // after DefaultListableBeanFactory bf = new DefaultListableBeanFactory(); new ServiceLocatorFactoryBean().setBeanFactory(bf);
Defensive patterns
Strategy: type-guard
Validate before calling
if (!(beanFactory instanceof ListableBeanFactory)) {
throw new IllegalStateException(
"Use a DefaultListableBeanFactory-backed context for ServiceLocatorFactoryBean");
}
fb.setBeanFactory(beanFactory); Type guard
static boolean isListable(BeanFactory bf) {
return bf instanceof ListableBeanFactory;
} Try / catch
try {
fb.setBeanFactory(beanFactory);
} catch (FatalBeanException ex) {
if (ex.getMessage().contains("ListableBeanFactory")) {
// wrap beanFactory in a DefaultListableBeanFactory-backed GenericApplicationContext
}
throw ex;
} Prevention
- Always run ServiceLocatorFactoryBean inside a standard ApplicationContext.
- When building factories manually, default to DefaultListableBeanFactory.
- Avoid custom BeanFactory wrappers that hide the ListableBeanFactory interface.
When it happens
Trigger: Registering a ServiceLocatorFactoryBean in a context whose BeanFactory is not ListableBeanFactory (rare; most application contexts - ClassPathXmlApplicationContext, AnnotationConfigApplicationContext - use DefaultListableBeanFactory which IS Listable). Constructing an ad-hoc BeanFactory implementation. Certain test harnesses or embedded Spring usage with a stripped factory.
Common situations: Programmatically creating a custom BeanFactory and adding a ServiceLocatorFactoryBean. Using a SimpleJndiBeanFactory or similar non-listable factory. Edge cases in framework testing utilities.
Related errors
- Property 'serviceLocatorInterface' is required
- CustomAutowireConfigurer needs to operate on a…
- Neither 'targetObject' nor 'targetBeanName' specified, and…
- 'propertyPath' is required
- Service locator exception
AI-assisted analysis of spring-projects/spring-framework@69bf83ad71 (2026-08-09).
Data as JSON: /api/errors/fc06fd9cbe67522c.
Report an issue: GitHub.
Appendix: source
Thrown at spring-beans/src/main/java/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.java:250
/**
* Set mappings between service ids (passed into the service locator)
* and bean names (in the bean factory). Service ids that are not defined
* here will be treated as bean names as-is.
* <p>The empty string as service id key defines the mapping for {@code null} and
* empty string, and for factory methods without parameter. If not defined,
* a single matching bean will be retrieved from the bean factory.
* @param serviceMappings mappings between service ids and bean names,
* with service ids as keys as bean names as values
*/
public void setServiceMappings(Properties serviceMappings) {
this.serviceMappings = serviceMappings;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (!(beanFactory instanceof ListableBeanFactory lbf)) {
throw new FatalBeanException(
"ServiceLocatorFactoryBean needs to run in a BeanFactory that is a ListableBeanFactory");
}
this.beanFactory = lbf;
}
@Override
public void afterPropertiesSet() {
if (this.serviceLocatorInterface == null) {
throw new IllegalArgumentException("Property 'serviceLocatorInterface' is required");
}
// Create service locator proxy.
this.proxy = Proxy.newProxyInstance(
this.serviceLocatorInterface.getClassLoader(),
new Class<?>[] {this.serviceLocatorInterface},
new ServiceLocatorInvocationHandler());
}
View on GitHub (pinned to 69bf83ad71)