This is the 16th day of my participation in Gwen Challenge

Accumulate over a long period, constant dripping wears away a stone 😄

preface

After last talked about program execution resolveBeforeInstantiation function, if the returned result is null, it needs to perform doCreateBean function to create a Bean. This article will analyze the code logic of doCreateBean

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
	throws BeanCreationException {

// Instantiate the bean.
// BeanWrapper is a wrapper around beans
BeanWrapper instanceWrapper = null;
//1. If this is a singleton, remove the cache
if (mbd.isSingleton()) {
	// factoryBeanObjectCache: Stores the object returned by factoryBean.getobject () corresponding to beanName
	// factoryBeanInstanceCache: Stores the FactoryBean instance of beanName
	instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}

// 2
if (instanceWrapper == null) {
	// Create bean instance 1, factory method 2, constructor auto-injection 3, simple initialization
	instanceWrapper = createBeanInstance(beanName, mbd, args);
}

// The wrapped instance object, which is the original object
final Object bean = instanceWrapper.getWrappedInstance();
// The type of the wrapped instance objectClass<? > beanType = instanceWrapper.getWrappedClass();// If not NullBean, set the resolvedTargetType property to the current WrappedClass
if(beanType ! = NullBean.class) { mbd.resolvedTargetType = beanType; }//3, find the injection point
synchronized (mbd.postProcessingLock) {
	if(! mbd.postProcessed) {try {
			// InjectedElement for @autowired, @value, and Resource
			/ / and the injection points added to the attribute of MBD externallyManagedConfigMembers
			applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
		}
		catch (Throwable ex) {
			throw new BeanCreationException(mbd.getResourceDescription(), beanName,
					"Post-processing of merged bean definition failed", ex);
		}
		mbd.postProcessed = true; }}/ / 4.
// If the current bean is a singleton and supports loop dependencies, and the current bean is being created,
// Just add an objectFactory to singletonFactories,
// If other beans depend on the bean, you can retrieve the bean from singletonFactories
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
		isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
	if (logger.isTraceEnabled()) {
		logger.trace("Eagerly caching bean '" + beanName +
				"' to allow for resolving potential circular references");
	}
	// Construct an ObjectFactory to add to singletonFactories
        // getEarlyBeanReference() allows you to modify the beans that are returned. Currently, all beans are returned directly, except perhaps for dynamic proxy objects (AOP)
	addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

Object exposedObject = bean;
try {
	// 5. Populate the bean to inject each property
	populateBean(beanName, mbd, instanceWrapper);

	// execute the initialization method
	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); }}// Check for loop dependencies
if (earlySingletonExposure) {
	// earlySingletonReference is not null only if a cyclic dependency is detected
	Object earlySingletonReference = getSingleton(beanName, false);
	if(earlySingletonReference ! =null) {
		// If the pre-exposed object (bean) is equal to the object after the full life cycle (exposedObject)
		// Assign the earlySingletonReference in the cache to exposedObject
		// It will eventually be added to singletonObjects
		// The initialized bean is equal to the original bean, indicating that it is not a proxy.
		//
		if (exposedObject == bean) {
			exposedObject = earlySingletonReference;
	}
		// Check whether the bean's dependon beans have been initialized
		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);
				}
			}
			/* * Because the bean is created; The bean it depends on must have been created. * actualDependentBeans not empty indicates that the bean has not * created all of its dependent beans since the bean was created, i.e. there is a circular dependency */
			if(! actualDependentBeans.isEmpty()) {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 {
            // Register DisposableBean
            registerDisposableBeanIfNecessary(beanName, bean, mbd);
    }
    catch (BeanDefinitionValidationException ex) {
            throw new BeanCreationException(
                            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
    }

    returnexposedObject; }}Copy the code

There are quite a few branches of code above, but to summarize the key points:

1. If it is a singleton, the cache needs to be cleared first

There are four ways to instantiate beans: factory method, Supplier callback, parameter constructor automatic injection, and default constructor injection

The use of 3, MergedBeanDefinitionPostProcessor

4. Dependency processing in singleton mode

Property population, populating all properties into an instance of the bean

6. Execute the initialization method

7, cyclic dependency check, there is a cyclic dependency throw exception

Register To DisposableBean

No more analysis of the first point. Let’s look at the second point: instantiate the Bean createBeanInstance method. This article examines two of these ways to instantiate beans. They are factory method and Supplier callback. The other two will be in the next paper.

createBeanInstance

Into the createBeanInstance method, this method is located in the AbstractAutowireCapableBeanFactory class

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {

    // Get the bean's class using the class loader to resolve the class according to the class property set or according to classNameClass<? > beanClass = resolveBeanClass(mbd, beanName);// Throw an exception if beanClass is not public and does not allow access to non-public methods and attributes
    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());
    }

    // This is the extension point Spring provides to developers
    // When a BeanDefinition has a Supplier class, Spring uses the class's get method to get the instance.Supplier<? > instanceSupplier = mbd.getInstanceSupplier();if(instanceSupplier ! =null) {
            return obtainFromSupplier(instanceSupplier, beanName);
    }

    // Instantiate this bean with factoryMethod
    // The name factorMethod is relatively common in XML, where bean objects are created using factory methods
    / / if a bean object is founded by @ bean annotation, also can go instantiateUsingFactoryMethod method to create
    if(mbd.getFactoryMethodName() ! =null) {
            return instantiateUsingFactoryMethod(beanName, mbd, args);
    }


    // Shortcut when re-creating the same bean... Shortcut when recreating the same bean
    boolean resolved = false;
    boolean autowireNecessary = false;
    // When the scope is prototyped and getBean() is called multiple times, no arguments are passed and the logic is fetched from the cache
// If it is a singleton, the second call to getBean() will fetch the object directly from the singleton pool, and it will not go there at all
    if (args == null) {
            synchronized (mbd.constructorArgumentLock) {
                    / / resolvedConstructorOrFactoryMethod cache the parsed constructor or factory method
                    if(mbd.resolvedConstructorOrFactoryMethod ! =null) {
        Resolved: // Resolved when resolved is true, the constructor for the current bean has been determined and it has been resolved before
                            resolved = true;
                            / / constructorArgumentsResolved: the constructor parameters marked as resolved, true is tag to resolved
                            // Default is false.
                            // If autowireNecessary is true, parameterized constructor injection is usedautowireNecessary = mbd.constructorArgumentsResolved; }}}if (resolved) {
            Resolved: // Resolved when resolved is true, the constructor for the current bean has been determined and it has been resolved before
            // autowireNecessary means using the parameterized constructor injection
            if (autowireNecessary) {
                    // Use the parameterized constructor for injection
                    return autowireConstructor(beanName, mbd, null.null);
            }
            else {
                    // If the constructor is specified but no constructor parameters are specified, then there are no constructor parameters and the bean is instantiated with a constructor with no parameters
                    returninstantiateBean(beanName, mbd); }}// This is the first time the bean has been created
    / / to SmartInstantiationAwareBeanPostProcessor access constructors,
    / / implementation in: AutowiredAnnotationBeanPostProcessorConstructor<? >[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);// Find the constructor using BeanPostProcessor
    // The autowire attribute of BeanDefinition is AUTOWIRE_CONSTRUCTOR. Autowire ="constructor"
    // Or BeanDefinition specifies constructor parameter values using the 
      
        tag
      
    // Or args is specified on getBean()
    if(ctors ! =null|| mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || ! ObjectUtils.isEmpty(args)) {// The parameterized constructor is inferred and instantiated
            return autowireConstructor(beanName, mbd, ctors, args);
    }

    / / no dice
    ctors = mbd.getPreferredConstructors();
    if(ctors ! =null) {
            return autowireConstructor(beanName, mbd, ctors, null);
    }

    // Instantiate the bean with a no-argument constructor
    return instantiateBean(beanName, mbd);
}
Copy the code

The above code is a lot of, its main logic is:

1. If there is a Supplier callback, call obtainFromSupplier() to initialize

2, if there is a factory method, using instantiateUsingFactoryMethod initialized ()

3, whether resolvedConstructorOrFactoryMethod isn’t empty, if not empty, there is the cache, directly use has been parsed. It then uses the autowireNecessary parameter to determine whether to use the parameterized constructor for automatic injection or the default constructor for injection.

4. If you don’t have one in the cache, you need to specify which constructor to use to do the parsing, because a class can have multiple constructors, and each constructor has different construction parameters, so you need to lock and initialize the constructor based on the parameters. The corresponding constructor with arguments is used if there are arguments, the default constructor is used otherwise.

obtainFromSupplier

Supplier is a functional interface to Java8. There is only one get() method in this interface. For details, go to Java8 — Lambda expressions

From the above code, you can see that Spring gets the Supplier from MBD. Now that you can get it, you can set it. The instanceSupplier property belongs to the AbstractBeanDefinition abstract class.

  • Set method to set
public void setInstanceSupplier(@NullableSupplier<? > instanceSupplier) {
	this.instanceSupplier = instanceSupplier;
}
Copy the code
  • Constructor method to set
public <T> RootBeanDefinition(@Nullable Class<T> beanClass, @Nullable Supplier<T> instanceSupplier) {
		super(a); setBeanClass(beanClass); setInstanceSupplier(instanceSupplier); }Copy the code
  • Register directly
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        ctx.registerBean(Person.class, new Supplier<Person>() {
            @Override
            public Person get(a) {
                return new Person("gongj"); }});Copy the code

use

Here is an example of using set mode. Start by creating two ordinary class objects, Person and User.

public class Person{

    private String name;

    public Person(a) {}public Person(String name) {
            this.name = name;
    }

    @Override
    public String toString(a) {
        return "Person{" +
                        "name='" + name + '\' ' +
                        '} '; }}public class User {}Copy the code

test

public static void main(String[] args) {
// Create BeanFactory
    DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
    / / build BeanDefinition
    RootBeanDefinition rootBeanDefinition = new RootBeanDefinition();
    rootBeanDefinition.setBeanClass(User.class);
    
    // Constructor reference The no-parameter constructor is called here
    rootBeanDefinition.setInstanceSupplier(Person::new);
    
    / / register BeanDefinition
    factory.registerBeanDefinition("user",rootBeanDefinition);
    Object user = factory.getBean("user");
    // Return the Person object
    System.out.println("Result:"+user); } result: Person{name='null'}
Copy the code

You can see that our setBeanClass is set to User, but the result is Person.

If instanceSupplier is set, call obtainFromSupplier() to initialize the bean as follows:

protected BeanWrapper obtainFromSupplier(Supplier
        instanceSupplier, String beanName) {
		Object instance;

    String outerBean = this.currentlyCreatedBean.get();
    // Set beanName to currentlyCreatedBean
    this.currentlyCreatedBean.set(beanName);
    try {
            Call Supplier's get() to return a Bean object
            instance = instanceSupplier.get();
    }
    finally {
            if(outerBean ! =null) {
                    this.currentlyCreatedBean.set(outerBean);
            }
            else {
                    this.currentlyCreatedBean.remove(); }}// If get() does not create a Bean object, a NullBean object is created
    if (instance == null) {
            instance = new NullBean();
    }
    // Create a wrapper class for the bean
    BeanWrapper bw = new BeanWrapperImpl(instance);
    // Initialize the bean wrapper
    initBeanWrapper(bw);
    return bw;
}

Copy the code

The code for the obtainFromSupplier method is simple: call the Supplier’s get() method to get an instance object, construct a BeanWrapper object bw from that instance object, and initialize the object.

instantiateUsingFactoryMethod

There are two ways to create an object with factory-method, either static factory injection (where the method must be static) or instance factory injection. For details, go to: reading-factory-method usage of XML files

The method in AbstractAutowireCapableBeanFactory class

protected BeanWrapper instantiateUsingFactoryMethod(
        String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
    return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
}
Copy the code

Then enter ConstructorResolver instantiateUsingFactoryMethod class

public BeanWrapper instantiateUsingFactoryMethod(
            String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
    // Construct the BeanWrapperImpl object
    BeanWrapperImpl bw = new BeanWrapperImpl();
    // Initialize BeanWrapperImpl
    // add the ConversionService object and the PropertyEditor PropertyEditor object to the BeanWrapper object
    this.beanFactory.initBeanWrapper(bw); Object factoryBean; Class<? > factoryClass;// The current factoryMethod is static
    boolean isStatic;

    // Get the value of the factory-bean property
    String factoryBeanName = mbd.getFactoryBeanName();
    if(factoryBeanName ! =null) {
            if (factoryBeanName.equals(beanName)) {
                    throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
                                    "factory-bean reference points back to the same bean definition");
            }
            // Get the factory instance according to factoryBeanName
// Go directly to getBean
            factoryBean = this.beanFactory.getBean(factoryBeanName);
            // Throws an exception if the current Bean is a singleton and an object of the beanName exists in the singleton pool
            if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
                    throw new ImplicitlyAppearedSingletonException();
            }
            factoryClass = factoryBean.getClass();
            isStatic = false;
    }
    Static factory mode
    else {
            // It's a static factory method on the bean class.
            // Static factory create bean, must provide the factory's full class name
            if(! mbd.hasBeanClass()) {throw new BeanDefinitionStoreException(mbd.getResourceDescription(), beanName,
                                    "bean definition declares neither a bean class nor a factory-bean reference");
            }
            factoryBean = null;
            factoryClass = mbd.getBeanClass();
            isStatic = true;
    }

    // Factory method
    Method factoryMethodToUse = null;
    ArgumentsHolder argsHolderToUse = null;
    / / parameters
    Object[] argsToUse = null;
    // When the developer calls the getBean method, it specifies the method parameters
    if(explicitArgs ! =null) {
            argsToUse = explicitArgs;
    }
    else {
            // If no parameter is specified, try to parse the argument from MBD
            Object[] argsToResolve = null;
            // First try to fetch it from the cache
            synchronized (mbd.constructorArgumentLock) {
                    // Get the parsed constructor or factory method
                    / / resolvedConstructorOrFactoryMethod: cache the parsed the constructor or factory method
                    factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
                    // Cache constructor found in MBD
                    if(factoryMethodToUse ! =null && mbd.constructorArgumentsResolved) {
                            // Found a cached factory method...
                            // Get the fully parsed constructor argument (the type of the argument has been determined and can be used directly)
                            / / resolvedConstructorArguments normal value is null
                            argsToUse = mbd.resolvedConstructorArguments;
                            if (argsToUse == null) {
                                    // Get some of the prepared constructor arguments (the type of which is indeterminate and needs to be parsed)argsToResolve = mbd.preparedConstructorArguments; }}}// If there are constructor arguments, then the parameter values are typed
            The Person(int) constructor for the given method will be configured after passing this method
            // convert "5 "to 5
            //<constructor-arg index="0" value="5"/>
            // The value in the cache may be the original value or the final value
            if(argsToResolve ! =null) {
                    // When to enter here? No arguments are passed when the scope is prototyped and getBean() is called multiple times
                    argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true); }}// If the current BeanDefinition does not resolve the specific factoryMethod object, or does not resolve the corresponding method parameters
    // This is the first time to create a cache
// When will the cache be resolved? When the scope is prototype, the getBean method is called multiple times without passing in arguments
    if (factoryMethodToUse == null || argsToUse == null) {
            // If the current class is a cglib generated proxy class, get its parent, otherwise return the class itself
            factoryClass = ClassUtils.getUserClass(factoryClass);
            // Set of methods
            List<Method> candidates = null;

            // Is the factory method unique
            if (mbd.isFactoryMethodUnique) {
                    if (factoryMethodToUse == null) {
                            factoryMethodToUse = mbd.getResolvedFactoryMethod();
                    }
                    if(factoryMethodToUse ! =null) { candidates = Collections.singletonList(factoryMethodToUse); }}if (candidates == null) {
                    candidates = new ArrayList<>();
                    // Get all pending methods in the factory class
                    Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
                    // Retrieve all methods add the methods that match the criteria to the collection
                    for (Method candidate : rawCandidates) {
                            // Whether the current method contains the static modifier, return true if it does, false otherwise, and compare with isStatic
                            // Whether the current method name is equal to the configured factoryMethod method name
                            if(Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) { candidates.add(candidate);  }}}// The number of methods found is 1 and no parameter configuration file is passed in and the constructor-arg attribute is not used
            if (candidates.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
                    Method uniqueCandidate = candidates.get(0);
                    // The number of arguments to the factory method is 0
                    if (uniqueCandidate.getParameterCount() == 0) {
                            // Cache the only factory method
                            mbd.factoryMethodToIntrospect = uniqueCandidate;
                            synchronized (mbd.constructorArgumentLock) {
                                    // Cache parsed constructors or factory methods
                                    mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
                                    // Mark the constructor argument as resolved
                                    mbd.constructorArgumentsResolved = true;
                                    // Cache fully parsed constructor arguments
                                    mbd.resolvedConstructorArguments = EMPTY_ARGS;
                            }
                            // Create an object
                            bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
                            returnbw; }}// If the number of matched methods is greater than 1, sort the methods
            // Sort the public constructor by the number of arguments in descending order
            // Then sort the non-public constructors in descending order
            if (candidates.size() > 1) {
                    candidates.sort(AutowireUtils.EXECUTABLE_COMPARATOR);
            }

            // Record the parsed constructor parameter values
            ConstructorArgumentValues resolvedValues = null;
            // autowireMode is constructor auto-injection, so the constructor is automatically selected
            boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
            int minTypeDiffWeight = Integer.MAX_VALUE;
            Set<Method> ambiguousFactoryMethods = null;
            // minNrOfArgs: specifies the number of arguments for the constructor with the fewest
            int minNrOfArgs;
// The developer uses the number of method arguments specified when calling the getBean method
            if(explicitArgs ! =null) {
                    minNrOfArgs = explicitArgs.length;
            }else {
                    Return true if there is a constructor parameter value defined for the bean
                    if (mbd.hasConstructorArgumentValues()) {
                            // The constructor argument values come from the constructor-arg tag
                            ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
                            resolvedValues = new ConstructorArgumentValues();
                            // The number of parse arguments comes from the index attribute in the constructor-arg tag
                            // The bean's constructor arguments are also parsed into resolvedValues objects, which involve other beans
                            minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
                    }else {
        // No arguments are specified and constructor-arg tags are defined
                            minNrOfArgs = 0;
                    }
            }

            LinkedList<UnsatisfiedDependencyException> causes = null;
            for (Method candidate : candidates) {
                    // The number of arguments to the method
                    int parameterCount = candidate.getParameterCount();
                    // The number of arguments of the current method is greater than or equal to that of the minimum constructor
                    if (parameterCount >= minNrOfArgs) {
                            ArgumentsHolder argsHolder;
                            // The parameter type for each parameter of the current methodClass<? >[] paramTypes = candidate.getParameterTypes();// getBean() is called with arguments
                            if(explicitArgs ! =null) {
                                    The length of the argument must match exactly. If it does not match, the current method will be skipped
                                    if(paramTypes.length ! = explicitArgs.length) {continue;
                                    }
                                    // The argument length already matches the ArgumentsHolder object built from the arguments passed in to the genBean() method
                                    argsHolder = new ArgumentsHolder(explicitArgs);
                            }else {
            // getBean() is called with no arguments
                                    try {
                                            String[] paramNames = null;
                                            // ParameterNameDiscoverer is used to parse parameter names on methods and constructors
                                            ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
                                            if(pnd ! =null) {
                                                    // Gets the parameter name of the specified method
                                                    paramNames = pnd.getParameterNames(candidate);
                                            }
                                            // Create an ArgumentsHolder object with the parsed constructor argument value (resolvedValues)
                                            argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw,
                                                            paramTypes, paramNames, candidate, autowiring, candidates.size() == 1);
                                    }
                                    catch (UnsatisfiedDependencyException ex) {
                                            if (logger.isTraceEnabled()) {
                                                    logger.trace("Ignoring factory method [" + candidate + "] of bean '" + beanName + "'." + ex);
                                            }
                                            if (causes == null) {
                                                    causes = new LinkedList<>();
                                            }
                                            causes.add(ex);
                                            continue; }}// Calculate the weight according to the parameter type and parameter value
                            // Lenient mode is enabled by default
                            // Strict pattern: When parsing a function, everything must match, otherwise an exception is thrown
                            // Loose mode: use the "closest mode" for matching
                            int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
                                            argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
                            // If the weight of the current method is smaller, the current method is more appropriate
                            if (typeDiffWeight < minTypeDiffWeight) {
                                    factoryMethodToUse = candidate;
                                    argsHolderToUse = argsHolder;
                                    argsToUse = argsHolder.arguments;
                                    minTypeDiffWeight = typeDiffWeight;
                                    ambiguousFactoryMethods = null;
                            }
                            // Collect this type option if methods with the same number of arguments have the same type difference weight
                            // However, this check is only performed in non-loose constructor parsing mode and overriding methods are explicitly ignored (with the same parameter signature)
                            else if(factoryMethodToUse ! =null&& typeDiffWeight == minTypeDiffWeight && ! mbd.isLenientConstructorResolution() && paramTypes.length == factoryMethodToUse.getParameterCount() && ! Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {if (ambiguousFactoryMethods == null) {
                                            ambiguousFactoryMethods = newLinkedHashSet<>(); ambiguousFactoryMethods.add(factoryMethodToUse); } ambiguousFactoryMethods.add(candidate); }}}// No factory method to execute, throw an exception
            if (factoryMethodToUse == null || argsToUse == null) {
                    if(causes ! =null) {
                            UnsatisfiedDependencyException ex = causes.removeLast();
                            for (Exception cause : causes) {
                                    this.beanFactory.onSuppressedException(cause);
                            }
                            throw ex;
                    }
                    List<String> argTypes = new ArrayList<>(minNrOfArgs);
                    if(explicitArgs ! =null) {
                            for(Object arg : explicitArgs) { argTypes.add(arg ! =null ? arg.getClass().getSimpleName() : "null"); }}else if(resolvedValues ! =null) {
                            Set<ValueHolder> valueHolders = new LinkedHashSet<>(resolvedValues.getArgumentCount());
                            valueHolders.addAll(resolvedValues.getIndexedArgumentValues().values());
                            valueHolders.addAll(resolvedValues.getGenericArgumentValues());
                            for(ValueHolder value : valueHolders) { String argType = (value.getType() ! =null? ClassUtils.getShortName(value.getType()) : (value.getValue() ! =null ? value.getValue().getClass().getSimpleName() : "null"));
                                    argTypes.add(argType);
                            }
                    }
                    String argDesc = StringUtils.collectionToCommaDelimitedString(argTypes);
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "No matching factory method found: "+ (mbd.getFactoryBeanName() ! =null ?
                                            "factory bean '" + mbd.getFactoryBeanName() + "'; " : "") +
                                    "factory method '" + mbd.getFactoryMethodName() + "(" + argDesc + ")'. " +
                                    "Check that a method with the specified name " +
                                    (minNrOfArgs > 0 ? "and arguments " : "") +
                                    "exists and that it is " +
                                    (isStatic ? "static" : "non-static") + ".");
            }
            else if (void.class == factoryMethodToUse.getReturnType()) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Invalid factory method '" + mbd.getFactoryMethodName() +
                                    "': needs to have a non-void return type!");
            }
            else if(ambiguousFactoryMethods ! =null) {
                    throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                                    "Ambiguous factory method matches found in bean '" + beanName + "'" +
                                    "(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " +
                                    ambiguousFactoryMethods);
            }

            if (explicitArgs == null&& argsHolderToUse ! =null) {
                    mbd.factoryMethodToIntrospect = factoryMethodToUse;
                    // Add the parsed constructor to the cacheargsHolderToUse.storeCache(mbd, factoryMethodToUse); }}// Reflection calls the factory method in the factoryBean object to instantiate an object
    bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, factoryMethodToUse, argsToUse));
    return bw;
}
Copy the code

Tired tired, this method is really long, and the branch is very much. Readers need to Debug a few more times.

Here’s a summary:

All of the above code is just validation: factory object is confirmed, constructor and constructor parameters are confirmed, and instantiate() of InstantiationStrategy object is called to instantiate.

  • Beanfactory.getbean () = beanFactory.getBean(); beanFactory.getBean() = beanFactory.getBean(); For static factory methods, you must provide the full name of the factory class and set factoryBean = null and isStatic = true. This is where the factory object is identified.

  • 2, after the factory object is determined, it is to confirm the construction parameters. Construction parameters can be validated in three main ways: explicitArgs parameters, retrieved in cache, and resolved in configuration files. The explicitArgs argument is the method argument we specify when we call the getBean method. If explicitArgs is not empty, you can confirm that the method argument is it, and there is no need to fetch it from the cache. Just determine the factory method. If explicitArgs is empty, it needs to be determined from the cache, where the factory method and construct parameters can be determined. Instantiate () on the InstantiationStrategy object is called directly to instantiate() if the factory method and construct parameters are specified.

  • 3. If the explicitArgs parameter is empty and not determined in the cache, the construct parameter information can only be obtained from the configuration file. If you read my previous article, you know that information in a configuration file is converted to a BeanDefinition object, so you can retrieve it from that object.

    • 3.1. First, obtain all methods in the factory object, including the methods of the parent class of the factory object, and then filter them according to the conditions. If the number of methods filtered out is 1 andexplicitArgsThe parameter is empty and the configuration file is not usedconstructor-argProperty, the instance object is called using the no-parameter factory methodInstantiationStrategyThe object’sinstantiate()To create an instance. Cache in passing:
if (uniqueCandidate.getParameterCount() == 0) {
    // Cache the only factory method
    mbd.factoryMethodToIntrospect = uniqueCandidate;
    synchronized (mbd.constructorArgumentLock) {
    // Cache parsed constructors or factory methods
    mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
    // Mark the constructor argument as resolved
    mbd.constructorArgumentsResolved = true;
    // Cache fully parsed constructor arguments
    mbd.resolvedConstructorArguments = EMPTY_ARGS;
}
Copy the code
    • 3.2. If the number of filtered methods is greater than 1, sort them. The sorting rule is: public constructor takes precedence, and the number of parameters is in descending order; Then the number of non-public construction parameters is in descending order. ifexplicitArgsIf the parameter is empty, obtain the construction parameter information from the configuration file and determine the construction parameter.
Return true if there is a constructor parameter value defined for the bean
if (mbd.hasConstructorArgumentValues()) {
    // The constructor argument values come from the constructor-arg tag
    ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
    resolvedValues = new ConstructorArgumentValues();
    // The number of parsed arguments is derived from the index attribute of the constructor-arg tag, which can be written arbitrarily
    // The bean's constructor arguments are also parsed into resolvedValues objects
    minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
Copy the code
    • 3.3. Iterate through the selected method through a loop. Again, I filter,The number of arguments to the current method needs to be greater than or equal to the minimum constructor argument (parameterCount >= minNrOfArgs). If the display provides arguments (explicitArgs ! = null), then directly compare whether the number of parameters of the two is equal. If they are equal, it means that they are found, according toexplicitArgs Parameters of the buildingArgumentsHolderObject. If no parameter is displayed, obtain this parameterParameterNameDiscovererObject that is used to resolve parameter names on methods and constructors. According to N multiple parametersArgumentsHolderObject that holds the parameter, which we call the parameter holder. When wrapping an object intoArgumentsHolderObject, we can use it to perform constructor matching, which is divided into strict mode and loose mode. The default is loose mode. That is, they can use itargsHolder.getTypeDifferenceWeight(paramTypes)Method to calculate.
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
							argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// If the weight of the current method is smaller, the current method is more appropriate.
if (typeDiffWeight < minTypeDiffWeight) {
    factoryMethodToUse = candidate;
    argsHolderToUse = argsHolder;
    argsToUse = argsHolder.arguments;
    minTypeDiffWeight = typeDiffWeight;
    ambiguousFactoryMethods = null;
}
Copy the code

Why do fewer points give higher priority?

It is primarily a calculation of how well the bean found matches the constructor parameter types.

Suppose the bean is of type A, A has A parent of B, and B has A parent of C. At the same time, A implements interface D

  • If the constructor argument is of type A, it is A perfect match with A score of 0
  • If the constructor argument is of type B, the score is 2
  • If the constructor argument is of type C, the score is 4
  • If the constructor argument is of type D, the score is 1

You can test it directly with the following code:

Object[] objects = new Object[]{new A()};
/ / 0
System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{A.class}, objects));
/ / 2
System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{B.class}, objects));
/ / 4
System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{C.class}, objects));
/ / 1
System.out.println(MethodInvoker.getTypeDifferenceWeight(new Class[]{D.class}, objects));
Copy the code

At this point, the factory object, factory method, and parameters have all been confirmed, and it’s time to call Instantiate () on the InstantiationStrategy object to create the bean instance.

instantiate

@Override
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner,
        @Nullable Object factoryBean, final Method factoryMethod, Object... args) {

try {
    // Ignore...
    if(System.getSecurityManager() ! =null) {
            AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                    ReflectionUtils.makeAccessible(factoryMethod);
                    return null;
            });
    }
    else {
            // Sets the given method to accessible
            ReflectionUtils.makeAccessible(factoryMethod);
    }

    Method priorInvokedFactoryMethod = currentlyInvokedFactoryMethod.get();
    try {
            currentlyInvokedFactoryMethod.set(factoryMethod);
            // Use reflection to create objects
// This is the point of the sentence
            Object result = factoryMethod.invoke(factoryBean, args);
            if (result == null) {
                    result = new NullBean();
            }
            return result;
    }
    finally {
            if(priorInvokedFactoryMethod ! =null) {
                    currentlyInvokedFactoryMethod.set(priorInvokedFactoryMethod);
            }
            else{ currentlyInvokedFactoryMethod.remove(); }}}catch (IllegalArgumentException ex) {
        / / catch omitted}}Copy the code

The sample code for this article has been uploaded to: gitee

  • If you have any questions or errors in this article, please feel free to comment. If you find this article helpful, please like it and follow it.