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.

Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Spring Full-Stack Practical Cases
Master Spring’s ObjectFactory & FactoryBean: Real-World Usage

1 Interface Comparison

ObjectFactory is a simple functional interface:

@FunctionalInterface
public interface ObjectFactory<T> {
    T getObject() throws BeansException;
}

FactoryBean is a factory bean interface used to customize object creation:

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();
}

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:

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
    }
}

The getSingleton method 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:

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));
        }
    }
}
ServletWebServerApplicationContext

further registers web scopes and resolvable dependencies for Request, Response, Session, and WebRequest, all implemented as ObjectFactory:

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();
    }
}

2.3 Custom ObjectFactory

When multiple beans of the same type exist, Spring throws NoUniqueBeanDefinitionException. Besides @Primary and @Qualifier, you can resolve it by registering a resolvable dependency.

Example of a custom BeanFactoryPostProcessor that registers a specific bean:

@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"));
    }
}

Custom ObjectFactory implementation:

public class AccountObjectFactory implements ObjectFactory<AccountDAO> {
    @Override
    public AccountDAO getObject() throws BeansException {
        return new AccountBDAO();
    }
}
// Register in BeanFactoryPostProcessor
beanFactory.registerResolvableDependency(AccountDAO.class, new AccountObjectFactory());

During bean property population, DefaultListableBeanFactory searches for candidates, including those registered via registerResolvableDependency. If the registered object is an ObjectFactory, its getObject() method is invoked to obtain the actual bean.

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;
}

End of article.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaBackend Developmentspringdependency-injectionFactoryBeanObjectFactory
Spring Full-Stack Practical Cases
Written by

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.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.