Make writing a habit together! This is my first day to participate in the “Gold Digging Day New Plan · April More text challenge”, click to see the details of the activity

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

preface

This is the last article on the createBeanInstance method. The first paper obtainFromSupplier, instantiateUsingFactoryMethod method were analyzed. The second article examines the autowireConstructor and instantiateBean methods. The previous two articles covered how Spring instantiates beans.

The source code

This article will analyze the determineConstructorsFromBeanPostProcessors method. This method determines the constructor and looks for the constructor in the class tagged by @AutoWired.

Normally we can use this to tell Spring that I want to use this constructor to instantiate the object.

@Autowired
public TestController(ABServiceImpl str){
	System.out.println("A");
}
Copy the code

How does Spring find the constructor we specify? GO ~ ~ ~. Interrupt point directly to the AbstractAutowireCapableBeanFactory createBeanInstance method of a class.

// The above code is omitted
// This is the first time the bean has been created
/ / according to SmartInstantiationAwareBeanPostProcessor constructor,
/ / there are three subclasses, besides AutowiredAnnotationBeanPostProcessor implementation logic, the other two are emptyConstructor<? >[] 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)) {// Instantiate the Bean with the parameter constructor
	return autowireConstructor(beanName, mbd, ctors, args);
}

// Preferred constructors for default construction?
/ / 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 logic is quite simple, and the Bean you want to create will follow this logic if it is created for the first time. First determine the constructor. It then determines whether to instantiate the Bean using a constructor with or without parameters based on four criteria.

Now, let’s look at the focus of this article, debugging into determineConstructorsFromBeanPostProcessors method, this method in AbstractAutowireCapableBeanFactory class.

The method has two input parameters, respectively:

  • BeanClass: The Class object of the currently created Bean
  • BeanName: indicates the Bean name
@Nullable
protectedConstructor<? >[] determineConstructorsFromBeanPostProcessors(@NullableClass<? > beanClass, String beanName)throws BeansException {

if(beanClass ! =null && hasInstantiationAwareBeanPostProcessors()) {
    for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof SmartInstantiationAwareBeanPostProcessor) {
            SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp;
            / / call determineCandidateConstructors methodConstructor<? >[] ctors = ibp.determineCandidateConstructors(beanClass, beanName);if(ctors ! =null) {
                returnctors; }}}}return null;
}
Copy the code

First, there is a judgment condition, the logic is used only if the judgment condition is met, otherwise null is returned. HasInstantiationAwareBeanPostProcessors () method, that is, whether hasInstantiationAwareBeanPostProcessors attribute to true.

protected boolean hasInstantiationAwareBeanPostProcessors(a) {
	return this.hasInstantiationAwareBeanPostProcessors;
}
Copy the code

When hasInstantiationAwareBeanPostProcessors attribute to true? When adding the BeanPostProcessor InstantiationAwareBeanPostProcessor just to true. We’ll talk about when addBeanPostProcessor is called in a future blog post.

@Override
public void addBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        Assert.notNull(beanPostProcessor, "BeanPostProcessor must not be null");
        this.beanPostProcessors.remove(beanPostProcessor);

        if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {
                If there is a InstantiationAwareBeanPostProcessor / /
                this.hasInstantiationAwareBeanPostProcessors = true;
        }
        if (beanPostProcessor instanceof DestructionAwareBeanPostProcessor) {
                If there is a DestructionAwareBeanPostProcessor / /
                this.hasDestructionAwareBeanPostProcessors = true;
        }
        // Add them to beanPostProcessors, in the order they were added
        this.beanPostProcessors.add(beanPostProcessor);
}
Copy the code

All the BeanPostProcessors passed judgment logic, then, if get a BeanPostProcessor SmartInstantiationAwareBeanPostProcessor, Is executed determineCandidateConstructors () method. Jump to SmartInstantiationAwareBeanPostProcessor interface.

@Nullable
defaultConstructor<? >[] determineCandidateConstructors(Class<? > beanClass, String beanName)throws BeansException {  
    return null;
}
Copy the code

SmartInstantiationAwareBeanPostProcessor interface default not the implementation of this method. The concrete implementation process is handed over to the implementation class. SmartInstantiationAwareBeanPostProcessor interface under three implementation class, AbstractAutoProxyCreator, InstantiationAwareBeanPostProcessorAdapter, AutowiredAnnotationBeanPostProcessor. Two classes of determineCandidateConstructors method is empty before implementation, only in AutowiredAnnotationBeanPostProcessor implementation class is the concrete implementation process.

@Override
@Nullable
publicConstructor<? >[] determineCandidateConstructors(Class<? > beanClass,final String beanName) throws BeanCreationException {

// Let's check for lookup methods here... Check the lookup method lookup-methods
// @lookup is found here by reflection
/ / (XML in the form of the replace method and lookup method in BeanDefinitionParserDelegate parsing)
// If the current beanName exists in lookupMethodsChecked, there is no need to parse again
if (!this.lookupMethodsChecked.contains(beanName)) {
    if (AnnotationUtils.isCandidateClass(beanClass, Lookup.class)) {
        // Whether the class contains the @lookup annotation
        try{ Class<? > targetClass = beanClass;do {
            // Iterate over all the methods in the current beanClass
            // Encapsulate the information about these methods as LookupOverride objects and load them into MBD
            ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// Get the Lookup annotation on the method
            Lookup lookup = method.getAnnotation(Lookup.class);
            if(lookup ! =null) {
                Assert.state(this.beanFactory ! =null."No BeanFactory available");
                LookupOverride override = new LookupOverride(method, lookup.value());
                try {
                    RootBeanDefinition mbd = (RootBeanDefinition)
                       this.beanFactory.getMergedBeanDefinition(beanName);
// Put the LookupOverride object into the Overrides Set collection
                    mbd.getMethodOverrides().addOverride(override);
                }
                catch (NoSuchBeanDefinitionException ex) {
                    throw new BeanCreationException(beanName,
       "Cannot apply @Lookup to beans without corresponding bean definition"); }}/ / if finished
        });
        // Get the parent of the current class
        targetClass = targetClass.getSuperclass();
    }
        while(targetClass ! =null&& targetClass ! = Object.class); }catch (IllegalStateException ex) {
            throw new BeanCreationException(beanName, "Lookup method resolution failed", ex); }}// lookupMethodsChecked is a set that records which beans' @lookup annotations were resolved,
    // Next time you don't need to parse
    this.lookupMethodsChecked.add(beanName);
}
If ===


/ / to check whether the cache of the current in the candidateConstructorsCache bean available in the constructorConstructor<? >[] candidateConstructors =this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
// Fully synchronized resolution now...
// Double check
synchronized (this.candidateConstructorsCache) {
candidateConstructors = this.candidateConstructorsCache.get(beanClass);

// Start filtering if the constructor has not been filtered
if (candidateConstructors == null) { Constructor<? >[] rawCandidates;try {
    // Get all the constructors of the current class (public and non-public),
    // If no constructor is written to the class, a constructor with no arguments is returned
    rawCandidates = beanClass.getDeclaredConstructors();
}
catch (Throwable ex) {
    throw new BeanCreationException(beanName,
       "Resolution of declared constructors on bean Class [" + beanClass.getName() +
        "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
}

// All the selected constructors are stored in the script.
// Add all the @autoWired annotation methods, but add a default constructorList<Constructor<? >> candidates =new ArrayList<>(rawCandidates.length);
// requiredConstructor: represents a constructor annotated by @autowired and required true
// Only one such constructor is allowed, so when the variable has a value
// Spring throws an exception if another constructor of the same condition is presentConstructor<? > requiredConstructor =null;
// defaultConstructor: saves the defaultConstructorConstructor<? > defaultConstructor =null;

// If it is kotlin's class, return null if it is a Java classConstructor<? > primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass);int nonSyntheticConstructors = 0;

// Iterate through all constructors
for(Constructor<? > candidate : rawCandidates) {// nonSyntheticConstructors this variable is and primaryConstructor! = null
if(! candidate.isSynthetic()) { nonSyntheticConstructors++; }else if(primaryConstructor ! =null) {
    continue;
}

// Check whether there are @autowired, @value, @inject annotations on this constructor.
// See if there are @autowired, @Value, @inject annotations on the corresponding constructor in the parent class of the proxy classMergedAnnotation<? > ann = findAutowiredAnnotation(candidate);if (ann == null) {
// If the current class is a cglib generated proxy class, get its parent, otherwise return the class itselfClass<? > userClass = ClassUtils.getUserClass(beanClass);if(userClass ! = beanClass) {try {
    // Get the constructor with argumentsConstructor<? > superCtor = userClass.getDeclaredConstructor(candidate.getParameterTypes());// Continue looking for @autowired, @value, @inject annotations
    ann = findAutowiredAnnotation(superCtor);
    }
    catch (NoSuchMethodException ex) {
    // Simply proceed, no equivalent superclass constructor found...}}}// ann ! = null indicates annotation on constructor
if(ann ! =null) {
// requiredConstructor: indicates that the programmer manually specifies which constructor must be used
// So if there are multiple constructors with @Autowired annotations, an error will be reported if the @Autowired annotation's required attribute is true for one of them.

if(requiredConstructor ! =null) {
throw new BeanCreationException(beanName,
        "Invalid autowire-marked constructor: " + candidate +
        ". Found constructor with 'required' Autowired annotation already: " +
        requiredConstructor);
}
// Check @autowired's required attribute value, which defaults to true
boolean required = determineRequiredStatus(ann);
if (required) {
        if(! candidates.isEmpty()) {throw new BeanCreationException(beanName,
        "Invalid autowire-marked constructors: " + candidates +
        ". Found constructor with 'required' Autowired annotation: " +
                        candidate);
        }
        requiredConstructor = candidate;
    }
    // The candidates save the constructor annotated with @autoWired
    candidates.add(candidate);
}
// If @autowired does not exist on the current constructor and is a no-parameter constructor, the no-parameter constructor is logged
// So we can use the method, when iterating through the constructor, to really only care about the no-argument constructor and the constructor annotated @autowired
else if (candidate.getParameterCount() == 0) { defaultConstructor = candidate; }}// If there is a constructor that adds @autowired
// The candidates fall into two categories: either the candidates contain a constructor required equals true
// Either the candidates include one or more constructors required equals false
if(! candidates.isEmpty()) {// If no constructor required is specified, add the no-argument constructor to the script and deduce the argument along
if (requiredConstructor == null) {
    if(defaultConstructor ! =null) {
            // Add the constructor with no arguments
            candidates.add(defaultConstructor);
    }
    // If no constructor is specified with required true, there is no constructor without arguments, and there is only one constructor
    else if (candidates.size() == 1 && logger.isInfoEnabled()) {
            // Give a hint that there is no constructor without arguments, and then there is only one constructor @autowired (required=false)
            // So this constructor must be used, so print a log telling the programmer that you can actually change required to true
            logger.info("Inconsistent constructor declaration on bean with name '" + beanName +
                            "': single autowire-marked constructor flagged as optional - " +
                            "this constructor is effectively required since there is no " +
                            "default constructor to fall back to: " + candidates.get(0)); }}// candidateConstructors is the return value of the current method
// The candidates are either one constructor @autoWired (required=true),
// Either multiple @autoWired (required=false)+ a no-argument constructor
candidateConstructors = candidates.toArray(newConstructor<? > [0]);
}
// There is no constructor annotated with @autowired, so it checks if there is only one constructor with arguments, and returns if so
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
        candidateConstructors = newConstructor<? >[] {rawCandidates[0]};
}
/ / regardless of
else if (nonSyntheticConstructors == 2&& primaryConstructor ! =null&& defaultConstructor ! =null && !primaryConstructor.equals(defaultConstructor)) {
        candidateConstructors = newConstructor<? >[] {primaryConstructor, defaultConstructor}; }/ / regardless of
else if (nonSyntheticConstructors == 1&& primaryConstructor ! =null) {
        candidateConstructors = newConstructor<? >[] {primaryConstructor}; }// If the @autowired annotation is added to no constructor and there are multiple constructors and no primaryConstructor
else {
        // Returns an empty Constructor array, indicating that no Constructor is inferred
        candidateConstructors = newConstructor<? > [0];
}
this.candidateConstructorsCache.put(beanClass, candidateConstructors); }}}// Either return a constructor required=true
// Either return multiple requreid=false+ constructor with no arguments
// return only one constructor with arguments
// If there is only one constructor with no arguments, null is returned and the outer logic is instantiated using the constructor without arguments by default
return (candidateConstructors.length > 0 ? candidateConstructors : null);
}
Copy the code

conclusion

The logic of this code is quite convoluted, let’s take it slowly:

  • 1. Check whether the current beanName is already in the cachelookupMethodsCheckedExists, if there is no need to parse again. Otherwise, determine whether the method in the current Class Class contains@LookupAnnotation, which encapsulates method information asLookupOverrideObjects are loaded into MBD.
  • Get the constructor available in the current bean from the cache, and return it if one exists.
  • 3, get all the constructors of the current class and assign the values torawCandidatesThe variable. If the current class is a cglib generated proxy class, the parent class is obtained.
  • 4. Iterate through each constructor to see if it contains@AutowiredAnnotation.
    • A: Existence: judgmentrequiredProperty is true, where the current constructor is assigned torequiredConstructorProperties. Then add the current constructor tocandidatesAttribute.
    • B: None: Check whether the parameters of the current constructor are 0; Assigns the current constructor todefaultConstructorProperties.
  • 5. End of loop
  • 6, judgment,candidatesProperty is null, indicating whether it exists@AutowiredAnnotation annotation constructor.
    • A: Not empty, currentcandidatesProperty holds annotated constructors. Then determine if there is anyrequiredConstructor that is true.
      • A1: Does not exist, judgmentdefaultConstructorProperty is not null, willdefaultConstructorProperty tocandidates.defaultConstructorProperty holds a no-argument constructor.
      • A2: Exist, willcandidatesAttribute assigned tocandidateConstructors.candidateConstructorsFinal return value.
    • B: Empty, that is, there is no addition@AutowiredAnnotation constructor. The judgerawCandidatesIs the size of the constructor equal to 1 and the number of arguments greater than or equal to 0, which means that there is only one constructor with arguments, if so, the value is assigned tocandidateConstructorsProperties. Otherwise, the empty array is assigned tocandidateConstructorsProperties.
  • 7, judgment,candidateConstructorsIf the length of the property is greater than 0, returncandidateConstructorsOtherwise null is returned.

figure

Figure come fromTuring college.

The instance

Since the above logic is troublesome. Here are a few examples to make it easier and faster to understand. Little thing, I can not understand you ~.

The preparatory work

  • Create two classes, one A and one B.
/ / A class
public class A {
	String name;

	private B b;

	public A(a) {
		System.out.println("Instantiate using the no-argument constructor");

	}
	
	public A(String name) {
		System.out.println("Instantiated using the name parameterized constructor");
		this.name = name;
	}

	public A(B b) {
		System.out.println("Instantiate = using the parameterized constructor B" + b);
		this.b = b;
	}

	public A(String name, B b) {
		System.out.println("Instantiate = with name + B parameterized constructor"+ b);

		this.name = name;
		this.b = b; }}/ / class B
public class B {}Copy the code
  • The configuration file
<bean class="com.gongj.determineConstructors.A" id="aa"></bean>

<bean class="com.gongj.determineConstructors.B" id="bb"></bean>

<context:component-scan base-package="com.gongj.create"></context:component-scan>
Copy the code
  • Start the class
public static void main(String[] args) {
    ClassPathXmlApplicationContext context =
        new ClassPathXmlApplicationContext("spring-config5.xml");
    A a = (A)context.getBean("aa");
    System.out.println(a);
}
Copy the code

Instance of a

There are two @AutoWired annotation methods in the class, one with true and one with false required.

Add the @autoWired annotation to the A(B B) constructor A(String Name) constructor of class A, one of which is true and the other is false.

@Autowired(required = false)
public A(String name) {
    System.out.println("Instantiated using the name parameterized constructor");
    this.name = name;
}

@Autowired
public A(B b) {
    System.out.println("Instantiate = using the parameterized constructor B" + b);
    this.b = b;
}

Copy the code

Guess what, guys! Guess not to be able to look at the source logic. Xiaojie will first answer said, will throw an exception.

If you directly start Main, the console will print the following logs:

If there’s a constructor in a class that says@AutowiredNotes, andrequiredProperties fortrueCannot be written on other constructors in the class@AutowiredAnnotations. An error will be reported if written to boot.

Example 2

There are multiple @AutoWired annotation methods in the class and the required attribute is false.

Change the required attribute on the constructor A(B B) to false. That is, there are two @AutoWired tagged methods in the class with the required attribute false.

@Autowired(required = false)
public A(B b) {
	System.out.println("Instantiate = using the parameterized constructor B" + b);
    this.b = b;
}
Copy the code

Guess again!You can see that there are three constructors. The two were@AutowiredAnnotation method plus no parameter constructor. theSpringWho will be used? Start theMainThe console prints the following:

Use B and constructor instantiates = com. Gongj. DetermineConstructors. B @ 2 a556333 com. Gongj. DetermineConstructors. @ d2fa64 32 ACopy the code

You can see we’re using the A(B, B) constructor. Why is that? Here is a brief introduction. If you want to understand, you need to play DeBug practice.

  • Now that we have the constructor from this step. The parameterized constructor is called to instantiate the Bean(autowireConstructor()).

  • The constructors are then sorted. Sort the public constructor by the number of arguments in descending order. Then sort the non-public constructors in descending order.

  • Then loop through the constructor.

  • Get the argument list for each constructor, the loop argument list. Based on the parameter type, the parameter name is first matched and converted to the value specified in the bean’s constructor-arg tag. If constructor-arg does not specify a value, then constructor automatic inference is performed, which, to put it bluntly, is to create or retrieve the Bean.

    In example 2, we get three constructors and sort them, A(B B)->A(String name)->A(). Then loop through each constructor; Get the argument list for each constructor, the loop argument list. Matches the value specified in the bean’s constructor-arg tag based on the parameter type and parameter name. The above examples will definitely not match constructor-arg because none of them will. That would be inferred automatically by the constructor.

    • The A(B B) constructor will create the Bean or get the Bean, and can create or get the Bean successfully. That is, the constructor matches successfully, but may not be the best fit, so save the information and continue to loop through the other constructors.

      // Take the current constructor and found parameter values as the ones to be used, and iterate over the next constructor
      if (typeDiffWeight < minTypeDiffWeight) {
          constructorToUse = candidate;
          argsHolderToUse = argsHolder;
          argsToUse = argsHolder.arguments;
          minTypeDiffWeight = typeDiffWeight;
          ambiguousConstructors = null;
      }
      Copy the code
    • A(String name) constructor, this cannot be inferred, end this loop.

    • A() : no argument constructor, will not be inferred, directly end the loop.

      if(constructorToUse ! =null&& argsToUse ! =null && argsToUse.length > parameterCount) {
          The selected constructor has been found and the number of found constructor parameters is greater than the current traversal, so no need to continue traversal
          // PS: the number of arguments is sorted in descending order
          break;
      }
      Copy the code

Let’s modify the code by adding the following code to the XML file to add the constructor-arg tag to A Bean.

<bean class="com.gongj.determineConstructors.A" id="aa">
    <constructor-arg index="0" value="gggg"></constructor-arg>
</bean>
Copy the code

Guess again, what does the console print?

Use the name and constructor instantiates com. Gongj. DetermineConstructors. @ 38 bc8ab5 ACopy the code

The A(B B) constructor first matches and converts the value specified by constructor-arg, but this does not convert at all. String is converted to B, and an exception is thrown to end the loop. It is then compared to the A(String name) constructor.

Examples of three

There is only one constructor in a class.

Example three needs to modify the code by first adding a C class and configuring the related configuration.

public class C {}// Related configuration
<bean class="com.gongj.determineConstructors.C" id="cc"></bean>
Copy the code

Add A C attribute to class A, and there is only one constructor A(C C, B B) that does not have the @autowired annotation.

public class A {
	String name;

	private C c;
	private B b;

//	public A() {
// system.out.println (" instantiate with no argument constructor ");
//
//	}
	//@Autowired(required = false)
//	public A(String name) {
// system.out.println (" instantiated with name argument constructor ");
// this.name = name;
//	}

	//@Autowired(required = false)
//	public A(B b) {
// system.out.println (" instantiate with B argument constructor =" + B);
// this.b = b;
//	}
	//@Autowired(required = false)
//	public A(String name, B b) {
// system.out. println(" instantiate with name + B argument constructor ="+ B);
//
// this.name = name;
// this.b = b;
//	}

	public A(C c, B b) {
		System.out.println("Instantiate = using C + B parameterized constructor"+ b);
		this.c = c;
		this.b = b; }}Copy the code

The startup console prints as follows:

Use C + B and constructor instantiates = com. Gongj. DetermineConstructors. @ 17 d677df com. B gongj. DetermineConstructors. @ f93a98 42 ACopy the code
  • 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.