Mastering Spring’s ObjectFactory and FactoryBean: When and How to Use Them
This article explains the differences between Spring's ObjectFactory and FactoryBean interfaces, shows how they are applied in bean creation, servlet API injection, and custom implementations, and provides code examples and best‑practice tips for resolving bean ambiguities in a Spring backend.
1 Interface Comparison
ObjectFactory is a simple functional interface with a single method getObject() that may throw BeansException .
FactoryBean defines three methods:
<code>public interface FactoryBean<T> {
// Return the actual object
T getObject() throws Exception;
// Return the object type
Class<?> getObjectType();
// Whether the bean is a singleton; if true the created object is cached
boolean isSingleton();
}</code>The FactoryBean is used to customize object creation. When a bean implements FactoryBean , Spring first checks if the requested bean is a FactoryBean; if so, it uses getObjectType() to match the required type and then calls getObject() to obtain the real instance. To retrieve the FactoryBean itself, prepend an ampersand ( & ) to the bean name (e.g., ctx.getBean("&a") ).
When a dependency is declared as ObjectFactory or ObjectProvider , Spring injects a DependencyObjectProvider that only invokes getObject() when the bean is actually needed.
2 Practical Applications
2.1 Creating Bean Instances
Spring’s core bean factory uses ObjectFactory extensively. For example, in AbstractBeanFactory.doGetBean the singleton retrieval is performed with a lambda that implements ObjectFactory :
<code>public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
// ... other code ...
if (mbd.isSingleton()) {
// getSingleton’s second argument is an ObjectFactory (lambda here)
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
// Remove instance from singleton cache on failure
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// ... other code ...
}
}</code>The getSingleton method delegates the actual creation to the supplied ObjectFactory , allowing different scopes (singleton, prototype, request, session) to be handled uniformly.
2.2 Servlet API Injection
Controller methods can inject HttpServletRequest , HttpServletResponse , etc., via ObjectFactory . During container startup the context AnnotationConfigServletWebServerApplicationContext calls postProcessBeanFactory to register these factories:
<code>public class AnnotationConfigServletWebServerApplicationContext {
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.postProcessBeanFactory(beanFactory);
// register component scanning and annotated classes
}
}</code> <code>public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext {
@Override
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
beanFactory.addBeanPostProcessor(new WebApplicationContextServletContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(ServletContextAware.class);
registerWebApplicationScopes();
}
private void registerWebApplicationScopes() {
ExistingWebApplicationScopes existingScopes = new ExistingWebApplicationScopes(getBeanFactory());
WebApplicationContextUtils.registerWebApplicationScopes(getBeanFactory());
existingScopes.restore();
}
}</code>The utility class registers resolvable dependencies for request, response, session, and web request objects:
<code>public abstract class WebApplicationContextUtils {
public static void registerWebApplicationScopes(ConfigurableListableBeanFactory beanFactory, @Nullable ServletContext sc) {
beanFactory.registerScope(WebApplicationContext.SCOPE_REQUEST, new RequestScope());
beanFactory.registerScope(WebApplicationContext.SCOPE_SESSION, new SessionScope());
if (sc != null) {
ServletContextScope appScope = new ServletContextScope(sc);
beanFactory.registerScope(WebApplicationContext.SCOPE_APPLICATION, appScope);
sc.setAttribute(ServletContextScope.class.getName(), appScope);
}
beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
beanFactory.registerResolvableDependency(ServletResponse.class, new ResponseObjectFactory());
beanFactory.registerResolvableDependency(HttpSession.class, new SessionObjectFactory());
beanFactory.registerResolvableDependency(WebRequest.class, new WebRequestObjectFactory());
// additional JSF support omitted
}
}</code>All four factories implement ObjectFactory , and they obtain their objects from thread‑local contexts, ensuring thread‑safe injection in controllers.
Note: The purpose of registerResolvableDependency is to provide a ready‑made instance (e.g., the current request) whenever a bean requires it.
2.3 Custom ObjectFactory
When multiple beans implement the same interface, Spring throws NoUniqueBeanDefinitionException . Instead of using @Primary or @Qualifier , you can register a custom ObjectFactory for the required type:
<code>// Example of duplicate beans
public interface AccountDAO {}
@Component public class AccountADAO implements AccountDAO {}
@Component public class AccountBDAO implements AccountDAO {}
@RestController @RequestMapping("/accounts") public class AccountController {
@Resource private AccountDAO dao;
}</code>Spring fails to start because it finds two candidates. A custom BeanFactoryPostProcessor can register a specific instance to resolve the ambiguity:
<code>@Component
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// Register a specific bean for AccountDAO
beanFactory.registerResolvableDependency(AccountDAO.class, beanFactory.getBean("accountBDAO"));
}
}</code>Alternatively, you can provide your own ObjectFactory implementation:
<code>public class AccountObjectFactory implements ObjectFactory<AccountDAO> {
@Override
public AccountDAO getObject() throws BeansException {
return new AccountBDAO();
}
}
// In a post‑processor
beanFactory.registerResolvableDependency(AccountDAO.class, new AccountObjectFactory());
</code>During bean property population, DefaultListableBeanFactory.findAutowireCandidates checks the resolvableDependencies map; if the registered value is an ObjectFactory , Spring calls factory.getObject() to obtain the actual bean.
<code>public Map<String, Object> findAutowireCandidates(@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager());
Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
for (Map.Entry<Class<?>, Object> entry : this.resolvableDependencies.entrySet()) {
Class<?> autowiringType = entry.getKey();
if (autowiringType.isAssignableFrom(requiredType)) {
Object value = entry.getValue();
value = AutowireUtils.resolveAutowiringValue(value, requiredType);
if (requiredType.isInstance(value)) {
result.put(ObjectUtils.identityToString(value), value);
break;
}
}
}
// ... process regular candidate beans ...
return result;
}
public static Object resolveAutowiringValue(Object autowiringValue, Class<?> requiredType) {
if (autowiringValue instanceof ObjectFactory && !requiredType.isInstance(autowiringValue)) {
ObjectFactory<?> factory = (ObjectFactory<?>) autowiringValue;
if (autowiringValue instanceof Serializable && requiredType.isInterface()) {
return Proxy.newProxyInstance(requiredType.getClassLoader(), new Class<?>[]{requiredType}, new ObjectFactoryDelegatingInvocationHandler(factory));
} else {
// Invoke getObject to obtain the real instance
return factory.getObject();
}
}
return autowiringValue;
}
</code>These mechanisms give developers fine‑grained control over dependency resolution and bean creation.
Done!
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.