In the last article Loading Bean s for Spring Ioc (1) , we analyzed 2.2 of the beans loading doGetBean() method in Spring Ioc to get a single bean from the cache and 2.3 to get the final bean instance object. We then analyzed the remaining steps.
Code directly:
//The ability to actually get beans from the IOC container is also where the dependent injection function is triggered protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { //Gets the name of the managed Bean based on the specified name, stripping the container dependency from the specified name // If an alias is specified, convert the alias to the canonical Bean name <1> final String beanName = transformedBeanName(name); Object bean; // Eagerly check singleton cache for manually registered singletons. // Get a single Bean that has been created 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: Bean Factory is the factory that manages beans in containers // FactoryBean is a factory Bean that creates objects, and there is a difference between them //Gets an instance object of a given Bean, either the bean instance itself or the Bean object created by FactoryBean //(Why retrieve it again, because the sharedInstance retrieved above is not necessarily complete) <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 resolves circular dependencies in singleton mode, an exception will be thrown if there is a circular dependency in prototype mode. <4> if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. //Check for BeanDefinition with the specified name in the IOC container, first check for //Required beans that can be obtained in the current Bean Factory, or delegate the current container if not //Find the parent container of the container, or, if not found, follow its inheritance system to find the parent container BeanFactory parentBeanFactory = getParentBeanFactory(); //The parent container of the current container exists and there is no Bean with the specified name 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); // Delegate parent processing if AbstractBeanFactory type if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args. //Delegate parent container finds based on specified name and explicit parameters return (T) parentBeanFactory.getBean(nameToLookup, args); } else { // No args -> delegate to standard getBean method. //Delegate parent container to find by specified name and type return parentBeanFactory.getBean(nameToLookup, requiredType); } } // Whether the beans created need type validation or not, generally not <5> if (!typeCheckOnly) { //The Bean specified to the container tag has been created markBeanAsCreated(beanName); } try { //Get the GenericBeanDefinition object corresponding to the beanName from the container and convert it to a RootBeanDefinition object // Mainly solves the problem of subclasses merging common attributes of parent classes when Bean inherits <6> final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // Check BeanDefinition (abstract class or not) for a given merge checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. // Processing dependent bean @DependsOn() // Gets the names of all dependent beans for the current Bean <7> String[] dependsOn = mbd.getDependsOn(); //If there is a dependency if (dependsOn != null) { for (String dep : dependsOn) { //Verify that the dependency has been registered with the current Bean if (isDependent(beanName, dep)) { //Registered, throwing exception throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } //If not, register the dependent bean s first registerDependentBean(dep, beanName); //Recursively calls getBean(), Mr. Generates a dependent bean getBean(dep); } } // Create bean instance. //Create a single Bean <8> 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); } //Create multiple Bean s else if (mbd.isPrototype()) { //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); } //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); } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // Check if required type matches the type of the actual bean instance. //Type check on created Bean instance objects <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 new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean; }
The code is very long and requires some patience. Let's analyze it step by step:
-
<1>: For detailed analysis, see 2.1 Getting the original beanName
-
<2>: For detailed analysis, see 2.2 Getting single bean from cache
-
<3>: For detailed analysis, see 2.3 for the final bean instance object
-
<4>See 2.4 Prototype Dependency Check and Getting Beans from parentBeanFactory for specific analysis
-
<5>: For detailed analysis, see 2.5 tagged bean s created or about to be created
-
<6>: For detailed analysis, see 2.6 Getting BeanDefinition
-
<7>: For detailed analysis, see 2.7 bean dependent processing
-
<8>: For detailed analysis, see 2.8 instantiations of bean s with different scopes
-
<9>See 2.9 Type Conversion for specific analysis
2.4, Prototype Dependency Check and Getting Beans from parentBeanFactory
The prototype pattern dependency check with the following code:
if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }
Follow 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) { //Remove the prototype being created from ThreadLocal Object curVal = this.prototypesCurrentlyInCreation.get(); return (curVal != null && (curVal.equals(beanName) || (curVal instanceof Set && ((Set<?>) curVal).contains(beanName)))); }
Spring only handles circular dependencies in singleton mode, while circular dependencies on prototype mode throw exceptions directly.
Spring stores the prototype pattern beans being created in ThreadLoacl, where ThreadLoacl is used to determine if the current beans have been created.
Get the Bean from parentBeanFactory with the following code:
// Check if bean definition exists in this factory. //Check for BeanDefinition with the specified name in the IOC container, first check for //Required beans that can be obtained in the current Bean Factory, or delegate the current container if not //Find the parent container of the container, or, if not found, follow its inheritance system to find the parent container BeanFactory parentBeanFactory = getParentBeanFactory(); //The parent container of the current container exists and there is no Bean with the specified name 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); // Delegate parent processing if AbstractBeanFactory type if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args. //Delegate parent container finds based on specified name and explicit parameters return (T) parentBeanFactory.getBean(nameToLookup, args); } else { // No args -> delegate to standard getBean method. //Delegate parent container to find by specified name and type return parentBeanFactory.getBean(nameToLookup, requiredType); } }
If there is no corresponding BeanDefinition object in the current container cache, an attempt is made to load from the parent BeanFactory and then recursively call the getBean(...) method
2.5, tag bean s are created or about to be created
The corresponding code is as follows:
//Whether the beans created need type validation or not, generally not if (!typeCheckOnly) { //The Bean specified to the container tag has been created markBeanAsCreated(beanName); }
typeCheckOnly is doGetBean(final String name, @Nullable final Class<T> requiredType,@Nullable A parameter in the final Object[] args, boolean typeCheckOnly) method that normally passes false
Then trace the markBeanAsCreated() method:
protected void markBeanAsCreated(String beanName) { // Not created if (!this.alreadyCreated.contains(beanName)) { synchronized (this.mergedBeanDefinitions) { // Check again: DCL Dual Check if (!this.alreadyCreated.contains(beanName)) { clearMergedBeanDefinition(beanName); // Add to created bean s collection this.alreadyCreated.add(beanName); } } } }
A well-known double check in single-case mode is used here
2.6, Get BeanDefinition
The corresponding code is as follows:
//Get the GenericBeanDefinition object corresponding to the beanName from the container and convert it to a RootBeanDefinition object //Mainly solves the problem of subclasses merging common attributes of parent classes when Bean inherits final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // Check BeanDefinition (abstract class or not) for a given merge checkMergedBeanDefinition(mbd, beanName, args);
This code comment is too detailed to explain.
2.7, bean dependency processing
The corresponding code is as follows:
// Guarantee initialization of beans that the current bean depends on. // Processing dependent bean @DependsOn() //Gets the names of all dependent beans for the current Bean <1> String[] dependsOn = mbd.getDependsOn(); //If there is a dependency if (dependsOn != null) { for (String dep : dependsOn) { //Verify that the dependency has been registered with the current Bean <2> if (isDependent(beanName, dep)) { //Registered, throwing exception throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } //If not, register the dependent bean s first <3> registerDependentBean(dep, beanName); //Recursively calls getBean(), Mr. Generates a dependent bean <4> getBean(dep); } }
There is a @DependsOn annotation in spring that relies on loading. For example, if the A object is not loaded until the B object is loaded, you can add the @DependsOn(value = "B") annotation to A to meet our requirements.
In fact, @DependsOn implements this code.
-
<1>, call the mbd.getDependsOn() method through the BeanDefinition we previously got from the IoC container to get all the dependencies of the current bean.
-
<2>, traverse these dependencies to determine if they are registered with the current Bean
-
<3>, no, register dependent beans first
-
<4>, recursively calls getBean(), Mr. Generates dependent bean s
<2>, traverse these dependencies to determine if they are registered with the current Bean
Code:
// Save the mapping relationship between the bean and its dependencies: B - > A private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64); //Save the mapping relationship 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 the collection of other beans that this bean depends on Set<String> dependentBeans = this.dependentBeanMap.get(canonicalName); if (dependentBeans == null) { return false; } // Exists, proving that the dependency is registered with the bean if (dependentBeans.contains(dependentBeanName)) { return true; } // Recursive Detection Dependency for (String transitiveDependency : dependentBeans) { if (alreadySeen == null) { alreadySeen = new HashSet<>(); } alreadySeen.add(beanName); if (isDependent(transitiveDependency, dependentBeanName, alreadySeen)) { return true; } } return false; }
This code is simple, mainly by getting all the dependent Beans corresponding to the current bean from the dependent BeanMap, then determining if they are registered, then checking the dependent beans recursively for dependencies and, if so, calling isDependent() check recursively
<3>, no, register dependent beans first
If a dependent Bean is not registered with the Bean, then register registerDependentBean(dep, beanName):
// Save the mapping relationship between the bean and its dependencies: B - > A private final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64); //Save the mapping relationship between the bean and its dependencies: A - > B private final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64); //Inject a dependent Bean for a 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 //First from the container: Bean Name --> All Dependent Bean Name Collections Find Dependent Beans for a given Bean Name synchronized (this.dependentBeanMap) { //Gets all dependent Bean names for a given name Bean dependentBeans = this.dependentBeanMap.get(canonicalName); if (dependentBeans == null) { //Set Dependent Bean Information for Beans dependentBeans = new LinkedHashSet<>(8); this.dependentBeanMap.put(canonicalName, dependentBeans); } //Save mapping relationships into a set dependentBeans.add(dependentBeanName); } //Find a dependent Bean for a given named Bean from a container: Bean Name-->Dependent Bean Collection for a 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); } //Save mapping relationships into a set dependenciesForBean.add(canonicalName); } }
To apply the example above, if A @DependsOn(value = "B"), that is, A depends on B, then in the registerDependentBean(dep, beanName), the parameter dep is B and the beanName is A.
This code actually registers the dependencies between bean s into two map s.
-
dependentBeanMap save (B,A)
-
Dependencies ForBeanMap Save In (A,B)
<4>, recursively calls getBean(dep), Mr. Generates a dependent bean
At this point, the getBean(beanName) method, doGetBean(beanName), is called recursively to rerun the current process to instantiate the dependent beans first.After the dependent beans are instantiated, the current beans execute next.
2.8. Instantiation of bean s in different scopes
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); } //Create multiple Bean s else if (mbd.isPrototype()) { //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); } //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); } }
This code is clearly divided into three parts:
-
singleton Bean instantiation
-
Prototype Bean instantiation
-
Other types of Bean instantiation (session,request, etc.)
Let's start with singleton Bean instantiation:
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); }
Spring Bean's scope defaults to singleton.There are other scopes, such as prototype, request, session, and so on.
Different scopes have different initialization strategies.
See in detail bean creation for spring scope s.
2.9, Type Conversion
Code:
// Check if required type matches the type of the actual bean instance. //Type check on created Bean instance objects if (requiredType != null && !requiredType.isInstance(bean)) { try { //Perform conversion T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); // Transform failed, 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 new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean;
The requiredType is a parameter passed in by the get Bean () method that allows you to get beans based on the specified beanName and requiredType.
In general, however, type checking is not required, requiredType is generally null, such as getBean(beanName)
This logic is used when the requiredType is not null.
Summary:
At this point, spring loading beans, or get Bean s (), have been roughly analyzed and a few more articles will be written detailing some of these steps.
Reference resources:
Taro Source