For more articles, see: [www.shangsw.com]
A bean load
In our example, the bean is called as follows:
MyBeanTest myTestBean = (MyBeanTest) beanFactory.getBean("myTestBean");
Copy the code
Let’s take a quick look at how Spring code works:
@param name beanName @return Corresponding Bean instance @throws BeansException Bean exception */ @override public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); }Copy the code
/**
* 返回一个实例, 这个实例可能是共享的也可能是独享的
* @param name beanName
* @param requiredType 类型。可以为null
* @param args 构造函数的参数。可以为null
* @param typeCheckOnly 是否需要进行类型检查。非实际使用
* @param <T> 类型
* @return 实例
* @throws BeansException Bean异常
*/
@SuppressWarnings("unchecked")
protected <T> T doGetBean(String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException {
String beanName = transformedBeanName(name);//提取对应的beanName
Object bean;
/*
* 检查缓存中或者实例工厂中是否有对应的实例
* 为什么首先会使用这段代码?
* 因为在创建单例bean的时候会存在依赖注入的情况, 而在创建依赖的时候为了避免循环依赖,
* Spring创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光,
* 也就是将ObjectFactory加入到缓存中, 一旦下个bean创建时候需要依赖上个bean则直接使用ObjectFactory
*/
Object sharedInstance = getSingleton(beanName);//直接尝试从缓存获取或者singletonFactories中的ObjectFactory中获取
if (sharedInstance != null && args == null) {//从二级或者三级缓存中获取到了对象并且构造参数为空
if (logger.isTraceEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {//如果这个对象在一级缓存中
logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
} else {
logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//返回对应的实例, 有时候存在诸如BeanFactory的情况并不是直接返回实例本身而是返回指定方法返回的实例
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
} else {//不存在
/*
* 只有在单例的情况下才会尝试解决循环依赖, 原型模式情况下, 如果存在
* A中有B的属性, B中也有A的属性, 那么当依赖注入的时候, 就会产生当A还未创建完成的时候因为
* 对于B的创建再次返回创建A, 造成循环依赖, 也就是下面的情况
*/
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
//如果beanDefinitionMap中也就是所有已经加载的的类中不包括beanName则尝试从parentBeanFactory中检测 Start
BeanFactory parentBeanFactory = getParentBeanFactory();//父类的beanFactory
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {//存在父beanFactory并且beanDefinitionMap中不存在该bean
String nameToLookup = originalBeanName(name);//将name添加前缀&
if (parentBeanFactory instanceof AbstractBeanFactory) {
return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
nameToLookup, requiredType, args, typeCheckOnly);//寻找父类的实例
} else if (args != null) {
return (T) parentBeanFactory.getBean(nameToLookup, args);//寻找父类的实例
} else if (requiredType != null) {
return parentBeanFactory.getBean(nameToLookup, requiredType);//寻找父类的实例
} else {
return (T) parentBeanFactory.getBean(nameToLookup);//寻找父类的实例
}
}
//如果beanDefinitionMap中也就是所有已经加载的的类中不包括beanName则尝试从parentBeanFactory中检测 End
//如果不是仅仅做类型检查则是创建bean, 这里需要记录 Start
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
//如果不是仅仅做类型检查则是创建bean, 这里需要记录 End
try {
//将存储XML文件的GenericBeanDefinition转换为RootBeanDefinition, 如果指定beanName是子Bean的话同时会合并父类的相关属性 Start
RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
//将存储XML文件的GenericBeanDefinition转换为RootBeanDefinition, 如果指定beanName是子Bean的话同时会合并父类的相关属性 End
//若存在依赖, 则需要递归实例化依赖的bean Start
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dep : dependsOn) {
if (isDependent(beanName, dep)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
}
registerDependentBean(dep, beanName);//缓存依赖调用
try {
getBean(dep);//尝试获取依赖项, 不出异常则没有问题
} catch (NoSuchBeanDefinitionException ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"'" + beanName + "' depends on missing bean '" + dep + "'", ex);
}
}
}
//若存在依赖, 则需要递归实例化依赖的bean End
//实例化依赖的bean后便可以实例化mbd本身了
if (mbd.isSingleton()) {//singleton模式的创建
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
} catch (BeansException ex) {
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
} else if (mbd.isPrototype()) {//protype模式创建
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
} finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
} else {//指定的scope上实例化bean
String scopeName = mbd.getScope();
if (!StringUtils.hasLength(scopeName)) {
throw new IllegalStateException("No scope name defined for bean ´" + beanName + "'");
}
Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, () -> {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
} finally {
afterPrototypeCreation(beanName);
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
} catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; consider " +
"defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex);
}
}
} catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
//检查需要的类型是否符合bean的实际类型 Start
if (requiredType != null && !requiredType.isInstance(bean)) {
try {
T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
if (convertedBean == null) {
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
return convertedBean;
} catch (TypeMismatchException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Failed to convert bean '" + name + "' to required type '" +
ClassUtils.getQualifiedName(requiredType) + "'", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
//检查需要的类型是否符合bean的实际类型 End
return (T) bean;
}
Copy the code
From the point of view of the code, loading beans goes through a fairly complex process that involves a variety of considerations. We can now get a rough idea of how Spring loads beans:
- Convert the corresponding beanName
The name passed in is not beanName. The parameter passed in May bean alias or a FactoryBean, so a series of parsing is required.
- Remove the modifier of a FactoryBean, that is, if name=”&aa”, then the & is removed first and name=”aa” is made.
- Take the final beanName represented by the specified alias. For example, if alias A points to A bean named B, return B. C is returned if alias A points to alias B, and alias B points to A bean named C
- Try to load a singleton from the cache
Singletons are created only once in the same Spring container, and the bean is retrieved directly from the singleton cache. Of course, you’re just trying to load, first from the cache, and then again from singletonFactories if it doesn’t work. In order to avoid cyclic dependencies, the principle of Spring bean creation is to expose the ObjectFactory of the bean to the cache before the bean is created. ObjectFactory is used directly whenever the next bean is created and needs to rely on the previous bean
- Instantiation of a bean
If you get the original state of the bean from the cache, you need to instantiate the bean. Note that the cache records only the original bean state, not necessarily the bean we ultimately want.
- Dependency checking for prototype patterns
Cyclic dependencies are only attempted in the singleton case, if there are properties of B in A and properties of A in B, then when dependency injection is done, it will create cyclic dependencies when A has not been created because the creation of B returns to create A again. Namely: isPrototypeCurrentlyInCreation (beanName) judgment to true.
- Detect parentBeanFactory
From the code, if there is no data in the cache, it goes directly to the parent factory to load. Judgment condition: parentBeanFactory! = null && ! containsBeanDefinition(beanName), parentBeanFactory ! = null. If parentBeanFactory is empty, there is nothing to say. But! ContainsBeanDefinition (beanName) is important. It checks that if the current loaded XML configuration file does not contain the beanName configuration, it will have to go to the parentBeanFactory and recurse to the getBean method.
- Convert GenericBeanDefinition, which stores XML configuration files, to RootBeanDefinition
Since the Bean information read from the XML configuration file is stored in GenericBeanDefinition, but all subsequent Bean processing is for RootBeanDefinition, a conversion is required. If the parent Bean is not empty, The attributes of the parent class are merged as well
- Looking for depend on
Because some properties are likely to be used during bean initialization, and some properties are likely to be dynamically configured and dependent on other beans, it is necessary to load the dependent beans first. Therefore, in Spring’s loading order, When a bean is initialized, its dependencies are first initialized
- Create beans for different scopes
There are different scopes in Spring, where the default is Singleton, but there are other configurations such as Prototype, Request, and so on. In this step, Spring performs different initialization strategies based on different configurations
- Type conversion
Usually the requiredType parameter is empty, but there may be cases where the returned bean is actually a String and requiredType is passed in an Integer type, and this step will work. Its function is to convert the returned bean to the type specified by requiredType.
The loading of the bean is complete after the above steps, the most important of which is step 8, which creates the bean for different scopes.
1.1 Use of FactoryBeans
A FactoryBean is a factory interface, unlike BeanFactory, which is a container class. A FactoryBean can generate instances of a Bean of a particular type. Its greatest benefit is that it allows you to customize the Bean creation process. The FactoryBean code looks like this:
public interface FactoryBean<T> {
String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
@Nullable
T getObject() throws Exception;
@Nullable
Class<?> getObjectType();
default boolean isSingleton() {
return true;
}
}
Copy the code
- T getObject() : Returns the bean instance created by FactoryBean. If isSingleton() returns true, the instance will be placed in the singleton cache pool in the Spring container
- Boolean isSingleton() : returns whether the scope of the bean instance created by FactoryBean isSingleton or prototype
- Class getObjectType() : Returns the bean type created by FactoryBean
When the implementation class configured by the class attribute in the configuration file is a FactoryBean, the getBean() method returns not the FactoryBean itself, but the object returned by the FactoryBean#getObject() method. The equivalent of FactoryBean#getObject() proxies the getBean() method.
For example, if the following Car is configured in the traditional way, each attribute of the Car corresponds to an element tag:
Public class Car {/** maxSpeed */ private int maxSpeed; /** brand */ private String brand; /** private double price; / / get/set methods}Copy the code
If implemented as a FactoryBean, you can be more flexible by specifying the configuration values for all attributes of the Car in a comma-separated manner:
Public class CarFactoryBean implements FactoryBean<Car> {/** Information, separated by commas. Private String carInfo; private String carInfo; @Override public Car getObject() throws Exception { Car car = new Car(); String[] infos = carInfo.split(","); car.setBrand(infos[0]); car.setMaxSpeed(Integer.parseInt(infos[1])); car.setPrice(Double.parseDouble(infos[2])); return car; } @Override public Class<? > getObjectType() { return Car.class; } //get/set/constructor }Copy the code
With CarFactoryBean in place, you can use the following custom configuration in your configuration file:
The < bean id = "car" class = "com. Bianjf. Factory. CarFactoryBean" > < constructor - arg index = "0" value = "super sports car, 400200000" / > < / bean >Copy the code
When getBean(“car”) is called and Spring discovers through reflection that The CarFactoryBean implements the Interface to the FactoryBean, the Spring container calls the interface method CarFactoryBean#getObject() to return. If you want to get an instance of CarFactoryBean, you need to prefix beanName with “& “when using the getBean(beanName) method, such as getBean(“&car”).
1.2 Obtaining singleton beans from the cache
The singleton is created only once in the same container in Spring, and the bean is fetched directly from the singleton cache. Of course, this is just an attempt to load, first from the cache and then again from the singletonFactories.
In order to avoid cyclic dependencies, Spring’s principle for creating beans is to expose the ObjectFactory of the bean to the cache before the bean is created. Once the next bean is created and needs to rely on the previous bean, the ObjectFactory is used directly.
@param beanName beanName @return may bea level 2 cache (no property injection) singleton, or a level 1 cache (full singleton) singleton, It can also be an object created by an object factory (that is, created directly without caching), Nullable public Object getSingleton(String beanName) {return getSingleton(beanName, true); }Copy the code
/** * returns the singleton object of the specified beanName. * First check to see if the singleton exists. If not, The parameter (allowEarlyReference) allows the creation of earlier reference objects (without property injection) * @param beanName beanName * @param allowEarlyReference Nullable protected Object getSingleton(String beanName, boolean allowEarlyReference) { Object singletonObject = this.singletonObjects.get(beanName); / / get the complete singleton (l1) / / level 1 cache without access to the singleton object and the object is created in situation (singletonsCurrentlyInCreation cache beanName) Start the if (singletonObject = = null && isSingletonCurrentlyInCreation(beanName)) { singletonObject = this.earlySingletonObjects.get(beanName); // Get the earlier singleton object (no property injection, Start if (singletonObject == null && allowEarlyReference) {synchronized (this singletonObjects) {/ / global variable Map, concurrent access problems, write to lock singletonObject = this. SingletonObjects. Get (beanName); / / double lock checking to ensure the uniqueness of the singleton the if (singletonObject = = null) {/ / there is no singletonObject = this. EarlySingletonObjects. Get (beanName); If (singletonObject == null) {if (singletonObject == null) { > singletonFactory = this.singletonFactories.get(beanName); If (singletonFactory! = null) { singletonObject = singletonFactory.getObject(); / / this singleton object. EarlySingletonObjects. Put (beanName singletonObject); / / deposit early singleton (no attribute injection) enclosing singletonFactories. Remove (beanName); // Remove}}}}} from the object factory cache // No early singleton was retrieved (level 2 cache) and early references are allowed to be created End} / / cache without access to the singleton object and the object is created in situation (singletonsCurrentlyInCreation cache beanName) End return singletonObject; // there is a direct return}Copy the code
This method first tries to get an instance from singletonObjects(level 1 cache), and if it doesn’t get it from earlySingletonObjects(level 2 cache), if it doesn’t get it, Then try to get the beanName ObjectFactory from singletonFactories and call getObject to create the bean. And put it in earlySingletonObjects, and remove it from singletonFactories, and all subsequent memory operations are only used for cyclic dependency detection. This is used only if allowEarlyRefrence is true.
There is no map for storing beans, as follows:
- SingletonObjects: A level 1 cache that holds the relationship between beanName and the creation of a full singleton bean. beanName -> bean instance
- EarlySingletonObjects: The second level cache, which also stores the relationship between beanName and created bean instances, is different from singletonObjects in that when a singleton bean is placed in it, it can be retrieved by the getBean method while the bean is being created. The purpose is to detect circular references, and the bean is actually an incomplete bean instance, which has no property injection
- SingletonFactories: A three-level cache used to store the relationship between beanName and the bean’s factory. beanName -> ObjectFactory
- RegisteredSingletons: Used to hold all currently registered beans
1.3 Getting an object from an instance of a bean
Among the getBean methods, getObjectForBeanInstance is a frequently used method, either to get beans from the cache or to load beans according to different Scope policies. Anyway, the first step we need to do once we get an instance of our bean is to call this method to verify correctness, which is essentially to check whether the current bean is a bean of type FactoryBean. If so, you need to call getObject() in the bean’s corresponding FactoryBean instance as the return value.
Either a bean fetched from the cache or loaded through a different scope policy is just the original bean state, and not necessarily the bean we want in the end. For example, if we need to process the factory bean, we get the initial state of the factory bean, but what we really need is the bean returned by the factory-method method defined in the factory bean. The getObjectForBeanInstance method does just that:
/** * get the bean we finally want. Either the bean fetched from the cache or loaded through a different scope policy is just the original bean state (no property injection), and * is not the bean we need. GetgetObjectForBeanInstance is ultimately want bean we * @ param beanInstance cache access to the bean instance * @ param name to name. May be beanName, It can also bea factoryBeanName(beanName prefixed with &) * @param beanName beanName * @param MBD root bean definition * @return bean */ protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @nullable RootBeanDefinition MBD) {// If it is a FactoryBean and you get an instance of it directly, Rather than a FactoryBean getObject bean instance of Start the if (BeanFactoryUtils. IsFactoryDereference (name)) {if (beanInstance instanceof NullBean) {// If it is an empty bean, return beanInstance; } if (! (beanInstance instanceof FactoryBean) {//beanInstance if not a FactoryBean, The verification through throw new BeanIsNotAFactoryException (beanName, beanInstance getClass ()); } if (mbd ! = null) { mbd.isFactoryBean = true; } return beanInstance; } // If it is a FactoryBean, and you get the instance of the FactoryBean directly, End // This bean can bea normal bean or a FactoryBean. If it is not a FactoryBean, Start if (! (beanInstance instanceof FactoryBean)) { return beanInstance; } // This bean can bea normal bean or a FactoryBean. If it is not a FactoryBean, End is returned. But beanName does not add & (that is, get the bean instance from the FactoryBean). if (mbd ! = null) { mbd.isFactoryBean = true; } else {/ / try to load Bean from the cache (there are returned directly) object = getCachedObjectForFactoryBean (beanName); } if (object == null) {if (object == null) {if (object == null) {if (object == null) {if (object == null) {if (object == null) {if (object == null) { > factory = (FactoryBean<? >) beanInstance; // ContainsBeanDefinitionCheck beanDefinitionMap (MBD == null &&) for loaded classes ContainsBeanDefinition (beanName) {// Convert GenericBeanDefinition, which stores XML configuration files, to RootBeanDefinition, If specified beanName is child bean and consolidate the relevant properties of the parent MBD = getMergedLocalBeanDefinition (beanName); } // Is the Boolean synthetic = (MBD! = null && mbd.isSynthetic()); object = getObjectFromFactoryBean(factory, beanName, ! synthetic); } return object; // Is a FactoryBean, but beanName does not add & (i.e. gets the bean instance from the FactoryBean) End}Copy the code
GetObjectForBeanInstance does the following:
- Validates the FactoryBean for correctness
- No processing is done for non-FactoryBeans
- Transform the bean
- Delegate parsing beans from Factory to getObjectFromFactoryBean
The code for the getObjectFromFactoryBean() method is as follows:
/** * Get the singleton from the given FactoryBean * @param Factory FactoryBean * @param beanName beanName * @param shouldPostProcess whether it should be postprocessed GetObjectFromFactoryBean (FactoryBean<? > factory, String beanName, boolean shouldPostProcess) { if (factory.isSingleton() && containsSingleton(beanName)) Synchronized (getSingletonMutex()) {synchronized (getSingletonMutex()) {synchronized (getSingletonMutex()) this.factoryBeanObjectCache.get(beanName); / / for singleton the if (object = = null) {/ / there is no the singleton object = doGetObjectFromFactoryBean (factory, beanName); / / from FactoryBean (there may be a circular dependencies) Object alreadyThere = this. FactoryBeanObjectCache. Get (beanName); // Get from cache (avoid circular references) if (alreadyThere! = null) { object = alreadyThere; {if} else {the if (shouldPostProcess) (isSingletonCurrentlyInCreation (beanName)) {/ / if the object is still in the process of creating, If the object is not created, return object; } beforeSingletonCreation(beanName); / / create before checking the try {object = postProcessObjectFromFactoryBean (object, beanName); } catch (Throwable ex) {throw new BeanCreationException(beanName, Throwable ex) {throw new BeanCreationException(beanName, Throwable ex); "Post-processing of FactoryBean's singleton object failed", ex); } finally { afterSingletonCreation(beanName); {}} the if (containsSingleton (beanName))/store/cache this. FactoryBeanObjectCache. Put (beanName, object); } } } return object; }} else {/ / level cache does not exist the Object Object Object = doGetObjectFromFactoryBean (factory, beanName); / / object if (shouldPostProcess) was obtained from the corresponding FactoryObject {try {object = postProcessObjectFromFactoryBean (object, beanName); } catch (Throwable ex) {throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex); } } return object; // Do nothing, return}}Copy the code
This method doesn’t see FactoryBean#getObject(). The only thing this method does is that if the bean is a singleton, it must be globally unique. Also, because it is a singleton, caching is used to improve performance. Otherwise you just get it.
In doGetObjectFromFactoryBean () method, we see the bean that was obtained from the factory, which is the object = factory. The getBean () :
/** * Obtains an instance of an object from a FactoryBean * @param Factory FactoryBean * @param beanName beanName * @return specifies the object * @throws BeanCreationException create Bean exception * / private Object doGetObjectFromFactoryBean (FactoryBean <? > factory, String beanName) throws BeanCreationException { Object object; try { if (System.getSecurityManager() ! AccessControlContext acc = getAccessControlContext(); try { object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc); } catch (PrivilegedActionException pae) { throw pae.getException(); }} else {// Get the object instance from the corresponding FactoryBean object = factory.getobject (); } } catch (FactoryBeanNotInitializedException ex) { throw new BeanCurrentlyInCreationException(beanName, ex.toString()); } catch (Throwable ex) { throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex); } if (object == null) { if (isSingletonCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException( beanName, "FactoryBean which is currently in creation returned null from getObject"); } object = new NullBean(); } return object; }Copy the code
If the bean is declared as a FactoryBean type, then when the bean is extracted it is not a FactoryBean, but the bean returned by the corresponding getObject method in the FactoryBean, And doGetObjectFromFactoryBean is the implementation of this feature.
Let’s return to the getObjectFromFactoryBean method:
/** * Get the singleton from the given FactoryBean * @param Factory FactoryBean * @param beanName beanName * @param shouldPostProcess whether it should be postprocessed GetObjectFromFactoryBean (FactoryBean<? > factory, String beanName, boolean shouldPostProcess) { if (factory.isSingleton() && containsSingleton(beanName)) {// Determine if this object is a singleton and exists in the first level cache... } else {/ / level cache does not exist the Object Object Object = doGetObjectFromFactoryBean (factory, beanName); / / object if (shouldPostProcess) was obtained from the corresponding FactoryObject {try {object = postProcessObjectFromFactoryBean (object, beanName); } catch (Throwable ex) {throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex); } } return object; // Do nothing, return}}Copy the code
Methods after doGetObjectFromFactoryBean did not directly return object, but did some post-processing operations. Tracking to class AbstractAutowireCapableBeanFactory postProcessObjectFromFactoryBean method:
/** * handle subsequent logic. * @param object singleton * @param beanName beanName * @return processed object */ @override protected Object postProcessObjectFromFactoryBean(Object object, String beanName) { return applyBeanPostProcessorsAfterInitialization(object, beanName); }Copy the code
/** * instantiated post-processor application * @param existingBean an existingBean * @param beanName beanName * @return a bean object that may be modified * @throws Abnormal BeansException * / @ Override public Object applyBeanPostProcessorsAfterInitialization (Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }Copy the code
We won’t go into the details here, because post-processing is linked to AOP and will be covered in subsequent AOP. Note here: as far as possible, ensure that all the bean will be called after initialization register BeanPostProcessor postProcessAfterInitialization method for processing.
1.4 Obtaining a Singleton
We have seen how to get a singleton from the cache. If there is no loaded singleton in the cache, we need to load the bean from scratch. Spring uses getSingleton’s overloaded method to load the bean:
/** * Create a singleton Bean object from the given beanName * @param beanName beanName * @param singletonFactory Singleton Bean object factory * @return registered singleton */ public Object getSingleton(String beanName, ObjectFactory<? > singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); Synchronized (enclosing singletonObjects) {/ / global variables need to lock the Object singletonObject = this. SingletonObjects. Get (beanName); / / first obtained from the singleton object (l1) / / level cache object does not exist the Start if (singletonObject = = null) {if (this. SingletonsCurrentlyInDestruction) {/ / if the object is destroyed, but users come and take, directly throw new exception BeanCreationNotAllowedException (beanName, "Singleton bean creation not allowed while singletons of this factory are in destruction " + "(Do not request a bean from a BeanFactory in a destroy method implementation!) "); } if (logger.isDebugEnabled()) { logger.debug("Creating shared instance of singleton bean '" + beanName + "'"); } beforeSingletonCreation(beanName); // Check Boolean newSingleton = false; / / logo whether the object is new the Boolean recordSuppressedExceptions = (enclosing suppressedExceptions = = null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { singletonObject = singletonFactory.getObject(); // Create newSingleton = true from the factory object; / / change the logo to true} the catch (an IllegalStateException ex) {singletonObject = this. SingletonObjects. Get (beanName); if (singletonObject == null) { throw ex; } } catch (BeanCreationException ex) { if (recordSuppressedExceptions) { for (Exception suppressedException : this.suppressedExceptions) { ex.addRelatedCause(suppressedException); } } throw ex; } finally { if (recordSuppressedExceptions) { this.suppressedExceptions = null; } afterSingletonCreation(beanName); AddSingleton (beanName, singletonObject);} if (newSingleton) {addSingleton(beanName, singletonObject); End return singletonObject; End return singletonObject; }}Copy the code
In fact, the above code uses the callback method, so that the program can do some preparation and processing operations before and after the singleton creation, but the real method to obtain the singleton bean is not implemented in this method, its implementation logic is implemented in the ObjectFactory singletonFactory instance. These preparation and processing operations include the following:
(1) Check whether the cache has been loaded
(2) If no beanName is loaded, record the loading status of the beanName
(3) Record the loading state before loading the singleton
You might think the beforeSingletonCreation method is an empty implementation, but it does something very important: Record loading state, that is, by enclosing singletonsCurrentlyInCreation. Add (beanName) record the current bean was created in the cache, so that we can to test the circular dependencies:
* @param beanName beanName */ protected void beforeSingletonCreation(String beanName) { if (! this.inCreationCheckExclusions.contains(beanName) && ! this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); }}Copy the code
(4) Instantiate the bean by calling the individual Object method of the ObjectFactory passed in as the parameter
(5) Processing method call after loading singleton
Similar to the loading state of the record in step (3), the loading state of the bean needs to be removed from the cache after the bean is loaded:
/** * Some logic after creating the bean: * inCreationCheckExclusions at this time if there is no * @ param beanName beanName * / protected void afterSingletonCreation (String beanName) { if (! this.inCreationCheckExclusions.contains(beanName) && ! this.singletonsCurrentlyInCreation.remove(beanName)) { throw new IllegalStateException("Singleton '" + beanName + "' isn't currently in creation"); }}Copy the code
(6) Record the result to the cache and delete various auxiliary states recorded during the loading of the bean
/** * add the singleton to the cache, * @param beanName beanName @param singletonObject singleton */ protected void addSingleton(String beanName, The Object singletonObject) {synchronized (enclosing singletonObjects) {/ / lock this global variable. SingletonObjects. Put (beanName, singletonObject); / / put the singleton object in the cache (l1) enclosing singletonFactories. Remove (beanName); / / delete object factory cache (level 3) enclosing earlySingletonObjects. Remove (beanName); / / remove early cache object (the second level cache) enclosing registeredSingletons. Add (beanName); // Cache singleton beanName}}Copy the code
(7) Return the processing result
The bean’s loading logic is defined in the singletonFactory argument passed as ObjectFactory:
sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; }});Copy the code
The core of the ObjectFactory simply calls the createBean method, so createBean is the core logic.
1.5 Preparing to Create beans
The code for createBean is as follows:
/** * The core method of this class: Create a bean instance and populate the bean instance (populate properties), Perform post processing * @param beanName beaName * @param MBD merges RootBeanDefinition for all attributes * @param args constructor arguments * @return ObjectFactory @override protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { if (logger.isTraceEnabled()) { logger.trace("Creating instance of bean '" + beanName + "'"); } RootBeanDefinition mbdToUse = mbd; Class<? > resolvedClass = resolveBeanClass(mbd, beanName); // Resolve the class if (resolvedClass! = null && ! mbd.hasBeanClass() && mbd.getBeanClassName() ! = null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); / / will parse the class stored} / / validation and the method of preparing cover Start try {mbdToUse. PrepareMethodOverrides (); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } / / / / validation and the method of preparing cover End to BeanPostProcessors an opportunity to return the agent instead of the real example Start try {Object bean = resolveBeforeInstantiation (beanName, mbdToUse); if (bean ! = null) { return bean; } } catch (Throwable ex) { throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName, "BeanPostProcessor before instantiation of bean failed", ex); } // Give BeanPostProcessors a chance to return the proxy instead of the real instance End // create and return the bean instance Start try {Object beanInstance = doCreateBean(beanName, processors) mbdToUse, args); if (logger.isTraceEnabled()) { logger.trace("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException( mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); } // Create and return the bean instance End}Copy the code
From the code, you can always see the specific steps and functions of the function:
- The class is resolved according to the class property set or according to className
- Mark and validate the Override attribute. There are lookup-method and repalce-method configurations in Spring, and the loading of these two configurations is to store the configurations in methodOverrides attribute of BeanDefinition
- The pre-initialization post-handler is applied to resolve whether the specified bean has a pre-initialization short-circuit operation
- Create a bean
1.5.1 Handling the Ovverride property
Check the AbstractBeanDefinition class’s prepareMethodOverrides method:
/ * * * override attributes * @ throws BeanDefinitionValidationException abnormal * / public void prepareMethodOverrides () throws BeanDefinitionValidationException {the if (hasMethodOverrides ()) {/ / judge whether there is any overrides properties getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride); }}Copy the code
/** * Validates and prepares the overrides attribute. * @ param mo MethodOverride * @ throws BeanDefinitionValidationException abnormal * / protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException { int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName()); If (count == 0) {if (count == 0) { An exception is thrown directly throw new BeanDefinitionValidationException (" Invalid method override: no method with name '" + mo.getMethodName() + "' on class [" + getBeanClassName() + "]"); } else if (count == 1) {mo.setoverloaded (false); }}Copy the code
There are two functions in Spring configuration: lookup-method and repalce-method, and the loading of these two configurations is to store the configurations in methodOverrides attribute of BeanDefinition. The implementation principle of these two functions is that, when the bean is instantiated, if the attribute of methodOverrides is detected, a proxy will be dynamically generated for the current bean and the corresponding interceptor will be used to enhance the bean. The implementation is detailed in the bean instantiation section.
1.5.2 Pre-processing of instantiation
In real call doCreate () method to create the instance of the bean before using such an approach resolveBeforeInstantiation (beanName, MBD) on properties of BeanDefinition do pre-processing. Of course, we can understand whether there is a corresponding logical implementation or not, because the real logical implementation is also a reflection of extensibility, but this is not important, in the function also provides a short-circuit judgment:
if (bean ! = null) { return bean; }Copy the code
If the result returned after the pre-processing is not empty, it will skip the subsequent Bean creation and return the result. This feature, though easily overlooked, plays a crucial role, and AOP functionality is based on this judgment.
/** * instantiation preprocessing * @param beanName beanName * @param MBD fusing RootBeanDefinition * @return shortcut Bean instance (possibly empty) */ @nullable protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; if (! Boolean. FALSE. Equals (MBD) beforeInstantiationResolved)) {/ / has not yet been parsed the if (! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class<? > targetType = determineTargetType(beanName, mbd); // determine the targetType if (targetType! = null) { bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); // The post-processor before instantiation applies if (bean! = null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); / / instantiate the post-processor after application.}}} MBD beforeInstantiationResolved = (bean! = null); } return bean; }Copy the code
ApplyBeanPostProcessorsBeforeInstantiation and applyBeanPostProcessorsAfterInitialization implementation is very simple, is the pre processing and post processing.
1.5.2.1 Post-processor applications before instantiation
The pre-instantiation call to the bean, which is the processing before converting AbstractBeanDefinition to BeanWrapper, gives subclasses an opportunity to modify the BeanDefinition, which means that by the time the application goes through this method, the bean may not be the bean we thought it was. Instead, it might be called a processed proxy bean.
/** * Post-processor application before instantiation. * that is, the processing before converting AbstractBeanDefinition to BeanWrapper. Give subclasses a chance to modify BeanDefinition. It may be AOP or some other way * @param beanClass The bean class to instantiate * @param beanName beanName * @return the bean Object specified */ @nullable protected Object applyBeanPostProcessorsBeforeInstantiation(Class<? > beanClass, String beanName) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName); if (result ! = null) { return result; } } } return null; }Copy the code
1.5.2.2 Post-processor application after instantiation
Rules of the Spring is in the bean after initialization, as far as possible to ensure that will register the post-processor postProcessAfterInitialization method is applied to the bean, because if the bean returned is not null, then not again experience the creation process of common bean, So only after application processor postProcessAfterInitialization method here:
/** * instantiated post-processor application * @param existingBean an existingBean * @param beanName beanName * @return a bean object that may be modified * @throws Abnormal BeansException * / @ Override public Object applyBeanPostProcessorsAfterInitialization (Object existingBean, String beanName) throws BeansException { Object result = existingBean; for (BeanPostProcessor processor : getBeanPostProcessors()) { Object current = processor.postProcessAfterInitialization(result, beanName); if (current == null) { return result; } result = current; } return result; }Copy the code
1.6 Cyclic Dependencies
1.6.1 What are Circular dependencies
Circular dependencies are circular references, where two or more beans hold references to each other, such as CircleA to CircleB, CircleB to CircleC, and CircleC to CircleA, and they eventually form a ring. As follows:
Circular calls are unresolvable, and unless there is a terminating condition, it is an infinite loop, which eventually leads to an out-of-memory error.
1.6.2 How does Spring solve Loop Dependencies
Detecting cyclic dependencies is relatively easy. Beans can be marked when they are created, and if recursive calls come back and find that they are being created, they indicate that they are cyclic dependent. Examples of Spring where circular dependencies occur are:
- Constructor loop dependencies
- Cyclic dependencies for the field property set
Here are three test classes:
public class CircleA { private CircleB circleB; public CircleA() { } public CircleA(CircleB circleB) { this.circleB = circleB; } public void setCircleB(CircleB circleB) { this.circleB = circleB; }}Copy the code
public class CircleB { private CircleC circleC; public CircleB() { } public CircleB(CircleC circleC) { this.circleC = circleC; } public void setCircleC(CircleC circleC) { this.circleC = circleC; }}Copy the code
public class CircleC { private CircleA circleA; public CircleC() { } public CircleC(CircleA circleA) { this.circleA = circleA; } public void setCircleA(CircleA circleA) { this.circleA = circleA; }}Copy the code
1.6.2.1 Constructor Loop Dependencies (this Spring can’t fix)
Through some code to read in front of, we know the Spring container will put Bean logo each is creating a “current to create large pools of Bean” (singletonsCurrentlyInCreation), beans will stay in the creation process in the pool, So if in the process of creating Bean found himself already in the pool, throwing an exception BeanCurrentlyInCreationException abnormal said circular dependencies. Beans that have been created are cleared from the currently created Bean pool.
The Spring container first creates A singleton CircleA, which depends on CircleB, and then places A in the “currently created Bean pool”, which creates CircleB, which depends on CircleC, and then places CircleB in the “currently created Bean pool”. CircleC is dependent on CircleA. CircleA is already in the pool.
The configuration is as follows:
<? The XML version = "1.0" encoding = "utf-8"? > <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd "> < bean id =" a "class =" com. Bianjf. Beans. CircleA "> <constructor-arg index="0" ref="b"/> </bean> <bean id="b" class="com.bianjf.bean.CircleB"> <constructor-arg index="0" ref="c"/> </bean> <bean id="c" class="com.bianjf.bean.CircleC"> <constructor-arg index="0" ref="a"/> </bean> </beans>Copy the code
The test code is as follows:
@Test
public void constructorTest() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beanCircleConstructorTest.xml");
CircleA a = (CircleA) context.getBean("a");
System.out.println(a);
context.close();
}
Copy the code
The final run will report an error:
1.6.2.2 Setter loop dependencies
Circular dependencies created by setter injection are done by the Spring container to expose beans that have just completed constructor injection but have not completed other steps (such as setter injection), and can only resolve singleton scoped bean circular dependencies. By exposing a singleton factory method in advance so that other beans can reference it, code like this:
<? The XML version = "1.0" encoding = "utf-8"? > <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd "> < bean id =" a "class =" com. Bianjf. Beans. CircleA "> <property name="circleB" ref="b"/> </bean> <bean id="b" class="com.bianjf.bean.CircleB"> <property name="circleC" ref="c"/> </bean> <bean id="c" class="com.bianjf.bean.CircleC"> <property name="circleA" ref="a"/> </bean> </beans>Copy the code
The test classes are as follows:
@Test
public void setTest() {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beanCircleTest.xml");
CircleA a = (CircleA) context.getBean("a");
System.out.println(a);
context.close();
}
Copy the code
The specific steps are as follows:
- The Spring container creates the singleton “circleA” bean by first creating the bean from the no-argument constructor and exposing an ObjectFactory to return a pre-exposed bean being created and placing the “circleA” identifier in the “currently created bean pool.” And then we do setter injection circleB
- The Spring container creates the singleton “circleB” bean by first creating the bean from the no-argument constructor and exposing an ObjectFactory to return a pre-exposed bean being created and placing the “circleB” identifier in the “currently created bean pool.” And then we do setter injection circleC
- The Spring container creates the singleton “circleC” bean by first creating the bean from the no-argument constructor and exposing an ObjectFactory to return a pre-exposed bean being created and placing the “circleC” identifier in the “currently created bean pool.” The setter injection “circleA” is then performed. The ObjectFactory factory was exposed early when the “circleA” was injected, so it is used to return a pre-exposed bean being created
- Finally, dependency injection “circleB” and “circleA” completes setter injection
The dependency that scope is Prototype does not resolve circular dependencies.
1.7 create a bean
When after resolveBeforeInstantiation method, the program has two choices:
- If you create a proxy or rewrite the InstantiationAwareBeanPostProcessor postProcessBeforeInstantiation method and the method of postProcessBeforeInstantiation changed in b Ean, returns directly
- Otherwise, regular bean creation is required.
Regular bean creation is done in doCreationBean:
/** * actually creates the specified Bean. The pre-processing has already occurred before this method is called. * @param beanName beanName * @param MBD fuses the property RootBeanDefinition * @param args constructor parameter. May be empty * @return created Bean Object * @throws BeanCreationException Bean create exception */ protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException {//Bean wrappers get Start BeanWrapper instanceWrapper = null; If (MBD) isSingleton ()) {/ / singleton instanceWrapper = this. FactoryBeanInstanceCache. Remove (beanName); If (instanceWrapper == null) {if (instanceWrapper == null) {if (instanceWrapper == null) {if (instanceWrapper == null) {if (instanceWrapper == null) {if (instanceWrapper == null) { InstanceWrapper = createBeanInstance(beanName, MBD, args); } / / Bean wrapper for the End Object Bean. = instanceWrapper getWrappedInstance (); Class<? > beanType = instanceWrapper.getWrappedClass(); if (beanType ! = NullBean.class) { mbd.resolvedTargetType = beanType; } synchronized (mbd.postProcessingLock) { if (! MBD. PostProcessed) {try {/ / application MergedBeanDefinitionPostProcessors applyMergedBeanDefinitionPostProcessors (MBD, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; }} /* * Whether early exposure is required: Singletons & allows loop dependencies & the current bean is being created, Detection of circular dependencies * / Boolean earlySingletonExposure = (MBD) isSingleton () && enclosing allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); AddSingletonFactory (beanName, () -> getEarlyBeanReference(beanName,); mbd, bean)); } // Initialize the bean instance Object exposedObject = bean; Try {// Populate the bean and inject the individual property values, where there may be properties that depend on other beans, the initial dependency bean populateBean(beanName, MBD, instanceWrapper) is recursively generated; // Call the initialization method: init-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); } } if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); if (earlySingletonReference ! = null) {/ / earlySingletonReference detected only in a circular dependencies will not empty the if (exposedObject = = beans) {/ / if exposedObject didn't be changed in the initialization method, ExposedObject = earlySingletonReference; } 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); }} /* * An actualDependentBeans is not null if the bean is not already created. */ 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 " + "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); } } } } try { registerDisposableBeanIfNecessary(beanName, bean, mbd); / / registered under the scope bean} the catch (BeanDefinitionValidationException ex) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }Copy the code
Logs and exceptions are very important, but we won’t go into logs and exceptions here. Let’s look at the general idea of the function:
- If it is a singleton, the cache is cleared first
- Instantiate the bean to convert BeanDefinition to BeanWrapper. Conversion is a complex process, but we can try to generalize the functionality:
- The factory method is used for initialization if one exists
- A class has multiple constructors, each of which takes different parameters, so it needs to be locked and initialized based on the parameters
- If neither a factory method nor a constructor with parameters exists, the bean is initialized using the default constructor
- MergedBeanDefinitionPostProcessor applications: bean combined treatment, Autowired annotation is achieved by this method, such as the type of parsing
- Dependency handling: There are cases of circular dependencies in Spring. For example, when A contains attributes of B, and B contains attributes of A, A circular dependency is formed. If both A and B are singletons, then the way to handle this in Spring is to design the automatic injection of A when B is created. Instead of directly re-creating A, you create the instance by putting the ObjectFactory in the cache, which solves the loop dependency problem
- Register the property population to populate all the properties into the instance of the bean
- Cyclic dependency check
- Register DisposableBean: If destory-method is configured, you will need to register it here so that it can be called at destruction time
- Complete the creation and return
1.7.1 Creating an instance of a bean
Once we understand circular dependencies, we can dig into each step of creating a bean, starting with createBeanInstance:
/** * Create a new instance. The strategies used are: Factory method, Construct injection or simple instance * @param beanName beanName * @param MBD fuses RootBeanDefinition for all properties * @param args Construct argument * @return BeanWrapper */ protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { Class<? > beanClass = resolveBeanClass(mbd, beanName); Class 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()); } // Get Start Supplier<? > instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier ! = null) { return obtainFromSupplier(instanceSupplier, beanName); } // Get End from the instance supply // If the factory method is not empty, use the factory method initialization policy Start if (mbd.getFactoryMethodName()! = null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } // Initialize policy with factory method if factory method is not empty End Boolean resolved = false; Boolean autowireNecessary = false; If (args == null) {if (args == null) {if (args == null) {if (args == null) { So call need according to the parameters before locking the constructor or the corresponding factory method Start synchronized (MBD) constructorArgumentLock) {if (MBD) resolvedConstructorOrFactoryMethod ! = null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; }} // A class has multiple constructors, each with different parameters, End} if (resolved) {if (resolved) {if (autowireNecessary) {// The constructor will automatically insert the return autowireConstructor(beanName, mbd, null, null); } else {// Create return instantiateBean(beanName, MBD) with default constructor; }} // Need to parse the Constructor<? >[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors ! = null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || ! ObjectUtils.isEmpty(args)) { return autowireConstructor(beanName, mbd, ctors, args); / / / / the default constructor injection automatically} structure of choice for the constructor Start ctors = MBD. GetPreferredConstructors (); if (ctors ! = null) { return autowireConstructor(beanName, mbd, ctors, null); } // Use the default constructor to construct return instantiateBean(beanName, MBD); }Copy the code
The main process is as follows:
- If there is a factoryMethodName attribute in the RootBeanDefinition, or if factory-Method is configured in the configuration file, Then Spring will try to use instantiateUsingFactoryMethod (beanName, MBD, args) method according to the configuration of the RootBeanDefinition generated the instance of the bean
- The constructor is parsed and instantiated. Because there may be multiple constructors in a bean’s corresponding class, and each constructor takes a different parameter, Spring determines which constructor will be used to instantiate the bean based on the parameter and type. But the judging process is a performance of consuming steps, so using caching mechanism, if has been parsed, don’t need to be repeated in analytic but directly from RootBeanDefinition attribute resolvedConstructorOrFactoryMethod cache value to get, Otherwise need to parse, and the analytic results added to the attribute resolvedConstructorOrFactoryMethod RootBeanDefinition.
1.7.1.1 autowireConstructor
There are two types of instance creation in Spring, generic instantiation and instantiation with parameters. The instantiation process with parameters is quite complex, because there is uncertainty, so a lot of work is done to determine the corresponding parameters:
/** * initialize the Bean with a constructor with arguments, Wrap it in a BeanWrapper * @param beanName beanName * @param MBD RootBeanDefinition * @param ctors constructor * @param explicitArgs @return BeanWrapper */ protected BeanWrapper autowireConstructor(String beanName, RootBeanDefinition MBD, @Nullable Constructor<? >[] ctors, @Nullable Object[] explicitArgs) { return new ConstructorResolver(this).autowireConstructor(beanName, mbd, ctors, explicitArgs); }Copy the code
/**
* 自动装配构造函数。
* 如果指定了显示的构造函数参数值, 也将使用此方法
* @param beanName beanName
* @param mbd 融合的RootBeanDefinition
* @param chosenCtors 选定的候选构造函数
* @param explicitArgs 通过getBean方法以编程方式传入的值
* @return Bean装配器
*/
public BeanWrapper autowireConstructor(String beanName, RootBeanDefinition mbd, @Nullable Constructor<?>[] chosenCtors, @Nullable Object[] explicitArgs) {
BeanWrapperImpl bw = new BeanWrapperImpl();
this.beanFactory.initBeanWrapper(bw);
Constructor<?> constructorToUse = null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;//构造函数参数
//解析构造参数 Start
if (explicitArgs != null) {//如果getBean方法指定了构造函数那么直接使用
argsToUse = explicitArgs;
} else {//getBean没有指定构造函数, 那么则尝试从配置文件中解析
Object[] argsToResolve = null;//配置的构造函数参数
//尝试从缓存中获取 Start
synchronized (mbd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod;
if (constructorToUse != null && mbd.constructorArgumentsResolved) {
argsToUse = mbd.resolvedConstructorArguments;//从缓存中获取
if (argsToUse == null) {
argsToResolve = mbd.preparedConstructorArguments;//配置的构造函数参数
}
}
}
//尝试从缓存中获取 End
if (argsToResolve != null) {//缓存中存在
/*
* 解析参数类型, 如给定方法的构造函数A(int, int)则通过此方法后就会把配置中的
* ("1", "1")转换为(1, 1), 缓存中的值可能是原始值也可能是最终值
*/
argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve);
}
}
//解析构造参数 End
//没有定位到缓存 Start
if (constructorToUse == null || argsToUse == null) {
//获取构造函数 Start
Constructor<?>[] candidates = chosenCtors;//指定的构造函数
if (candidates == null) {//不存在指定的构造函数
Class<?> beanClass = mbd.getBeanClass();
try {
candidates = (mbd.isNonPublicAccessAllowed() ?
beanClass.getDeclaredConstructors() : beanClass.getConstructors());
} catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Resolution of declared constructors on bean Class [" + beanClass.getName() +
"] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex);
}
}
//获取构造函数 End
//如果只有一个构造函数(无参构造)并且没有指定构造函数的参数 Start
if (candidates.length == 1 && explicitArgs == null && !mbd.hasConstructorArgumentValues()) {
Constructor<?> uniqueCandidate = candidates[0];//构造函数
if (uniqueCandidate.getParameterCount() == 0) {//无参构造函数
synchronized (mbd.constructorArgumentLock) {
mbd.resolvedConstructorOrFactoryMethod = uniqueCandidate;//无参构造
mbd.constructorArgumentsResolved = true;
mbd.resolvedConstructorArguments = EMPTY_ARGS;
}
bw.setBeanInstance(instantiate(beanName, mbd, uniqueCandidate, EMPTY_ARGS));
return bw;
}
}
//如果只有一个构造函数(无参构造)并且没有指定构造函数的参数 End
boolean autowiring = (chosenCtors != null ||
mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR);
ConstructorArgumentValues resolvedValues = null;
int minNrOfArgs;//构造函数参数的个数
if (explicitArgs != null) {//用户在getBean时指定了构造函数参数
minNrOfArgs = explicitArgs.length;
} else {//没有指定
ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues();//提取配置文件中配置的构造函数参数
resolvedValues = new ConstructorArgumentValues();//用户承载解析后的构造函数参数的值
minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues);//能解析到参数的个数
}
AutowireUtils.sortConstructors(candidates);//先对构造函数进行排序(public函数优先参数数量降序、非public构造函数参数数量降序)
int minTypeDiffWeight = Integer.MAX_VALUE;
Set<Constructor<?>> ambiguousConstructors = null;
LinkedList<UnsatisfiedDependencyException> causes = null;
for (Constructor<?> candidate : candidates) {
int parameterCount = candidate.getParameterCount();//构造函数参数的数量
if (constructorToUse != null && argsToUse != null && argsToUse.length > parameterCount) {
/*
* 如果已经找到选用的构造函数或者需要的参数个数小于当前的构造函数参数个数则终止,
* 因为已经按照参数个数降序排序
*/
break;
}
//参数个数不相等 Start
if (parameterCount < minNrOfArgs) {//参数个数不相等
continue;
}
//参数个数不相等 End
ArgumentsHolder argsHolder;
Class<?>[] paramTypes = candidate.getParameterTypes();
if (resolvedValues != null) {//有参构造
try {
//从注解(ConstructorProperties)上获取参数名称
String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, parameterCount);
if (paramNames == null) {
ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer();//获取参数名称探索器
if (pnd != null) {
//获取指定构造函数的参数名称
paramNames = pnd.getParameterNames(candidate);
}
}
//通过名称和数据类型创建参数持有者
argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, getUserDeclaredConstructor(candidate), autowiring, candidates.length == 1);
} catch (UnsatisfiedDependencyException ex) {
if (logger.isTraceEnabled()) {
logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex);
}
if (causes == null) {
causes = new LinkedList<>();
}
causes.add(ex);
continue;
}
} else {//无参构造
if (parameterCount != explicitArgs.length) {
continue;
}
argsHolder = new ArgumentsHolder(explicitArgs);//构造函数没有参数
}
//探测是否有不确定性的构造函数存在, 例如不同构造函数的参数为父子关系
int typeDiffWeight = (mbd.isLenientConstructorResolution() ?
argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes));
if (typeDiffWeight < minTypeDiffWeight) {//如果它代表着当前最接近的匹配则选择作为构造函数
constructorToUse = candidate;
argsHolderToUse = argsHolder;
argsToUse = argsHolder.arguments;
minTypeDiffWeight = typeDiffWeight;
ambiguousConstructors = null;
} else if (constructorToUse != null && typeDiffWeight == minTypeDiffWeight) {
if (ambiguousConstructors == null) {
ambiguousConstructors = new LinkedHashSet<>();
ambiguousConstructors.add(constructorToUse);
}
ambiguousConstructors.add(candidate);
}
}
if (constructorToUse == null) {
if (causes != null) {
UnsatisfiedDependencyException ex = causes.removeLast();
for (Exception cause : causes) {
this.beanFactory.onSuppressedException(cause);
}
throw ex;
}
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Could not resolve matching constructor " +
"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities)");
} else if (ambiguousConstructors != null && !mbd.isLenientConstructorResolution()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Ambiguous constructor matches found in bean '" + beanName + "' " +
"(hint: specify index/type/name arguments for simple parameters to avoid type ambiguities): " + ambiguousConstructors);
}
if (explicitArgs == null && argsHolderToUse != null) {
argsHolderToUse.storeCache(mbd, constructorToUse);//将解析的构造函数加入缓存
}
}
//没有定位到缓存 End
Assert.state(argsToUse != null, "Unresolved constructor arguments");
bw.setBeanInstance(instantiate(beanName, mbd, constructorToUse, argsToUse));//将构造的实例加入BeanWrapper中
return bw;
}
Copy the code
This function has the following functions:
(1) Determination of constructor parameters
- Determine by the explicitArgs parameter
If the argument passed in explicitArgs is not empty, then the argument can be determined directly because explicitArgs is specified by the user when the Bean is called. In the BeanFactory class there is a method like this:
Object getBean(String name, Object ... args) throws BeansException;
Copy the code
When you get a bean, you can specify not only the name of the bean but also the constructor or method parameters of the bean’s factory method. This is mainly used for static factory method calls. In this case, the explicitArgs parameter is not null. You can determine that the constructor argument is it.
- Fetch in cache
The method of determining parameters can be used directly if it has been analyzed previously, that is, if the constructor parameters are already recorded in the cache. The cache may cache the final type of the parameter or the initial type of the parameter. For example, if the constructor argument is required to be of type int, but the original argument may be “1” of type String, then even if the argument is retrieved from the cache, it needs to be filtered through the type converter to ensure that the argument type corresponds exactly to the corresponding constructor argument type.
- Obtaining configuration files
If the arguments to the constructor cannot be determined based on the argument passed in, explicitArgs, and the relevant information is not available in the cache, then other analysis must be turned on.
Can begin from the configuration file, then in the Spring by calling the BeanDefinition getConstructorArgumentValues constructor () to retrieve configuration information, with the information in the configuration can obtain the corresponding parameter value information, obtain information on the parameter values including specified values directly, For example, specify a value in the constructor as a primitive String or as a reference to another bean.
(2) The determination of the constructor
After the first step, the constructor parameters have been determined, and then the corresponding constructor is locked according to the construction parameters, and the matching method is matched according to the number of parameters. Therefore, the constructors need to be ordered in descending order by the number of public constructor priority arguments and the number of non-public constructor arguments before matching. This allows you to quickly determine if the number of trailing constructor arguments meets the criteria while traversing.
(3) Convert the corresponding parameter types according to the determined constructors
The main conversion is done using either a type converter provided in Spring or a custom type converter provided by the user
(4) Checking the uncertainty of the constructor
Sometimes the constructor may not be locked directly even if the constructor, parameter name, parameter type, and parameter value are specified. The arguments of different constructors are parent-child, so Spring does another verification at the end
(5) Instantiate the bean based on the instantiation strategy and the resulting constructor and constructor parameters
1.7.1.2 instantiateBean
Having gone through the constructor instance construction with arguments, here is the constructor instantiation without arguments:
/** * Use the default constructor to initialize the Bean * @param beanName beanName * @param MBD fusion RootBeanDefinition * @return Bean assembler */ protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) { try { Object beanInstance; if (System.getSecurityManager() ! = null) {/ / permission check beanInstance = the AccessController. DoPrivileged ((PrivilegedAction < Object >) () - > getInstantiationStrategy().instantiate(mbd, beanName, this), getAccessControlContext()); } else {// Initialize (CGLIB) beanInstance = getInstantiationStrategy().instantiate(MBD, beanName, this); } BeanWrapper bw = new BeanWrapperImpl(beanInstance); initBeanWrapper(bw); return bw; } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex); }}Copy the code
There is no real logic to this approach
1.7.1.3 Instantiating policies
The instantiation strategy is mentioned in the instantiation process. In fact, we have all the information we need to instantiate, and we could have used the simplest reflection method to construct the instance object directly, but Spring doesn’t do that:
SimpleInstantiationStrategy# instantiate () function code is as follows:
/** * Initialize @param BD fuses the RootBeanDefinition attribute * @param beanName beanName * @param Owner BeanFactory(by adding & to beanName) * @return Object */ @Override public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { if (! Bd.hasmethodoverrides ()) {const const... > constructorToUse; synchronized (bd.constructorArgumentLock) { constructorToUse = (Constructor<? >) bd.resolvedConstructorOrFactoryMethod; // Constructor if (constructorToUse == null) {final Class<? > clazz = bd.getBeanClass(); / / the corresponding class if (clazz isInterface ()) {/ / if this class is an interface that will throw an exception (cannot be instantiated) throw new BeanInstantiationException (clazz, "Specified class is an interface"); } try { if (System.getSecurityManager() ! = null) {/ / permissions constructorToUse = the AccessController. DoPrivileged ((PrivilegedExceptionAction < Constructor <? >>) clazz::getDeclaredConstructor); } else { constructorToUse = clazz.getDeclaredConstructor(); / / structure} bd. ResolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } return BeanUtils.instantiateClass(constructorToUse); } else {/ / override attribute, use the additional subclasses return instantiateWithMethodInjection (bd, beanName, owner); }}Copy the code
CglibSubclassingInstantiationStrategy# instantiate (ctor, args) code is as follows:
public Object instantiate(@Nullable Constructor<? > ctor, Object... args) { Class<? > subclass = createEnhancedSubclass(this.beanDefinition); Object instance; if (ctor == null) { instance = BeanUtils.instantiateClass(subclass); } else { try { Constructor<? > enhancedSubclassConstructor = subclass.getConstructor(ctor.getParameterTypes()); instance = enhancedSubclassConstructor.newInstance(args); } catch (Exception ex) { throw new BeanInstantiationException(this.beanDefinition.getBeanClass(), "Failed to invoke constructor for CGLIB enhanced subclass [" + subclass.getName() + "]", ex); } } Factory factory = (Factory) instance; factory.setCallbacks(new Callback[] {NoOp.INSTANCE, new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner), new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)}); return instance; }Copy the code
We can see that if beanDefinition getMethodOverrides () is empty, that is no use the replace or lookup configuration by the user, so direct using reflection, simple and quick. However, if you have these two features, you need to use a dynamic proxy to set up the interception enhancer that contains the corresponding logic of the two features, so that when calling a method, it is guaranteed to be enhanced by the corresponding interceptor and return the proxy instance containing the interceptor.
1.7.2 Record the ObjectFactory of the created bean
There is also this code in the doCreate function:
/* * Whether early exposure is required: Singletons & allows loop dependencies & the current bean is being created, Detection of circular dependencies * / Boolean earlySingletonExposure = (MBD) isSingleton () && enclosing allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isTraceEnabled()) { logger.trace("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); AddSingletonFactory (beanName, () -> getEarlyBeanReference(beanName,); mbd, bean)); }Copy the code
This code is not complex, so we need to think globally about Spring’s dependency solution:
- EarlySingletonExposure: a single case of early exposure
- Mbd.issingleton () : Whether it is a singleton
- Enclosing allowCircularReferences: whether to allow circular dependencies. Didn’t find how to configure in the configuration, but in AbstractRefreshableApplicationContext provides setting function, can be set by means of hard coded or can be set through the custom namespace
- IsSingletonCurrentlyInCreation (beanName) : whether the bean is created. In Spring, there will be a special attribute defaults to DefaultSingletonBeanRegistry singletonsCurrentlyInCreation to record the bean’s load condition. BeanName is recorded in the property before the bean is created, and removed from the property after the bean is created. This is actually in DefaultSingletonBeanRegistry type public Object getSingleton (String beanName, BeforeSingletonCreation (beanName) and afterSingletonCreation(beanName) of ObjectFactory singletonFactory.
After the above analysis, we know whether the variable earlySingletonExposure is a singleton, whether circular dependencies are allowed, and whether the corresponding bean is a combination of conditions being created.
For the simplest AB loop dependency, class A contains attribute class B, and class B contains attribute class A, so the initialization of beanA is as follows:
The following figure shows that the beanName of A is recorded when A is created and the beanA creation factory is added to the cache. Then the recursive creation of B is performed when the POPULATE method is called. Also, since the A property is present in B, the POPULATE method that instantiates B initializes B again and then calls getBean(A). When getBean(A) is used, it is not to instantiate A directly, but to detect whether there is A corresponding bean already created in the cache, or whether an ObjectFactory has been created. At this time, we have already created the ObjectFactory of A, so that it will not be executed later. ObjectFactory = ObjectFactory; ObjectFactory = ObjectFactory; ObjectFactory = ObjectFactory;
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
Copy the code
/** * get the early bean references, mainly to solve the problem of circular references. * Here is another step: @param beanName beanName @param MBD RootBeanDefinition @param bean instance @return bean instance */ protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof SmartInstantiationAwareBeanPostProcessor) { SmartInstantiationAwareBeanPostProcessor ibp = (SmartInstantiationAwareBeanPostProcessor) bp; exposedObject = ibp.getEarlyBeanReference(exposedObject, beanName); } } } return exposedObject; }Copy the code
There’s not a lot of logic in the getEarlyBeanReference function, or processing beyond post-processor calls.
According to the above analysis, we can basically clarify Spring’s solution to handle cyclic dependencies. When creating dependency A in B, we can interrupt the property filling in A through the instantiation method provided by ObjectFactory, so that A held in B is only A newly initialized A without any property filling. The initial initialization of A is still carried out when A is initially created, but since the address of the attribute represented by A in B is the same as that of A in B, the fill of the attribute created in A can naturally be obtained by A in B, thus solving the problem of loop dependence.
1.7.3 Attribute Injection
We mentioned the populateBean function, which populates attributes, as follows:
/** * Populates bean instance properties * @param beanName beanName * @param MBD original configuration * @Param BW bean loader */ @suppressWarnings ("deprecation") protected void populateBean(String beanName, RootBeanDefinition mbd, @nullable BeanWrapper bw) {Start if (bw == null) {if (mbd.haspropertyValues ()) {throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else {// No property to populate return; }} / / data validation End / * * to InstantiationAwareBeanPostProcessor last chance in the attribute is set to change the bean * such as the type of injection can be used to support attributes * / if (! mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; if (! Ibp. PostProcessAfterInstantiation (bw. GetWrappedInstance (), beanName)) {/ / this function can control whether the program continues fill the return; } } } } PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); int resolvedAutowireMode = mbd.getResolvedAutowireMode(); / / input type if (resolvedAutowireMode = = AUTOWIRE_BY_NAME | | resolvedAutowireMode = = AUTOWIRE_BY_TYPE) {MutablePropertyValues newPvs = new MutablePropertyValues(pvs); Start if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {autowireByName(beanName, MBD, bw, newPvs); } Start if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {autowireByType(beanName, MBD, bw, newPvs); } // Automatically inject End PVS = newPvs; } boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); Boolean needsDepCheck = (mbd.getDependencyCheck()! = AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); // Dependency check PropertyDescriptor[] filteredPds = null; if (hasInstAwareBpps) { if (pvs == null) { pvs = mbd.getPropertyValues(); } for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName); If (pvsToUse == null) {if (filteredPds == null) {filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvsToUse == null) { return; } } pvs = pvsToUse; } } } if (needsDepCheck) { if (filteredPds == null) { filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); } checkDependencies(beanName, mbd, filteredPds, pvs); } if (pvs ! = null) { applyPropertyValues(beanName, mbd, bw, pvs); // Apply the property to the bean}}Copy the code
The processing flow of populateBean function is as follows:
- InstantiationAwareBeanPostProcessor processor postProcessAfterInstantiation function of the application, this function can control whether the program to continue their fill
- Depending on the injection type (byName/byType), extract the dependent bean and agree to store it in PropertyValues
- InstantiationAwareBeanPostProcessor processor postProcessPropertyValues method was applied, the attribute to get finished before filling the process attribute again, Typical application RequiredAnnotationBeanPostProcessor class verification of attributes
- Populate all properties from PropertyValues into BeanWrapper
1.7.3.1 autowireByName
(byName/byType); (byName/byType); (byName/byType);
/** * Automatic injection by name * @param beanName beanName * @param MBD RootBeanDefinition * @param BW bean assemper * @param PVS variable attribute value */ protected void autowireByName(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); For (String propertyName: PropertyNames) {if (containsBean(propertyName)) {// Recursively initialize the associated bean Start Object bean = getBean(propertyName); pvs.add(propertyName, bean); // Recursively initialize the associated bean End registerDependentBean(propertyName, beanName); Logger.istraceenabled ()) {logger.trace("Added Autowiring by name from bean name '" + beanName + "' via property '" + propertyName + "' to bean named '" + propertyName + "'"); } } else { if (logger.isTraceEnabled()) { logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName + "' by name: no matching bean found"); }}}}Copy the code
This function is as simple as finding the loaded bean in the passed parameter PVS, recursively instantiating it, and then adding it to the PVS
1.7.3.2 autowireByType
AutowireByType is similar to autowireByName in degree of use, but the complexity of its implementation is quite different:
/** * Automatic injection based on type * @param beanName beanName * @param MBD RootBeanDefinition * @param BW bean assemper * @param PVS variable attribute value */ protected void autowireByType(String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues PVS) {Start TypeConverter Converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } End Set<String> autowiredBeanNames = new LinkedHashSet<>(4); String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); For (String propertyName: propertyNames) { try { PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); if (Object.class ! = pd.getPropertyType()) { MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); Boolean eager =! (bw.getWrappedInstance() instanceof PriorityOrdered); DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); * @autoWired * private List<A> aList; /* @autowired * private List<A> aList; /* @autoWired * private List<A> aList; */ Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, Converter); if (autowiredArgument ! = null) { pvs.add(propertyName, autowiredArgument); } for (String autowiredBeanName : autowiredBeanNames) { registerDependentBean(autowiredBeanName, beanName); If (logger.istraceEnabled ()) {logger.trace("Autowiring by type from bean name '" + beanName + "' via property '" + propertyName + "' to bean named '" + autowiredBeanName + "'"); } } autowiredBeanNames.clear(); } } catch (BeansException ex) { throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex); }}}Copy the code
The first step to achieve automatic matching by name is to look for properties in BW that need dependency injection. Similarly, for automatic matching by type, the first step is also to look for properties in BW that need dependency injection, and then iterate over these properties and find beans that match the type. The most complicated thing is to look for beans that match. Spring also provides support for type injection of collections in the following ways:
@Autowired
private List<Test> tests;
Copy the code
Spring will find all types that match Test and inject them into the Tests property. Because of this, in the autowireByType function, we created a new local traversal autowiredBeanNames to store all dependent beans. This property is useless if only for property injection of non-collection types.
For type matching the implementation of the logic encapsulated in the DefaultListableBeanFactory# resolveDependency function:
@param requestingBeanName beanName @Param autowiredBeanNames @Param autowiredBeanNames AutowiredBeanNames * @param typeConverter typeConverter * @return object instance * @throws BeansException Bean exception */ @override @nullable public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { descriptor.initParameterNameDiscovery(getParameterNameDiscoverer()); If (Optional) class = = descriptor) getDependencyType ()) {/ / if it's Optional handle return createOptionalDependency (descriptor, requestingBeanName); } else if (ObjectFactory.class == descriptor.getDependencyType() || ObjectProvider.class == Descriptor. GetDependencyType ()) {/ / if it's the ObjectFactory or ObjectProvider handle return new DependencyObjectProvider (descriptor, requestingBeanName); } else if (javaxInjectProviderClass = = descriptor. GetDependencyType ()) {/ / Java injection return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName); } else {/ / general processing Object result = getAutowireCandidateResolver () getLazyResolutionProxyIfNecessary (descriptor, requestingBeanName); if (result == null) { result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter); } return result; }}Copy the code
/** * Parse dependency * @param beanName beanName * @Param autowiredBeanNames autowiredBeanNames * @param typeConverter typeConverter * @return parses dependent objects * @throws BeansException BeanException */ @nullable public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor); try { Object shortcut = descriptor.resolveShortcut(this); if (shortcut ! = null) { return shortcut; } // support @value Start Class<? > type = descriptor.getDependencyType(); Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); if (value ! = null) { if (value instanceof String) { String strVal = resolveEmbeddedValue((String) value); BeanDefinition bd = (beanName ! = null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null); value = evaluateBeanDefinitionString(strVal, bd); } TypeConverter converter = (typeConverter ! = null ? typeConverter : getTypeConverter()); try { return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor()); } catch (UnsupportedOperationException ex) { return (descriptor.getField() ! = null ? converter.convertIfNecessary(value, type, descriptor.getField()) : converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); End Object multipleBeans = resolveMultipleBeans(Descriptor, beanName, autowiredBeanNames, typeConverter); // Parse various types of if (multipleBeans! = null) { return multipleBeans; } Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (matchingBeans.isEmpty()) { if (isRequired(descriptor)) { raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } return null; } String autowiredBeanName; Object instanceCandidate; if (matchingBeans.size() > 1) { autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { if (isRequired(descriptor) || ! indicatesMultipleBeans(type)) { return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans); } else { return null; } } instanceCandidate = matchingBeans.get(autowiredBeanName); } else { Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next(); autowiredBeanName = entry.getKey(); instanceCandidate = entry.getValue(); } if (autowiredBeanNames ! = null) { autowiredBeanNames.add(autowiredBeanName); } if (instanceCandidate instanceof Class) { instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this); } Object result = instanceCandidate; if (result instanceof NullBean) { if (isRequired(descriptor)) { raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor); } result = null; } if (! ClassUtils.isAssignableValue(type, result)) { throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass()); } return result; } finally { ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint); }}Copy the code
@param beanName beanName @Param autowiredBeanNames @Param autowiredBeanNames AutowiredBeanNames * @param typeConverter typeConverter * @return Object */ @nullable private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) { Class<? > type = descriptor.getDependencyType(); If (descriptor instanceof StreamDependencyDescriptor) {/ / Stream type Map < String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor); if (autowiredBeanNames ! = null) { autowiredBeanNames.addAll(matchingBeans.keySet()); } Stream<Object> stream = matchingBeans.keySet().stream() .map(name -> descriptor.resolveCandidate(name, type, this)) .filter(bean -> ! (bean instanceof NullBean)); if (((StreamDependencyDescriptor) descriptor).isOrdered()) { stream = stream.sorted(adaptOrderComparator(matchingBeans)); } return stream; } else if (type.isarray ()) {// Array type <? > componentType = type.getComponentType(); ResolvableType resolvableType = descriptor.getResolvableType(); Class<? > resolvedArrayType = resolvableType.resolve(type); if (resolvedArrayType ! = type) { componentType = resolvableType.getComponentType().resolve(); } if (componentType == null) { return null; } Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, new MultiElementDescriptor(descriptor)); if (matchingBeans.isEmpty()) { return null; } if (autowiredBeanNames ! = null) { autowiredBeanNames.addAll(matchingBeans.keySet()); } TypeConverter converter = (typeConverter ! = null ? typeConverter : getTypeConverter()); Object result = converter.convertIfNecessary(matchingBeans.values(), resolvedArrayType); if (result instanceof Object[]) { Comparator<Object> comparator = adaptDependencyComparator(matchingBeans); if (comparator ! = null) { Arrays.sort((Object[]) result, comparator); } } return result; } else if (Collection. Class. IsAssignableFrom && (type) the isInterface ()) {/ / interface class <? > elementType = descriptor.getResolvableType().asCollection().resolveGeneric(); if (elementType == null) { return null; } Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType, new MultiElementDescriptor(descriptor)); if (matchingBeans.isEmpty()) { return null; } if (autowiredBeanNames ! = null) { autowiredBeanNames.addAll(matchingBeans.keySet()); } TypeConverter converter = (typeConverter ! = null ? typeConverter : getTypeConverter()); Object result = converter.convertIfNecessary(matchingBeans.values(), type); if (result instanceof List) { if (((List<? >) result).size() > 1) { Comparator<Object> comparator = adaptDependencyComparator(matchingBeans); if (comparator ! = null) { ((List<? >) result).sort(comparator); } } } return result; } else if (Map.class == type) {//Map ResolvableType mapType = descriptor.getResolvableType().asMap(); Class<? > keyType = mapType.resolveGeneric(0); if (String.class ! = keyType) { return null; } Class<? > valueType = mapType.resolveGeneric(1); if (valueType == null) { return null; } Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType, new MultiElementDescriptor(descriptor)); if (matchingBeans.isEmpty()) { return null; } if (autowiredBeanNames ! = null) { autowiredBeanNames.addAll(matchingBeans.keySet()); } return matchingBeans; } else { return null; }}Copy the code
When looking for a type match execution order, the first attempt is to use a parser to parse. If the parser fails, either the default parser did nothing, or a custom parser was used. However, for collections and other types, it is not within the scope of parsing, so again, different types are treated in different cases, but the general idea is similar.
1.7.3.3 applyPropertyValues
At this point in the program, we have obtained all the injected properties, but the obtained properties are in the form of PropertyValues and have not yet been applied to the instantiated bean. This works in applyPropertyValues:
/** * Apply the given attribute value * @param beanName beanName * @param MBD RootBeanDefinition * @param BW bean assemper * @param PVS attribute value */ protected void applyPropertyValues(String beanName, BeanDefinition mbd, BeanWrapper bw, PropertyValues pvs) { if (pvs.isEmpty()) { return; } if (System.getSecurityManager() ! = null && bw instanceof BeanWrapperImpl) { ((BeanWrapperImpl) bw).setSecurityContext(getAccessControlContext()); } MutablePropertyValues mpvs = null; List<PropertyValue> original; Start if (PVS instanceof MutablePropertyValues) {MPVS = (MutablePropertyValues) PVS; if (mpvs.isConverted()) { try { bw.setPropertyValues(mpvs); return; } catch (BeansException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", ex); } } original = mpvs.getPropertyValueList(); } else { original = Arrays.asList(pvs.getPropertyValues()); } // get the attribute value End // get the corresponding parser Start TypeConverter Converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter); End List<PropertyValue> deepCopy = new ArrayList<>(original.size()); boolean resolveNecessary = false; For (PropertyValue pv: original) {for (PropertyValue pv: original) {// in line with the property, convert the property to the corresponding property type in line with the class if (pv.is.php ()) {deepcopy.add (pv); } else { String propertyName = pv.getName(); Object originalValue = pv.getValue(); if (originalValue == AutowiredPropertyMarker.INSTANCE) { Method writeMethod = bw.getPropertyDescriptor(propertyName).getWriteMethod(); if (writeMethod == null) { throw new IllegalArgumentException("Autowire marker for property without write method: " + pv); } originalValue = new DependencyDescriptor(new MethodParameter(writeMethod, 0), true); } Object resolvedValue = valueResolver.resolveValueIfNecessary(pv, originalValue); Object convertedValue = resolvedValue; boolean convertible = bw.isWritableProperty(propertyName) && ! PropertyAccessorUtils.isNestedOrIndexedProperty(propertyName); if (convertible) { convertedValue = convertForProperty(resolvedValue, propertyName, bw, converter); } if (resolvedValue == originalValue) { if (convertible) { pv.setConvertedValue(convertedValue); } deepCopy.add(pv); } else if (convertible && originalValue instanceof TypedStringValue && ! ((TypedStringValue) originalValue).isDynamic() && ! (convertedValue instanceof Collection || ObjectUtils.isArray(convertedValue))) { pv.setConvertedValue(convertedValue); deepCopy.add(pv); } else { resolveNecessary = true; deepCopy.add(new PropertyValue(pv, convertedValue)); } } } if (mpvs ! = null && ! resolveNecessary) { mpvs.setConverted(); } try { bw.setPropertyValues(new MutablePropertyValues(deepCopy)); } catch (BeansException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Error setting property values", ex); }}Copy the code
1.7.4 Initializing beans
During bean configuration, there is an init-method property in the bean. The purpose of this property is to call the method specified by init-method before bean instantiation to perform the corresponding instantiation according to the user business. This method is executed at the point where the bean has been instantiated in Spring and the properties have been populated, at which point the user-defined initialization method will be called:
/** * Initializes the given bean instance. It mainly applies the initialization method of factory and the method of BeanPostProcessor. The afterPropertiesSet method in InitializingBean may also be called * @param beanName beanName * @Param bean instance object * @param MBD MBD * @return protected Object initializeBean(String beanName, Object bean, @ Nullable RootBeanDefinition MBD) {/ / execution Aware relevant methods Start the if (System. GetSecurityManager ()! = null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); // Execute aware related methods (apply specific aware, then set related properties) return null; }, getAccessControlContext()); } else { invokeAwareMethods(beanName, bean); // Implement aware related methods (mainly adapting specific Aware, Then set the relevant properties)} executed Aware method End / / / / BeanPostProcessor postProcessBeforeInitialization method Start Object wrappedBean = bean; if (mbd == null || ! mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } / / execution BeanPostProcessor postProcessBeforeInitialization method End / / perform initialization method (InitializingBean. AfterPropertiesSet or user-defined) Start try { invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) {throw new BeanCreationException((MBD! = null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } / / execution initialization method (InitializingBean afterPropertiesSet or user-defined) End End / / perform BeanPostProcessor postProcessAfterInitialization method if (mbd == null || ! mbd.isSynthetic()) { wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } / / execution BeanPostProcessor postProcessAfterInitialization method End return wrappedBean; }Copy the code
Without much explanation, two methods of the BeanPostProcessor are executed here. Delegate the primary initialization to the invokeInitMethods function as follows:
/** * Execute initialization method * @param beanName beanName * @param bean bean * @param MBD RootBeanDefinition * @throws Throwable exception */ protected void invokeInitMethods(String beanName, Object bean, @nullable RootBeanDefinition MBD) throws Throwable {// If this is an InitializingBean, Start Boolean isInitializingBean = (Bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || ! mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isTraceEnabled()) { logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() ! = null) { try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { ((InitializingBean) bean).afterPropertiesSet(); }} // If the bean is an InitializingBean, execute the afterPropertiesSet method End // User defined initialization method Start if (MBD! = null && bean.getClass() ! = NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && ! (isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && ! mbd.isExternallyManagedInitMethod(initMethodName)) { invokeCustomInitMethod(beanName, bean, mbd); } // User defined initialization method End}Copy the code
This function does two main things:
- Call the afterPropertiesSet method of the InitializingBean. This is actually very important, SpringMVC’s HandlerMapping scan starts from here, and we’ll talk more about it in SpringMVC
- Performs custom initialization methods. That method is a manually specified init method
1.7.5 registered DisposableBean
Spring provides extensions to initialization methods as well as to destruction methods. In addition to the familiar destory-method method, Users can also register the post-processor DestructionAwareBeanPostProcessor to destroy method of dealing with the bean, the code is as follows:
protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {
AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);
if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {
if (mbd.isSingleton()) {
registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
} else {
Scope scope = this.scopes.get(mbd.getScope());
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope name '" + mbd.getScope() + "'");
}
scope.registerDestructionCallback(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));
}
}
}
Copy the code