In the previous BeanWrapper article, we introduced the origin of BeanWrapper. Now let’s move on to how Spring constructs a Bean.
The code is not long or particularly complex
/** * Create beans using the appropriate instantiation strategy: FactoryMethod, constructor auto-injection, or simple no-argument constructor */
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) {
// Make sure bean class is actually resolved at this point.Class<? > beanClass = resolveBeanClass(mbd, beanName); . Supplier<? > instanceSupplier = mbd.getInstanceSupplier();if(instanceSupplier ! =null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
if(mbd.getFactoryMethodName() ! =null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if(mbd.resolvedConstructorOrFactoryMethod ! =null) {
resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; }}}if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null.null);
}
else {
returninstantiateBean(beanName, mbd); }}// Candidate constructors for autowiring?Constructor<? >[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);if(ctors ! =null|| mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || ! ObjectUtils.isEmpty(args)) {return autowireConstructor(beanName, mbd, ctors, args);
}
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if(ctors ! =null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
return instantiateBean(beanName, mbd);
}
Copy the code
Class<? > beanClass = resolveBeanClass(mbd, beanName);Copy the code
This step is to explain that the bean’s corresponding type is interpreted as a Class and placed in a BeanDefinition (BeanDefinition may be created with a className instead of a Class object).
InstanceSupplier
Supplier<? > instanceSupplier = mbd.getInstanceSupplier();if(instanceSupplier ! =null) {
return obtainFromSupplier(instanceSupplier, beanName);
}
Copy the code
This step is to get the Supplier object, call the get method of the object to get the created bean, and then build and initialize the BeanWrapper. Register the appropriate PropertyEditor.
protected BeanWrapper obtainFromSupplier(Supplier
instanceSupplier, String beanName) {
Object instance;
String outerBean = this.currentlyCreatedBean.get();
this.currentlyCreatedBean.set(beanName);
try {
instance = instanceSupplier.get();
}
finally {
if(outerBean ! =null) {
this.currentlyCreatedBean.set(outerBean);
}
else {
this.currentlyCreatedBean.remove(); }}if (instance == null) {
instance = new NullBean();
}
BeanWrapper bw = new BeanWrapperImpl(instance);
initBeanWrapper(bw);
return bw;
}
protected void initBeanWrapper(BeanWrapper bw) {
bw.setConversionService(getConversionService());
registerCustomEditors(bw);
}
Copy the code
How do we register this Supplier?
if (context instanceof GenericApplicationContext) {
((GenericApplicationContext) context).registerBean(Service.class,()->{
System.out.println("create bean in supplier");
return new Service();
});
}
Service bean = context.getBean(Service.class);
Copy the code
FactoryMethod
if(mbd.getFactoryMethodName() ! =null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
protected BeanWrapper instantiateUsingFactoryMethod(
String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
return new ConstructorResolver(this).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs);
}
Copy the code
The second way to instantiate beans. What is FactoryMethod?
There are static factories and factory methods in design patterns, and the same is true here. The beans we declare in the configuration class are similar to this pattern.
@Configuration
public class Config {
@Bean
public Service service(a) {
return new Service();
}
@Bean
public static Service staticService(a) {
return newService(); }}Copy the code
Step by step code analysis
public BeanWrapper instantiateUsingFactoryMethod(
String beanName, RootBeanDefinition mbd, @Nullable Object[] explicitArgs) {
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw); Object factoryBean; Class<? > factoryClass;boolean isStatic;
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");
}
factoryBean = this.beanFactory.getBean(factoryBeanName);
if (mbd.isSingleton() && this.beanFactory.containsSingleton(beanName)) {
throw new ImplicitlyAppearedSingletonException();
}
factoryClass = factoryBean.getClass();
isStatic = false;
}
else {
// It's a static factory method on the bean class.
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; }...Copy the code
Mbd.getfactorybeanname () returns the beanId, in this case config, of its configuration class if the method corresponding to the @Bean annotation is non-static
Null is returned if the @bean annotation corresponds to a static method. This setting is the process of use in ConfigurationClassPostProcessor ConfigurationClassBeanDefinitionReader explain set scanning registered bean.
The factoryBean is an instance of this configuration class, retrieved from the Spring container. For static methods, the invoke object can be NULL when the @Bean-modified method is subsequently invoked via reflection, so when @Bean modifies a static method, factoryBean = NULL.
. Method factoryMethodToUse =null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;
if(explicitArgs ! =null) {
argsToUse = explicitArgs;
}
else {
Object[] argsToResolve = null;
synchronized (mbd.constructorArgumentLock) {
factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
// Method used the last time the bean was created, mainly for the Prototype type
if(factoryMethodToUse ! =null && mbd.constructorArgumentsResolved) {
// The constructor argument used last time the bean was created
argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
// Last used constructor argument (not yet transformed or needs extra processing)argsToResolve = mbd.preparedConstructorArguments; }}}if(argsToResolve ! =null) {
See the previous article for more information on type conversions and property editing
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true); }}...Copy the code
This is mainly about the interpretation of parameters and the selection of the method to call, which is mainly taken from the BeanDefinition and, if it is the first time, does not get values from it.
ExplicitArgs is not null if this value is specified when calling the Spring#getBean method. If this value is specified, then even if there is a method in the BeanDefinition that produced the bean that was explained last time, it will not be used, and only the most appropriate one will be selected again.
@Bean()
@Scope(value = SCOPE_PROTOTYPE)
public static Service staticService(a) {
return new Service();
}
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(JunitSpringBootApplication.class, args);
Object staticService = context.getBean("staticService");
staticService = context.getBean("staticService");
}
Copy the code
The second call to context.getBean(“staticService”) will enter the cache that was created last time.
if (factoryMethodToUse == null || argsToUse == null) {
// Need to determine the factory method...
// Try all methods with this name to see if they match the given arguments.
factoryClass = ClassUtils.getUserClass(factoryClass);
List<Method> candidates = null;
if (mbd.isFactoryMethodUnique) {
if (factoryMethodToUse == null) {
factoryMethodToUse = mbd.getResolvedFactoryMethod();
}
if(factoryMethodToUse ! =null) { candidates = Collections.singletonList(factoryMethodToUse); }}if (candidates == null) {
candidates = new ArrayList<>();
Method[] rawCandidates = getCandidateMethods(factoryClass, mbd);
for (Method candidate : rawCandidates) {
if(Modifier.isStatic(candidate.getModifiers()) == isStatic && mbd.isFactoryMethod(candidate)) { candidates.add(candidate); }}}...Copy the code
Determining whether a BeanDefinition’s FactoryMethod is unique is based on whether the @Bean modified method name is unique in the configuration class, since the method name is its beanId. Two methods with the same method name, whether static or non-static, are not unique. If it is unique, it becomes a candidate method directly.
If not, all methods of the configuration class are reflected and filtered based on method names and whether they are static. Multiple candidate approaches may exist at this point.
if (candidates.size() == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
Method uniqueCandidate = candidates.get(0);
if (uniqueCandidate.getParameterCount() == 0) {
mbd.factoryMethodToIntrospect = uniqueCandidate;
synchronized (mbd.constructorArgumentLock) {
mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;
mbd.constructorArgumentsResolved = true;
mbd.resolvedConstructorArguments = EMPTY_ARGS;
}
bw.setBeanInstance(instantiate(beanName, mbd, factoryBean, uniqueCandidate, EMPTY_ARGS));
returnbw; }}Copy the code
If there is only one candidate method, and no arguments are passed in, and the @bean modified method has no input arguments, then it is very simple to call the method by reflection.
If there is more than one candidate method, it is sorted. Public over non-public, more input over less input (Spring always wants to give config class beans the best love)
if (candidates.size() > 1) { // explicitly skip immutable singletonList
candidates.sort(AutowireUtils.EXECUTABLE_COMPARATOR);
}
public static final Comparator<Executable> EXECUTABLE_COMPARATOR = (e1, e2) -> {
int result = Boolean.compare(Modifier.isPublic(e2.getModifiers()), Modifier.isPublic(e1.getModifiers()));
returnresult ! =0 ? result : Integer.compare(e2.getParameterCount(), e1.getParameterCount());
};
Copy the code
Beandefinitions created by @beans in all configuration classes are AUTOWIRE_CONSTRUCTOR.
MinTypeDiffWeight represents the weight of the difference between the method parameter type and the bean parameter type actually found in Spring. If the difference is small, it is selected as the last candidate method and called to create the return bean.
ConstructorArgumentValues resolvedValues = null;
boolean autowiring = (mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
int minTypeDiffWeight = Integer.MAX_VALUE;
Set<Method> ambiguousFactoryMethods = null;
int minNrOfArgs;
if(explicitArgs ! =null) {
minNrOfArgs = explicitArgs.length;
}
else {
// We don't have arguments passed in programmatically, so we need to resolve the
// arguments specified in the constructor arguments held in the bean definition.
if (mbd.hasConstructorArgumentValues()) {
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();
resolvedValues = new ConstructorArgumentValues();
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);
}
else {
minNrOfArgs = 0;
}
}
LinkedList<UnsatisfiedDependencyException> causes = null;
Copy the code
This involves dependency handling, by default finding all the Beannames of the parameter type from the BeanFactory, using the bean if there is one, and comparing beanids if there are multiple. If neither can be found or which bean can be determined as a method entry, an exception is thrown, and the for loop moves on to the next candidate method for comparison filtering. If there is a consistent type difference, it will log and exit the loop later to throw an exception that the model cannot pick the correct method.
for (Method candidate : candidates) {
int parameterCount = candidate.getParameterCount();
if(parameterCount >= minNrOfArgs) { ArgumentsHolder argsHolder; Class<? >[] paramTypes = candidate.getParameterTypes();if(explicitArgs ! =null) {
// Explicit arguments given -> arguments length must match exactly.
if(paramTypes.length ! = explicitArgs.length) {continue;
}
argsHolder = new ArgumentsHolder(explicitArgs);
}
else {
// Resolved constructor arguments: type conversion and/or autowiring necessary.
try {
String[] paramNames = null;
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();
if(pnd ! =null) {
paramNames = pnd.getParameterNames(candidate);
}
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);
}
// Swallow and try next overloaded factory method.
if (causes == null) {
causes = new LinkedList<>();
}
causes.add(ex);
continue; }}int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
// Choose this factory method if it represents the closest match.
if (typeDiffWeight < minTypeDiffWeight) {
factoryMethodToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousFactoryMethods = null;
}
else if(factoryMethodToUse ! =null&& typeDiffWeight == minTypeDiffWeight && ! mbd.isLenientConstructorResolution() && paramTypes.length == factoryMethodToUse.getParameterCount() && ! Arrays.equals(paramTypes, factoryMethodToUse.getParameterTypes())) {if (ambiguousFactoryMethods == null) {
ambiguousFactoryMethods = new LinkedHashSet<>();
ambiguousFactoryMethods.add(factoryMethodToUse);
}
// I can't do thatambiguousFactoryMethods.add(candidate); }}}Copy the code
That’s the end of the FactoryMethod. Finally, select the most appropriate method to generate the bean
When we configure classes and overload methods, we will not have a unique BeanFactory. If our arguments are still dependent on the bean’s parent class/parent interface, we will become ambiguous and throw exceptions
Constructor
// Shortcut when re-creating the same bean...
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if(mbd.resolvedConstructorOrFactoryMethod ! =null) {
resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; }}}if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null.null);
}
else {
returninstantiateBean(beanName, mbd); }}Copy the code
This is the cached, explained operation. When your bean is prototype and not declared in a configuration class, the second fetch goes into this code block.
// Candidate constructors for autowiring?Constructor<? >[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);if(ctors ! =null|| mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || ! ObjectUtils.isEmpty(args)) {return autowireConstructor(beanName, mbd, ctors, args);
}
Copy the code
This code will eventually come to AutowiredAnnotationBeanPostProcessor# determineCandidateConstructors
Forget Lookup annotations and caching
- Find the constructor declared by this class
- Find all the constructors that use the @autowire annotation. If a constructor of required true already exists, then no second constructor can be decorated with @autowire. If both are false, there can be more than one
- If there is no constructor required = true (there is one decorated with @autowire), then if there is a default constructor, add it to the array as well
- If there is no @autowire modified constructor, but there is a non-default constructor with an input parameter greater than 0, return it
If the returned constructor array is not null, the ConstructorResolver#autowireConstructor method is entered. Depending on the constructor passed in, select it to create the bean if there is only one, or try it one by one if there are multiple, in the same logical way as selecting FactoryMethod.
- Sort first (public first, multiple parameters first)
- For loop, look for parameter objects in Spring, compare the difference of parameter types, and select the constructor with the least difference
- Reflection calls the constructor, creating the bean
// Preferred constructors for default construction?
ctors = mbd.getPreferredConstructors();
if(ctors ! =null) {
return autowireConstructor(beanName, mbd, ctors, null);
}
// No special handling: simply use no-arg constructor.
return instantiateBean(beanName, mbd);
Copy the code
GetPreferredConstructors This method returns null by default only one subclass tries to return. ClassDerivedBeanDefinition the class only when we try to take the initiative to register: Supplier will use the class InstanceSupplier (above), and other conditions are returns null
@Override
@Nullable
publicConstructor<? >[] getPreferredConstructors() { Class<? > clazz = getBeanClass();// Kotlin can return non-nullConstructor<? > primaryCtor = BeanUtils.findPrimaryConstructor(clazz);if(primaryCtor ! =null) {
return newConstructor<? >[] {primaryCtor}; }// The constructor for publicConstructor<? >[] publicCtors = clazz.getConstructors();if (publicCtors.length > 0) {
return publicCtors;
}
return null;
}
Copy the code
While instantiateBean (beanName, MBD); It is very simple to create the bean by calling the no-argument constructor reflection. This involves method injection (replace/lookup), which will be covered in a future article.
The last
Supplier is simple, and FactoryMethod is simpler than Constructor because instead of finding the corresponding method, FactoryMethod can filter out the appropriate method by sorting and rules, which also apply Its constructor.
Constructor needs to select an appropriate constructor, @autowire modified, or none, and then use the default constructor. Multiple times, filtering out the appropriate constructor is consistent with the FactoryMethod.