1.FactoryBeandefine

Interface to be implemented by objects used within a {@link BeanFactory} which

* are themselves factories for individual objects. If a bean implements this

* interface, it is used as a factory for an object to expose, not directly as a

* bean instance that will be exposed itself

Copy the code

The Bean implementing this interface is used as a BeanFactory to generate a single object, exposing that the generated object is not the current Bean

2.FactoryBeanThe sample

2.1 defineBean

@Component

public class UserFactoryBean implements FactoryBean<User{

}

Copy the code

2.2 Specifying the Returned Object

@Override

public User getObject(a) throws Exception {

    return new User();

}

Copy the code

2.3 Specifying the return type

@Override

publicClass<? > getObjectType() {

    return User.class;

}

Copy the code

2.4 Complete Example

@Component

public class UserFactoryBean implements FactoryBean<User{



    @Override

    public User getObject(a) throws Exception {

        return new User();

    }



    @Override

    publicClass<? > getObjectType() {

        return User.class;

    }

}

Copy the code

3.BeanAnd exposed objects

Normally, to get a Bean object in a container, just use the beanfactory.getBean (” Bean name “) method to get the result. Take a look at the following example

beanFactory.getBean("userFactoryBean");

Copy the code

Console output:

User(id=null, name=null, age=null)

Copy the code

As you can see from the console, this is not what we expected. What if we want to get UserFactoryBean? Just add “&” before the Bean name

beanFactory.getBean("&userFactoryBean");

Copy the code

Console output:

com.boot.example.factory.UserFactoryBean@5273143e

Copy the code

3.1 Implementation Principle

3.1.1 Name Conversion

public static String transformedBeanName(String name) {

    Assert.notNull(name, "'name' must not be null");

    // If the Bean name does not begin with "&", return it

    if(! name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {

        return name;

    }

    // If the Bean name begins with "&", it is intercepted. For example, if the name of the Bean passed in is "&UserFactoryBean", the result will be "userFactoryBean".

    return transformedBeanNameCache.computeIfAbsent(name, beanName -> {

        do {

            beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());

        }

        while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));

        return beanName;

    });

}

Copy the code

3.1.2 Obtaining an Instance

// Get the instance from the container based on the Bean name

Object sharedInstance = getSingleton(beanName);

Copy the code

3.1.3 Obtaining the Target Object

protected Object getObjectForBeanInstance(

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



    // 1. If the Bean name begins with "&"

    if (BeanFactoryUtils.isFactoryDereference(name)) {

        if (beanInstance instanceof NullBean) {

            return beanInstance;

        }

        if(! (beanInstanceinstanceof FactoryBean)) {

            throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());

        }

        if(mbd ! =null) {

            mbd.isFactoryBean = true;

        }

        // 2. Return the instance object directly

        return beanInstance;

    }



    // 3. Return if the instance object is not of type FactoryBean

    if(! (beanInstanceinstanceof FactoryBean)) {

        return beanInstance;

    }



    Object object = null;

    if(mbd ! =null) {

        mbd.isFactoryBean = true;

    }

    else {

        // 4. Fetch the target object from the cache

        object = getCachedObjectForFactoryBean(beanName);

    }

    // 5. Cache does not have a target object

    if (object == null) {

        // Return bean instance from factory.

FactoryBean<? > factory = (FactoryBean<? >) beanInstance;

        // Caches object obtained from FactoryBean if it is a singleton.

        if (mbd == null && containsBeanDefinition(beanName)) {

            mbd = getMergedLocalBeanDefinition(beanName);

        }

        booleansynthetic = (mbd ! =null && mbd.isSynthetic());

        // 6. Get the target object from the FactoryBean

object = getObjectFromFactoryBean(factory, beanName, ! synthetic);

    }

    return object;

}

Copy the code

3.1.4 callFactoryBeanthegetObject()

private Object doGetObjectFromFactoryBean(FactoryBean
        factory, 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 {

            // Call the FactoryBean getObject() method to get the target object

            object = factory.getObject();

        }

    }

    return object;

}

Copy the code

3.1.5 Caching target Objects

protected Object getObjectFromFactoryBean(FactoryBean<? > factory, String beanName,boolean shouldPostProcess) {

    if (factory.isSingleton() && containsSingleton(beanName)) {

        synchronized (getSingletonMutex()) {

            Object object = this.factoryBeanObjectCache.get(beanName);

            if (object == null) {

                // Call the FactoryBean getObject() method to get the target object

                object = doGetObjectFromFactoryBean(factory, beanName);

                Object alreadyThere = this.factoryBeanObjectCache.get(beanName);

                if(alreadyThere ! =null) {

                    object = alreadyThere;

                } else {

                    if (containsSingleton(beanName)) {

                        // Cache the target object to avoid repeated creation

                        this.factoryBeanObjectCache.put(beanName, object);

                    }

                }

            }

            return object;

        }

    }

}

Copy the code

3.1.6 summary

  • Beanfactory.getbean (“&userFactoryBean”) gets from the containerBeanobject
  • Beanfactory.getbean (“userFactoryBean”) is first fetched from the containerBeanObject, callBeanThe object’sgetObject()Method to get the target object