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 and
explicitArgs
The parameter is empty and the configuration file is not usedconstructor-arg
Property, the instance object is called using the no-parameter factory methodInstantiationStrategy
The object’sinstantiate()
To create an instance. Cache in passing:
- 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 and
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. if
explicitArgs
If the parameter is empty, obtain the construction parameter information from the configuration file and determine the construction parameter.
- 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. if
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 buildingArgumentsHolder
Object. If no parameter is displayed, obtain this parameterParameterNameDiscoverer
Object that is used to resolve parameter names on methods and constructors. According to N multiple parametersArgumentsHolder
Object that holds the parameter, which we call the parameter holder. When wrapping an object intoArgumentsHolder
Object, 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.
- 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 (
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.