In Spring, there are many different scopes for beans, such as Singleton, Prototype, request, etc. This article examines how to create a Bean for each scope
A singleton,
Code:
// Create bean instance.
// Create a singleton Bean
if (mbd.isSingleton()) {
// An anonymous inner class is used to create Bean instance objects and register them with dependent objects
sharedInstance = getSingleton(beanName, () -> {
try {
// Create a specified Bean instance object and merge the subclass and superclass definitions 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 the container singleton pattern Bean cache
destroySingleton(beanName);
throwex; }});// Get the instance object of the given Bean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
Copy the code
-
The anonymous inner class is used, and an ObjectFactory is first obtained through the createBean(beanName, MBD, args) method
-
Pass ObjectFactory as an argument to the getSingleton(beanName, ObjectFactory) method
-
GetObjectForBeanInstance (sharedInstance, name, sharedInstance) is passed in as an argument using the sharedInstance returned by getSingleton(beanName,objectFactory) method. BeanName, MBD) to go back to the final Bean instance (see Spring Ioc’s Bean Loading (1))
The createBean(beanName, MBD, args) method is more complex and will be examined in more detail in a later article. We will skip it here and look directly at the getSingleton(beanName,objectFactory) method.
// DefaultSingletonBeanRegistry.java
public Object getSingleton(String beanName, ObjectFactory
singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
// Global lock
synchronized (this.singletonObjects) {
// Get the singleton bean from the cache
// This step must be checked because the Singleton pattern is simply reusing beans that have already been created
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
// Whether the bean is 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 the bean
// 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) {
throwex; }}catch (BeanCreationException ex) {
if (recordSuppressedExceptions) {
for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); }}throw ex;
}
finally {
if (recordSuppressedExceptions) {
this.suppressedExceptions = null;
}
// post-processing
afterSingletonCreation(beanName);
}
if (newSingleton) {
// Add to the cacheaddSingleton(beanName, singletonObject); }}returnsingletonObject; }}Copy the code
In this code, it is mainly done some preparation and pretreatment steps, create real Bean is in singletonFactory getObject () method, and singletonFactory is by createBean after () method to create the callback parameter. So what’s the main thing this code does?
-
Try to fetch a singleton Bean from the cache and return it if it is already loaded, otherwise start the loading process
-
Loading preprocessing
-
Get Bean instance
-
Post processing
-
Join the cache
1.1. Pre-loading processing
BeforeSingletonCreation (beanName) is a tag method, let’s look at the code:
// For adding flags, the bean is currently being created
protected void beforeSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
// Add failed, throw an exception
throw newBeanCurrentlyInCreationException(beanName); }}Copy the code
Add beanName to singletonsCurrentlyInCreationmap, used to represent the singleton bean is created, if you add failures, throw an exception.
1.2. Get the Bean instance
Get the Bean from the singletonFactory returned by the createBean(beanName) method.
1.3. Post-processing
AfterSingletonCreation (beanName) is also a representation:
// To remove the tag, the Bean is not currently under creation
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
// Remove failed, throw an exception
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); }}Copy the code
Remove the create flag after the Bean is created. Pre processing and post processing of the creation, will be in the call isSingletonCurrentlyInCreation (String beanName) when used, this method is used to judge whether the current bean has been created.
1.4. Add to 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); }}Copy the code
One PUT, one Add, and two Remove operations.
-
【 PUT 】 The singletonObjects property, a cache of singleton beans.
-
The [remove] singletonFactories property, a cache of singleton bean Factories.
-
[remove] earlySingletonObjects property, a cache of singleton beans created “earlier”.
-
[add] registeredSingletons property, a registered singleton cache.
Second, the Prototype
Code:
// Create a multiinstance Bean
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
// Prototype creates a new object each time
Object prototypeInstance = null;
try {
The default function is to register the currently created prototype object
beforePrototypeCreation(beanName);
// Create the specified Bean object instance
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
The default function tells the IOC container that the prototype object for the specified Bean is no longer created
afterPrototypeCreation(beanName);
}
// Get the instance object of the given Bean
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
Copy the code
The prototype pattern is simple: just create a new instance instead of fetching it from the cache. BeforePrototypeCreation (beanName) preprocessing that marks the current bean as the prototype being created. AfterPrototypeCreation (beanName) post-processing to cancel the creation of the current bean. Call the getObjectFrBeanInstance() method to get the final bean. (See loading Spring Ioc beans for details (1))
Other scopes
// The Bean to be created is neither Singleton nor Prototype
// For example, the lifecycle of request, Session, and Application
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
// The Bean definition is invalid if the lifecycle scope is not configured in the Bean definition resource
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
// An anonymous inner class is used to get an instance of the specified lifecycle range
Object scopedInstance = scope.get(beanName, () -> {
// preprocessing
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
// post-processingafterPrototypeCreation(beanName); }});// Get the instance object of the 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); }}Copy the code
It is divided into the following steps:
-
Get the Scope name from the Scope annotation
-
pre-processing
-
createBean()
-
Post processing
-
Scope. The get () to obtain a bean
-
The getObjectForBeanInstance() method gets the Bean
The core process is the same as the prototype pattern, except that scope.get() is called to get the bean.
Object get(String name, ObjectFactory
objectFactory);
Copy the code
Scope.get () is an interface that has multiple implementation classes:
Let’s take a look at one of spring’s built-in implementations, SimpleThreadScope:
//SimpleThreadScope.java
private final ThreadLocal<Map<String, Object>> threadScope =
new NamedThreadLocal<Map<String, Object>>("SimpleThreadScope") {
@Override
protected Map<String, Object> initialValue(a) {
return newHashMap<>(); }};// Get the instance of the bean
@Override
public Object get(String name, ObjectFactory
objectFactory) {
// Get the scope cache
Map<String, Object> scope = this.threadScope.get();
Object scopedObject = scope.get(name);
if (scopedObject == null) {
scopedObject = objectFactory.getObject();
// add cache
scope.put(name, scopedObject);
}
return scopedObject;
}
Copy the code
Other scope implementation is not a look, interested friends can have a look.
conclusion
There are two important methods in the code above:
-
createBean(beanName, mbd, args)
-
getObjectForBeanInstance(Object beanInstance, String name, String beanName, RootBeanDefinition mbd)
Both methods are used in all three branches of code, and createBean will be examined in detail in the next article. The getObjectForBeanInstance method was examined in Spring Ioc’s Bean loading (1). Here is a reference to the analysis of this method in Spring Source In-depth Analysis:
The main purpose of this method is to verify the correctness of the bean we obtained below, which is to check whether the current bean is a bean of type FactoryBean. If so, you need to call the getObject() method of the bean’s corresponding FactoryBean instance as the return value. Either a bean retrieved from the cache or loaded through a different scope policy is just the original bean state and is not necessarily the bean we want in the end. For example, if we need to process the factory bean, we actually get the initial state of the factory bean, but what we really need is the bean returned from the factory-method method defined in the factory bean, The getObjectForBeanInstance(Object beanInstance, String Name, String beanName, RootBeanDefinition MBD) method does just that.
Reference: “Spring source in-depth analysis” – Hao Jia taro source code