DoGetBean source code:
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// First convert the name passed into the container's real beanName
// There are three ways to get beanName
// One is the original beanName, one is the ampersand, and one is the alias
final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
Object sharedInstance = getSingleton(beanName);
// If you have previously created an instance of a singleton Bean, and the getBean method is called with an empty argument, the logic of the plane is executed
// Args is required to be null because if args is present, further assignment is required, so it cannot be returned directly
if(sharedInstance ! =null && args == null) {
if (logger.isTraceEnabled()) {
// If the Bean is still being created, it is a circular reference
if (isSingletonCurrentlyInCreation(beanName)) {
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'"); }}Return getObject if it is a plain bean, or getObject if it is a FactoryBean
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
BeanFactory parentBeanFactory = getParentBeanFactory();
if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if(args ! =null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else if(requiredType ! =null) {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
else {
return(T) parentBeanFactory.getBean(nameToLookup); }}if(! typeCheckOnly) { markBeanAsCreated(beanName); }try {
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
String[] dependsOn = mbd.getDependsOn();
if(dependsOn ! =null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);
try {
getBean(dep);
}
catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex); }}}// Create bean instance.
// If BeanDefinition is singleton
if (mbd.isSingleton()) {
// An anonymous inner class is used to create Bean instance objects and register them with dependent objects
sharedInstance = getSingleton(beanName, () -> {
try {
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.
destroySingleton(beanName);
throwex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally{ afterPrototypeCreation(beanName); }}); 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); }}}catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throwex; }}// Check if required type matches the type of the actual bean instance.
if(requiredType ! =null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T) bean;
}
Copy the code
We discussed the code in doGetBean to fetch the bean from the cache. The else in doGetBean will handle cases where the scope of the bean is prototype or singleton mode but the bean does not yet exist in the cache:Spring also designs a cache list for Scope’s Bean for Prototype
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
Copy the code
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
return(curVal ! =null &&
(curVal.equals(beanName) || (curVal instanceofSet && ((Set<? >) curVal).contains(beanName)))); }Copy the code
In contrast to the singleton set, ThreadLocal records only the scope prototype Bean created by the current thread. If (true) above proves that there is a loop dependency.
After passing the cyclic dependency check, see if the container has a parent. If it does, and there is no BeanDefinition instance of the bean in the current container, try to recursively query from the parent
To prevent the previous beanName from being transformed, append & and call the doGetBean or getBean method of the parent class, or doGetBean if the parent class is AbstractBeanFactory
If the current container contains a BeanDefinition instance of the bean, execution continues
// typeCheckOnly is used to determine whether getBean() is called only to get the bean for type checking and not to create the bean
if(! typeCheckOnly) {// Create the bean instead of just doing type checking
markBeanAsCreated(beanName);
}
Copy the code
So inside the markBeanAsCreated method,
protected void markBeanAsCreated(String beanName) {
// Double check locking mechanism
if (!this.alreadyCreated.contains(beanName)) {
synchronized (this.mergedBeanDefinitions) {
if (!this.alreadyCreated.contains(beanName)) {
// Let the bean definition get re-merged now that we're actually creating
// the bean... just in case some of its metadata changed in the meantime.
// Set the status of the previously merged RootBeanDefinition to true
// Indicates that the data needs to be merged again to prevent changes to the original data
clearMergedBeanDefinition(beanName);
// add the names of alreadyCreated or currently created beans to the alreadyCreated cache
this.alreadyCreated.add(beanName); }}}}Copy the code
There is a double lock checking mechanism. The name of the created Bean is added to the alreadyCreated (type Set) cache. Before adding to the cache, the original MergedBeanDefinition is Set to the previous identifier to be cleared. The goal is to re-merge the subclass and parent beanDefinitions when they are retrieved from the container. This prevents the metadata from being changed and the BeanDefinition from being created using the original data
protected void clearMergedBeanDefinition(String beanName) {
RootBeanDefinition bd = this.mergedBeanDefinitions.get(beanName);
if(bd ! =null) {
bd.stale = true; }}Copy the code
ClearMergedBeanDefinition back the container to obtain RootBeanDefinition instances, and combine the instance needs to be the state of the set to true (mentioned before, as long as the attribute specifies the parent, The two Beandefinitions are combined into one.)
Return to doGetBean, then call getMergedLocalBeanDefinition method to merge BeanDefinition of parents and children, into the method:
protected RootBeanDefinition getMergedLocalBeanDefinition(String beanName) throws BeansException {
// Quick check on the concurrent map first, with minimal locking.
RootBeanDefinition mbd = this.mergedBeanDefinitions.get(beanName);
if(mbd ! =null && !mbd.stale) {
return mbd;
}
return getMergedBeanDefinition(beanName, getBeanDefinition(beanName));
}
Copy the code
Fetch the merged RootBeanDefinition instance from the mergedBeanDefinitions cache. If stale is true, the mergedBeanDefinition instance will be returned.
Go back to the doGetBean, get the BeanDefinition, and verify the validity of the relevant instance
checkMergedBeanDefinition(mbd, beanName, args);
Copy the code
Into the checkMergedBeanDefinition method:
protected void checkMergedBeanDefinition(RootBeanDefinition mbd, String beanName, @Nullable Object[] args)
throws BeanDefinitionStoreException {
if (mbd.isAbstract()) {
throw newBeanIsAbstractException(beanName); }}Copy the code
Let’s see if RootBeanDefinition is abstract.
Go back to the doGetBean and try to get explicit dependencies from the BeanDefinition, and check for circular dependencies if you are dependent -onIf there are no circular dependencies, the associated dependencies are registered
registerDependentBean(dep, beanName);
Copy the code
public void registerDependentBean(String beanName, String dependentBeanName) {
String canonicalName = canonicalName(beanName);
synchronized (this.dependentBeanMap) {
// computeIfAbsent: If the value corresponding to the key is empty, the return value of the second parameter is saved and returned
DependentBeanMap contains a collection of referenced beans for the current Bean
// If the Bean name is userInfo, userInfo has an attribute of type Human
Human =[userInfo]
Set<String> dependentBeans =
this.dependentBeanMap.computeIfAbsent(canonicalName, k -> new LinkedHashSet<>(8));
if(! dependentBeans.add(dependentBeanName)) {return; }}synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean =
this.dependenciesForBeanMap.computeIfAbsent(dependentBeanName, k -> new LinkedHashSet<>(8)); dependenciesForBean.add(canonicalName); }}Copy the code
The first registration is a dependentBeanMap (key is the name of the dependentBean, value is the list of dependentBean names)
The second re-registration writes the opposite key/value pair to the dependenciesForBeanMap
Back to doGetBean, after the explicit dependency registration is performed, getBean(DEP) is recursively called to create the dependent bean. After that, different bean creation operations are performed for different scopes, including Singleton, Prototype, and others.
Except Prototype is called directlycreateBean(beanName, mbd, args)
In addition to creating the Bean instance, scope= encapsulates createBean in an anonymous parameterEnter the scope.get() method
Object get(String name, ObjectFactory
objectFactory);
Copy the code
As you can see, the following anonymous function implements the getObject method of the ObjectFactory and calls the createBean method to create an instance that fits the Scope.
Scope = singleton:
if (mbd.isSingleton()) {
// An anonymous inner class is used to create Bean instance objects and register them with dependent objects
sharedInstance = getSingleton(beanName, () -> {
try {
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 remove the bean instance from the singleton cache
// Because it may already exist in singleton mode to resolve cyclic dependencies, it is destroyed
destroySingleton(beanName);
throwex; }}); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); }Copy the code
Enter the getSingleton method:Is also to receive aObjectFactory
Object then implements its getObject method.
Enter the createBean:
protected abstract Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
throws BeanCreationException;
Copy the code
This is an abstract method that requires subclasses to implement.
Back to the getSingleton method above:
public Object getSingleton(String beanName, ObjectFactory
singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
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 + "'");
}
beforeSingletonCreation(beanName);
boolean newSingleton = false;
boolean recordSuppressedExceptions = (this.suppressedExceptions == null);
if (recordSuppressedExceptions) {
this.suppressedExceptions = new LinkedHashSet<>();
}
try {
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;
}
afterSingletonCreation(beanName);
}
if(newSingleton) { addSingleton(beanName, singletonObject); }}returnsingletonObject; }}Copy the code
Lock the first level cache singletonObjects. After obtaining the lock, try again to obtain the instance from the first level cache in case another thread has created it. If it cannot obtain the instance, start to create it:
Let’s see if the container is currently destroying all singletons: singletonsCurrentlyInDestruction
A flag.
Beforesington Creation(beanName) after verification, enter:
protected void beforeSingletonCreation(String beanName) {
/ / inCreationCheckExclusions cache cannot load current bean directly,
// It is mainly used in web container interceptors, so it can be ignored here because it definitely does not exist
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) {
throw newBeanCurrentlyInCreationException(beanName); }}Copy the code
The first condition of if can be ignored. The second condition is an attempt to place beanName in the list of singleton names being created. Failure to add beanName will throw an exception because the normal process of creating a bean is not supposed to enter this. If beanName appears in the list of names being created, it indicates that the same bean was created prior to this operation, which should have been obtained when the first step of doGetBean was to fetch the bean instance from the level 3 cache.
In the concurrent scenario, neither thread gets a singleton in the level 3 cache, and they end up here.
Back to the getSingleton method above, it’s time to create and get the bean instance: newSingleton
Set to true for subsequent callsaddSingleton
To add a level 1 cache.
Skip exception handling and look at finally:Enter the afterSingletonCreation:
protected void afterSingletonCreation(String beanName) {
if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.remove(beanName)) {
throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); }}Copy the code
Due to the bean instance is created, so have the is creating removes beanName singletonsCurrentlyInCreation bean name list
Back outside, the next step is to check if it is a newly created singleton, with the flag bit already true, and enter addSingleton:
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
// After the Bean instance is created, only the level 1 cache and the order in which beanName is registered are retained, and the rest is cleared
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName); }}Copy the code
After executing addSingleton, the full bean instance will be returned
Back to the creation singleton logic for doGetBean, which is executed once the full bean instance is returned
// If it is a normal bean, return it directly, is a FactoryBean, return its getObject
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
Copy the code
Create scope for PrototypeEnter thebeforePrototypeCreation
:
protected void beforePrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal == null) {
this.prototypesCurrentlyInCreation.set(beanName);
}
else if (curVal instanceof String) {
Set<String> beanNameSet = new HashSet<>(2);
beanNameSet.add((String) curVal);
beanNameSet.add(beanName);
this.prototypesCurrentlyInCreation.set(beanNameSet);
}
else{ Set<String> beanNameSet = (Set<String>) curVal; beanNameSet.add(beanName); }}Copy the code
To the type of ThreadLocal prototypesCurrentlyInCreation (the current thread is to create the Prototype of the bean name list) to add the record of the bean, avoid circular dependencies, said the bean is created.
PrototypeInstance = createBean(beanName, MBD, args) is also called to erase the previously registered Bean information
afterPrototypeCreation(beanName);
Copy the code
protected void afterPrototypeCreation(String beanName) {
Object curVal = this.prototypesCurrentlyInCreation.get();
if (curVal instanceof String) {
this.prototypesCurrentlyInCreation.remove();
}
else if (curVal instanceof Set) {
Set<String> beanNameSet = (Set<String>) curVal;
beanNameSet.remove(beanName);
if (beanNameSet.isEmpty()) {
this.prototypesCurrentlyInCreation.remove(); }}}Copy the code
Go back to doGetBean, which will be called later
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
Copy the code
Get the bean instance or bean instance created by the beanFactory
And then finally looking at the other cases,To create bean instances, choose the appropriate method to instantiate the bean based on the lifecycle range, such as Request, which ensures that one instance is generated per request.
Finally, the doGetBean does a type check and returns an instance:
if(requiredType ! =null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T) bean;
Copy the code