In the previous article on loading Spring Ioc beans (I), we looked at the doGetBean() method of loading Spring Ioc beans in 2.2 fetching a singleton Bean from the cache and 2.3 fetching the final Bean instance object. We then looked at the remaining steps. Directly on the code:
// The ability to actually fetch beans from the IOC container is where dependency injection is triggered
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
@Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
// Get the name of the managed Bean with the specified name, stripping container dependencies from the specified name
// If an alias is specified, the alias is converted to the canonical Bean name
<1> final String beanName = transformedBeanName(name);
Object bean;
// Eagerly check singleton cache for manually registered singletons.
// Get the created singleton Bean from the cache
<2> Object sharedInstance = getSingleton(beanName);
// If there is one in the cache
if(sharedInstance ! =null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); }}// Note: BeanFactory is the factory that manages beans in the container
// FactoryBeans are factory beans that create objects. There is a difference between the two
// Gets the instance object of the given Bean, either the Bean instance itself or the Bean object created by FactoryBean
SharedInstance = sharedInstance = sharedInstance;
<3> bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
// Because Spring only addresses loop dependencies in singleton mode, in prototype mode an exception is thrown if a loop dependency exists.
<4> if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// Check if bean definition exists in this factory.
// Check if there is a BeanDefinition with the specified name in the IOC container
// Can get the desired Bean from the current BeanFactory, if not delegate to the current container
// Find the parent of the container. If the parent cannot be found, find the parent of the container along the inheritance system of the container
BeanFactory parentBeanFactory = getParentBeanFactory();
// The parent of the current container exists, and the specified Bean does not exist in the current container
if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
// Resolve the original name of the specified Bean name
String nameToLookup = originalBeanName(name);
// If it is an AbstractBeanFactory type, delegate to the parent class
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if(args ! =null) {
// Delegation to parent with explicit args.
Delegate the parent container to look up by the specified name and explicit arguments
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
Delegate the parent container to look up the specified name and type
returnparentBeanFactory.getBean(nameToLookup, requiredType); }}// Whether the created Bean requires type validation
<5> if(! typeCheckOnly) {// The Bean specified to the container tag has been created
markBeanAsCreated(beanName);
}
try {
// Get the corresponding GenericBeanDefinition object from the container and convert it to a RootBeanDefinition object
// The main solution is to merge the public attributes of the parent class when the Bean is inherited
<6> final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Check the given merged BeanDefinition.
checkMergedBeanDefinition(mbd, beanName, args);
// Guarantee initialization of beans that the current bean depends on.
$DependsOn(); $DependsOn();
// Get the names of all the dependent beans of the current Bean
<7> String[] dependsOn = mbd.getDependsOn();
// If there are dependencies
if(dependsOn ! =null) {
for (String dep : dependsOn) {
// Verify that the dependency has been registered with the current Bean
if (isDependent(beanName, dep)) {
// Registered, exception thrown
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// If no, register the dependent bean first
registerDependentBean(dep, beanName);
// Recursively call getBean(), which becomes the dependent beangetBean(dep); }}// Create bean instance.
// Create a singleton Bean
<8> 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);
}
// Create a multiinstance Bean
else if (mbd.isPrototype()) {
// 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);
}
// 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); }}}catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throwex; }}// Check if required type matches the type of the actual bean instance.
// Type check the created Bean instance object
<9> 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.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T) bean;
}
Copy the code
The code is long and requires some patience, so let’s walk through it step by step:
-
<1> : for specific analysis, see 2.1 Obtaining original beanName
-
<2> : for detailed analysis, see 2.2 Obtaining singleton bean from cache
-
<3> : for detailed analysis, see 2.3 Obtaining the final bean instance object
-
<4> : see 2.4 Prototype Dependency Check and getting Bean from parentBeanFactory for detailed analysis
-
<5> : for detailed analysis, see 2.5 marking bean as created or to be created
-
<6> : For detailed analysis, see 2.6 Obtaining BeanDefinition
-
<7> : for detailed analysis, see 2.7 Bean dependency Processing
-
<8> : for detailed analysis, see 2.8 instantiation of beans with different scopes
-
<9> : for detailed analysis, see 2.9 Type Conversion
2.4. Prototype dependency check and get Bean from parentBeanFactory
Prototype mode dependency check, corresponding code is as follows:
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
Copy the code
Track in:
/** Names of beans that are currently in creation */
private final ThreadLocal<Object> prototypesCurrentlyInCreation =
new NamedThreadLocal<>("Prototype beans currently in creation");
protected boolean isPrototypeCurrentlyInCreation(String beanName) {
// Retrieve the prototype being created from ThreadLocal
Object curVal = this.prototypesCurrentlyInCreation.get();
return(curVal ! =null &&
(curVal.equals(beanName) || (curVal instanceofSet && ((Set<? >) curVal).contains(beanName)))); }Copy the code
Spring only handles singleton loop dependencies and throws exceptions for prototype loop dependencies. Spring stores the prototype pattern Bean being created into ThreadLoacl, where ThreadLoacl is used to determine whether the current Bean has been created.
Get the Bean from parentBeanFactory as follows:
// Check if bean definition exists in this factory.
// Check if there is a BeanDefinition with the specified name in the IOC container
// Can get the desired Bean from the current BeanFactory, if not delegate to the current container
// Find the parent of the container. If the parent cannot be found, find the parent of the container along the inheritance system of the container
BeanFactory parentBeanFactory = getParentBeanFactory();
// The parent of the current container exists, and the specified Bean does not exist in the current container
if(parentBeanFactory ! =null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
// Resolve the original name of the specified Bean name
String nameToLookup = originalBeanName(name);
// If it is an AbstractBeanFactory type, delegate to the parent class
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);
}
else if(args ! =null) {
// Delegation to parent with explicit args.
Delegate the parent container to look up by the specified name and explicit arguments
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
Delegate the parent container to look up the specified name and type
returnparentBeanFactory.getBean(nameToLookup, requiredType); }}Copy the code
If there is no corresponding BeanDefinition object in the current container cache, it tries to load it from the parentBeanFactory, and then recursively calls getBean(…). methods
2.5. Mark the bean as created or about to be created
The corresponding code is as follows:
// Whether the created Bean requires type validation
if(! typeCheckOnly) {// The Bean specified to the container tag has been created
markBeanAsCreated(beanName);
}
Copy the code
TypeCheckOnly is doGetBean(final String Name,@Nullable Final Class requiredType,@Nullable final Object[] args, Boolean typeCheckOnly) a parameter in the method. Generally, this parameter is passed false
Then trace the markBeanAsCreated() method:
protected void markBeanAsCreated(String beanName) {
// Not created
if (!this.alreadyCreated.contains(beanName)) {
synchronized (this.mergedBeanDefinitions) {
// Double check for DCL
if (!this.alreadyCreated.contains(beanName)) {
clearMergedBeanDefinition(beanName);
// Add to the created bean collection
this.alreadyCreated.add(beanName); }}}}Copy the code
The familiar double check from the singleton pattern is used here
2.6 get BeanDefinition
The corresponding code is as follows:
// Get the corresponding GenericBeanDefinition object from the container and convert it to a RootBeanDefinition object
// The main solution is to merge the public attributes of the parent class when the Bean is inherited
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
// Check the given merged BeanDefinition.
checkMergedBeanDefinition(mbd, beanName, args);
Copy the code
This code is commented in detail and won’t be explained much.
2.7 bean dependency processing
The corresponding code is as follows:
// Guarantee initialization of beans that the current bean depends on.
$DependsOn(); $DependsOn();
// Get the names of all the dependent beans of the current Bean
<1> String[] dependsOn = mbd.getDependsOn();
// If there are dependencies
if(dependsOn ! =null) {
for (String dep : dependsOn) {
// Verify that the dependency has been registered with the current Bean
<2> if (isDependent(beanName, dep)) {
// Registered, exception thrown
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
// If no, register the dependent bean first
<3> registerDependentBean(dep, beanName);
// Recursively call getBean(), which becomes the dependent bean
<4> getBean(dep); }}Copy the code
A @dependson (value = “B”) annotation is available in Spring. If the object A is not loaded until the object B is loaded, A @dependson (value = “B”) annotation is available. The @dependson implementation is based on this code.
-
<1> Call mbd.getDependson () on the BeanDefinition we obtained from the IoC container to obtain all dependencies of the current bean.
-
<2>, iterate over these dependencies to determine if the dependency is registered with the current Bean
-
<3>, if no, the dependent Bean is registered first
-
<4>, recursively call getBean(), which becomes the dependent bean
<2>, iterate over these dependencies to determine if the dependency is registered with the current Bean
Code:
// Save the mapping between the bean and its dependencies: B - > A
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
// Save the mapping between the bean and its dependencies: A - > B
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
private boolean isDependent(String beanName, String dependentBeanName, @Nullable Set<String> alreadySeen) {
if(alreadySeen ! =null && alreadySeen.contains(beanName)) {
return false;
}
// Get the current original beanName
String canonicalName = canonicalName(beanName);
// Get a collection of other beans that the bean depends on
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans == null) {
return false;
}
If the dependency exists, the dependency is registered with the bean
if (dependentBeans.contains(dependentBeanName)) {
return true;
}
// Recursively detect dependencies
for (String transitiveDependency : dependentBeans) {
if (alreadySeen == null) {
alreadySeen = new HashSet<>();
}
alreadySeen.add(beanName);
if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) {
return true; }}return false;
}
Copy the code
DependentBeans this code is simple: obtain all dependentBeans for the current bean using a dependentBeanMap, check whether the bean is registered, and recursively check whether the dependent bean has dependencies. If so, recursively call isDependent() to check
<3>, if no, the dependent Bean is registered first
RegisterDependentBean (DEP, beanName) if no dependent Bean is registered to the Bean:
// Save the mapping between the bean and its dependencies: B - > A
private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);
// Save the mapping between the bean and its dependencies: A - > B
private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);
// Inject the dependent Bean into the specified Bean
public void registerDependentBean(String beanName, String dependentBeanName) {
// A quick check for an existing entry upfront, avoiding synchronization...
// Get the original beanName
String canonicalName = canonicalName(beanName);
Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName);
if(dependentBeans ! =null && dependentBeans.contains(dependentBeanName)) {
return;
}
// No entry yet -> fully synchronized manipulation of the dependentBeans Set
// Find the dependent beans of the given named bean from the container: bean name --> All dependent bean names collection
synchronized (this.dependentBeanMap) {
// Get all the dependent Bean names for the Bean with the given name
dependentBeans = this.dependentBeanMap.get(canonicalName);
if (dependentBeans == null) {
// Set the dependency Bean information for the Bean
dependentBeans = new LinkedHashSet<>(8);
this.dependentBeanMap.put(canonicalName, dependentBeans);
}
// Put the mapping into the collection
dependentBeans.add(dependentBeanName);
}
// Find the dependent beans of the given named bean from the container: bean name --> collection of dependent beans of the specified named bean
synchronized (this.dependenciesForBeanMap) {
Set<String> dependenciesForBean = this.dependenciesForBeanMap.get(dependentBeanName);
if (dependenciesForBean == null) {
dependenciesForBean = new LinkedHashSet<>(8);
this.dependenciesForBeanMap.put(dependentBeanName, dependenciesForBean);
}
// Put the mapping into the collectiondependenciesForBean.add(canonicalName); }}Copy the code
If A @dependson (value = “B”) registerDependentBean(dep, beanName) registerDependentBean(dep, beanName) registerDependentBean(dep, beanName) registerDependentBean(dep, beanName) What this code does is register the dependencies between beans into two maps.
-
DependentBeanMap deposit (B, A)
-
DependenciesForBeanMap deposit (A, B)
<4>, recursive call getBean(DEP), the dependent bean
At this point, a recursive call to the getBean(beanName) method, doGetBean(beanName), reruns the current process to instantiate the dependent Bean first. After the dependent Bean is instantiated, the current Bean continues execution.
2.8 instantiation of beans of different scopes
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);
}
// Create a multiinstance Bean
else if (mbd.isPrototype()) {
// 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);
}
// 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
The code is clearly divided into three parts:
-
Instantiate the Singleton Bean
-
Instantiate the Prototype Bean
-
Instantiation of other Bean types (session, Request, etc.)
Let’s start with singleton Bean instantiation:
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 Spring Bean’s scope defaults to Singleton. There are other scopes, such as Prototype, Request, Session, etc. Different scopes have different initialization strategies. See Spring Ioc’s Bean loading (3) : Bean creation for each scope.
2.9. Type conversion
Code:
// Check if required type matches the type of the actual bean instance.
// Type check the created Bean instance object
if(requiredType ! =null && !requiredType.isInstance(bean)) {
try {
// Perform the conversion
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
// Failed to convert, throw exception
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw newBeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); }}return (T) bean;
Copy the code
RequiredType is an argument that the getBean() method can pass in, that is, the Bean can be fetched based on the specified beanName and requiredType.
RequiredType is null, such as getBean(beanName).
This logic is used when requiredType is not null.
Conclusion:
At this point, spring loads beans, also known as getBean(), and we’re done with the overview, and we’ll write a few more articles detailing some of these steps.
Reference: impression channel source code