https://blog.csdn.net/zxd1435513775/article/details/120935494?spm=1001.2014.3001.5501
Spring source code series (IV) -- function analysis of configuration class postprocessor
https://blog.csdn.net/zxd1435513775/article/details/121113933?spm=1001.2014.3001.5501
Spring source code series (6) -- container refresh (Part 2)
In Articles 4 and 6, we have analyzed a method, getBean(). This line of code means: get the Bean from the container. If not, create the Bean. Therefore, when viewing the Spring source code, as long as you see this method, you should know that it is to instantiate beans.
The specific implementation of this method is located in the AbstractBeanFactory class. Let's analyze it below.
1, getBean()
Let's take a look at the detailed code of this method:
// Get the corresponding Bean instance according to name public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly){ /** * The following method is to obtain the beanName through name. Why not use name as the beanName directly? * There are two reasons: * 1,name The value of may begin with the & character, indicating that the caller wants to get the FactoryBean itself, not the bean created by the FactoryBean implementation class. * In BeanFactory, the implementation class of FactoryBean is consistent with other bean storage methods, that is < beanName, bean >, and there is no & this character in beanName. * Therefore, you need to remove the first character & of name to get the FactoryBean instance from the cache. * 2,It's also an alias problem. The conversion requires & beanname */ String beanName = transformedBeanName(name); Object bean; /** * The following getSingleton() method will also be called during the above getBean() and during the initialization of the Bean * Why do you need to do this? * That is, when initializing a Bean, the Spring container first obtains the Bean object and determines whether the object has been instantiated, * Has it been created. In general, it is absolutely empty, but in one case, it may not be empty * Get a Bean from the Spring container, because the Bean container in Spring is stored with a map (singleton objects) * Therefore, it can be understood that getSingleton(beanName) is equal to beanMap.get(beanName) * Because the getBean() method will be called once when the Spring environment is initialized (that is, when the object is created) * It will also be called again when the Bean is initialized * This is the first call of getSingleton(), which means to query whether the beanName has been created in the cache */ Object sharedInstance = getSingleton(beanName); // See the second part below for a detailed explanation of this method // sharedInstance is obtained here. If it is not empty, it means that it has been instantiated, but it is uncertain whether the attribute is filled // When analyzing FactoryBean, the getObjectForBeanInstance() method is analyzed. Let's analyze else if (sharedInstance != null && args == null) { /** * If sharedInstance is an ordinary singleton bean, the following method will return directly. * However, if the sharedInstance is of FactoryBean type, you need to call the getObject factory method to get the real instance * bean example. If the user wants to get the FactoryBean itself, no special processing will be done here. Just return it directly. * After all, the implementation class of FactoryBean itself is also a bean, but it has a little special function. */ bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { // Judge whether it is a prototype Bean. If it is a prototype, it should not be created when the Spring container is initialized // Why throw exceptions directly? Because in the finishBeanFactoryInitialization() method in the refresh() method // The bean has been judged. The bean definition is not abstract, not lazy loaded, but single column. It will be entered and executed here // So it throws exceptions if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check whether the bean definition exists in the Spring 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 { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } } // This parameter is passed in for the method if (!typeCheckOnly) { // Added to the alreadyCreated set set, indicating that it has been created once markBeanAsCreated(beanName); } try { // If the definition of a given bean has a parent class, the RootBeanDefinition of a given top-level bean is returned by merging with the parent bean. // When configuring beans with xml, there is a parent attribute to specify the parent class RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); // Check again to see if the merged bean is abstract checkMergedBeanDefinition(mbd, beanName, args); // Get the dependent bean and ensure that the bean that the current bean depends on is instantiated. // This means that the dependent Bean must be instantiated first String[] dependsOn = mbd.getDependsOn(); if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { // Throw exception code omitted } registerDependentBean(dep, beanName); try { // Call the getBean() method again. The dependent bean obtained here is called recursively, and is created upon acquisition getBean(dep); } catch (NoSuchBeanDefinitionException ex) { // Throw exception code omitted } } } // This is the second call to the getSingleton() method if (mbd.isSingleton()) { // This is the second call of getSingleton(). The two calls have different formal parameters // First execute the getSingleton() method, in which you can call the lamda expression sharedInstance = getSingleton(beanName, () -> { try { // See Part 4 below for this method return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } }); 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(); if (!StringUtils.hasLength(scopeName)) { // Throw exception code omitted } Scope scope = this.scopes.get(scopeName); if (scope == null) { // Throw exception code omitted } 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 exception code omitted } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // 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 exception code omitted } return convertedBean; } catch (TypeMismatchException ex) { // Throw exception code omitted } } return (T) bean; }
2, getSingleton() is called for the first time
Let's look at the detailed code of this method: in the DefaultSingletonBeanRegistry class
// The first step, getSingleton(), is to call this method and query singletonObjects according to beanName to see if it has been instantiated protected Object getSingleton(String beanName, boolean allowEarlyReference) { // Get the bean from singletonObjects. If it is not empty, it will be returned directly without instantiation Object singletonObject = this.singletonObjects.get(beanName); // The issingletoncurrent yincreation() method determines whether the current beanName is being created // If null and creating if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { synchronized (this.singletonObjects) { // Go to earlySingletonObjects to get it. Note that the objects stored in this row collection have not been filled with attributes singletonObject = this.earlySingletonObjects.get(beanName); // If you don't get it, it means that the creation has not been completed if (singletonObject == null && allowEarlyReference) { // Go to the collection of Bean factories to see if it is a Bean factory ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { singletonObject = singletonFactory.getObject(); this.earlySingletonObjects.put(beanName, singletonObject); // This collection is designed to solve the circular reference problem this.singletonFactories.remove(beanName); } } } } return singletonObject; } public boolean isSingletonCurrentlyInCreation(String beanName) { // The Bean being created will be placed in the singletonsCurrentlyInCreation collection return this.singletonsCurrentlyInCreation.contains(beanName); }
Here I would like to talk about the four functions of singletonObjects, earlySingletonObjects, singletonscurrently in creation and singletonFactories, which are very important!!!
- singletonObjects is a Map < string, Object >, which is used to store fully initialized beans. Bean s taken from this Map can be used directly
- earlySingletonObjects is a map < string, Object >, which is used to store the original Bean object and solve the problem of Bean circular dependency. Note: the object stored in it has not been filled with attributes, that is, the initialization has not been completed!!!
- singletonsCurrentlyInCreation is a set < string >, which is used to store the Bean being created
- singletonFactories is a map < string, objectfactory <? > >, It is used to store Bean factory objects and solve circular dependencies
3, The second call to getSingleton()
// The second place getSingleton() calls this method, and the second parameter passed in is the lamda expression public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { // First take the Bean from singletonObjects Object singletonObject = this.singletonObjects.get(beanName); // If it is not empty, return directly if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { // Throw exception code omitted } // Add beanName to a set such as singletonsCurrentlyInCreation // Indicates that the bean corresponding to beanName is being created // Before creating, put the beanName into the singletonsCurrentlyInCreation collection // When calling getSingleto() code for the first time above, one condition is whether the current beanName is being created beforeSingletonCreation(beanName); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { // Analysis here, and finally to create the object!!!!! // The singletonFactory here is passed in, that is, the lamda expression. Call the createBean() function to create a Bean // Let's look at the fourth part singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // Throw exception code omitted } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); } // Put the created Bean into singletonObjects if (newSingleton) { // This method is followed by addSingleton(beanName, singletonObject); } } return singletonObject; } } protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { // The created Bean is put into singletonObjects this.singletonObjects.put(beanName, singletonObject); // When were the following two collections put in? Think about it ~ ~ ~ in the doCreateBean() method // Remove from singletonFactories // Storing bean factory objects to solve circular dependencies this.singletonFactories.remove(beanName); // Remove from earlySingletonObjects // Store the original bean object to solve the circular dependency. Note: the object stored in it has not been filled with attributes this.earlySingletonObjects.remove(beanName); // The created Bean is placed in the registeredsinglets this.registeredSingletons.add(beanName); } }
4, createBean()
Let's look at the detailed code of createBean(): AbstractAutowireCapableBeanFactory class
// This method completes: creating bean instance, filling bean instance, calling post processor, etc protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){ RootBeanDefinition mbdToUse = mbd; // It's not important to check Class<?> resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); } // Handle the lookup method and replace method configurations, which Spring refers to collectively as override method try { mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { // Throwing anomaly } try { // Post processing is applied before bean initialization. If the Bean returned by post processing is not empty, it will be returned directly // Give BeanPostProcessors the opportunity to return a proxy instead of the target bean instance. // Notice this post processor: the instance aware bean post processor Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { // Throwing anomaly } try { // Call doCreateBean to create a bean // Please see the fourth part below Object beanInstance = doCreateBean(beanName, mbdToUse, args); return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { throw ex; } catch (Throwable ex) { // Throwing anomaly } }
5, doCreateBean()
Let's look at the detailed code of doCreateBean(): AbstractAutowireCapableBeanFactory class
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // Instantiate bean BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { /** * Create a bean instance and wrap the instance in the BeanWrapper implementation class object. * createBeanInstance There are three ways to create bean instances: * 1. Create bean instances through factory methods * 2. Create bean instances through automatic wire by constructor * 3. Create a bean instance through a parameterless constructor * If lookup method and replace method are configured in bean configuration information, CGLIB will be used * Enhanced bean instance. * createBeanInstance()Look at the sixth point */ instanceWrapper = createBeanInstance(beanName, mbd, args); } // Here, the Bean instance has been created!!!!! Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // The post processor modifies the merged bean definition synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { // Throwing anomaly } mbd.postProcessed = true; } } // BeanDefinition is singleton, supports circular references, and is being created // Seeing the attribute allowCircularReferences, do you guess that Spring can be configured to support circular references? boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { // Put the Bean being created into the collection. At this time, the attribute of the Bean has not been assigned addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Start initializing the Bean object, that is, property filling Object exposedObject = bean; try { // Attribute filling is very important!!!!! Look at the eighth dot populateBean(beanName, mbd, instanceWrapper); // Execute the post processor. aop is the processing completed here. Look at the ninth point exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { // Throw exception code omitted } if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { // Throw exception code omitted } } } } // Register bean as disposable. try { registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { // Throw exception code omitted } return exposedObject; } protected void addSingletonFactory(String beanName, ObjectFactory<?> singletonFactory) { synchronized (this.singletonObjects) { if (!this.singletonObjects.containsKey(beanName)) { // Put it in singletonFactories this.singletonFactories.put(beanName, singletonFactory); // The getSingleton() method is put on the first call // Remove from earlySingletonObjects this.earlySingletonObjects.remove(beanName); // Put it in registeredsinglets this.registeredSingletons.add(beanName); } } }
6, createBeanInstance()
// Use the appropriate instantiation strategy to create a new instance for the specified bean: factory method, constructor, automatic assembly, and simple instantiation. protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // Gets the Class converted by BeanDefinition Class<?> beanClass = resolveBeanClass(mbd, beanName); // Check the access permission of a class. spring does not allow access to non-public classes by default. if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { // Throwing anomaly } Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } // If the factory method is not empty, the bean object is built through the factory method if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } /** * From the original annotation of Spring, we can know that this is a Shortcut. What does it mean? * This Shortcut can be used when building the same bean multiple times, * That is, it is no longer necessary to infer which way bean s should be constructed each time * For example, when building a bean of the same prototype type multiple times, you can use the shortcut here * Resolved and MBD. Constructorarguments resolved here will be instantiated for the first time in the bean * Is set during initialization */ // A class may have multiple constructors, so Spring has to determine the constructor to call according to the number and type of parameters // After using the constructor to create an instance, Spring will save the constructor or factory method determined after parsing in the cache to avoid parsing again when creating the same bean again boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { // class constructor that has been resolved resolved = true; // If you have resolved the parameters of the construction method, you must use a construction method with parameters to the instance autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { // The class constructor that has been resolved shall be used if (autowireNecessary) { // Constructor auto injection return autowireConstructor(beanName, mbd, null, null); } else { // By default parameterless construction method return instantiateBean(beanName, mbd); } } // The post processor determines which constructor to return and which constructor to instantiate. This method is the process of selecting the constructor // The constructor needs to be parsed and determined according to the parameters Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); // The resolved constructor is not empty 𞓜 the injection type is constructor | automatic injection | there are constructor parameters in the bean definition | the incoming parameters are not empty if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { // Constructor auto injection return autowireConstructor(beanName, mbd, ctors, args); } // Initialize using the default parameterless constructor return instantiateBean(beanName, mbd); }
The code logic of the createBeanInstance() method is as follows:
- If there is an InstanceSupplier in the Bean definition, this callback interface will be used to create objects. It should be added after 3.X, but not in the source code of 3.X
- Create a Bean based on the configured factoryMethodName or factory mtehod
- Resolve constructor and instantiate
Because a class may have multiple constructors, it is necessary to determine the constructor to be called finally according to the parameters configured in the configuration file or the parameters passed in. Because the judgment process will consume performance, Spring will cache the parsed and determined constructor into the resolvedconstructorfactorymethod field in BeanDefinition. The next time the same Bean is created, it will be directly obtained from the cached value of the property resolvedconstructorfactorymethod in the RootBeanDefinition to avoid parsing again.
7, autowireConstructor()
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) { // Create a BeanWrapperImpl. The BeanWrapper returned externally in front of it is actually the BeanWrapperImpl // Because BeanWrapper is an interface BeanWrapperImpl bw = new BeanWrapperImpl(); this.beanFactory.initBeanWrapper(bw); Constructor<?> constructorToUse = null; ArgumentsHolder argsHolderToUse = null; Object[] argsToUse = null; // If the parameter passed in getBean() is not empty, the passed in parameter is used // Determine parameter value list // argsToUse can be set in two ways // The first is set through beanDefinition // The second is set by xml if (explicitArgs != null) { argsToUse = explicitArgs; } else { // Otherwise, you need to resolve the parameters in the configuration file Object[] argsToResolve = null; synchronized (mbd.constructorArgumentLock) { // There is usually no way to obtain the resolved constructor, because the constructor usually provides a // Unless there are more than one, there will be construction methods that have been parsed constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod; if (constructorToUse != null && mbd.constructorArgumentsResolved) { // When the constructor is found in the cache, continue to look for the cached constructor parameters from the cache argsToUse = mbd.resolvedConstructorArguments; if (argsToUse == null) { // If there are no cached parameters, you need to obtain the parameters configured in the configuration file argsToResolve = mbd.preparedConstructorArguments; } } } // If there are no cached parameters in the cache, that is, argsToResolve is not empty, you need to resolve the configured parameters if (argsToResolve != null) { // Resolve parameter types, such as converting the configured String type to int, boolean and other types argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve); } } // If there is no cache, you need to start parsing from the constructor if (constructorToUse == null) { // If there is no resolved constructor, you need to resolve the constructor // Judge whether the construction method is empty and whether it is automatically injected according to the construction method boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR); ConstructorArgumentValues resolvedValues = null; // The minimum number of parameters is defined // If you give a specific value to the parameter list of the constructor // Then the number of these values is the number of constructor parameters int minNrOfArgs; if (explicitArgs != null) { // Parameters passed in by getBean method minNrOfArgs = explicitArgs.length; } else { // Parameters in the configuration file ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues(); // The value used to carry the resolved constructor parameter resolvedValues = new ConstructorArgumentValues(); /** * Determine the number of construction method parameters, assuming the following configuration: * <bean id="scorpios" class="com.scorpios.Scorpios"> * <constructor-arg index="0" value="str1"/> * <constructor-arg index="1" value="1"/> * <constructor-arg index="2" value="str2"/> * </bean> * * If a value case is given inside spring, it means that the minimum number of parameters of your construction method is certain * minNrOfArgs = 3 */ // Resolve the parameters in the configuration file and return the number of parameters minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } // If the passed in constructor array is not empty, the passed in constructor parameters are used. Otherwise, the constructor defined in the class is obtained through reflection Constructor<?>[] candidates = chosenCtors; if (candidates == null) { Class<?> beanClass = mbd.getBeanClass(); try { // Use public constructors or all constructors candidates = (mbd.isNonPublicAccessAllowed() ? beanClass.getDeclaredConstructors() : beanClass.getConstructors()); } } // Sort according to the access permission level and the number of parameters of the construction method // How is it sorted? /** * Limited rhetorical permission, followed by the number of parameters * You can write a test to see if it's the same as I said * 1. public Scorpios(Object o1, Object o2, Object o3) * 2. public Scorpios(Object o1, Object o2) * 3. public Scorpios(Object o1) * 4. protected Scorpios(Integer i, Object o1, Object o2, Object o3) * 5. protected Scorpios(Integer i, Object o1, Object o2) * 6. protected Scorpios(Integer i, Object o1) */ // Sort constructors. public constructors take precedence and the number of parameters is sorted in descending order AutowireUtils.sortConstructors(candidates); //A difference variable is defined. This variable has great weight and is annotated later int minTypeDiffWeight = Integer.MAX_VALUE; Set<Constructor<?>> ambiguousConstructors = null; LinkedList<UnsatisfiedDependencyException> causes = null; //Loop all construction methods for (Constructor<?> candidate : candidates) { Class<?>[] paramTypes = candidate.getParameterTypes(); /** * This judgment is hard to understand, although it has only one line of code * First, constructortouse= This is easy to understand * As mentioned earlier, constructorToUse is mainly used to load the construction methods that have been resolved and are being used * Only when it is equal to null can it have the meaning of continuation, because if a conforming construction method is parsed below * Will be assigned to this variable. Therefore, if this variable is not equal to null, there is no need to resolve it, indicating that spring has * Find a suitable construction method and use it directly * argsToUse.length > paramTypes.length This code is quite complex * First, assume argsToUse = [1,"luban", obj], which requires a constructor with three parameters * Then it will match 1 and 5 of the above construction method * Since constructor 1 has higher access rights, all select 1, although 5 looks more matched * But when we look at 2, the number of direct parameters is wrong, so we ignore it directly */ if (constructorToUse != null && argsToUse.length > paramTypes.length) { break; } if (paramTypes.length < minNrOfArgs) { continue; } // Encapsulates the parsed parameter information ArgumentsHolder argsHolder; if (resolvedValues != null) { try { // Judge whether the ConstructorProperties annotation is added. If so, take out the value // You can write a code to test it // @ConstructorProperties(value = {"xxx", "111"}) String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length); if (paramNames == null) { ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer(); if (pnd != null) { //Gets the list of constructor parameter names /** * Suppose you have a (String scorpios,Object hua) * paramNames=[scorpios,hua] */ paramNames = pnd.getParameterNames(candidate); } } // Gets the list of construction method parameter values /** * This method is complex * Because spring can only provide parameter values of strings * Therefore, conversion is required * argsHolder The value contained is the value after conversion */ argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, getUserDeclaredConstructor(candidate), autowiring); } } else { // Explicit arguments given -> arguments length must match exactly. if (paramTypes.length != explicitArgs.length) { continue; } argsHolder = new ArgumentsHolder(explicitArgs); } /** * typeDiffWeight Difference quantity, what is difference quantity? * argsHolder.arguments Differences between and paramTypes * The difference between the type of each parameter value and the type of the constructor parameter list * A suitable construction method is measured or determined by this difference * * It is worth noting that constructorToUse=candidate * The first cycle must typediffweight < minTypeDiffWeight, because the value of minTypeDiffWeight is very large, Integer.MAX_VALUE * Then, each loop assigns typeDiffWeight to minTypeDiffWeight (minTypeDiffWeight = typeDiffWeight) * else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) * The first cycle certainly won't enter this * The second time, if you enter this branch, what does it represent? * Does it mean that two construction methods meet our requirements? Then spring is confused * spring Confused how to do? * ambiguousConstructors.add(candidate); * ambiguousConstructors=null Very important? * Why is it important because it needs to be emptied * This also explains why it finds two methods that meet the requirements and does not throw exceptions directly * If this ambiguousConstructors always exists, spring will go to exception outside the loop */ // Because the number of parameters of different constructors is the same, and the parameter type is parent-child relationship, you need to find the constructor with the most appropriate type //Spring uses a form of weight to express the degree of type difference. The smaller the difference weight, the better int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); // If the current constructor matches the most, clear the previous ambiguousConstructors list if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; argsHolderToUse = argsHolder; argsToUse = argsHolder.arguments; minTypeDiffWeight = typeDiffWeight; ambiguousConstructors = null; } else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) { // If there are constructors with the same weight, add the constructor to an ambiguousConstructors list variable // Note that constructor touse still points to the first matching constructor if (ambiguousConstructors == null) { ambiguousConstructors = new LinkedHashSet<>(); ambiguousConstructors.add(constructorToUse); } ambiguousConstructors.add(candidate); } } // At the end of the cycle, no suitable construction method was found if (constructorToUse == null) { //If there is no matching constructor, an exception is thrown. slightly } // Exception if ambiguousConstructors still exist? Why is exception directly in the above method? // It is explained in the notes above else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) { // If there are multiple constructors with the same matching degree, and isLenientConstructorResolution is set to false in BeanDefinition (the default value is true), // Indicates that if the constructor is created as a strict pattern, an exception will be thrown. Exception code omitted } // This step is to put the parsed constructor into the cache resolvedconstructorfactorymethod, and cache the parameters if necessary // And set constructorargumentresolved to true, indicating that the constructor has been resolved if (explicitArgs == null) { /* * Cache related information, such as: * 1. Resolved constructor object resolvedconstructorfactorymethod * 2. Whether the constructor parameter list has resolved the flag constructorArgumentsResolved * 3. Parameter value list resolvedConstructorArguments or preparedConstructorArguments * This information can be used elsewhere for quick judgment */ argsHolderToUse.storeCache(mbd, constructorToUse); } } try { // Using reflection to create instance lookup method to enhance bean instance through CGLIB final InstantiationStrategy strategy = beanFactory.getInstantiationStrategy(); Object beanInstance; if (System.getSecurityManager() != null) { final Constructor<?> ctorToUse = constructorToUse; final Object[] argumentsToUse = argsToUse; beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () -> strategy.instantiate(mbd, beanName, beanFactory, ctorToUse, argumentsToUse), beanFactory.getAccessControlContext()); } else { beanInstance = strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse); } bw.setBeanInstance(beanInstance); return bw; } }
The autowireConstructor() method code is very long. The overall functional logic is as follows
- Determine parameters
- If the parameter passed in when calling the getBean() method is not empty, you can directly use the parameter passed in
- Try again to get the parameters from the cache
- Otherwise, you need to resolve the constructor parameters configured by the < bean > node
- Determine the constructor. According to the parameters determined in the first step, the next task is to determine the constructor to be called finally according to the number and type of parameters. Firstly, all constructors are sorted in ascending order according to the number of parameters, and then the constructors with matching number of parameters are filtered. Because the parameter value can be set through the parameter location index or parameter name in the configuration file, such as < constructor name = "title" >, the parameter name also needs to be resolved. Finally, the constructor can be determined according to the resolved parameter name, parameter type and actual parameters, and the parameters can be converted to the corresponding types
- Convert the determined constructor to the corresponding parameter type
- Verification of constructor uncertainty. Because the parameter types of some constructors are parent-child relationship, Spring will do a verification
- If the conditions are met (the passed in parameters are empty), the parsed constructor and parameters are put into the cache
- Instantiate the constructor and parameters into beans according to the instantiation strategy
This method is to find the most appropriate constructor to instantiate a Bean. It needs to determine the number of parameters of the constructor.
8, populateBean()
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { if (bw == null) { if (mbd.hasPropertyValues()) { // Throwing anomaly } else { // Empty object returns directly return; } } // Give instantiaawarebeanpostprocessors one last chance to modify the Bean's property value before property injection // Specifically, by calling the postprocessafterinstance method, if the call returns false, it means that there is no need to continue dependency injection and return directly if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } } // pvs is an instance of MutablePropertyValues, which implements the PropertyValues interface and provides the implementation of property read-write operations. At the same time, it can call the constructor to realize deep copy PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); // Complete the injection according to the dependency injection method configured by the Bean. The default is 0, that is, the following logic is not followed. All dependency injection needs to be explicitly configured in the xml file // If the related dependency assembly method is set, the properties in the Bean will be traversed, and the corresponding injection will be completed according to the type or name without additional configuration int resolvedAutowireMode = mbd.getResolvedAutowireMode(); if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { // Deep copy existing configuration MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // Injection by name if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // Injection according to type if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } // Overwrite the current configuration in combination with the injected configuration pvs = newPvs; } // Does the container register the instantiaawarebeanpostprocessor boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); // Dependency check boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); if (hasInstAwareBpps || needsDepCheck) { if (pvs == null) { pvs = mbd.getPropertyValues(); } // Filter out all attribute editors that need dependency checking PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); if (hasInstAwareBpps) { for (BeanPostProcessor bp : getBeanPostProcessors()) { // If there is a related post processor, perform post-processing if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; } } } } if (needsDepCheck) { // Check whether the relevant dependencies are satisfied. For the corresponding dependencies on attribute, you need to ensure that all dependent beans are initialized first checkDependencies(beanName, mbd, filteredPds, pvs); } } if (pvs != null) { // Fill all the attributes on pvs into the Bean instance corresponding to BeanWrapper. Note that the student attribute of TestBean is still RuntimeBeanReference, that is, the actual student instance has not been resolved applyPropertyValues(beanName, mbd, bw, pvs); } }
Spring assembly problems:
public static final int AUTOWIRE_NO = 0;
public static final int AUTOWIRE_BY_NAME = 1;
public static final int AUTOWIRE_BY_TYPE = 2;
public static final int AUTOWIRE_CONSTRUCTOR = 3;
Spring's default assembly type is AUTOWIRE_NO means no default assembly, unless you manually add @ Autowired, @ Resouce and other annotations to the code
AUTOWIRE_BY_TYPE represents assembly by type. When instantiating a class, it is based on whether the attribute has a set method. If so, it is assembled
9, initializeBean()
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // Permission verification if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { // Call the postProcessBeforeInitialization() method of BeanPostProcessor wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { // Call the initialization method, first execute the method that implements the InitializingBean interface, and then execute the custom init method method invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { // Throwing anomaly } if (mbd == null || !mbd.isSynthetic()) { // Call the postProcessAfterInitialization() method of BeanPostProcessor wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
10, Invokeinitialmethods()
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { // Is InitializingBean implemented boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (System.getSecurityManager() != null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { // Call the afterpropertieset() method ((InitializingBean) bean).afterPropertiesSet(); } } if (mbd != null && bean.getClass() != NullBean.class) { // Is there a custom init method method String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { // Execute the custom init method method invokeCustomInitMethod(beanName, bean, mbd); } } }
11, Summary
The above getBean () method has finally been analyzed. doCreateBean() method is more important. We should focus on it. autowireConstructor() method is difficult to understand, but it's good to know its role
Let's summarize this process with a picture.
In the above figure, the red and blue boxes represent the problem of circular dependency, which is briefly described here.
X and Y depend on each other. Instantiate x first. In the process of instantiating x, you will find that x depends on y, so you instantiate y, which is the red dotted line in the figure
In the process of instantiating y, it is found that y depends on X, but x is being created and saved in singletonFactories, so x in singletonFactories is assigned to y, so y's dependence is solved. Therefore, the instantiation of Y ends and Y returns normally
Let's continue the process of instantiating X.