Last article Loading of Beans for Spring Ioc Source Analysis (4): createBean() In this article, we will give a detailed analysis of the createBeanInstance() method for instantiating bean s, and the remaining steps will be described in other articles.
Instantiate Bean
At doCreateBean() code <2>, there is a line of code instanceWrapper = createBeanInstance(beanName, mbd, args); Let's follow in:
//AbstractAutowireCapableBeanFactory.java //Create an instance object of a Bean protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // Make sure bean class is actually resolved at this point. //Resolve beanName to class Class<?> beanClass = resolveBeanClass(mbd, beanName); //Check to confirm that beans are instantiable if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean class isn't public, and non-public access not allowed: " + beanClass.getName()); } //If a Supplier callback exists, the policy is initialized using the given callback method Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } //Use FactoryBean's factory-method to create, support static and instance factories if (mbd.getFactoryMethodName() != null) { //Call factory method instantiation return instantiateUsingFactoryMethod(beanName, mbd, args); } // Shortcut when re-creating the same bean... //Constructor auto-injection for instantiation boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { if (mbd.resolvedConstructorOrFactoryMethod != null) { // If the cached parsed constructor or factory method is not empty, you can use the constructor to parse // Because the process consumes performance because you need to determine which constructor to use based on the parameters, all caching mechanisms are used resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } // If it has already been parsed, you do not need to parse it again if (resolved) { if (autowireNecessary) { //Constructor auto-injection for instantiation //A class has multiple constructors, each with different parameters, so you need to lock the constructor to instantiate the bean based on the parameters return autowireConstructor(beanName, mbd, null, null); } else { //Instantiate using default parameterless construction method return instantiateBean(beanName, mbd); } } // Need to determine the constructor... //Constructors need to be parsed based on parameters Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { //Invoke matching construction method instantiation using container auto-assembly feature return autowireConstructor(beanName, mbd, ctors, args); } // No special handling: simply use no-arg constructor. //Instantiate using default parameterless construction method return instantiateBean(beanName, mbd); }
In this code, Spring divides Bean's sample words into four ways:
- Supplier Callback
- Factory Method Initialization
- Constructor Auto Injection Initialization
- Default parameterless construction method initialization
1.1, Supplier callback
If there is a Supplier callback, the obtainFromSupplier (Supplier<?> instanceSupplier, String beanName) method is called to initialize.
Supplier is an interface defined as follows:
public interface Supplier<T> { T get(); }
What does this interface do?Callbacks to specify the creation of bean s.If we set such a callback, other constructors or factory methods will be useless
The settings are in the constructor of the BeanDefinition, such as:
// RootBeanDefinition.java public <T> RootBeanDefinition(@Nullable Class<T> beanClass, String scope, @Nullable Supplier<T> instanceSupplier) { super(); setBeanClass(beanClass); setScope(scope); // Set instanceSupplier property setInstanceSupplier(instanceSupplier); }
1.2. Factory method initialization
If a factory method exists, it is initialized using the factory method.This part of the code is very long and complex, let's not go into details here.
1.3. Constructor auto-injection initialization
First, determine the cache, and if it exists (resolved==true), that is, it has already been parsed, use the parsed one directly.Otherwise, the constructor is parsed first, then initialized by auto-injection of the constructor.
1.3.1,autowireConstructor()
autowireConstructor()
This initialization method can be simply understood as initializing Bean objects through a construction method with parameters.The instantiation process with parameters is quite complex, and because of this uncertainty, a lot of work has been done to determine the corresponding parameters.
The code snippet is as follows:
//AbstractAutowireCapableBeanFactory.java public BeanWrapper autowireConstructor(final String beanName, final RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable final Object[] explicitArgs) { // Encapsulate BeanWrapperImpl object and complete initialization BeanWrapperImpl bw = new BeanWrapperImpl(); this.beanFactory.initBeanWrapper(bw); Constructor<?> constructorToUse = null;// The final constructor to use ArgumentsHolder argsHolderToUse = null;// Construction parameters Object[] argsToUse = null;// Construction parameters // Determine if there is an explicit parameter specified, and if there is one, use it first, such as xxBeanFactory.getBean ("teacher", "Li Hua", 3); <1> if (explicitArgs != null) { argsToUse = explicitArgs; } // Parse parameters in configuration file without explicitly specifying parameters <2> else { Object[] argsToResolve = null; synchronized (mbd.constructorArgumentLock) { // First try to get it from the cache. spring's parsing of parameters is complex and time consuming, so try to get the parsed constructor parameters from the cache first constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod; //If neither the constructor nor the parameter is Null if (constructorToUse != null && mbd.constructorArgumentsResolved) { // Found a cached constructor... // Get the construction parameters in the cache argsToUse = mbd.resolvedConstructorArguments; if (argsToUse == null) { argsToResolve = mbd.preparedConstructorArguments; } } } // If present in the cache, parse the parameters stored in BeanDefinition // If the constructor A (int, int) of a given method is used, the ("1", "1") in the configuration file will be converted to (1,1) // The value in the cache may be the original value or the final value if (argsToResolve != null) { argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve); } } // If the cache does not exist, the constructor parameters need to be parsed to determine which constructor to use for instantiation <3> if (constructorToUse == null) { // Need to resolve the constructor. boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR); // Value used to hold the parsed constructor parameter ConstructorArgumentValues resolvedValues = null; //Number of parameters <4> int minNrOfArgs; if (explicitArgs != null) { minNrOfArgs = explicitArgs.length; } else { // Get construction parameters from BeanDefinition, that is, extract them from the configuration file ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues(); resolvedValues = new ConstructorArgumentValues(); // Number of parameters that can be resolved minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } // Take specified constructors, if any. //Use the specified constructor, if any <5> Constructor<?>[] candidates = chosenCtors; //No, if (candidates == null) { Class<?> beanClass = mbd.getBeanClass(); try { //Get all constructors by reflection 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); } } // Sort all constructors, public with the most arguments takes first place <6> AutowireUtils.sortConstructors(candidates); int minTypeDiffWeight = Integer.MAX_VALUE; //Ambiguous set of constructors Set<Constructor<?>> ambiguousConstructors = null; LinkedList<UnsatisfiedDependencyException> causes = null; // Iterate through all constructors, parsing to determine which one to use <7> for (Constructor<?> candidate : candidates) { // Gets the parameter type of the constructor <8> Class<?>[] paramTypes = candidate.getParameterTypes(); // Terminates if the selected constructor has been found or if the number of required parameters is less than the current number of constructor parameters. // Because, they are already sorted in descending order by the number of parameters 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; } // Number of parameters is not equal, skip if (paramTypes.length < minNrOfArgs) { continue; } // ArgumentsHolder object for parameter holder ArgumentsHolder argsHolder; <9> if (resolvedValues != null) { try { // Get the parameter name on the comment by @ConstructorProperties String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length); if (paramNames == null) { ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer(); if (pnd != null) { // Gets the parameter name of the specified constructor paramNames = pnd.getParameterNames(candidate); } } // Create parameter holder ArgumentsHolder object based on constructor and constructor parameters 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. if (paramTypes.length != explicitArgs.length) { continue; } // Create an ArgumentsHolder object based on explicitArgs passed in by getBean() argsHolder = new ArgumentsHolder(explicitArgs); } //By comparing the difference values of the constructor parameters, the best constructor to use is obtained. // isLenientConstructorResolution determines whether the constructor is parsed in relaxed or strict mode (default relaxation) // Strict pattern: when parsing a constructor, all must match, otherwise an exception is thrown // Loose mode: Match using the closest mode int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); // Choose this constructor if it represents the closest match. // If it represents the closest match currently available, select it as the constructor //The smaller the difference, the more matches, and the smallest comparison of each and every score <10> if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; argsHolderToUse = argsHolder; argsToUse = argsHolder.arguments; minTypeDiffWeight = typeDiffWeight; ambiguousConstructors = null; } // If the difference between the two construction methods and the list of parameter value types is consistent, then both methods can act as // Candidate, ambiguous at this point, put the ambiguous constructors first else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) { if (ambiguousConstructors == null) { ambiguousConstructors = new LinkedHashSet<>(); ambiguousConstructors.add(constructorToUse); } //Add candidate constructors to ambiguous set of constructors ambiguousConstructors.add(candidate); } } // No executable construction method, throw exception if (constructorToUse == null) { if (causes != null) { 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)"); } //Throw an exception if the ambiguous constructor is not empty and the pattern is strict else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) { 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); } // Cache parsed constructors, parameters <11> if (explicitArgs == null) { /* * Cache related information, such as: * 1. Resolved construction method object resolvedConstructorOrFactoryMethod * 2. Whether the constructor parameter list has resolved flag constructorArgumentsResolved * 3. Parameter value list resolvedConstructorArguments or preparedConstructorArguments * * This information can be used elsewhere for quick judgment */ argsHolderToUse.storeCache(mbd, constructorToUse); } } try { //Get Bean Initialization Policy final InstantiationStrategy strategy = beanFactory.getInstantiationStrategy(); Object beanInstance; //Create Bean Object <12> 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); } //Set to bw bw.setBeanInstance(beanInstance); return bw; } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean instantiation via constructor failed", ex); } }
The code is long, but don't panic. Let's analyze it one step at a time:
- At <1>, determine if there are explicit construction parameters specified
- At <2>, if no parameter is explicitly specified, it is fetched from the cache
- Cache does not exist at <3>, parse constructor parameters
- Get the number of construction parameters at <4>
- Get all construction methods at <5>
- Sort all construction methods at <6>
- <7>, traverse all construction methods
- Construct method by parameter check at <8>
- At <9>, create parameter holder ArgumentsHolder
- At <10>, a suitable construction method was selected.
- Cache parsed constructors and parameters at <11>
- Instantiate Bean Object at <12>
1.3.1.1, Determine whether to explicitly specify a construction parameter
- explicitArgs
Specified construction parameter passed in externally - argsToUse
Construction parameters to use
explicitArgs refers to specified construction parameters that are passed in externally, such as xxBeanFactory.getBean ("teacher", "Li Hua", "3), (Li Hua and 3) being the specified parameters that are passed in.
ArgsToUses is the construction parameter that we will use when instantiating, so let's say if explicitArgs is not null, then we will assign explicitArgs to argsToUse.
1.3.1.2, get from cache if no parameters are explicitly specified
Object[] argsToResolve = null; synchronized (mbd.constructorArgumentLock) { // First try to get it from the cache. spring's parsing of parameters is complex and time consuming, so try to get the parsed constructor parameters from the cache first constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod; //If neither the constructor nor the parameter is Null if (constructorToUse != null && mbd.constructorArgumentsResolved) { // Found a cached constructor... // Get the construction parameters in the cache argsToUse = mbd.resolvedConstructorArguments; if (argsToUse == null) { argsToResolve = mbd.preparedConstructorArguments; } } } // If present in the cache, parse the parameters stored in BeanDefinition // If the constructor A (int, int) of a given method is used, the ("1", "1") in the configuration file will be converted to (1,1) // The value in the cache may be the original value or the final value if (argsToResolve != null) { argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve); }
First get the construction method from the mbd.resolvedConstructorOrFactoryMethod in the cache. If there are construction methods and parameters in the cache, parse the construction parameters.
Because the constructor in the cache is not necessarily the final value, such as the constructor A (int, int) of a given method, the ("1", "1") in the configuration file will be converted to (1,1) by this method.
1.3.1.3, Cache does not exist, parse constructor parameters
If the cache does not exist, the constructor parameters need to be parsed to determine which constructor to use for instantiation
1.3.1.4, Get the number of construction parameters
//Number of parameters int minNrOfArgs; if (explicitArgs != null) { minNrOfArgs = explicitArgs.length; } else { // Get construction parameters from BeanDefinition, that is, extract them from the configuration file ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues(); resolvedValues = new ConstructorArgumentValues(); // Number of parameters that can be resolved minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); }
-
If explicitArgs is not null
Get it directly. -
Is null
Need to parse parameters saved in BeanDefinition constructor
And get the number of parameters that can be resolved
1.3.1.5, Get all construction methods
Try to get the specified construction method first, and if not, use reflection to get all the construction methods
1.3.1.6, Sort all construction methods
The main purpose of sorting is to make it easier to find the most matching construction method, since the confirmation of the construction method is based on the number of parameters.The rule for sorting is to ascend by the public/non-public construction method and then descend by the number of construction parameters.
1.3.1.7, traverse all construction methods
Iterate through all the construction methods to select the best one
1.3.1.8, Constructing by Parameter Check
// Gets the parameter type of the constructor Class<?>[] paramTypes = candidate.getParameterTypes(); ///The judgement construction method and the construction method parameters are not empty here because the construction methods have been ordered previously.So when the number of parameters used is greater than the number of parameters of the current construction method, you actually get the desired construction method. 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; } // The current number of construction parameters is less than the number we require, skip if (paramTypes.length < minNrOfArgs) { continue; }
This code is not complicated either. The first if is a break branch and jumps out of the for loop when the condition is met, meaning that the best matching construction method is found.
EX:
Suppose you now have a set of construction methods sorted according to the sorting rules above, and the sorting results are as follows:
1. public Hello(Object, Object, Object) 2. public Hello(Object, Object) 3. public Hello(Object) 4. protected Hello(Integer, Object, Object, Object) 5. protected Hello(Integer, Object, Object) 6. protected Hello(Integer, Object)
Since it is sorted in descending order, it first matches construction method 1 and finds argsToUse.length > paramTypes.length
The second if is to quickly determine if the current construction method meets our requirements.
- paramTypes
Number of parameters for the current construction method - minNrOfArgs
Number of parameters for the construction method we require If the current number of construction parameters is less than the number we require, the current construction method does not meet our requirements, continue directly
1.3.1.9, Create parameter holder ArgumentsHolder
// ArgumentsHolder object for parameter holder ArgumentsHolder argsHolder; if (resolvedValues != null) { try { // Get the parameter name on the comment by @ConstructorProperties String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length); if (paramNames == null) { // ParameterNameDiscoverer is an interface for parsing method and constructor parameter names and is a parameter name detector ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer(); if (pnd != null) { // Gets the parameter name of the specified constructor paramNames = pnd.getParameterNames(candidate); } } // Create parameter holder ArgumentsHolder object based on constructor and constructor parameters 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. if (paramTypes.length != explicitArgs.length) { continue; } // Create an ArgumentsHolder object based on explicitArgs passed in by getBean() argsHolder = new ArgumentsHolder(explicitArgs); }
There are two main logic here:
- resolvedValues != null
That is, the specified construction parameter is not displayed - resolvedValues == null
That is, the display specifies the construction parameters
First branch:
Get the name of the construction parameter through the @ConstructorProperties annotation first, if not, then through ParameterNameDiscoverer, and finally create ArgumentsHolder
Second branch:
New ArgumentsHolder directly using explicitArgs, which displays the incoming construction parameter
Wrap parameters as ArgumentsHolder objects.This object holds parameters, which we call parameter holders.During this process, the construction parameters are parsed again, and type conversions are made, such as converting string s in the configuration file to the required int.
When you wrap an object as an ArgumentsHolder object, you can use it to match constructors.Matching is divided into strict and relaxed modes:
-
Strict mode: When parsing a constructor, all parameters must match or an exception will be thrown.
-
Loose mode: Choose the closest from ambiguous construction methods.
This is based on the type difference weight obtained from the BeanDefinition's isLenientConstructorResolution property, which is the parameter we are passing in constructing the AbstractBeanDefinition object.
1.3.1.10, Screen out conforming construction methods
//By comparing the difference values of the constructor parameters, the best constructor to use is obtained. // isLenientConstructorResolution determines whether the constructor is parsed in relaxed or strict mode (default relaxation) // Strict pattern: when parsing a constructor, all must match, otherwise an exception is thrown // Loose mode: Match using the closest mode int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); // Choose this constructor if it represents the closest match. // If it represents the closest match currently available, select it as the constructor //The smaller the difference, the more matches, and the smallest comparison of each and every score if (typeDiffWeight < minTypeDiffWeight) { constructorToUse = candidate; argsHolderToUse = argsHolder; argsToUse = argsHolder.arguments; minTypeDiffWeight = typeDiffWeight; ambiguousConstructors = null; } // If the difference between the two construction methods and the list of parameter value types is consistent, then both methods can act as // Candidate, ambiguous at this point, put the ambiguous constructors first else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) { if (ambiguousConstructors == null) { ambiguousConstructors = new LinkedHashSet<>(); ambiguousConstructors.add(constructorToUse); } //Add candidate constructors to ambiguous set of constructors ambiguousConstructors.add(candidate); } / No executable construction method, throw exception if (constructorToUse == null) { if (causes != null) { 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)"); /Throw an exception if the ambiguous constructor is not empty and the pattern is strict else if (ambiguousConstructors != null && mbd.isLenientConstructorResolution()) { 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); }
By calculating the difference value typeDiffWeight of the current construction method, the smallest difference value is filtered out and a best matching construction method is finally compared.
If the difference is greater than the minimum difference, join the ambiguous Constructors, which I call ambiguous construction methods, which are used in Loose Mode.
So far, all construction methods have been traversed.Throw an exception if the construction method is not yet filtered out.
If the ambiguous construction method is not empty, but the pattern is strict, an exception is thrown.
1.3.1.11, Add parsed constructors, parameters to the cache
// Cache parsed constructors, parameters if (explicitArgs == null) { /* * Cache related information, such as: * 1. Resolved construction method object resolvedConstructorOrFactoryMethod * 2. Whether the constructor parameter list has resolved flag constructorArgumentsResolved * 3. Parameter value list resolvedConstructorArguments or preparedConstructorArguments * * This information can be used elsewhere for quick judgment */ argsHolderToUse.storeCache(mbd, constructorToUse); }
Continue tracking:
// ArgumentsHolder.java public final Object rawArguments[]; public final Object arguments[]; public final Object preparedArguments[]; public void storeCache(RootBeanDefinition mbd, Executable constructorOrFactoryMethod) { synchronized (mbd.constructorArgumentLock) { mbd.resolvedConstructorOrFactoryMethod = constructorOrFactoryMethod; mbd.constructorArgumentsResolved = true; if (this.resolveNecessary) { mbd.preparedConstructorArguments = this.preparedArguments; } else { mbd.resolvedConstructorArguments = this.arguments; } } }
I believe you should be familiar with resolvedConstructorOrFactoryMethod and resolvedConstructorArguments here.
As you would expect, these parameters are used to determine whether a cache exists or not.
1.3.1.12, instantiating Bean objects
strategy.instantiate There's still a lot of code in this section, so let's move on to the next chapter.
1.3.2, Diagram process
Because this code is still quite complex, I have drawn a branch flowchart (explicitArgs=null) for easy understanding.
1.3, Default parameterless construction method initialization
After the destruction of the initialization source code by the parametric construction method, looking at the parametric source code again, you will find that it is much simpler.
return instantiateBean(beanName, mbd);
Continue tracking:
//Instantiate Bean object using default parameterless construction method protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) { try { Object beanInstance; final BeanFactory parent = this; //Get the security management interface of the system, JDK standard security management API if (System.getSecurityManager() != null) { //Here is an anonymous built-in class that creates instance objects based on instantiation policies beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, parent), getAccessControlContext()); } else { //Encapsulate instantiated objects beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent); } BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } catch (Throwable ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); } }
After looking at the source code for initialization of a parametric construction method, and then looking at the parametric one, it is found that the code is really too simple, and there is no complicated logic to determine the construction parameters and methods.
instantiate(mbd, beanName, parent)
//SimpleInstantiationStrategy.java //Instantiate Bean Object with Initialization Policy @Override public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { // Don't override the class with CGLIB if no overrides. // No overrides, just use reflection instantiation if (!bd.hasMethodOverrides()) { Constructor<?> constructorToUse; synchronized (bd.constructorArgumentLock) { //Get the object's construction method or factory method from the cache constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod; //Cache does not if (constructorToUse == null) { //Use JDK's reflection mechanism to determine if the Bean to instantiate is an interface final Class<?> clazz = bd.getBeanClass(); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { if (System.getSecurityManager() != null) { //Here is an anonymous built-in class that uses reflection mechanisms to get the construction of beans constructorToUse = AccessController.doPrivileged( (PrivilegedExceptionAction<Constructor<?>>) () -> clazz.getDeclaredConstructor()); } else { constructorToUse = clazz.getDeclaredConstructor(); } bd.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } //Instantiate using BeanUtils by invoking the "construction method. newInstance(arg)" via a reflection mechanism return BeanUtils.instantiateClass(constructorToUse); } else { // Must generate CGLIB subclass. //Method override, use CGLIB to instantiate objects //Method override, intercepts the calling process when calling the target method, calls the interceptor for enhancements, and returns the agent of the original instance //So use cglib dynamic proxy return instantiateWithMethodInjection(bd, beanName, owner); } }
Simple steps:
- Determine if there is a way to override
- Try to get the construction method from the cache
- Check if the bean is an interface
- Get default construction method from reflection
- Instantiate with BeanUtils
BeanUtils.instantiateClass(constructorToUse)
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException { Assert.notNull(ctor, "Constructor must not be null"); try { // Set up construction methods, accessible ReflectionUtils.makeAccessible(ctor); // Create object newInstance using construction method return (KotlinDetector.isKotlinType(ctor.getDeclaringClass()) ? KotlinDelegate.instantiateClass(ctor, args) : ctor.newInstance(args)); } catch (InstantiationException ex) { throw new BeanInstantiationException(ctor, "Is it an abstract class?", ex); } catch (IllegalAccessException ex) { throw new BeanInstantiationException(ctor, "Is the constructor accessible?", ex); } catch (IllegalArgumentException ex) { throw new BeanInstantiationException(ctor, "Illegal arguments for constructor", ex); } catch (InvocationTargetException ex) { throw new BeanInstantiationException(ctor, "Constructor threw exception", ex.getTargetException()); } }
First set up strong kiss access, then newInstance() creates the object.
summary
For the createBeanInstance() method, he chooses the appropriate instantiation strategy to create instance objects for bean s, which is:
-
Supplier callback mode
-
Factory Method Initialization
-
Constructor Auto Injection Initialization
-
Default constructor injection.
Among them, factory method initialization and constructor auto-injection initialization are the most complex, mainly due to the uncertainty of constructor and constructor parameters. Spring needs a lot of effort to determine the constructor and constructor parameters. If so, it is OK to choose instantiation strategy directly.
Of course, when instantiating, methods will be overridden or replaced dynamically according to whether they need to be overridden or woven, because if there are overrides or woven, a dynamic proxy will be created to weave the methods. At this time, only the CGLIB method can be selected to instantiate, otherwise the reflection method can be used directly, which is convenient and fast.
Last:
The code to instantiate Bean here is analyzed. This part of the source code still seems difficult. Look at my hair panic and I'm tired of writing, but I will continue to write it. You will find that when you study and understand a source code, the sense of achievement is really good 0.0.
PS: Code words are not easy, I hope you can give more praise and power to your younger brother T.T.
Reference: Public Number-Taro Source