Bean loading for Spring Ioc: Bean creation for individual scope s

Bean s have many different scopes in Spring, such as singleton, prototype, request, etc. This article will analyze how each scope's beans are created.

1. singleton

Code:

// Create bean instance.
	//Create a single Bean
	if (mbd.isSingleton()) {
	//An anonymous internal class is used to create Bean instance objects and register them with dependent objects
	sharedInstance = getSingleton(beanName, () -> {
		try {
			//Creates a specified Bean instance object, merges the definitions of child and parent classes if there is parent inheritance
			return createBean(beanName, mbd, args);
		}
		catch (BeansException ex) {
			// Explicitly remove instance from singleton cache: It might have been put there
			// eagerly by the creation process, to allow for circular reference resolution.
			// Also remove any beans that received a temporary reference to the bean.
			//Explicitly Clear Instance Objects from Container Singleton Mode Bean Cache
			destroySingleton(beanName);
			throw ex;
		}
	});
	//Gets the instance object for a given Bean
	bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
	}
  • An anonymous internal class is used here, first an ObjectFactory is obtained using the createBean(beanName, mbd, args) method

  • Pass ObjectFactory as a parameter into the getSingleton(beanName,objectFactory) method

  • SharedInstance returned using the getSingleton(beanName,objectFactory) method is passed as a parameter into getObjectForBeanInstance(sharedInstance, name, beanName, mbd) to go back to the final Bean instance (see details below) Loading Bean s for Spring Ioc (1))

The createBean(beanName, mbd, args) method is complex and will be analyzed in more detail in subsequent articles, skipped here and looked directly at getSingleton(beanName,objectFactory) method.

// DefaultSingletonBeanRegistry.java

public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
	Assert.notNull(beanName, "Bean name must not be null");
//Global Locking
synchronized (this.singletonObjects) {
	// Get a single bean from the cache
	// Because singleton mode is essentially a reuse of bean s already created, this step must be checked
	Object singletonObject = this.singletonObjects.get(beanName);
	if (singletonObject == null) {
		//Is the bean being destroyed
		if (this.singletonsCurrentlyInDestruction) {
			throw new BeanCreationNotAllowedException(beanName,
					"Singleton bean creation not allowed while singletons of this factory are in destruction " +
					"(Do not request a bean from a BeanFactory in a destroy method implementation!)");
		}
		if (logger.isDebugEnabled()) {
			logger.debug("Creating shared instance of singleton bean '" + beanName + "'");
		}
		// Load Preprocessing
		beforeSingletonCreation(beanName);
		boolean newSingleton = false;
		boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
		if (recordSuppressedExceptions) {
			this.suppressedExceptions = new LinkedHashSet<>();
		}
		try {
			// Initialize bean s
			// This process actually calls the createBean() method
			singletonObject = singletonFactory.getObject();
			newSingleton = true;
		}
		catch (IllegalStateException ex) {
			// Has the singleton object implicitly appeared in the meantime ->
			// if yes, proceed with it since the exception indicates that state.
			singletonObject = this.singletonObjects.get(beanName);
			if (singletonObject == null) {
				throw ex;
			}
		}
		catch (BeanCreationException ex) {
			if (recordSuppressedExceptions) {
				for (Exception suppressedException : this.suppressedExceptions) {
					ex.addRelatedCause(suppressedException);
				}
			}
			throw ex;
		}
		finally {
			if (recordSuppressedExceptions) {
				this.suppressedExceptions = null;
			}
			//Postprocessing
			afterSingletonCreation(beanName);
		}
		if (newSingleton) {
			//Join Cache
			addSingleton(beanName, singletonObject);
		}
	}
	return singletonObject;
	}
	}

In this code, there are actually some preparatory and preprocessing steps. The real creation of beans is achieved by the singletonFactory.getObject() method, while singletonFactory is a parameter that is called back after the creation of the create Bean() method.
So what's the main thing this code does?

  • Attempting to get a single Bean from the cache
    Return directly if already loaded, otherwise start the loading process

  • Load Preprocessing

  • Get Bean Instances

  • Postprocessing

  • Join Cache

1.1, Load Preprocessing

beforeSingletonCreation(beanName) is a markup method, let's look at the code:

// Used to add flags, bean s are currently being created
	protected void beforeSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
			//Add failed, throw exception
			throw new BeanCurrentlyInCreationException(beanName);
		}
	}

Add the beanName to the singletonsCurrentlyInCreationmap to indicate that the singleton bean is being created and throw an exception if the addition fails.

1.2, Get Bean Instances

Get the Bean from the singletonFactory returned by the createBean(beanName) method.

1.3. Post-processing

afterSingletonCreation(beanName) is also a representation:

// For removing tags, the current Bean is not in creation
	protected void afterSingletonCreation(String beanName) {
		if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
			//Remove failed, throw exception
			throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation");
		}
	}

Remove the create tag after the Bean is created.
This create flag for preprocessing and postprocessing is used when isSingletonCurrentlyInCreation(String beanName) is called, which is used to determine if the current bean is already being created.

1.4, Join Cache

Look directly at the code:

protected void addSingleton(String beanName, Object singletonObject) {
	synchronized (this.singletonObjects) {
		this.singletonObjects.put(beanName, singletonObject);
		this.singletonFactories.remove(beanName);
		this.earlySingletonObjects.remove(beanName);
		this.registeredSingletons.add(beanName);
	    }
	}

One put, one add, two remove operations.

  • [put] singletonObjects property, the cache of a single bean.

  • [remove] singletonFactories property, cache for a single bean Factory.

  • [remove] earlySingletonObjects property, "Cache for earlier" created single bean s.

  • [add] registeredSingletons property, registered singleton cache.

2. Prototype

Code:

        //Create multiple Bean s
	else if (mbd.isPrototype()) {
		// It's a prototype -> create a new instance.
		//Prototype creates a new object each time
		Object prototypeInstance = null;
		try {
			//Load preprocessing, the default function is to register the currently created prototype object
			beforePrototypeCreation(beanName);
			//Create an instance of the specified Bean object
			prototypeInstance = createBean(beanName, mbd, args);
		}
		finally {
			//Load post-processing, the default function tells the IOC container that the prototype object for the specified Bean is no longer created
			afterPrototypeCreation(beanName);
		}
		//Gets the instance object for a given Bean
		bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
	}

The prototype mode is simple, just create a new instance and don't get it from the cache anymore.
beforePrototypeCreation(beanName) preprocesses to mark the current bean as the prototype being created.
The afterPrototypeCreation(beanName) postprocessing cancels the creation label of the current bean.
Call the getObjectFrBeanInstance() method to get the final bean.(See details Loading Bean s for Spring Ioc (1))

3. Other Scopes

//Bean s to be created are neither Singleton s nor Prototype s
	//Lifecycle such as request, session, application, etc.
	else {
		String scopeName = mbd.getScope();
		final Scope scope = this.scopes.get(scopeName);
		//Bean definition is illegal if there is no life cycle scope configured in the Bean definition resource
		if (scope == null) {
			throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
		}
		try {
			//An anonymous internal class is used here to get an instance of a specified lifecycle range
			Object scopedInstance = scope.get(beanName, () -> {
				//pre-processing
				beforePrototypeCreation(beanName);
				try {
					return createBean(beanName, mbd, args);
				}
				finally {
					//Postprocessing
					afterPrototypeCreation(beanName);
				}
			});
			//Gets the instance object for a given Bean
			bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
		}
		catch (IllegalStateException ex) {
			throw new BeanCreationException(beanName,
					"Scope '" + scopeName + "' is not active for the current thread; consider " +
					"defining a scoped proxy for this bean if you intend to refer to it from a singleton",
					ex);
		}
	}

It is divided into the following steps:

  • Get the scope name from the Scope comment

  • pre-processing

  • createBean()

  • Postprocessing

  • scope.get() Gets the bean

  • GetObjectFor Bean Instance() method to get beans

The core process is the same as the prototype, except scope.get() is called here to get the bean s.

Object get(String name, ObjectFactory<?> objectFactory);

scope.get() is an interface that has several implementation classes:

Let's look at one implementation of SimpleThreadScope that comes with spring:

//SimpleThreadScope.java

private final ThreadLocal<Map<String, Object>> threadScope =
			new NamedThreadLocal<Map<String, Object>>("SimpleThreadScope") {
				@Override
				protected Map<String, Object> initialValue() {
					return new HashMap<>();
				}
			};


	//Get an instance of a bean
	@Override
	public Object get(String name, ObjectFactory<?> objectFactory) {
		// Get scope cache
		Map<String, Object> scope = this.threadScope.get();
		Object scopedObject = scope.get(name);
		if (scopedObject == null) {
			scopedObject = objectFactory.getObject();
			// Join Cache
			scope.put(name, scopedObject);
		}
		return scopedObject;
	}

Other scope s'implementations will not be looked at individually, and interested friends can look at them themselves.

summary

There are two important methods in the code above:

  • createBean(beanName, mbd, args)

  • getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd)

These two methods are used in all three code branches, and will be analyzed in more detail in the next article, the getObjectForBeanInstance method in Loading Bean s for Spring Ioc (1) Already analyzed.
Here is a further reference to the analysis of this method in Spring Source Depth Resolution:

This method is mainly to verify the correctness of the beans we get below, which is to detect whether the current beans are FactoryBean type beans.
If so, the getObject() method of the FactoryBean instance corresponding to the bean needs to be called as the return value.
Whether beans are retrieved from the cache or loaded through different scope policies, they are only the original state of the beans, not necessarily the beans we ultimately want.
For example, if we need to process factory beans, what we get here is the initial state of the factory beans, but what we really need is the beans returned from the factory-method method defined in the factory beans, whereas getObjectForBeanInstance (Object beanInstance, String name, String beanName, Roo)The tBeanDefinition mbd) method does this.

Reference resources:
Spring Source Depth Resolution - Haojia
Taro Source

Tags: Programming Spring Java Session

Posted on Mon, 09 Sep 2019 20:26:34 -0700 by rscott7706