Repository navigation
Add a bean lookup API that returns null when no bean is found #37618
Description
Current behavior
BeanFactory provides getBean() overloads to look up beans by name or type. They throw NoSuchBeanDefinitionException when no matching bean is found.
I propose adding findBean() methods that return null when no matching bean is found. The existing non-null contract of getBean() would remain unchanged.
Motivation
When a bean is optional, callers have to handle the case where it is missing. This leads to repeated code even when the caller only needs to assign the result to a field or return it.
Spring itself uses both of the following patterns.
Catching NoSuchBeanDefinitionException
RequestMappingHandlerAdapter.initLocaleResolver() looks up a bean by name and returns null if no bean is found.
private @Nullable LocaleResolver initLocaleResolver() {
if (getBeanFactory() != null) {
try {
return getBeanFactory().getBean(
DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class);
}
catch (NoSuchBeanDefinitionException ignored) {
}
}
return null;
}Calling containsBean() before getBean()
The RequestContext constructor checks whether the bean exists before assigning it to a field. If the bean is missing, the field retains its default value of null.
if (this.webApplicationContext.containsBean(RequestContextUtils.REQUEST_DATA_VALUE_PROCESSOR_BEAN_NAME)) {
this.requestDataValueProcessor = this.webApplicationContext.getBean(
RequestContextUtils.REQUEST_DATA_VALUE_PROCESSOR_BEAN_NAME, RequestDataValueProcessor.class);
}Both examples look up an optional bean by name and use null when it is missing. A dedicated API would express this intent directly and reduce boilerplate at call sites.
Proposed API
I propose adding a findBean() counterpart for each existing getBean() overload. These could be default methods on BeanFactory or static methods on BeanFactoryUtils. The signatures and call-site examples below use instance methods for illustration:
// null: if no bean with the given name exists.
// BeansException: if the bean cannot be retrieved.
@Nullable Object findBean(String name);
// null: if no bean with the given name exists.
// BeanNotOfRequiredTypeException: if the bean is not of the required type.
// BeansException: if the bean cannot be created.
<T> @Nullable T findBean(String name, Class<T> requiredType);
// null: if no bean with the given name exists.
// BeanNotOfRequiredTypeException: if the bean is not of the required type.
// BeansException: if the bean cannot be created.
<T> @Nullable T findBean(String name, ParameterizedTypeReference<T> typeReference);
// null: if no bean with the given name exists.
// BeanDefinitionStoreException: if explicit arguments are supplied for a non-prototype bean.
// BeansException: if the bean cannot be created.
@Nullable Object findBean(String name, @Nullable Object @Nullable ... args);
// null: if no bean of the required type exists.
// NoUniqueBeanDefinitionException: if multiple beans match and no unique candidate can be selected.
// BeansException: if the bean cannot be created.
<T> @Nullable T findBean(Class<T> requiredType);
// null: if no bean of the required type exists.
// BeanDefinitionStoreException: if explicit arguments are supplied for a non-prototype bean.
// BeansException: if the bean cannot be created.
<T> @Nullable T findBean(Class<T> requiredType, @Nullable Object @Nullable ... args);If added to BeanFactory, these could be default methods to avoid requiring changes to existing implementations. For example, the overload that accepts a name and a type could be implemented as follows:
default <T> @Nullable T findBean(String name, Class<T> requiredType) {
return (containsBean(name) ? getBean(name, requiredType) : null);
}This implementation propagates any exceptions thrown by getBean() after the existence check. The check and lookup are not atomic: another thread could register or remove a bean between the two calls.
If added to BeanFactoryUtils instead, the methods would accept a BeanFactory as their first argument. For example:
BeanFactoryUtils.findBean(beanFactory, beanName, requiredType);Code that could be simplified
| Location | Proposed simplification |
|---|---|
RequestContext constructor |
Replace the containsBean() check and getBean() call with a direct assignment from findBean(). |
AbstractView.getRequestDataValueProcessor() |
Return the findBean() result directly after checking that the application context is not null. |
RequestMappingHandlerAdapter.initLocaleResolver() |
Return the findBean() result directly after checking that the bean factory is not null. |
DispatcherServlet.initMultipartResolver() |
Replace the separate assignments for a resolved bean and a missing bean with this.multipartResolver = context.findBean(...). |
WebHttpHandlerBuilder.applicationContext() |
Use findBean() for the four optional lookups: WebSessionManager, ServerCodecConfigurer, LocaleContextResolver, and ForwardedHeaderTransformer. |
MultipartFilter.lookupMultipartResolver() |
Replace containsBean(beanName) followed by getBean(beanName, MultipartResolver.class) with a single findBean() call. |
RedirectView.updateTargetUrl() |
Replace the existence check and lookup for the processor with findBean(). |
AutoProxyUtils.applyDefaultProxyConfig() |
Replace containsBean() followed by getBean() with findBean() and a null check on the result. |
For example, RequestContext could assign the result directly to the field:
this.requestDataValueProcessor = this.webApplicationContext.findBean(
RequestContextUtils.REQUEST_DATA_VALUE_PROCESSOR_BEAN_NAME,
RequestDataValueProcessor.class);RequestMappingHandlerAdapter.initLocaleResolver() could return the result directly, keeping only the null check for the bean factory:
private @Nullable LocaleResolver initLocaleResolver() {
BeanFactory beanFactory = getBeanFactory();
return (beanFactory != null ?
beanFactory.findBean(DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME, LocaleResolver.class) : null);
}Feedback
Would this API be a useful addition? I would appreciate advice on whether it belongs on BeanFactory as default methods or on BeanFactoryUtils as static utility methods. I would also welcome feedback on whether it should cover all the getBean() overloads listed above.
getType() returns null when the type cannot be determined, but throws an exception when the bean is missing. Would it make sense to add findType() counterparts for its two overloads that return null in both cases?
If you agree with this direction, I would be happy to submit a PR with the implementation and tests.
Activity
[-]Add a `BeanFactory` API that returns null when no bean is found[/-][+]Add a bean lookup API that returns null when no bean is found[/+]