spring-projects/spring-framework · error · IllegalStateException

BeanFactory must be set on {getClass().getSimpleName()} to a

Error message

BeanFactory must be set on {getClass().getSimpleName()} to access qualified executor '{qualifier}'

What it means

Thrown by findQualifiedExecutor when an async method carries an executor qualifier (e.g. @Async("customExecutor")) but the aspect's BeanFactory was never injected. Resolving a qualified executor requires a BeanFactory to look the bean up by type+qualifier; without one the lookup cannot proceed. The class name in the message is the concrete aspect subclass (AnnotationAsyncExecutionInterceptor, an AspectJ aspect, etc.).

Source

Thrown at spring-aop/src/main/java/org/springframework/aop/interceptor/AsyncExecutionAspectSupport.java:214

	 * been specified and that the {@linkplain #setExecutor(Executor) default executor}
	 * should be used.
	 * @param method the method to inspect for executor qualifier metadata
	 * @return the qualifier if specified, otherwise empty String or {@code null}
	 * @see #determineAsyncExecutor(Method)
	 * @see #findQualifiedExecutor(BeanFactory, String)
	 */
	protected abstract @Nullable String getExecutorQualifier(Method method);

	/**
	 * Retrieve a target executor for the given qualifier.
	 * @param qualifier the qualifier to resolve
	 * @return the target executor, or {@code null} if none available
	 * @since 4.2.6
	 * @see #getExecutorQualifier(Method)
	 */
	protected @Nullable Executor findQualifiedExecutor(@Nullable BeanFactory beanFactory, String qualifier) {
		if (beanFactory == null) {
			throw new IllegalStateException("BeanFactory must be set on " + getClass().getSimpleName() +
					" to access qualified executor '" + qualifier + "'");
		}
		return BeanFactoryAnnotationUtils.qualifiedBeanOfType(beanFactory, Executor.class, qualifier);
	}

	/**
	 * Retrieve or build a default executor for this advice instance.
	 * <p>An executor returned from here will be cached for further use.
	 * <p>The default implementation searches for a unique {@link TaskExecutor} bean
	 * in the context, or for an {@link Executor} bean named "taskExecutor" otherwise.
	 * If neither of the two is resolvable, this implementation will return {@code null}.
	 * @param beanFactory the BeanFactory to use for a default executor lookup
	 * @return the default executor, or {@code null} if none available
	 * @since 4.2.6
	 * @see #findQualifiedExecutor(BeanFactory, String)
	 * @see #DEFAULT_TASK_EXECUTOR_BEAN_NAME
	 */
	protected @Nullable Executor getDefaultExecutor(@Nullable BeanFactory beanFactory) {

View on GitHub (pinned to e8729d0438)

Solutions

  1. Register the async aspect/interceptor as a Spring bean so the container invokes setBeanFactory automatically (e.g. via @EnableAsync or <task:annotation-driven/>).
  2. If constructing programmatically, call aspect.setBeanFactory(applicationContext) explicitly before any async invocation.
  3. Drop the qualifier from @Async so the default-executor path (which does not need a BeanFactory) is used, or pass a concrete Executor to setExecutor().

Example fix

// before
AsyncExecutionInterceptor aspect = new AsyncExecutionInterceptor();
advisor = new DefaultPointcutAdvisor(pointcut, aspect);
// beanFactory never set -> error 100 on @Async("poolA")

// after
AsyncExecutionInterceptor aspect = new AsyncExecutionInterceptor();
aspect.setBeanFactory(applicationContext);
advisor = new DefaultPointcutAdvisor(pointcut, aspect);
Defensive patterns

Strategy: validation

Validate before calling

// before relying on a qualified executor, confirm the aspect has a BeanFactory
if (aspect instanceof BeanFactoryAware && applicationContext != null) {
    aspect.setBeanFactory(applicationContext);
}
// or, when scanning for @Async qualifiers, ensure the owning bean is Spring-managed:
boolean managed = applicationContext.containsBeanDefinition(beanName);

Prevention

When it happens

Trigger: Invoking an @Async method whose qualifier is non-empty while the AsyncExecutionAspectSupport instance was built programmatically and never had setBeanFactory(BeanFactory) called. Also fires when an AnnotationAsyncExecutionAspect (AspectJ) is woven but not registered as a Spring bean.

Common situations: Manually new-ing an AsyncExecutionInterceptor/Aspect instead of letting <task:annotation-driven/> or @EnableAsync wire it; using AspectJ load-time weaving for @Async without the Spring container managing the aspect; qualifier typo that still resolves to a non-empty string.

Related errors


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