Original address programmer Jiong Hui boss
preface
Go ahead Explanation of getBean Enter the createBean method.
Code block 1: createBean
@Override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { if (logger.isDebugEnabled()) { logger.debug("Creating instance of bean '" + beanName + "'"); } RootBeanDefinition mbdToUse = mbd; // Make sure bean class is actually resolved at this point, and // clone the bean definition in case of a dynamically resolved Class // which cannot be stored in the shared merged bean definition. 1,analysis beanName Corresponding bean Type, for example com.zgf.service.impl.AccountServiceImpl Class<?> resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { 2,resolvedClass Not empty&&mbd of beanClass no Class type&&mbd of beanClassName If it is not empty, then mbd of beanClass Saved is class of Name Copy and replace mbd Subsequent operations mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); } // Prepare method overrides. try { mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } try { // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. 3,use InstantiationAwareBeanPostProcessor Create a proxy bean Intercept and return to'short circuit'effect. Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } try { 4,Really create bean Method of Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean '" + beanName + "'"); } 5,return bean example return beanInstance; } catch (BeanCreationException ex) { // A previously detected exception with proper bean creation context already... throw ex; } catch (ImplicitlyAppearedSingletonException ex) { // An IllegalStateException to be communicated up to DefaultSingletonBeanRegistry... throw ex; } catch (Throwable ex) { throw new BeanCreationException( mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); } }
3. Create a proxy bean with instantiaawarebeanpostprocessor to intercept and return, so as to achieve the effect of "short circuit". See code block II
4. See code block 3 for the method of creating bean s
Code block 2: resolvebeforeinstance
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // Make sure bean class is actually resolved at this point. 1,mbd Not synthetic&&BeanFactory Exist in InstantiationAwareBeanPostProcessors Interface if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class<?> targetType = determineTargetType(beanName, mbd); if (targetType != null) { 2,Preprocessing bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); if (bean != null) { 3,Post processing bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } 4,If bean If it is not empty, the beforeInstantiationResolved Assign as true,Before instantiation, the representation has been resolved mbd.beforeInstantiationResolved = (bean != null); } return bean; }
Before instantiation, execute the postProcessBeforeInstantiation method of instantiaawarebeanpostprocessor, which can return the proxy of the bean instance, thus skipping the default instantiation process of Spring.
Code block 3: doCreateBean
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. 1,bean Packaging class BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { 1.1,judge beanName Is it factoryBean,If yes, the removal is incomplete bean Cache of instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { 2,If not factoryBean Then create beanName Packaging class instanceWrapper = createBeanInstance(beanName, mbd, args); } 3.1,obtain bean example final Object bean = instanceWrapper.getWrappedInstance(); 3.2,obtain bean Instance type of Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { 3.3,If bean If the instance type is not empty, set the instance type to mbd mbd.resolvedTargetType = beanType; } // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { 4.1,MergedBeanDefinitionPostProcessor Post processing of. Modification is allowed bean 4.2,@Autowired It's officially pre parsed here applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. 5.1,judge bean Early circular reference in advance,Early exposure. 5.2,mbd It's a single case&&Allow circular dependencies&¤t bean Being created boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } 6.1,getEarlyBeanReference: utilize SmartInstantiationAwareBeanPostProcessor Post processing return of bean An earlier reference of the. If not, it returns bean itself 6.2,Early exposure bean,Add to singleton factory to resolve circular references addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { 7,Attribute filling populateBean(beanName, mbd, instanceWrapper); 8,initialization bean exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } 9.1,If early exposure is allowed, perform a cyclic dependency check if (earlySingletonExposure) { 9.2,earlySingletonReference Not in case of circular dependency null Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference != null) { if (exposedObject == bean) { 9.3,If you are initializing bean It didn't change at the time bean Object, the previous circular reference is not affected exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { 9.4,If bean If it is changed during initialization, the original circular reference will be affected, because the referenced old data needs to be eliminated and the latest data will be referenced bean object 9.5,Get depends on the current beanName All of bean of Name array String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { 9.6,Try removing these bean Because of these bean Dependent bean Has been enhanced, they rely on bean Equivalent to dirty data if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { 9.7,Remove failed add to actualDependentBeans in actualDependentBeans.add(dependentBean); } } if (!actualDependentBeans.isEmpty()) { 9.8,If the removal fails, an exception is thrown because it exists bean Dependent on "dirty data" throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } } // Register bean as disposable. try { 10,Register for destruction bean,There are three types of destruction operations: custom destroy Methods DisposableBean Interface DestructionAwareBeanPostProcessor registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } 11,Return created bean return exposedObject; }
The real method of creating bean s is the core method of this series. It is relatively complex and needs to be read repeatedly.
2. If it is not factoryBean, create the wrapper class of beanName. See code block 4.
Code block 4: createBeanInstance
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // Make sure bean class is actually resolved at this point. 1,analysis bean Type information of Class<?> beanClass = resolveBeanClass(mbd, beanName); if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { 2,If bean Type is empty&&beanClass Not public class&&Access by non-public classes is not allowed throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } 3, Yes no bean of Supplier Interface, if any, is created through callback bean Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } 4,If the factory method is not empty, an instance is created using the factory method if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } // Shortcut when re-creating the same bean... 5.1,resolved Whether the constructor has resolved the flag; autowireNecessary Whether automatic assembly is required boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { 5.2,The constructor or factory method has been resolved if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } if (resolved) { if (autowireNecessary) { 6.1,Should bean The constructor has been parsed and needs to be automatically assembled. Use constructor automatic injection for instantiation return autowireConstructor(beanName, mbd, null, null); } else { 6.2,Should bean The constructor has been parsed and does not need to be automatically assembled. The default constructor is used for instantiation return instantiateBean(beanName, mbd); } } // Need to determine the constructor... 7.1,Get bean Candidate constructor for Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { 7.2,ctors Not empty||mbd The injection method is AUTOWIRE_CONSTRUCTOR||mbd Constructor parameters defined||Dominant structural parameters args Not empty return autowireConstructor(beanName, mbd, ctors, args); } // No special handling: simply use no-arg constructor. 8.If it is not satisfied, the default constructor is used for initialization return instantiateBean(beanName, mbd); }
- If you inherit the Supplier interface, create a bean instance through the Supplier.
- If factoryMethodName exists in RootBeanDefinition
Property, or if factory method is configured in the configuration file, Spring will try to use the instantiateUsingFactoryMethod method to generate Bean instances according to the configuration in RootBeanDefinition. It should be noted that if the method in a class is modified by @ Bean annotation, Spring will encapsulate it into a ConfigurationClassBeanDefinition. At this time, factoryMethodName is also assigned. Therefore, the instantiateUsingFactoryMethod method will also be called to complete the method call through reflection and inject the results into the Spring container. - Instead of the above two methods, try to instantiate using the constructor. First, check whether the bean has a constructor cache. Here, the resolvedconstructorfactorymethod cache is used.
- If the cache exists, if automatic assembly is required, the autowireConstructor method is used for instance. On the contrary, the default instantiateBean method is used. (autowireConstructor with parameters and instantiateBean without parameters)
- The cache does not exist. First, obtain the candidate constructor through the determineconstructionsfrombeanpostprocessors method and make the final selection through autowireConstructor or instantiateBean. The candidate constructors are selected in the autowireConstructor, and the most appropriate constructors are selected to build beans. If the parsed constructors are cached, the parsed constructors are directly used to create beans without election.
6.1. See code block 5 for automatic constructor injection
7.1. See code block 6 for obtaining candidate constructors
Code block 5: determineconstructors frombeanpostprocessors
protected Constructor<?>[] determineConstructorsFromBeanPostProcessors(@Nullable Class<?> beanClass, String beanName) throws BeansException { if (beanClass != null && hasInstantiationAwareBeanPostProcessors()) { 1,Traverse all BeanPostProcessor for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; 2.call SmartInstantiationAwareBeanPostProcessor of determineCandidateConstructors method, This method can return the beanClass Candidate constructor for For example: use@Autowire If the annotation modifies the constructor, the constructor will be modified here AutowiredAnnotationBeanPostProcessor find Constructor<?>[] ctors = ibp.determineCandidateConstructors(beanClass, beanName); if (ctors != null) { 3.If ctors If it is not empty, no other operations will be performed SmartInstantiationAwareBeanPostProcessor return ctors; } } } } return null; }
2. Call the determineCandidateConstructors method of smartinstantiaawarebeanpostprocessor, which can return the candidate constructor to be used for beanClass. If @ Autowire annotation is used to modify the constructor, the constructor will be found here by AutowiredAnnotationBeanPostProcessor. This content will be introduced separately in the article introducing @ Autowire later.
Code block 6: autowireConstructor
public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable final Object[] explicitArgs) { 1,establish bean Packaging class BeanWrapperImpl bw = new BeanWrapperImpl(); this.beanFactory.initBeanWrapper(bw); 2.1,Final instantiated constructor Constructor<?> constructorToUse = null; 2.2,Parameter holder of final instantiation ArgumentsHolder argsHolderToUse = null; 2.4,Final instantiated constructor parameter array Object[] argsToUse = null; 3,If an argument to the constructor is explicitly specified, it is used if (explicitArgs != null) { argsToUse = explicitArgs; } else { 4.1,Trying to get constructor parameters from cache Object[] argsToResolve = null; synchronized (mbd.constructorArgumentLock) { 4.2,Get the resolved constructor or factory method from the cache constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod; 4.3,If constructorToUse Not empty&&Constructor arguments resolved if (constructorToUse != null && mbd.constructorArgumentsResolved) { // Found a cached constructor... 4.4,Get the resolved constructor parameters argsToUse = mbd.resolvedConstructorArguments; if (argsToUse == null) { 4.5,If argsToUse If it is empty, get the constructor parameters that are ready to be resolved Here if mbd.constructorArgumentsResolved by true When, the constructor parameter must have been resolved or ready to be resolved, and there must be a cached parameter argsToResolve = mbd.preparedConstructorArguments; } } } if (argsToResolve != null) { 4.6,Parse prepared constructor parameters argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve); } } 5.1,If there is no constructor in the cache if (constructorToUse == null) { // Need to resolve the constructor. 5.2,If chosenCtors Not empty||autowireMode yes AUTOWIRE_CONSTRUCTOR,Automatic injection is required boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); ConstructorArgumentValues resolvedValues = null; 5.3,Number of constructor parameters int minNrOfArgs; if (explicitArgs != null) { 5.4,If the construction parameter is explicitly specified, the length is obtained minNrOfArgs = explicitArgs.length; } else { 5.5,mbd Construction parameter value of indexedArgumentValues Cache is with index Value of genericArgumentValues Cached values are generic ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues(); 5.6,Value of construction parameter after bearing analysis resolvedValues = new ConstructorArgumentValues(); 5.7,analysis mbd And returns the number of parameters minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); // Note: parsing the constructor parameter values in mbd here mainly deals with the parameters injected by the constructor defined in xml, // However, if we directly modify the constructor through @ Autowire annotation, mbd does not have these parameter values } // Take specified constructors, if any. 6.1,Get all candidate constructors, if chosenCtors If it is not empty, it is a candidate Constructor<?>[] candidates = chosenCtors; if (candidates == null) { Class<?> beanClass = mbd.getBeanClass(); try { 6.2,If the constructor can be accessed by non-public, get all, otherwise only get the common constructor candidates = (mbd.isNonPublicAccessAllowed() ? beanClass.getDeclaredConstructors() : beanClass.getConstructors()); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Resolution of declared constructors on bean Class [" + beanClass.getName() + "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex); } } 7,Sort the candidate constructors, public Constructor parameters from more to less, non public Parameters from more to less AutowireUtils.sortConstructors(candidates); int minTypeDiffWeight = Integer.MAX_VALUE; 8.Ambiguous constructors, two constructors with equal weight and minimum tangent Set<Constructor<?>> ambiguousConstructors = null; LinkedList<UnsatisfiedDependencyException> causes = null; 9.1,Traverse all candidate constructors for (Constructor<?> candidate : candidates) { Class<?>[] paramTypes = candidate.getParameterTypes(); 9.2,A suitable constructor has been found&&If the number of constructor parameters is greater than the number of current constructor parameters, jump out of the loop Because the appropriate constructor has been found and the number of parameters is in descending order, there is no need to compare if (constructorToUse != null && argsToUse.length > paramTypes.length) { // Already found greedy constructor that can be satisfied -> // do not look any further, there are only less greedy constructors left. break; } 9.3,The number of current candidate construction parameters is less than the required construction parameters. It is definitely inappropriate to skip directly if (paramTypes.length < minNrOfArgs) { continue; } ArgumentsHolder argsHolder; if (resolvedValues != null) { 10.1, resolvedValues Not empty try { 10.2,Analytical use ConstructorProperties Constructor parameters for annotations String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length); if (paramNames == null) { 10.3,Get parameter name parser ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer(); if (pnd != null) { 10.4,Use the parameter name parser to get the parameter name of the constructor currently traversed paramNames = pnd.getParameterNames(candidate); } } 10.5,It mainly resolves the parameters required by the constructor or factory method through the parameter type and parameter name (if the parameters are other parameters) bean,The dependent will be resolved bean) argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, getUserDeclaredConstructor(candidate), autowiring); } catch (UnsatisfiedDependencyException ex) { if (this.beanFactory.logger.isTraceEnabled()) { this.beanFactory.logger.trace( "Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex); } // Swallow and try next constructor. if (causes == null) { causes = new LinkedList<>(); } causes.add(ex); continue; } } else { // Explicit arguments given -> arguments length must match exactly. 11.1,If resolvedValues Null. The current function parameter is not equal to the explicitly specified parameter. Skip and continue to find if (paramTypes.length != explicitArgs.length) { continue; } 11.2,Construct with explicit parameters ArgumentsHolder argsHolder = new ArgumentsHolder(explicitArgs); } 12.1,take argsHolder Parameters and paramTypes Compare and calculate paramTypes Weight value of type difference int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); // Choose this constructor if it represents the closest match. 12.2,The smaller the weight value of type difference,It means that the more the constructor matches, this constructor will be selected if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; argsHolderToUse = argsHolder; argsToUse = argsHolder.arguments; minTypeDiffWeight = typeDiffWeight; 12.3,If a candidate with a smaller weight value appears, the ambiguousConstructors Empty, allowing candidates with the same weight value to exist before ambiguousConstructors = null; } 12.4,If there are two candidates with the same weight value and the smallest weight value that has been traversed at present else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) { if (ambiguousConstructors == null) { ambiguousConstructors = new LinkedHashSet<>(); 12.5,Add to ambiguousConstructors ambiguousConstructors.add(constructorToUse); } ambiguousConstructors.add(candidate); } } if (constructorToUse == null) { if (causes != null) { If no matching constructor is found, exception handling is performed UnsatisfiedDependencyException ex = causes.removeLast(); for (Exception cause : causes) { this.beanFactory.onSuppressedException(cause); } throw ex; } throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Could not resolve matching constructor " + "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)"); } else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) { If a matching constructor is found, but more than one exists( ambiguousConstructors (not null) && If the mode of the parsing constructor is strict mode, an exception is thrown throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Ambiguous constructor matches found in bean '" + beanName + "' " + "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " + ambiguousConstructors); } if (explicitArgs == null) { 13,Put the parsed constructor and parameters into the cache argsHolderToUse.storeCache(mbd, constructorToUse); } } try { final InstantiationStrategy strategy = beanFactory.getInstantiationStrategy(); Object beanInstance; 14,Instantiate according to the instantiation strategy and the obtained constructor and constructor parameters bean. 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); } 15,Set instance to wrapper class return bw.setBeanInstance(beanInstance); return bw; } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean instantiation via constructor failed", ex); } }
General process:
- Whether the construction parameter is explicitly specified. If it is explicitly specified, explicatargs is used as the construction parameter.
- If the construction parameters are not specified, they will be obtained from the cache, that is, they will be resolved from resolvedConstructorArguments and preparedConstructorArguments.
- If the constructor is not obtained from the cache, the chosenCtors of the input parameter will be used as the candidate constructor. If chosenCtors is null, the candidate constructor will be obtained through reflection.
- For candidates, the number of priority parameters of public constructor is in descending order, and the number of parameters of non-public constructor is in descending order
The purpose of regular sorting is to judge whether there is an appropriate constructor more quickly when retrieving later. - Traverse the constructor and select the constructor with the smallest matching difference to create the bean instance.
5.7. Analyze the parameters of the constructor of mbd and return the number of parameters. See code block 7
10.5. Mainly resolve the parameters required by the constructor or factory method through the parameter type and parameter name (if the parameter is other beans, it will resolve the dependent beans). See code block 8
13. Put the parsed constructor and parameters into the cache, see the code block
Code block 7: resolveConstructorArguments
private int resolveConstructorArguments(String beanName, RootBeanDefinition mbd, BeanWrapper bw, ConstructorArgumentValues cargs, ConstructorArgumentValues resolvedValues) { // 1. Build bean definition value parser TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); TypeConverter converter = (customConverter != null ? customConverter : bw); BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this.beanFactory, beanName, mbd, converter); // 2.minNrOfArgs is initialized to the sum of indexedArgumentValues and genericArgumentValues int minNrOfArgs = cargs.getArgumentCount(); // 3. Traverse and parse the parameter value with index for (Map.Entry<Integer, ConstructorArgumentValues.ValueHolder> entry : cargs.getIndexedArgumentValues().entrySet()) { int index = entry.getKey(); if (index < 0) { // index starts from 0 and cannot be less than 0 throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid constructor argument index: " + index); } // 3.1 if the index is greater than minNrOfArgs, modify minNrOfArgs if (index > minNrOfArgs) { // The index starts from 0 and increases in order, so when the index with parameters = 5, it means that the method has at least 6 parameters minNrOfArgs = index + 1; } ConstructorArgumentValues.ValueHolder valueHolder = entry.getValue(); // 3.2 analytical parameter values if (valueHolder.isConverted()) { // 3.2.1 if the parameter value has been converted, add index and valueHolder directly to the indexedArgumentValues property of resolvedValues resolvedValues.addIndexedArgumentValue(index, valueHolder); } else { // 3.2.2 if the value has not been converted, convert it first Object resolvedValue = valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue()); // 3.2.3 use the converted resolvedValue to build a new ValueHolder ConstructorArgumentValues.ValueHolder resolvedValueHolder = new ConstructorArgumentValues.ValueHolder(resolvedValue, valueHolder.getType(), valueHolder.getName()); // 3.2.4 save the valueholder before conversion to the source attribute of the new valueholder resolvedValueHolder.setSource(valueHolder); // 3.2.5 add index and new ValueHolder to indexedArgumentValues property of resolvedValues resolvedValues.addIndexedArgumentValue(index, resolvedValueHolder); } } // 4. Traverse and parse general parameter values (without index) for (ConstructorArgumentValues.ValueHolder valueHolder : cargs.getGenericArgumentValues()) { if (valueHolder.isConverted()) { // 4.1 if the parameter value has been converted, add valueHolder directly to the genericArgumentValues attribute of resolvedValues resolvedValues.addGenericArgumentValue(valueHolder); } else { // 4.2 if the value has not been converted, convert it first Object resolvedValue = valueResolver.resolveValueIfNecessary("constructor argument", valueHolder.getValue()); // 4.3 use the converted resolvedValue to build a new ValueHolder ConstructorArgumentValues.ValueHolder resolvedValueHolder = new ConstructorArgumentValues.ValueHolder(resolvedValue, valueHolder.getType(), valueHolder.getName()); // 4.4 save the valueholder before conversion to the source attribute of the new valueholder resolvedValueHolder.setSource(valueHolder); // 4.5 add a new ValueHolder to the genericArgumentValues property of resolvedValues resolvedValues.addGenericArgumentValue(resolvedValueHolder); } } // 5. Return the number of constructor parameters return minNrOfArgs; }
Code block 8: createArgumentArray
private ArgumentsHolder createArgumentArray( String beanName, RootBeanDefinition mbd, @Nullable ConstructorArgumentValues resolvedValues, BeanWrapper bw, Class<?>[] paramTypes, @Nullable String[] paramNames, Executable executable, boolean autowiring) throws UnsatisfiedDependencyException { TypeConverter customConverter = this.beanFactory.getCustomTypeConverter(); TypeConverter converter = (customConverter != null ? customConverter : bw); 1,Used to store matching parameters ArgumentsHolder args = new ArgumentsHolder(paramTypes.length); Set<ConstructorArgumentValues.ValueHolder> usedValueHolders = new HashSet<>(paramTypes.length); Set<String> autowiredBeanNames = new LinkedHashSet<>(4); 2.1,Traversal class construction parameter array for (int paramIndex = 0; paramIndex < paramTypes.length; paramIndex++) { 2.2,Get the current parameter type Class<?> paramType = paramTypes[paramIndex]; 2.3,Get the current parameter name String paramName = (paramNames != null ? paramNames[paramIndex] : ""); // Try to find matching constructor argument value, either indexed or generic. ConstructorArgumentValues.ValueHolder valueHolder = null; if (resolvedValues != null) { 2.4,Verify the parameter name and type of the current traversal, index,Whether it matches the configuration (i.e. comparison) xml Configuration parameters and construction methods in the class) valueHolder = resolvedValues.getArgumentValue(paramIndex, paramType, paramName, usedValueHolders); // If we couldn't find a direct match and are not supposed to autowire, // let's try the next generic, untyped argument value as fallback: // it could match after type conversion (for example, String -> int). 2.5,If no match is found&&It's not automatic assembly. Try demotion, for example String matching int type if (valueHolder == null && (!autowiring || paramTypes.length == resolvedValues.getArgumentCount())) { valueHolder = resolvedValues.getGenericArgumentValue(null, null, usedValueHolders); } } if (valueHolder != null) { // We found a potential match - let's give it a try. // Do not consider the same value definition multiple times! usedValueHolders.add(valueHolder); 3.1,Get the original value Object originalValue = valueHolder.getValue(); 3.2,Used to store the converted value Object convertedValue; if (valueHolder.isConverted()) { 3.3,If it has been converted, get the converted value convertedValue = valueHolder.getConvertedValue(); 3.4,Store in args The corresponding position is used as the preliminary parameter args.preparedArguments[paramIndex] = convertedValue; } else { 3.5,Without conversion, encapsulate the method (constructor here) and parameter index into MethodParameter(MethodParameter Is a tool class that encapsulates method and parameter indexes) MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex); try { 3.5,Convert convertedValue = converter.convertIfNecessary(originalValue, paramType, methodParam); } catch (TypeMismatchException ex) { throw new UnsatisfiedDependencyException( mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), "Could not convert argument value of type [" + ObjectUtils.nullSafeClassName(valueHolder.getValue()) + "] to required type [" + paramType.getName() + "]: " + ex.getMessage()); } Object sourceHolder = valueHolder.getSource(); if (sourceHolder instanceof ConstructorArgumentValues.ValueHolder) { 3.5,If sourceHolder yes ConstructorArgumentValues.ValueHolder The representation of the type has not been resolved Object sourceValue = ((ConstructorArgumentValues.ValueHolder) sourceHolder).getValue(); 3.6,Flag needs to be resolved args.resolveNecessary = true; 3.7,Save to preliminary parameters args.preparedArguments[paramIndex] = sourceValue; } } 3.8,take convertedValue As args stay paramIndex Parameters of position args.arguments[paramIndex] = convertedValue; 3.9,take originalValue As args stay paramIndex Original parameters of position args.rawArguments[paramIndex] = originalValue; } else { 4.1,There are no matching parameters, valueHolder by null MethodParameter methodParam = MethodParameter.forExecutable(executable, paramIndex); // No explicit match found: we're either supposed to autowire or // have to fail creating an argument array for the given constructor. 4.2,If it is not automatic injection, an exception will be thrown directly, because there are two ways to configure construction parameters, automatic injection and automatic injection xml The non automatic injection configuration has been resolved earlier xml The configuration now resolves automatic injection if (!autowiring) { throw new UnsatisfiedDependencyException( mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), "Ambiguous argument values for parameter of type [" + paramType.getName() + "] - did you specify the correct bean references as arguments?"); } try { 4.3,If the parameter is used for automatic assembly, the result returned is bean Instance object For example:@Autowire Modify the constructor and automatically inject the parameters in the constructor bean Just deal with it here Object autowiredArgument = resolveAutowiredArgument(methodParam, beanName, autowiredBeanNames, converter); 4.4,Assign the parameters parsed through automatic assembly to args args.rawArguments[paramIndex] = autowiredArgument; args.arguments[paramIndex] = autowiredArgument; args.preparedArguments[paramIndex] = new AutowiredArgumentMarker(); args.resolveNecessary = true; } catch (BeansException ex) { throw new UnsatisfiedDependencyException( mbd.getResourceDescription(), beanName, new InjectionPoint(methodParam), ex); } } } 5.1,Dependent bean Register dependencies for (String autowiredBeanName : autowiredBeanNames) { this.beanFactory.registerDependentBean(autowiredBeanName, beanName); if (this.beanFactory.logger.isDebugEnabled()) { this.beanFactory.logger.debug("Autowiring by type from bean name '" + beanName + "' via " + (executable instanceof Constructor ? "constructor" : "factory method") + " to bean named '" + autowiredBeanName + "'"); } } return args; }
4.3. In case of automatic assembly, call the method used to parse the automatic assembly parameters, and the returned result is the dependent bean instance object. See code block 9
Code block 9: resolveAutowiredArgument
protected Object resolveAutowiredArgument( MethodParameter param, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) { 1,If the parameter type is InjectionPoint if (InjectionPoint.class.isAssignableFrom(param.getParameterType())) { 1,1 Get the current InjectionPoint(Stores the parameter information of the dependent method currently being resolved, DependencyDescriptor) InjectionPoint injectionPoint = currentInjectionPoint.get(); if (injectionPoint == null) { 1,2 current injectionPoint If it is empty, an exception will be thrown: there is no available InjectionPoint throw new IllegalStateException("No current InjectionPoint available for " + param); } 1,3 Returns the current InjectionPoint return injectionPoint; } 2,Resolve specified dependencies, DependencyDescriptor: take MethodParameter The method parameter index information is encapsulated into DependencyDescriptor return this.beanFactory.resolveDependency( new DependencyDescriptor(param, true), beanName, autowiredBeanNames, typeConverter); }
2. See code block 10 for resolving the specified dependency
Code block 10: resolveDependency
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); 1,Optional Special handling of class injection if (Optional.class == descriptor.getDependencyType()) { return createOptionalDependency(descriptor, requestingBeanName); } 2,ObjectFactory perhaps ObjectProvider Treatment of else if (ObjectFactory.class == descriptor.getDependencyType() || ObjectProvider.class == descriptor.getDependencyType()) { return new DependencyObjectProvider(descriptor, requestingBeanName); } 3,Jsr330ProviderFactory Class processing else if (javaxInjectProviderClass == descriptor.getDependencyType()) { return new Jsr330ProviderFactory().createDependencyProvider(descriptor, requestingBeanName); } else { 4,For the processing of general classes, the above three processing methods will actually follow the general processing method in the end 4.1,Delayed loading Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary( descriptor, requestingBeanName); if (result == null) { 4.2,Not delayed loading, parsing returns bean example result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; } }
General process:
- Optional: JDK8 provides an API. It is mainly used to set dependency as non mandatory dependency, i.e. descriptor required=false.
- Deferred dependency injection support: ObjectFactory, ObjectProvider, javax inject. There is no essential difference between providers.
- Another support for delayed injection - @ Lazy attribute.
- Find dependencies by type - doResolveDependency.
4.2. Instead of delayed loading, see code block 11 for the bean instance returned by parsing
Code block 11: doResolveDependency
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); try { 1,Directly through getBean Get by Object shortcut = descriptor.resolveShortcut(this); if (shortcut != null) { return shortcut; } 2,Get the type of the current field parameter Class<?> type = descriptor.getDependencyType(); 2.1,obtain@Value Annotation value Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value != null) { if (value instanceof String) { 2.2,analysis String Value of, such as parsing placeholders, etc String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); value = evaluateBeanDefinitionString(strVal, bd); } 2.3,Convert to the required type and return, for example @Value("clown ") Then it returns"clown " TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter()); return (descriptor.getField() != null ? converter.convertIfNecessary(value, type, descriptor.getField()) : converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); } 3,Resolve complex types, such as: Array,List,Map,Collection etc. Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); if (multipleBeans != null) { return multipleBeans; } 4,If it is not the above type, find the required type bean example, map(key: bean Your name; value: bean An instance or bean Instance type) Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); 4.1,If require Attribute is true,And found a match Bean If it is empty, an exception is thrown if (matchingBeans.isEmpty()) { if (isRequired(descriptor)) { raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } 4.2,If require Attribute is false,And found a match Bean If it is empty, return null return null; } 5.1,Final instance name String autowiredBeanName; 5.2,Final instance or instance type Object instanceCandidate; if (matchingBeans.size() > 1) { 5.3,If the candidate instance is not unique, the best one needs to be selected and returned name autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { return descriptor.resolveNotUnique(type, matchingBeans); } else { // In case of an optional Collection/Map, silently ignore a non-unique case: // possibly it was meant to be an empty collection of multiple regular beans // (before 4.3 in particular when we didn't even look for collection beans). return null; } } 5.4,Get autowiredBeanName of bean Instance or bean Instance type instanceCandidate = matchingBeans.get(autowiredBeanName); } else { 5.5,If only, use this directly // We have exactly one match. Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } if (autowiredBeanNames != null) { 6.1,Will depend on beanName Add autowiredBeanNames in autowiredBeanNames.add(autowiredBeanName); } if (instanceCandidate instanceof Class) { 7,If instanceCandidate Instance type, resolve to bean example instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this); } Object result = instanceCandidate; if (result instanceof NullBean) { if (isRequired(descriptor)) { raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } result = null; } if (!ClassUtils.isAssignableValue(type, result)) { throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass()); } return result; } finally { ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); } }
technological process:
- Get directly through getBean
- @Value annotation processing scenario
- Collection dependent query
- Single dependent query
4. Find the bena instance of bena candidate, see code block 12
5.3 if the candidate instance is not unique, the best one needs to be selected. See code block 14
Code block 12: findAutowireCandidates
protected Map<String, Object> findAutowireCandidates( @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) { 1,Gets all of the parameters of the current parameter type beanNname String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); Map<String, Object> result = new LinkedHashMap<>(candidateNames.length); 2,from spring Internal Cache resolvableDependencies obtain for (Class<?> autowiringType : this.resolvableDependencies.keySet()) { if (autowiringType.isAssignableFrom(requiredType)) { 2.1,judge autowiringType and requiredType Does the type match 2.2,obtain bean example Object autowiringValue = this.resolvableDependencies.get(autowiringType); 2.3,Resolve the given auto assembly value according to the given required type autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType); if (requiredType.isInstance(autowiringValue)) { 2.4,If autowiringValue yes requiredType Type is put directly into result,here value by bena example result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue); break; } } } 3.1,Traverse from container beanName for (String candidate : candidateNames) { 3.2,Not self reference&&Qualified as a candidate if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { 3.3,Add candidate to result in addCandidateEntry(result, candidate, descriptor, requiredType); } } 4,The above instances are not obtained and put into result in&&If it is not a complex type, use degraded matching if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) { // Consider fallback matches if the first pass failed to find anything... DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch(); for (String candidate : candidateNames) { if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor)) { addCandidateEntry(result, candidate, descriptor, requiredType); } } 5,If the degraded matching result is still empty, self reference is considered if (result.isEmpty()) { // Consider self references as a final pass... // but in the case of a dependency collection, not the very same bean itself. for (String candidate : candidateNames) { if (isSelfReference(beanName, candidate) && (!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) && isAutowireCandidate(candidate, fallbackDescriptor)) { addCandidateEntry(result, candidate, descriptor, requiredType); } } } } return result; }
The isAutowireCandidate method has three rules for filtering candidate objects: ① bd.autowirecandidate = true - > ② generic matching - > ③ @ Qualifier.
3.3. Add the candidate to the result. See code block 13
Code block 13: addCandidateEntry
private void addCandidateEntry(Map<String, Object> candidates, String candidateName, DependencyDescriptor descriptor, Class<?> requiredType) { // 1. If the descriptor is a MultiElementDescriptor, the type | candidateName already exists in the singletonObjects cache if (descriptor instanceof MultiElementDescriptor || containsSingleton(candidateName)) { // 2.1 resolveCandidate: get the corresponding bean instance through candidate name // 2.2 add the mapping of beanname - > bean instance to candidates (in this case, value is bean instance) candidates.put(candidateName, descriptor.resolveCandidate(candidateName, requiredType, this)); } else { // 3. Add the mapping of bean name - > bean instance type to candidates (at this time, the value is the bean instance type) candidates.put(candidateName, getType(candidateName)); } }
Return to code block 11 5.3 and enter code block 14 to select the best candidate.
Code block 14: determineAutowireCandidate
protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) { Class<?> requiredType = descriptor.getDependencyType(); 1,according to@Primary Annotation to select the optimal solution String primaryCandidate = determinePrimaryCandidate(candidates, requiredType); if (primaryCandidate != null) { return primaryCandidate; } 2,according to@Priority Select the optimal solution String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType); if (priorityCandidate != null) { return priorityCandidate; } // Fallback 3,If the optimal solution cannot be selected through the above two steps, the most basic strategy is used for (Map.Entry<String, Object> entry : candidates.entrySet()) { String candidateName = entry.getKey(); Object beanInstance = entry.getValue(); 3.1,containsValue: First, if this beanInstance Already by Spring If you have registered a dependency, you can use it directly beanInstance As the optimal solution 3.2,atchesBeanName: If you have not registered this beanInstance The dependency of the parameter is matched according to the parameter name if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) || matchesBeanName(candidateName, descriptor.getDependencyName())) { return candidateName; } } return null; } protected boolean matchesBeanName(String beanName, String candidateName) { // candidateName is equal to beanName 𞓜 beanName has an alias that is equal to candidateName, then true is returned return (candidateName != null && (candidateName.equals(beanName) || ObjectUtils.containsElement(getAliases(beanName), candidateName))); }
Returning to code block 6, 13 enters code block 15 and puts the parsed constructor and parameters into the cache
Code block 15: storeCache
public void storeCache(RootBeanDefinition mbd, Object constructorOrFactoryMethod) { synchronized (mbd.constructorArgumentLock) { // Put the constructor or factory method into the resolvedconstructorfactorymethod cache mbd.resolvedConstructorOrFactoryMethod = constructorOrFactoryMethod; // Constructor arguments resolved is marked as resolved mbd.constructorArgumentsResolved = true; if (this.resolveNecessary) { // If the parameter needs to be resolved, put preparedArguments into the preparedConstructorArguments cache mbd.preparedConstructorArguments = this.preparedArguments; } else { // If the parameters do not need to be resolved, put the arguments into the resolvedConstructorArguments cache mbd.resolvedConstructorArguments = this.arguments; } } }
Put the parsed constructors and parameters into the cache, and check these caches in the first code block 6.
summary
This article introduces an important content of creating bean instance: creating a new bean instance. The rest of creating bean instances: filling properties, bean instance initialization and so on will be introduced in the next article.