Master Spring’s ObjectFactory & FactoryBean: Real-World Usage
This article explains the differences between Spring’s ObjectFactory and FactoryBean interfaces, demonstrates how they are used internally for bean creation, dependency injection, and servlet API injection, and shows how to customize ObjectFactory behavior to resolve bean conflicts and inject specific implementations.
1 Interface Comparison
ObjectFactory is a simple functional interface:
<code>@FunctionalInterface
public interface ObjectFactory<T> {
T getObject() throws BeansException;
}
</code>FactoryBean is a factory bean interface used to customize object creation:
<code>public interface FactoryBean<T> {
// Return the actual object
T getObject() throws Exception;
// Return the object type
Class<?> getObjectType();
// Whether it is a singleton; if true the created object is cached
boolean isSingleton();
}
</code>Note: If a class implements FactoryBean and you want to obtain the bean itself, prefix the bean name with '&'.
When a property is of type ObjectFactory or ObjectProvider, Spring injects a DependencyObjectProvider that creates the object only when
getObject()is called.
2 Practical Applications
2.1 Creating Bean Instances
ObjectFactory is widely used in Spring source code, e.g., in
AbstractBeanFactory:
<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)
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
// Explicitly remove instance from singleton cache...
destroySingleton(beanName);
throw ex;
}
});
beanInstance = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
// other code
}
}
</code>The
getSingletonmethod creates beans according to different scopes (singleton, prototype, request, session) using an ObjectFactory.
2.2 Servlet API Injection
In controllers, Request and Response objects are injected via ObjectFactory interfaces.
The context instantiated at container startup is
AnnotationConfigServletWebServerApplicationContext, which calls
postProcessBeanFactory:
<code>public class AnnotationConfigServletWebServerApplicationContext {
protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
super.postProcessBeanFactory(beanFactory);
if (this.basePackages != null && this.basePackages.length > 0) {
this.scanner.scan(this.basePackages);
}
if (!this.annotatedClasses.isEmpty()) {
this.reader.register(ClassUtils.toClassArray(this.annotatedClasses));
}
}
}
</code> ServletWebServerApplicationContextfurther registers web scopes and resolvable dependencies for Request, Response, Session, and WebRequest, all implemented as ObjectFactory:
<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>2.3 Custom ObjectFactory
When multiple beans of the same type exist, Spring throws
NoUniqueBeanDefinitionException. Besides
@Primaryand
@Qualifier, you can resolve it by registering a resolvable dependency.
Example of a custom
BeanFactoryPostProcessorthat registers a specific bean:
<code>@Component
public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// beanFactory is DefaultListableBeanFactory; it maintains a map of resolvableDependencies
beanFactory.registerResolvableDependency(AccountDAO.class, beanFactory.getBean("accountBDAO"));
}
}
</code>Custom
ObjectFactoryimplementation:
<code>public class AccountObjectFactory implements ObjectFactory<AccountDAO> {
@Override
public AccountDAO getObject() throws BeansException {
return new AccountBDAO();
}
}
// Register in BeanFactoryPostProcessor
beanFactory.registerResolvableDependency(AccountDAO.class, new AccountObjectFactory());
</code>During bean property population,
DefaultListableBeanFactorysearches for candidates, including those registered via
registerResolvableDependency. If the registered object is an
ObjectFactory, its
getObject()method is invoked to obtain the actual bean.
<code>protected 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 autowiringValue = entry.getValue();
autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
if (requiredType.isInstance(autowiringValue)) {
result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
break;
}
}
}
// ... add other candidates
return result;
}
// AutowireUtils.resolveAutowiringValue
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 {
return factory.getObject();
}
}
return autowiringValue;
}
</code>End of article.
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.