• Spring Reading Directory

BeanFactory is a FactoryBean.

define

public interface FactoryBean<T> {

	String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
    // The object instance returned
	@Nullable
	T getObject(a) throws Exception;

     // Returns the object type
	@NullableClass<? > getObjectType();// Whether the object returned by getObject is a singleton. True indicates singletons, false indicates non-singletons
	default boolean isSingleton(a) {
		return true; }}Copy the code

Look at the three method names, we can see the meaning of the name. Bean instance, Bean type, whether the Bean is a singleton. Let’s implement the FactoryBean interface, see how FactoryBean is used, and then see how Spring interprets beans in FactoryBean from a source code perspective.


practice

GongjFactoryBean

GongjFactoryBean Implements FactoryBean and overwrites its three methods.

@Component
public class GongjFactoryBean implements FactoryBean {

	/** * This method is executed only once * when the Bean is a singleton@return
	 * @throws Exception
	 */
	@Override
	public Object getObject(a) throws Exception {
		User user = new User();
		System.out.println("= = = =" + user);
		return user;
	}

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

	/** * controls whether the scope of the Bean is singleton or stereotype *@return* /
	@Override
	public boolean isSingleton(a) {
		return true; }}Copy the code

AppConfig

Create AppConfig as the start scan class

@ComponentScan("com.gongj")
public class AppConfig {}Copy the code

Main

Using AnnotationConfigApplicationContext container startup Spring

public class Main {
	public static void main(String[] args) {
		// Start the Spring container ----> create a non-lazy-loaded singleton Bean
		// Scan the packet path
		AnnotationConfigApplicationContext annotationConfigApplicationContext =
				new AnnotationConfigApplicationContext(AppConfig.class);
  
System.out.println(annotationConfigApplicationContext.getBean("gongjFactoryBean")); 	System.out.println(annotationConfigApplicationContext.getBean("gongjFactoryBean"));
System.out.println(annotationConfigApplicationContext.getBean("&gongjFactoryBean"));
System.out.println(annotationConfigApplicationContext.getBean("&gongjFactoryBean")); }}Copy the code

Starting the main method yields the following result:

====com.gongj.entity.User@cac736f# This is the com.gongj.entity.user that I printed in GongjFactoryBean@cac736f
com.gongj.entity.User@cac736f
com.gongj.service.GongjFactoryBean@5e265ba4
com.gongj.service.GongjFactoryBean@5e265ba4
Copy the code

From the above example we can draw the following conclusions:

  • GongjFactoryBean#getObject() is executed only once
  • GongjFactoryBean obtains the same User object each time.
  • GongjFactoryBean is the gongjFactoryBean object obtained according to &gongjFactoryBean and the gongjFactoryBean object obtained each time is consistent.

It is suspected that this phenomenon is controlled by isSingleton function. I’m going to change the value returned by the isSingleton function to false.

@Override
	public boolean isSingleton(a) {
		return false;
	}
Copy the code

Execute the main method again and output the following:

====com.gongj.entity.User@cac736f# This is the com.gongj.entity.user that I printed in GongjFactoryBean@cac736f====com.gongj.entity.User@5e265ba4 # This is com.gongj.entity.User@5e265ba4 that I printed in GongjFactoryBean com.gongj.service.GongjFactoryBean@156643d4 com.gongj.service.GongjFactoryBean@156643d4Copy the code

Based on the above example, we can draw the following conclusions:

  • The isSingleton() function controlsgongjFactoryBeanWhether the object obtained is a singleton.
  • Changes to the isSingleton() function do not affect the&gongjFactoryBeanObject, which is still singleton.

See this we should have doubts, let us begin with doubts step by step DEGUG source code.

Change the return value of isSingleton() to true.According to the graph above, we can draw the conclusion that:

  • GongjFactoryBean is added to the Spring container at startup. GongjFactoryBean is obviously a non-lazy-loaded singleton Bean.

debugging

  • Enter theAbstractApplicationContext#getBean
@Override
	public Object getBean(String name) throws BeansException {
		assertBeanFactoryActive();
		return getBeanFactory().getBean(name);  》》
	}
Copy the code
  • AbstractBeanFactory#getBean
@Override
	public Object getBean(String name) throws BeansException {
		return doGetBean(name, null.null.false); 》》
	}
Copy the code
  • AbstractBeanFactory#doGetBean
	protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
			@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

		// Convert beanName to "gongjFactoryBean"
		final String beanName = transformedBeanName(name); 》》
		Object bean;

		// Get the singleton Bean instance from the cache according to beanNameObject sharedInstance = getSingleton(beanName); " "if(sharedInstance ! =null && args == null) {
		 / /... Omit code
            // If sharedInstance is a FactoryBean,
	   // If it's a FactoryBean, then all you really need is the object returned by getObject
	  // beanName is a beanName obtained by spinrg after parsing
            // name the beanName we passed manually
			//sharedInstance The singleton Bean object obtained from beanName
			bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);  》》
		}

       else{
          / /... Omit the code in the next analysis}}Copy the code
  • AbstractBeanFactory#transformedBeanNameFirst callBeanFactoryUtils.transformedBeanNameMethod, and then get the return value, and then executecanonicalNameMethods.
// Return beanName, remove the & prefix of the name passed in, and use the returned name as an alias to look for the original beanName in aliasMap
protected String transformedBeanName(String name) { 
		return canonicalName(BeanFactoryUtils.transformedBeanName(name));
	}
Copy the code
  • BeanFactoryUtils#transformedBeanName
private static final Map<String, String> transformedBeanNameCache = new ConcurrentHashMap<>();
String FACTORY_BEAN_PREFIX = "&";

public static String transformedBeanName(String name) {
		Assert.notNull(name, "'name' must not be null");
		// If beanName does not begin with an &, it returns directly
		if(! name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {// gongjFactoryBean
			return name;
		}
		// If beanName begins with &, intercept the beanName after & and store the name before and after the intercept in transformedBeanNameCache
// There may be multiple &&& loops
		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
  • SimpleAliasRegistry#canonicalName
private final Map<String, String> aliasMap = new ConcurrentHashMap<>(16);

public String canonicalName(String name) {
		String canonicalName = name;
		String resolvedName;
		do {
Gongjfg = gongjFactoryBean
			resolvedName = this.aliasMap.get(canonicalName); 
			if(resolvedName ! =null) { canonicalName = resolvedName; }}while(resolvedName ! =null);
		return canonicalName;
	}
Copy the code

End of transformedBeanName method!


  • getSingleton

How does Spring fetch singleton beans from the cache based on beanName

@Override
	@Nullable
	public Object getSingleton(String beanName) {
		return getSingleton(beanName, true);
	}
Copy the code
@Nullable
	protected Object getSingleton(String beanName, boolean allowEarlyReference) {
		// Get instance singletonObjects level 1 cache from singleton pool
		Object singletonObject = this.singletonObjects.get(beanName);

		// If no instance is retrieved from the singleton pool and the specified singleton bean is being created
		if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
		// Lock the global variable and process it
			synchronized (this.singletonObjects) {
				//earlySingletonObjects level 2 cache
				singletonObject = this.earlySingletonObjects.get(beanName);
				// Not from level 2 cache
				if (singletonObject == null && allowEarlyReference) {
					// The addSingletonFactory method is called when some methods need to be pre-initialized
					// The ObjectFactory initialization is stored in the singletonFactories Map
					// singletonFactoriesObjectFactory<? > singletonFactory =this.singletonFactories.get(beanName);
					if(singletonFactory ! =null) {
						singletonObject = singletonFactory.getObject(); 
EarlySingletonObjects and singletonFactories are mutually exclusive
						this.earlySingletonObjects.put(beanName, singletonObject);
						this.singletonFactories.remove(beanName); }}}}return singletonObject;
	}
Copy the code

This method first tries to fetch the instance from singletonObjects(level 1 cache). If it can’t get it from earlySingletonObjects(level 2 cache). If it can’t get it, Then try to retrieve the beanName ObjectFactory from singletonFactories and call getObject to retrieve the bean. And put it in earlySingletonObjects and remove the ObjectFactor gamma from the Singleton Tories.

  • SingletonObjects: a cache of singletonObjects: Bean names to Bean instances, often referred to as singleton pools (level 1 caching).
  • Tories: Singleton factory cache (tertiary cache), Bean name to ObjectFactory, this reference will be removed once the final object is created (via objectFactory.getobject ())
  • EarlySingletonObjects: Used to store a reference (level 2 cache) to the original Bean created early in the Bean creation process. Note that this is the original Bean, which is an object created using a factory method or constructor. Once the object is finally created, this reference information will be deleted

The getSingleton method ends here!


  • AbstractBeanFactory#getObjectForBeanInstanceThe key part is in this method, going into the method code
protected Object getObjectForBeanInstance(
			Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) {

		// name is the beanName I pass in. If it starts with an am&, it returns the object in the SingletonObjects pool
		if (BeanFactoryUtils.isFactoryDereference(name)) {
			if (beanInstance instanceof NullBean) {
				return beanInstance;
			}
// Determine if the singleton beanInstance implements a FactoryBean, if no exception is thrown
			if(! (beanInstanceinstanceof FactoryBean)) {
				throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass());
			}
			if(mbd ! =null) {
				mbd.isFactoryBean = true;
			}
            // returns the object in the SingletonObjects pool
			return beanInstance;
		}

		// If beanInstance is not a FactoryBean, return it directly
		if(! (beanInstanceinstanceof FactoryBean)) {
			return beanInstance;
		}

		Object object = null;
		if(mbd ! =null) {
			mbd.isFactoryBean = true;
		}
		else {
// Get the Bean object from the factoryBeanObjectCache based on the real BeanName
			object = getCachedObjectForFactoryBean(beanName);
		}

		// Create from factoryBeanObjectCache if you don't get it
		if (object == null) { FactoryBean<? > factory = (FactoryBean<? >) beanInstance;// Determine if the Bean exists from beanDefinitionMap
			if (mbd == null && containsBeanDefinition(beanName)) {
// Perform Bean merge
				mbd = getMergedLocalBeanDefinition(beanName); 
			}
// Whether it is defined by the user rather than the application itself
			booleansynthetic = (mbd ! =null && mbd.isSynthetic());
			// Call getObject to get the objectobject = getObjectFromFactoryBean(factory, beanName, ! synthetic); " "}return object;
	}
Copy the code
  • FactoryBeanRegistrySupport#getObjectFromFactoryBean
protected Object getObjectFromFactoryBean(FactoryBean<? > factory, String beanName,boolean shouldPostProcess) {
		// whether the name is singleton && Determine whether the name exists in singletonObjects based on beanName
		if (factory.isSingleton() && containsSingleton(beanName)) {
			synchronized (getSingletonMutex()) {
				Object object = this.factoryBeanObjectCache.get(beanName);
				if (object == null) {
					// Call getObject to get an objectobject = doGetObjectFromFactoryBean(factory, beanName); " "// Get it from the cache again
					Object alreadyThere = this.factoryBeanObjectCache.get(beanName);
					if(alreadyThere ! =null) {
						object = alreadyThere;
					}
					else {
						if (shouldPostProcess) {
							// Returns whether the specified singleton bean is currently being created
							if (isSingletonCurrentlyInCreation(beanName)) {
								return object;
							}
                          // indicates that these beans are being created normally and cannot be created again until they are completed
							beforeSingletonCreation(beanName);
							try {
								// Call BeanPostProcessor to perform the initialized logic, mainly AOP
								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)) {
						// Put the created Bean into factoryBeanObjectCache
							this.factoryBeanObjectCache.put(beanName, object); }}}returnobject; }}else {
			// Create one at a time, not from factoryBeanObjectCache
			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); }}returnobject; }}Copy the code
  • FactoryBeanRegistrySupport#doGetObjectFromFactoryBeandoGetObjectFromFactoryBeanIn the method we finally see the method we want to see, which isobject = factory.getObject();
private Object doGetObjectFromFactoryBean(finalFactoryBean<? > 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) {
					throwpae.getException(); }}else {
      // Call getObject() in FectoryBeanobject = factory.getObject(); }}catch (FactoryBeanNotInitializedException ex) {
			throw new BeanCurrentlyInCreationException(beanName, ex.toString());
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
		}
		// If calling getObject() returns null, a NullBean is returned
		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;
	}
Copy the code

That’s the end of the FactoryBean! Of course the getBean method is just the tip of the iceberg.

A FactoryBean is a FactoryBean that produces or decorates object generation. A Bean that implements the FactoryBean interface actually gets the object returned by getObject() from the Bean name, not the Bean instance itself. If you want to get an instance of the Bean itself, you need to prefix the name with an am&.