FactoryBean Interface for Spring Extension Points

Keywords: Java Spring

Preface

Let's first look at the interface definition.

public interface FactoryBean<T> {

    /**
     * Return object instance
     */
    @Nullable
    T getObject() throws Exception;

    /**
     * Returns the object type.
     */
    @Nullable
    Class<?> getObjectType();

    /**
     * Is the object of the factory management a singleton?
     */
    default boolean isSingleton() {
        return true;
    }

}

As can be seen from the interface definition, the beans that implement this interface are not the main functions, and the objects created by getObject() are the key points. So here we can guess that you could use FactoryBean to create some beans with more complex instantiation processes.

Registration of FactoryBean

The processing logic of FactoryBean is in the AbstractBeanFactory.doGetBean method

protected <T> T doGetBean(
            final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
            throws BeansException {
        //Get the bean name
        final String beanName = transformedBeanName(name);
        Object bean;
              //Omit part of content
            //Here is the processing of FactoryBean, which will be expanded below.
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
             //Omit part of content

        return (T) bean;
    }

Take a look at the specific logic. Here we need to notice that Spring has a hidden rule for bean name, and all bean names at the beginning are FactoryBean by default.

protected Object getObjectForBeanInstance(
        Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

      // If bean Name starts with factory reference
    if (BeanFactoryUtils.isFactoryDereference(name)) {
        if (beanInstance instanceof NullBean) {
            return beanInstance;
        }
        // If the name starts with & and the bean Instance is not a FactoryBean type, an exception is thrown
        if (!(beanInstance instanceof FactoryBean)) {
            throw new BeanIsNotAFactoryException(transformedBeanName(name), beanInstance.getClass());
        }
    }

    // If the bean Instance is not of FactoryBean type, return the bean Instance directly
    // Or the name starts with & and returns directly to the bean Instance, indicating that we want to get an instance of FactoryBean
    if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) {
        return beanInstance;
    }

    Object object = null;
    if (mbd == null) {
        object = getCachedObjectForFactoryBean(beanName);
    }
    if (object == null) {
        // At this point, the bean Instance is a FactoryBean type, and the name does not start with &; this is the case with our s amp le project, and it is also the most common and frequently used case.
        // Strongly convert bean Instance to FactoryBean type
        FactoryBean<?> factory = (FactoryBean<?>) beanInstance;
        // Get the instance objects we need from the cache
        if (mbd == null && containsBeanDefinition(beanName)) {
            mbd = getMergedLocalBeanDefinition(beanName);
        }
        boolean synthetic = (mbd != null && mbd.isSynthetic());
        // Calling the getObject method of FactoryBean to create the instance objects we need
        object = getObjectFromFactoryBean(factory, beanName, !synthetic);
    }
    return object;
}

protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) {
    //Processing for singletons
    if (factory.isSingleton() && containsSingleton(beanName)) {
        synchronized (getSingletonMutex()) {
            Object object = this.factoryBeanObjectCache.get(beanName);
            if (object == null) {
                //Get it through factory.getObject
                object = doGetObjectFromFactoryBean(factory, beanName);
                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
                if (alreadyThere != null) {
                    object = alreadyThere;
                }
                else {
                    if (shouldPostProcess) {
                        if (isSingletonCurrentlyInCreation(beanName)) {
                            // Temporarily return non-post-processed object, not storing it yet..
                            return object;
                        }
                        beforeSingletonCreation(beanName);
                        try {
                            object = postProcessObjectFromFactoryBean(object, beanName);
                        }
                        catch (Throwable ex) {
                            throw new BeanCreationException(beanName,
                                    "Post-processing of FactoryBean's singleton object failed", ex);
                        }
                        finally {
                            afterSingletonCreation(beanName);
                        }
                    }
                    if (containsSingleton(beanName)) {
                        //Store the acquired object in factoryBeanObjectCache singleton cache map
                        this.factoryBeanObjectCache.put(beanName, object);
                    }
                }
            }
            return object;
        }
    }
    else {
        //Non-singleton processing, obtained directly through factory.getObejct, and then returned to the user
        Object object = doGetObjectFromFactoryBean(factory, beanName);
        if (shouldPostProcess) {
            try {
                object = postProcessObjectFromFactoryBean(object, beanName);
            }
            catch (Throwable ex) {
                throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex);
            }
        }
        return object;
    }
}

Method of generating bean object:

private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
      throws BeanCreationException {

   Object object;
   try {
      if (System.getSecurityManager() != null) {
         AccessControlContext acc = getAccessControlContext();
         try {
            object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
         }
         catch (PrivilegedActionException pae) {
            throw pae.getException();
         }
      }
      else {
         object = factory.getObject();//Generated object
      }
   }
   catch (FactoryBeanNotInitializedException ex) {
      throw new BeanCurrentlyInCreationException(beanName, ex.toString());
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
   }

   // Do not accept a null value for a FactoryBean that's not fully
   // initialized yet: Many FactoryBeans just return null then.
   if (object == null) {
      if (isSingletonCurrentlyInCreation(beanName)) {
         throw new BeanCurrentlyInCreationException(
               beanName, "FactoryBean which is currently in creation returned null from getObject");
      }
      object = new NullBean();
   }
   return object;}

Implementation of Spring

There are many bean s that implement this interface in Spring, but what we are most familiar with and most importantly is that I mentioned in my previous article that ProxyFactoryBean is the key point to realize AOP technology. Let's review it briefly.

public Object getObject() throws BeansException {
        initializeAdvisorChain();
        if (isSingleton()) {
            return getSingletonInstance();
        }
        else {
            if (this.targetName == null) {
                logger.warn("Using non-singleton proxies with singleton targets is often undesirable. " +
                        "Enable prototype proxies by setting the 'targetName' property.");
            }
            return newPrototypeInstance();
        }
    }
private synchronized Object getSingletonInstance() {
        if (this.singletonInstance == null) {
            this.targetSource = freshTargetSource();
            if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
                // Rely on AOP infrastructure to tell us what interfaces to proxy.
                Class<?> targetClass = getTargetClass();
                if (targetClass == null) {
                    throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
                }
                setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
            }
            // Initialize the shared singleton instance.
            super.setFrozen(this.freezeProxy);
            this.singletonInstance = getProxy(createAopProxy());
        }
        return this.singletonInstance;
    }

Posted by ewillms on Wed, 02 Oct 2019 17:10:03 -0700