Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Spring Bean Lifecycle: Understanding FactoryBean Resolution and Smart Initialization

Tech May 10 2

FactoryBean Handling in Spring's Bean Instantiation

Spring's getBean() method includes dedicated logic for FactoryBean instances. A FactoryBean is a special bean that produces an object via its getObject() method. When you call getBean() for a FactoryBean, Spring obtains the factory bean itself first and then invokes getObject() to retrieve the actual managed instance. If you need the raw FactoryBean rather than its product, prefix the bean name with &, for example &myFactoryBean.

After Spring parses all eligible bean definitions into merged bean definitions, the preInstantiateSingletons() method drives singleton creation. It determines whether a bean is a FactoryBean and triggers the appropriate instantiation path.

public void preInstantiateSingletons() throws BeansException {
    if (logger.isTraceEnabled()) {
        logger.trace("Pre-instantiating singletons in " + this);
    }

    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

    for (String beanName : beanNames) {
        RootBeanDefinition mergedDef = getMergedLocalBeanDefinition(beanName);

        if (!mergedDef.isAbstract() && mergedDef.isSingleton() && !mergedDef.isLazyInit()) {
            if (isFactoryBean(beanName)) {
                Object factoryInstance = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (factoryInstance instanceof FactoryBean) {
                    FactoryBean<?> factory = (FactoryBean<?>) factoryInstance;
                    boolean eager;
                    if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
                        eager = AccessController.doPrivileged(
                                (PrivilegedAction<Boolean>) ((SmartFactoryBean<?>) factory)::isEagerInit,
                                getAccessControlContext());
                    } else {
                        eager = (factory instanceof SmartFactoryBean &&
                                ((SmartFactoryBean<?>) factory).isEagerInit());
                    }
                    if (eager) {
                        getBean(beanName);
                    }
                }
            } else {
                getBean(beanName);
            }
        }
    }

    for (String beanName : beanNames) {
        Object singleton = getSingleton(beanName);
        if (singleton instanceof SmartInitializingSingleton) {
            StartupStep step = this.getApplicationStartup().start("spring.beans.smart-initialize")
                    .tag("beanName", beanName);
            SmartInitializingSingleton smartBean = (SmartInitializingSingleton) singleton;
            if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                    smartBean.afterSingletonsInstantiated();
                    return null;
                }, getAccessControlContext());
            } else {
                smartBean.afterSingletonsInstantiated();
            }
            step.end();
        }
    }
}

The logic unfolds in three stages:

  1. For each bean name, the merged RootBeanDefinition is fetched.
  2. If the definition is concrete, singleton, non-lazy, a check with isFactoryBean decides whether to go through the FactoryBean path or the standard singleton path.
  3. After all non-lazy singletons are created, any bean implementing SmartInitializingSingleton has its afterSingletonsInstantiated() callback invoked automatically.

How isFactoryBean Determines the Bean Type

The isFactoryBean method is central to this decision. It first looks in the singleton cache, then falls back to the bean definition or delegates to a parent factory.

public boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException {
    String beanName = transformedBeanName(name);
    Object beanInstance = getSingleton(beanName, false);
    if (beanInstance != null) {
        return (beanInstance instanceof FactoryBean);
    }
    if (!containsBeanDefinition(beanName) && getParentBeanFactory() instanceof ConfigurableBeanFactory) {
        return ((ConfigurableBeanFactory) getParentBeanFactory()).isFactoryBean(name);
    }
    return isFactoryBean(beanName, getMergedLocalBeanDefinition(beanName));
}

Key steps:

  1. transformedBeanName strips the & prefix, returning the plain bean name for singleton lookup.
  2. getSingleton checks the singleton pool. If an instance exists and is a FactoryBean, it returns true immediately.
  3. If the current factory lacks a definition but has a parent that is a ConfigurableBeanFactory, the check is delegated to the parent. This supports hierarchical contexts like:
AnnotationConfigApplicationContext parentCtx = new AnnotationConfigApplicationContext();
parentCtx.register(AppConfig.class);
parentCtx.refresh();

AnnotationConfigApplicationContext childCtx = new AnnotationConfigApplicationContext();
childCtx.setParent(parentCtx);
childCtx.register(AppConfig1.class);
childCtx.refresh();

UserService service = childCtx.getBean(UserService.class);
service.test();

  1. If no isntance exists, the merged bean definition is examined:
protected boolean isFactoryBean(String beanName, RootBeanDefinition mbd) {
    Boolean result = mbd.isFactoryBean;
    if (result == null) {
        Class<?> beanType = predictBeanType(beanName, mbd, FactoryBean.class);
        result = (beanType != null && FactoryBean.class.isAssignableFrom(beanType));
        mbd.isFactoryBean = result;
    }
    return result;
}

Spring predicts the bean type from the definition's beanClass property and checks if it implements FactoryBean. The outcome is cached in the root definition for subsequent calls.

Eager Initialization with SmartFactoryBean

After confirming a bean is a FactoryBean, Spring determines whether to eagerly invoke getObject() by inspecting the SmartFactoryBean extension. SmartFactoryBean adds an isEagerInit() method. If it returns true, the actual object is created during container startup rather than lazily on first access.

@Component
public class UserFactory implements SmartFactoryBean {
    @Override
    public Object getObject() throws Exception {
        return new User();
    }

    @Override
    public Class<?> getObjectType() {
        return User.class;
    }

    @Override
    public boolean isEagerInit() {
        return true;
    }
}

When eager initialization is requested, preInstantiateSingletons calls getBean(beanName) immediately after obtaining the factory bean, causing the getObject() result to be stored in the singleton cache. Otherwise, the factory bean itself is stored, and getObject() is deferred until the bean is actually retrieved.

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.