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

  1. Run the bean inside a standard ApplicationContext (ClassPathXmlApplicationContext, GenericApplicationContext, AnnotationConfigApplicationContext) - their factories are ListableBeanFactory implementations.
  2. If constructing the factory manually, use DefaultListableBeanFactory instead of a non-listable implementation.
  3. 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

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


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)