For the function of loading bean s, the calling methods in Spring are:
MyTestBean bean = (MyTestBean)bf.getBean("myTestBean");
What function does this code realize?
Let's see what's written in AbstractBeanFactory
getBean
public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); } protected <T> T doGetBean( String name, @Nullable Class<T> requiredType, @Nullable Object[] args, boolean typeCheckOnly) throws BeansException { //Extract the corresponding beanName String beanName = transformedBeanName(name); Object bean; /** * Check whether there is a corresponding instance in the cache or in the instance factory * Why use this code first * Because dependency injection occurs when creating a singleton bean, and to avoid circular dependency when creating dependencies, * String The principle of creating a bean is to expose the ObjectFactory of the bean in advance before the bean is created * That is, add ObjectFactory to the cache. Once the next bean needs to rely on the previous bean when it is created, use ObjectFactory directly */ //Try to get directly from the cache or from the ObjectFactory in singletonFactories // Eagerly check singleton cache for manually registered singletons. Object sharedInstance = getSingleton(beanName); if (sharedInstance != null && args == null) { if (logger.isDebugEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); } } //Return the corresponding instance. Sometimes, for example, BeanFactory does not directly return the instance itself, but the instance returned by the specified method bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } else { //Only in the case of singleton will we try to solve the circular dependency. In the case of prototype mode, if it exists //If A has the attribute of B and B has the attribute of A, when dependency injection occurs, it will occur when A has not been created because //For the creation of B, the creation of A is returned again, resulting in circular dependency, that is, the following situation //isPrototypeCurrentlyInCreation(beanName) is true // Fail if we're already creating this bean instance: // We're assumably within a circular reference. if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. BeanFactory parentBeanFactory = getParentBeanFactory(); //If beanName is not included in beanDefinitionMap, that is, in all loaded classes, try to detect it from parentBeanFactory if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } } //If you don't just do type detection, you need to create a bean and record it here if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { //Convert GenericBeanDefinitino that stores XML configuration files to RootBeanDefinition //If the specified BeanName is a child Bean, the related properties of the parent class will be merged RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. String[] dependsOn = mbd.getDependsOn(); //If there is a dependency, you need to recursively instantiate the dependent bean 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 + "'"); } //Cache dependent call registerDependentBean(dep, beanName); try { getBean(dep); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } //After instantiating the dependent bean, you can instantiate the mbd itself //Creation of singleton pattern // Create bean instance. if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } else if (mbd.isPrototype()) { //prototype pattern creation (new) // It's a prototype -> create a new instance. Object prototypeInstance = null; try { beforePrototypeCreation(beanName); prototypeInstance = createBean(beanName, mbd, args); } finally { afterPrototypeCreation(beanName); } bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { //Instantiate the bean on the specified scope 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; } } //Check whether the required type matches the instance type of the bean // Check if required type matches the type of the actual bean instance. 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.isDebugEnabled()) { logger.debug("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean; }
It can be seen from the amount of code that bean loading is quite complex. After reading this method, you can roughly understand the whole Spring bean loading process. The steps involved in the loading process are as follows:
(1) Transform corresponding beanName
String beanName = transformedBeanName(name); The parameter name passed in here is not necessarily beanname, but also an alias or FactoryBean. A series of resolutions are required, including the following.
- Remove the modifier of FactoryBean, that is, if name = "& aa", it will be removed first and make name = "aa".
- Take the final beanName represented by the specified alias. For example, if alias A points to A bean with name B, B will be returned; If alias A points to alias B and alias B points to A bean named C, C is returned
(2) Attempt to load a singleton from the cache
The singleton will only be created once in the same container of Spring, and the subsequent beans will be directly obtained from the singleton cache. Of course, this is just an attempt to load. First, try to load from the cache. If the load is unsuccessful, try to load from singletonFactories again. When creating a singleton bean, there will be dependency injection. In order to avoid circular dependency, the principle of creating a bean in Spring is to add the ObjectFactory of the created bean to the cache before the bean is created. Once the next bean needs to depend on the previous bean, the ObjectFactory will be used directly
(3) bean instantiation
If you get the original state of the bean from the cache, you need to instantiate the bean. It is necessary to emphasize that what is recorded in the cache is only the original bean state, not necessarily the bean we want in the end. For example, if we need to process the factory bean, what we get here is actually the initial state of the factory bean, but what we really need is the Bean returned from the factory method method defined in the factory bean, and getObjectForBeanInstance completes this work.
(4) Dependency checking for prototype patterns
Only in the single case can we try to solve the circular dependency. If there is an attribute of B in A and an attribute of A in B, when dependency injection occurs, it will cause circular dependency when A has not been created, because the creation of B returns to create A again, that is, isPrototypeCurrentlyInCreation(beanName) determines true.
(5) Detect parentBeanFactory
! containsBeanDefinition(beanName), which detects that if the currently loaded XML configuration file does not contain the configuration corresponding to beanName, you can only try it in parentBeanFactory, and then recursively call the getBean method.
(6) Convert the GenericBeanDefinition that stores the XML configuration file to RootBeanDefinition.
Because the bean information read from the XML configuration file is stored in GenericBeanDefinition, but all subsequent bean processing is aimed at RootBeanDefinition, a conversion is required here. At the same time, if the parent bean is not empty, the properties of the parent class will be merged together.
(7) Look for dependencies.
Because some properties are likely to be used during bean initialization, and some properties are likely to be dynamically configured and configured to depend on other beans, it is necessary to load the dependent beans first. Therefore, in the loading sequence of Spring, when initializing a bean, the dependency corresponding to the bean will be initialized first.
(8) Create bean s for different scope s.
There are different scope s in Spring. The default is singleton, but there are other configurations such as prototype and request. In this step, Spring will implement different initialization strategies according to different configurations.
(9) Type conversion
Generally, the calling parameter requiredType of this method is empty, but there may be a case where the returned bean is actually a String, but the requiredType is passed in the Integer type. Then this step will work. Its function is to convert the returned bean to the type specified by the requiredType. Of course, the conversion from String to Integer is the simplest one. Various converters are provided in Spring. Users can also extend the converter to meet their needs.
Before refining and analyzing the functions provided by various steps, first understand the usage of FactoryBean.
Use of FactoryBean
The process of instantiating beans is complex. If you follow the traditional method, you need to provide a lot of configuration information in the. The flexibility of the configuration method is limited. At this time, you may get a simple scheme by coding. Spring provides a factory class interface of FactoryBean for this purpose. Users can customize the logic of instantiating beans by implementing this interface.
From spring 3 Starting from 0, FactoryBean began to support generics, that is, the interface declaration was changed to the form of FactoryBean:
public interface FactoryBean<T> { T getObject() throws Exception; Class<?> getObjectType(); default boolean isSingleton() { return true; } }
When the implementation class configured by the class attribute in the configuration file is FactoryBean, the object returned by the getObject() method of FactoryBean is not the FactoryBean itself, which is equivalent to that the getObject() of FactoryBean represents the getBean() method.
For example, if the following Car is configured in a traditional way, each attribute of the Car corresponds to an element label.
public class Car{ private Integer maxSpeed; private String brand; private Double price; //get/set }
If implemented by FactoryBean, it will be more flexible. The following example specifies configuration values for all attributes of Car at one time by means of comma separator:
public class CarFactoryBean implements FactoryBean<Car>{ private String carInfo; public Car getObject() throws Exception{ Car car = new Car(); car.set(xxxxx); ... ... return car; } public Class<Car> getObjectType(){return Car.class;} public boolean isSingleton(){return false;} public String getCarInfo(){return this.carInfo;} public void setCarInfo(String carInfo){this.carInfo=carInfo;} }
With this CarFactoryBean, you can configure the CarBean in the configuration file using the following custom configuration method:
<bean id="car" class="com.test.factorybean.CarFactoryBean" carInfo="xx,xx,xx" />
When calling getBean("car"), Spring finds that CarFactoryBean implements the interface of FactoryBean through the reflection mechanism. At this time, the Spring container calls the getObject() method of CarFactoryBean to return. If you want to obtain an instance of CarFactoryBean, you need to prefix the displayed before beanName with "&", such as getBean("& car") when using the getBean(beanName) method.
Get singleton bean from cache
The singleton will only be created once in the same container of Spring, and the subsequent beans will be obtained directly from the singleton cache. First try loading from the cache, and then try loading from singletonFactories again. When creating a singleton bean, there will be dependency injection. In order to avoid circular dependency when creating a dependency, Spring's principle for creating a bean is to add the ObjectFactory of the created bean to the cache in advance before the bean is created. Once the next bean needs to depend on the previous bean, the ObjectFactory will be used directly.
public Object getSingleton(String beanName) { return getSingleton(beanName, true); } protected Object getSingleton(String beanName, boolean allowEarlyReference) { //Check whether there are instances in the cache Object singletonObject = this.singletonObjects.get(beanName); if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) { //If empty, the global variable is locked and processed synchronized (this.singletonObjects) { //If this bean is loading, it will not be processed singletonObject = this.earlySingletonObjects.get(beanName); if (singletonObject == null && allowEarlyReference) { //When some methods need to be initialized in advance, the addSingletonFactory method will be called to store the corresponding ObjectFactory initialization policy in singletonFactories ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName); if (singletonFactory != null) { //Call the preset getObject method singletonObject = singletonFactory.getObject(); //Recorded in the cache, earlySingletonObjects and singletonFactories are mutually exclusive this.earlySingletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); } } } } return singletonObject; }
First, try to get an instance from singletonObjects. If you do not get it, you can get it from earlySingletonObjects. If you can not get it, try to get the beanName corresponding ObjectFactory from singletonFactories, then call the getObject of ObjectFactory to create bean and put it in earlySingletonObjects. And remove the objectfactory from singletonfactories. All subsequent memory operations are only used for circular dependency detection, that is, when alloweerlyreference is true.
Gets an object from an instance of a bean
getObjectForBeanInstance is a frequently used method, whether to get beans from the cache or load beans according to different scope policies.
It is used to detect whether the current bean is a FactoryBean type bean. If so, you need to call getObject in the FactoryBean instance corresponding to the bean as the return value.
Both the beans obtained from the cache and the beans loaded through different scope policies are only the most original bean state, not necessarily the beans we want in the end. For example, if we need to process the factory bean, what we get here is actually the initial state of the factory bean, but what we really need is the Bean returned from the factory method method defined in the factory bean, and the getObjectForBeanInstance method completes this work.
protected Object getObjectForBeanInstance( Object beanInstance, String name, String beanName, @Nullable RootBeanDefinition mbd) { //If the specified name is factory related (prefixed with &) and beanInstance is not FactoryBean type, the verification fails // Don't let calling code try to dereference the factory if the bean isn't a factory. if (BeanFactoryUtils.isFactoryDereference(name)) { if (beanInstance instanceof NullBean) { return beanInstance; } if (!(beanInstance instanceof FactoryBean)) { throw new BeanIsNotAFactoryException(beanName, beanInstance.getClass()); } } // Now we have an instance of a bean, which may be a normal bean or FactoryBean // If it is a FactoryBean, we use it to create an instance, but if the user wants to get the factory instance directly instead of the factory instance // The name passed in should be prefixed with the instance corresponding to the getObject method& if (!(beanInstance instanceof FactoryBean) || BeanFactoryUtils.isFactoryDereference(name)) { return beanInstance; } //Load FactoryBean Object object = null; if (mbd == null) { //Attempt to load bean from cache object = getCachedObjectForFactoryBean(beanName); } if (object == null) { //It is clear here that beanInstance must be of FactoryBean type FactoryBean<?> factory = (FactoryBean<?>) beanInstance; //containsBeanDefinition check beanDefinitionMap, that is, check whether beanName is defined in all loaded classes if (mbd == null && containsBeanDefinition(beanName)) { //Convert GenericBeanDefinition that stores XML configuration files to RootBeanDefinition //If the specified BeanName is a child bean, the related properties of the parent class will be merged at the same time mbd = getMergedLocalBeanDefinition(beanName); } //Is it user-defined rather than the application itself boolean synthetic = (mbd != null && mbd.isSynthetic()); object = getObjectFromFactoryBean(factory, beanName, !synthetic); } return object; }
Let's see what we've done
1. Verify the correctness of FactoryBean
2. Do not process non factorybeans
3. Convert bean s
4. Delegate the bean parsing from Factory to getObjectFromFactoryBean
protected Object getObjectFromFactoryBean(FactoryBean<?> factory, String beanName, boolean shouldPostProcess) { if (factory.isSingleton() && containsSingleton(beanName)) { synchronized (getSingletonMutex()) { Object object = this.factoryBeanObjectCache.get(beanName); if (object == null) { object = doGetObjectFromFactoryBean(factory, beanName); // Only post-process and store if not put there already during getObject() call above // (e.g. because of circular reference processing triggered by custom getBean calls) Object alreadyThere = this.factoryBeanObjectCache.get(beanName); if (alreadyThere != null) { object = alreadyThere; } else { if (shouldPostProcess) { if (isSingletonCurrentlyInCreation(beanName)) { // Temporarily return non-post-processed object, not storing it yet.. return object; } beforeSingletonCreation(beanName); try { object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's singleton object failed", ex); } finally { afterSingletonCreation(beanName); } } if (containsSingleton(beanName)) { this.factoryBeanObjectCache.put(beanName, object); } } } return object; } } else { Object object = doGetObjectFromFactoryBean(factory, beanName); if (shouldPostProcess) { try { object = postProcessObjectFromFactoryBean(object, beanName); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Post-processing of FactoryBean's object failed", ex); } } return object; } }
This code is the returned bean. If it is a singleton, it must be globally unique. At the same time, because it is a singleton, it does not need to be created repeatedly. You can use the cache to improve performance, that is, if it has been loaded, it must be recorded for reuse next time, otherwise it will be obtained directly.
Let's look at the doGetObjectFromFactoryBean method
private Object doGetObjectFromFactoryBean(FactoryBean<?> factory, String beanName) throws BeanCreationException { Object object; try { if (System.getSecurityManager() != null) { AccessControlContext acc = getAccessControlContext(); try { object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { //Call the getObject method directly 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); } // Do not accept a null value for a FactoryBean that's not fully // initialized yet: Many FactoryBeans just return null then. 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; }
Here you see the direct call to factory GetObject () method, finally!
Get singleton
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) { Assert.notNull(beanName, "Bean name must not be null"); //Global variables need to be synchronized synchronized (this.singletonObjects) { //First, check whether the corresponding bean has been loaded, because the singleton pattern is actually a bean reused to create, so this step is necessary Object singletonObject = this.singletonObjects.get(beanName); //If it is empty, the singleton bean can be initialized if (singletonObject == null) { if (this.singletonsCurrentlyInDestruction) { throw new 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); boolean newSingleton = false; boolean recordSuppressedExceptions = (this.suppressedExceptions == null); if (recordSuppressedExceptions) { this.suppressedExceptions = new LinkedHashSet<>(); } try { //Initialize bean singletonObject = singletonFactory.getObject(); newSingleton = true; } catch (IllegalStateException ex) { // Has the singleton object implicitly appeared in the meantime -> // if yes, proceed with it since the exception indicates that state. 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); } if (newSingleton) { //Add cache addSingleton(beanName, singletonObject); } } return singletonObject; } }
The callback method is actually used in the above code, so that the program can do some preparation and processing operations before and after the singleton creation. In fact, the real method to obtain the singleton bean is not implemented in this method, and its implementation logic is implemented in the singletonFactory instance of ObjectFactory type. These preparation and processing operations include the following:
1. Check whether the cache has been loaded.
2. If it is not loaded, the loading status of beanName is recorded
3. Record the loading status before loading the singleton.
Look at the upper and lower beforeSingletonCreation methods. This is not an empty implementation method. It does one operation: record the loading status
protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }
4. Instantiate the bean by calling the individual Object method of the ObjectFactory passed in by the parameter
5. Processing method call after loading a singleton
Synchronization step 3: after the bean is loaded, it is necessary to remove the record afterSingletonCreation(beanName) of the loading state of the bean in the cache;
6. Record the results to the cache and delete the various auxiliary states recorded during bean loading.
protected void addSingleton(String beanName, Object singletonObject) { synchronized (this.singletonObjects) { this.singletonObjects.put(beanName, singletonObject); this.singletonFactories.remove(beanName); this.earlySingletonObjects.remove(beanName); this.registeredSingletons.add(beanName); } }
7. Return processing results
You can deduce the parameters by looking at the getSingleton method
sharedInstance = getSingleton(beanName, new ObjectFactory<Object> { public Object getObject() throws BeansException{ try { return createBean(beanName, mbd, args); } catch (BeansException ex) { destroySingleton(beanName); throw ex; } } });
In fact, the core part of ObjectFactory only calls the createBean method, so we have to take a look at the createBean method
Ready to create bean
Look at the createBean function
protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { if (logger.isDebugEnabled()) { logger.debug("Creating instance of bean '" + beanName + "'"); } RootBeanDefinition mbdToUse = mbd; // Make sure bean class is actually resolved at this point, and // clone the bean definition in case of a dynamically resolved Class // which cannot be stored in the shared merged bean definition. //Lock the class and resolve the class according to the set class attribute or className Class<?> resolvedClass = resolveBeanClass(mbd, beanName); if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) { mbdToUse = new RootBeanDefinition(mbd); mbdToUse.setBeanClass(resolvedClass); } // Prepare method overrides. //Methods of verifying and preparing coverage try { mbdToUse.prepareMethodOverrides(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(), beanName, "Validation of method overrides failed", ex); } try { //Give BeanPostProcessors a chance to return the proxy to replace the real instance // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. 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); } try { Object beanInstance = doCreateBean(beanName, mbdToUse, args); if (logger.isDebugEnabled()) { logger.debug("Finished creating instance of bean '" + beanName + "'"); } return beanInstance; } catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) { // A previously detected exception with proper bean creation context already, // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry. throw ex; } catch (Throwable ex) { throw new BeanCreationException( mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex); } }
1. Resolve the class according to the set class attribute or according to the className
2. Mark and verify the override attribute
3. The post processor before initialization is applied to parse whether the specified bean has a short circuit operation before initialization.
4. Create bean
Handle override attribute
View the prepareMethodOverrides method of the AbstractBeanDefinition class
public void prepareMethodOverrides() throws BeanDefinitionValidationException { // Check that lookup methods exist and determine their overloaded status. if (hasMethodOverrides()) { getMethodOverrides().getOverrides().forEach(this::prepareMethodOverride); } } protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException { //Gets the number of corresponding method names in the corresponding class int count = ClassUtils.getMethodCountForName(getBeanClass(), mo.getMethodName()); if (count == 0) { throw new BeanDefinitionValidationException( "Invalid method override: no method with name '" + mo.getMethodName() + "' on class [" + getBeanClassName() + "]"); } else if (count == 1) { //The mark MethodOverride has not been overridden to avoid the cost of parameter type checking // Mark override as not overloaded, to avoid the overhead of arg type checking. mo.setOverloaded(false); } }
In Spring configuration, there are two configuration functions: lookup method and replace method. In fact, the loading of these two configurations is to uniformly store the configuration in the methodOverrides attribute in BeanDefinition. The implementation principle of these two functions is that if the methodOverrides attribute is detected during bean instantiation, It will dynamically generate a proxy for the current bean and use the corresponding interceptor to enhance the bean. See the instantiation part of the bean for the relevant logic.
Instantiated preprocessing
There are these two lines of code before actually calling the doCreate method to create an instance of the bean
Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; }
Here is a judgment. If the result is not empty after preprocessing, it will directly skip the subsequent Bean creation and return the result directly. Although this feature is easy to be ignored, it plays a very important role. AOP function is judged here.
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) { Object bean = null; //If not resolved if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) { // Make sure bean class is actually resolved at this point. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { Class<?> targetType = determineTargetType(beanName, mbd); if (targetType != null) { bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName); if (bean != null) { bean = applyBeanPostProcessorsAfterInitialization(bean, beanName); } } } mbd.beforeInstantiationResolved = (bean != null); } return bean; }
There are two ways
Applybeanpostprocessorsbeforeinstance: call the postprocessbeforeinstance method for all post processors of instantiaawarebeanpostprocessor type in the post processor.
applyBeanPostProcessorsAfterInitialization: call to the postProcessAfterInitialization method of BeanPostProcessor.
- Post processor application before instantiation
Bean is called before instantiation, that is, converting AbstractBeanDefinition to BeanWrapper before processing. Give subclasses an opportunity to modify the BeanDefinition, that is, after the program passes through this method, the bean may not be what we think it is, but may become a processed proxy bean, which may be generated through cglib or other technologies. We only need to know that the post processor method will be called for processing before bean instantiation.
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; }
- Post processor application after instantiation
The rule in Spring is to ensure that the postProcessAfterInitialization method of the registered post processor is applied to the bean as much as possible after the bean is initialized. Because if the returned bean is not empty, it will not go through the creation process of ordinary bean again.
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; }
What is circular dependency
Circular dependency is circular reference, that is, two or more bean s hold each other.
Circular dependency cannot be solved. Unless there is a termination condition, it is an endless loop, which eventually leads to a memory overflow error.
How does Spring solve circular dependency
Spring container cyclic dependency includes constructor cyclic dependency and setter cyclic dependency
-
Constructor loop dependency
The circular dependency formed by constructor injection cannot be solved. Only beancurrentyincreationexception exception can be thrown to indicate the circular dependency.
The Spring container places each bean identifier being created in a "currently created bean pool", and the bean identifier will always remain in this pool during the creation process. Therefore, if you find yourself in the "currently created bean pool" during the creation process, you will throw a beancurrentyincreationexception exception to indicate circular dependency, The created beans will be cleared from the "currently created bean pool". -
setter cyclic dependency
Represents the circular dependency formed by setter injection. The dependency caused by setter injection is completed by exposing the beans that have just completed the constructor injection but have not completed other steps in advance by the Spring container, and can only solve the bean circular dependency of the singleton scope. By exposing a singleton factory method in advance, other beans can reference the bean.
Create bean
The creation of regular bean s is done in doCreateBean
protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { //If it is a singleton, you need to clear the cache first. instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { //Create a new instance according to the specified bean using the corresponding policy, such as factory method, constructor automatic injection, and simple initialization //Convert BeanDefinition to BeanWrapper instanceWrapper = createBeanInstance(beanName, mbd, args); } Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { //Apply MergedBeanDefinitionPostProcessor //After bean merging, Autowired annotation implements pre parsing such as type through this method 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: Singleton & allow circular dependency & the current bean is being created. Circular dependency is detected */ // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } /** * To avoid late circular dependency, you can add the ObjectFactory that creates the instance to the factory before the bean initialization is completed * The arrow function here actually returns an ObjectFactory object and overrides the getObject method * The getEarlyBeanReference method is returned in the getObject method * Once again, the bean depends on the reference, mainly using SmartInstantiationAware BeanPostProcessor * The well-known AOP is to dynamically weave advice into beans here. If not, it will directly return to beans without any processing */ addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { //Fill the bean and inject each attribute value. If there may be attributes that depend on other beans, the initial dependent bean will be recursive populateBean(beanName, mbd, instanceWrapper); //Call initialization methods, such as 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); //earlySingletonReference will not be empty unless a circular dependency is detected if (earlySingletonReference != null) { //If the exposedObject is not changed in the initialization method, it is not enhanced if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { //Detection dependency if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } /** * Because after a bean is created, the bean it depends on must have been created * actualDependentBeans If it is not empty, it means that all the dependent beans of the current bean have not been created after it is created, that is, there is a circular dependency */ if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesForType' with the 'allowEagerInit' flag turned off, for example."); } } } } // Register bean as disposable. try { //Register bean s according to scopes registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }
1. If it is a singleton, you need to clear the cache first
2. Instantiate the bean and convert the BeanDefinition to BeanWrapper
3. Application of MergedBeanDefinitionPostProcessor
4. Dependency processing
5. Property fill. Populate all properties into the instance of the bean
6. Circular dependency check. Resolving circular dependency is only valid for single cases. Other exceptions have to be thrown
7. Register DisposableBean. If destroy method is configured, it needs to be registered here so that it can be called during destruction.
8. Finish creating and return
Create an instance of the bean
Start with createBeanInstance
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) { // Make sure bean class is actually resolved at this point. //Parse class Class<?> beanClass = resolveBeanClass(mbd, beanName); 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()); } //If the factory method is not empty, the factory method is used to initialize the policy Supplier<?> instanceSupplier = mbd.getInstanceSupplier(); if (instanceSupplier != null) { return obtainFromSupplier(instanceSupplier, beanName); } if (mbd.getFactoryMethodName() != null) { return instantiateUsingFactoryMethod(beanName, mbd, args); } // Shortcut when re-creating the same bean... boolean resolved = false; boolean autowireNecessary = false; if (args == null) { synchronized (mbd.constructorArgumentLock) { //A class has multiple constructors, and each constructor has different parameters, so you need to lock the constructor or the corresponding factory method according to the parameters before calling if (mbd.resolvedConstructorOrFactoryMethod != null) { resolved = true; autowireNecessary = mbd.constructorArgumentsResolved; } } } //If it has been resolved, the resolved constructor method does not need to be locked again if (resolved) { if (autowireNecessary) { //Constructor auto injection return autowireConstructor(beanName, mbd, null, null); } else { //Use default constructor construction return instantiateBean(beanName, mbd); } } //The constructor needs to be parsed according to the parameters // Candidate constructors for autowiring? Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName); if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR || mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) { //Constructor auto injection return autowireConstructor(beanName, mbd, ctors, args); } //Use default constructor construction // No special handling: simply use no-arg constructor. return instantiateBean(beanName, mbd); }
1. If the factoryMethodName attribute exists in the RootBeanDefinition, or the factory method is configured in the configuration file, Spring will try to use the instantiateUsingFactoryMethod method to generate an instance of the bean according to the configuration in the RootBeanDefinition
2. Resolve the constructor and instantiate it. Because there may be multiple constructors in the class corresponding to a bean, and the parameters of each constructor are different, Spring judges which constructor will be used for instantiation according to the parameters and types. However, the judgment process is a performance consuming step, so the caching mechanism is adopted. If it has been parsed, it does not need to be parsed again, but directly get the cached value of the attribute resolvedconstructorfactorymethod in RootBeanDefinition, otherwise it needs to be parsed again, And add the resolved result to the property resolvedconstructorfactorymethod in RootBeanDefinition.
autowireConstructor method
The creation of instances can be divided into two cases: General instantiation and instantiation with parameters.
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; //Explicatargs is passed in through the getBean method //If you specify method parameters when calling the getBean method, you can use them directly if (explicitArgs != null) { argsToUse = explicitArgs; } else { //If the getBean method is not specified, try to resolve from the configuration file Object[] argsToResolve = null; //Trying to get from cache synchronized (mbd.constructorArgumentLock) { constructorToUse = (Constructor<?>) mbd.resolvedConstructorOrFactoryMethod; if (constructorToUse != null && mbd.constructorArgumentsResolved) { // Found a cached constructor... //Fetch from cache argsToUse = mbd.resolvedConstructorArguments; if (argsToUse == null) { //Configured constructor parameters argsToResolve = mbd.preparedConstructorArguments; } } } //If it exists in the cache if (argsToResolve != null) { //Resolve the parameter type. For example, if the constructor A(int,int) of a given method, use this method to change ("1", "1") in the configuration to (1,1) //The value in the cache may be the original value or the final value argsToUse = resolvePreparedArguments(beanName, mbd, bw, constructorToUse, argsToResolve); } } //Not cached if (constructorToUse == null) { // Need to resolve the constructor. boolean autowiring = (chosenCtors != null || mbd.getResolvedAutowireMode() == AutowireCapableBeanFactory.AUTOWIRE_CONSTRUCTOR); ConstructorArgumentValues resolvedValues = null; int minNrOfArgs; if (explicitArgs != null) { minNrOfArgs = explicitArgs.length; } else { //Extract the constructor parameters of the configuration in the configuration file ConstructorArgumentValues cargs = mbd.getConstructorArgumentValues(); //The value used to carry the resolved constructor parameter resolvedValues = new ConstructorArgumentValues(); //Number of parameters that can be resolved minNrOfArgs = resolveConstructorArguments(beanName, mbd, bw, cargs, resolvedValues); } // Take specified constructors, if any. 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); } } //Sort the given constructor. The number of public constructor priority parameters is in descending order, and the number of non-public constructor parameters is in descending order AutowireUtils.sortConstructors(candidates); int minTypeDiffWeight = Integer.MAX_VALUE; Set<Constructor<?>> ambiguousConstructors = null; LinkedList<UnsatisfiedDependencyException> causes = null; for (Constructor<?> candidate : candidates) { Class<?>[] paramTypes = candidate.getParameterTypes(); if (constructorToUse != null && argsToUse.length > paramTypes.length) { // Already found greedy constructor that can be satisfied -> // do not look any further, there are only less greedy constructors left. //If the selected constructor has been found or the number of parameters required is less than the number of current constructor parameters, it will be terminated because it has been arranged in descending order according to the number of parameters break; } if (paramTypes.length < minNrOfArgs) { //The number of parameters is not equal continue; } ArgumentsHolder argsHolder; if (resolvedValues != null) { //If there are parameters, the parameters of the corresponding parameter type are constructed according to the value try { String[] paramNames = ConstructorPropertiesChecker.evaluate(candidate, paramTypes.length); if (paramNames == null) { //Get parameter name Explorer ParameterNameDiscoverer pnd = this.beanFactory.getParameterNameDiscoverer(); if (pnd != null) { //Gets the parameter name of the specified constructor paramNames = pnd.getParameterNames(candidate); } } //Create parameter holder based on name and data type argsHolder = createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, getUserDeclaredConstructor(candidate), autowiring); } catch (UnsatisfiedDependencyException ex) { if (logger.isTraceEnabled()) { logger.trace("Ignoring constructor [" + candidate + "] of bean '" + beanName + "': " + ex); } // Swallow and try next constructor. if (causes == null) { causes = new LinkedList<>(); } causes.add(ex); continue; } } else { // Explicit arguments given -> arguments length must match exactly. if (paramTypes.length != explicitArgs.length) { continue; } //Constructor without parameters argsHolder = new ArgumentsHolder(explicitArgs); } //Detect whether there are uncertain constructors. For example, the parameters of different constructors are parent-child relationships int typeDiffWeight = (mbd.isLenientConstructorResolution() ? argsHolder.getTypeDifferenceWeight(paramTypes) : argsHolder.getAssignabilityWeight(paramTypes)); // Choose this constructor if it represents the closest match. //If it represents the current closest match, select as the constructor 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) { //Add resolved constructor to cache argsHolderToUse.storeCache(mbd, constructorToUse); } } try { final InstantiationStrategy strategy = beanFactory.getInstantiationStrategy(); Object beanInstance; if (System.getSecurityManager() != null) { final Constructor<?> ctorToUse = constructorToUse; final Object[] argumentsToUse = argsToUse; beanInstance = AccessController.doPrivileged((PrivilegedAction<Object>) () -> strategy.instantiate(mbd, beanName, beanFactory, ctorToUse, argumentsToUse), beanFactory.getAccessControlContext()); } else { beanInstance = strategy.instantiate(mbd, beanName, this.beanFactory, constructorToUse, argsToUse); } //Add the built instance to the BeanWrapper bw.setBeanInstance(beanInstance); return bw; } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Bean instantiation via constructor failed", ex); } }
This method is too complicated
1. Determination of constructor parameters
- Judge according to the explicatargs parameter
This method passes a parameter object [] explicatargs. If the passed parameter is not empty, you can directly determine the parameter, because the parameter is specified by the user when calling Bean. Such a method exists in BeanFactory class:
Object getBean(String name,Object... args)
When obtaining a bean, the user can not only specify the name of the bean, but also specify the constructor of the class corresponding to the bean or the method parameters of the factory method, which is mainly used for the call of the static factory. Here, exactly matching parameters need to be given. Therefore, it can be judged that if the passed in parameter explicatargs is not empty, it can be determined that the constructor parameter is it.
- Get from cache
If the constructor parameters are already recorded in the cache, they can be used directly. Moreover, it should be mentioned here that what is cached in the cache may be the final type of the parameter or the initial type of the parameter. For example, if the int type is required but the String type is passed, even if the parameter is obtained in the cache, it needs to be filtered by the type converter to ensure that the parameter type is completely corresponding to the corresponding constructor parameter type
- Profile acquisition
If it cannot be obtained from the parameters or from the cache, you can only start a new round of analysis.
The analysis starts from obtaining the constructor information configured in the configuration file. After conversion, the information in the configuration file in spring will be carried through the BeanDefinition instance, that is, it is contained in the parameter mbd, so you can call mbd Getconstructorargumentvalues() to get the configured constructor information. With the information in the configuration, you can obtain the corresponding parameter value information. The information to obtain the parameter value includes directly specifying the value, such as directly specifying a value in the constructor as the original type, String type, or a reference to other beans. This processing is delegated to the resolveConstructorArguments method and returns the number of parameters that can be resolved.
2. Determination of constructor
Determine constructor: determine the corresponding constructor according to the constructor parameters. The matching method is to match according to the number of parameters. In order to quickly judge whether the number of constructor parameters in the back row meets the conditions in the case of traversal, the constructor will be sorted in descending order according to the number of parameters.
Specify the parameter name and set the parameter: first determine the parameter name in the constructor. There are two ways to obtain the parameter name, one is through annotation, and the other is to obtain it by using the tool class ParameterNameDiscoverer provided in Spring.
3. Convert the corresponding parameter type according to the determined constructor
It mainly uses the type converter provided in Spring or the user-defined type converter provided by users for conversion.
4. Verification of constructor uncertainty
Sometimes, even if the constructor, parameter name, parameter type and parameter value are determined, the constructor may not be locked directly. The parameters of different constructors are parent-child relationship, so spring did another verification at the end.
5. Instantiate the Bean according to the instantiation strategy and the obtained constructor and constructor parameters.
instantiateBean
The instance construction of a constructor with parameters is very complex, and it is easy to understand without parameters
protected BeanWrapper instantiateBean(String beanName, RootBeanDefinition mbd) { try { Object beanInstance; if (System.getSecurityManager() != null) { beanInstance = AccessController.doPrivileged( (PrivilegedAction<Object>) () -> getInstantiationStrategy().instantiate(mbd, beanName, this), getAccessControlContext()); } else { 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); } }
Directly call instantiation policy to instantiate
Instantiation strategy
The instantiation strategy here is the instantiate method. Let's take a look at this method
SimpleInstantiationStrategy.java
public Object instantiate(RootBeanDefinition bd, @Nullable String beanName, BeanFactory owner) { // Don't override the class with CGLIB if no overrides. if (!bd.hasMethodOverrides()) { Constructor<?> constructorToUse; synchronized (bd.constructorArgumentLock) { constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod; if (constructorToUse == null) { final Class<?> clazz = bd.getBeanClass(); if (clazz.isInterface()) { throw new BeanInstantiationException(clazz, "Specified class is an interface"); } try { if (System.getSecurityManager() != null) { constructorToUse = AccessController.doPrivileged( (PrivilegedExceptionAction<Constructor<?>>) clazz::getDeclaredConstructor); } else { constructorToUse = clazz.getDeclaredConstructor(); } bd.resolvedConstructorOrFactoryMethod = constructorToUse; } catch (Throwable ex) { throw new BeanInstantiationException(clazz, "No default constructor found", ex); } } } return BeanUtils.instantiateClass(constructorToUse); } else { // Must generate CGLIB subclass. return instantiateWithMethodInjection(bd, beanName, owner); } }
CglibSubclassingInstantiationStrategy
public Object instantiate(@Nullable Constructor<?> ctor, @Nullable 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); } } // SPR-10785: set callbacks directly on the instance instead of in the // enhanced class (via the Enhancer) in order to avoid memory leaks. Factory factory = (Factory) instance; factory.setCallbacks(new Callback[] {NoOp.INSTANCE, new LookupOverrideMethodInterceptor(this.beanDefinition, this.owner), new ReplaceOverrideMethodInterceptor(this.beanDefinition, this.owner)}); return instance; } /** * Create an enhanced subclass of the bean class for the provided bean * definition, using CGLIB. */ private Class<?> createEnhancedSubclass(RootBeanDefinition beanDefinition) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(beanDefinition.getBeanClass()); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); if (this.owner instanceof ConfigurableBeanFactory) { ClassLoader cl = ((ConfigurableBeanFactory) this.owner).getBeanClassLoader(); enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(cl)); } enhancer.setCallbackFilter(new MethodOverrideCallbackFilter(beanDefinition)); enhancer.setCallbackTypes(CALLBACK_TYPES); return enhancer.createClass(); }
First, judge whether there is a replace ment or lookup configuration method. If reflection is not directly used, and if these two features are used, you need to cut in the functions provided by these two configurations. Therefore, you must use the dynamic agent to set the interception enhancer containing the logic corresponding to the two features, In this way, it can be ensured that the method will be enhanced by the corresponding interceptor when calling the method, and the return value is the proxy instance containing the interceptor.
Record the ObjectFactory of the created bean
/** * Whether early exposure is required: Singleton & allow circular dependency & the current bean is being created. Circular dependency is detected */ // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } /** * To avoid late circular dependency, you can add the ObjectFactory that creates the instance to the factory before the bean initialization is completed * The arrow function here actually returns an ObjectFactory object and overrides the getObject method * The getEarlyBeanReference method is returned in the getObject method * Once again, the bean depends on the reference, mainly using SmartInstantiationAware BeanPostProcessor * The well-known AOP is to dynamically weave advice into beans here. If not, it will directly return to beans without any processing */ addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); }
Let's talk about circular dependency first. When creating A, the creation factory of A will be added to the cache, and when filling the attributes of A, B will be created recursively. Similarly, A also exists in B, so A will be created again when B is instantiated. Instead of directly instantiating A, we first check 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 we will not execute it backwards, but directly call the ObjectFactory to create A.
Look at the code for getEarlyBeanReference
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; }
Here is the post processor called.
When creating dependency A in B, interrupt the attribute filling in A through the instantiation method provided by ObjectFactory, so that the A held in B is only the A that has just initialized without filling any attributes, and the step of initializing A is carried out when creating A at the beginning, but because the attribute address represented by A in A and B is the same, Therefore, the attribute filling created in A can naturally be obtained through A in B, which solves the problem of circular dependency.
Attribute injection
protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) { if (bw == null) { if (mbd.hasPropertyValues()) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance"); } else { //There are no properties to populate // Skip property population phase for null instance. return; } } // Give any InstantiationAwareBeanPostProcessors the opportunity to modify the // state of the bean before properties are set. This can be used, for example, // to support styles of field injection. if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; //The return value is whether to continue to populate the bean if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) { return; } } } } PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null); //Resolvedauutowiremode: automatically inject the model. The default is AUTOWIRE_CONSTRUCTOR, so there will be no if here, // If the injection model of the BeanDefinition is both, you can try byType and byName respectively. int resolvedAutowireMode = mbd.getResolvedAutowireMode(); if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) { MutablePropertyValues newPvs = new MutablePropertyValues(pvs); // Add property values based on autowire by name if applicable. if (resolvedAutowireMode == AUTOWIRE_BY_NAME) { autowireByName(beanName, mbd, bw, newPvs); } // Add property values based on autowire by type if applicable. if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) { autowireByType(beanName, mbd, bw, newPvs); } pvs = newPvs; } //The post processor has been initialized boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors(); //Dependency check required boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE); if (hasInstAwareBpps || needsDepCheck) { if (pvs == null) { pvs = mbd.getPropertyValues(); } PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching); if (hasInstAwareBpps) { for (BeanPostProcessor bp : getBeanPostProcessors()) { if (bp instanceof InstantiationAwareBeanPostProcessor) { InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp; //Post process all attributes requiring dependency checking pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName); if (pvs == null) { return; } } } } if (needsDepCheck) { //Dependency check checkDependencies(beanName, mbd, filteredPds, pvs); } } if (pvs != null) { //Apply properties to bean s applyPropertyValues(beanName, mbd, bw, pvs); } }
1. The postprocessafterinstance method of the instantiaawarebeanpostprocessor processor is used to determine whether to continue property filling.
2. According to the injection type (byName/byType), the dependent bean s are extracted and uniformly stored in PropertyValues.
3. Apply the postProcessPropertyValues method of the instantiawarebeanpostprocessor processor to process the property again before the property is obtained and filled
4. Populate the BeanWrapper with properties from all PropertyValues.
autowireByName
protected void autowireByName( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { //Find the attributes that need dependency injection in bw String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { if (containsBean(propertyName)) { //Recursive initialization of related bean s Object bean = getBean(propertyName); pvs.add(propertyName, bean); //Registration dependency registerDependentBean(propertyName, beanName); if (logger.isDebugEnabled()) { logger.debug("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"); } } } }
Find the loaded bean in the passed parameter pvs, recursively instantiate it, and then add it to pvs
autowireByType
protected void autowireByType( String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) { TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } Set<String> autowiredBeanNames = new LinkedHashSet<>(4); //Find the attributes that need dependency injection in bw String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw); for (String propertyName : propertyNames) { try { PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName); // Don't try autowiring by type for type Object: never makes sense, // even if it technically is a unsatisfied, non-simple property. if (Object.class != pd.getPropertyType()) { //Probe the set method of the specified property MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd); // Do not allow eager init for type matching in case of a prioritized post-processor. boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered); DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager); //Resolve the matching value of the specified beanName attribute, and store the resolved attribute name in autowiredBeanNames. When there are multiple encapsulated beans in the attribute, such as: //@Autowired private List<A> aList; All bean s matching type A will be found and injected Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter); if (autowiredArgument != null) { pvs.add(propertyName, autowiredArgument); } for (String autowiredBeanName : autowiredBeanNames) { //Registration dependency registerDependentBean(autowiredBeanName, beanName); if (logger.isDebugEnabled()) { logger.debug("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); } } }
The first step to realize automatic matching by name is to find the attributes that need dependency injection in bw. Similarly, for the implementation of automatic matching by type, the first step is to find the attributes that need dependency injection in bw, and then traverse these attributes and find beans with type matching. The most complex one is to find beans with type matching. At the same time, Spring provides support for collection type injection, such as annotation:
@Autowired private List<Test> tests;
Spring will find all types matching test and inject them into the test attribute. Because of this factor, a new local traversal autowiredBeanNames is created in the autowireByType function to store all dependent beans. If it is only for attribute injection of non collection classes, this attribute is useless.
The logical implementation for finding type matching is encapsulated in the resolveDependency function
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; } //Get the type of the attribute to be injected (the relevant information of the attribute to be injected is encapsulated in the DependencyDescriptor) Class<?> type = descriptor.getDependencyType(); //Used to support the annotation @ Value added in Spring Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor); //If the value value is not empty, the TypeConverter converts the value to the corresponding type and returns it directly 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()); return (descriptor.getField() != null ? converter.convertIfNecessary(value, type, descriptor.getField()) : converter.convertIfNecessary(value, type, descriptor.getMethodParameter())); } //If the property to be injected is array or List, etc Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter); if (multipleBeans != null) { return multipleBeans; } //If it is not an array or other type, find out all bean s that meet the requirements and carry out further processing 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; //When there are multiple bean s matching the type if (matchingBeans.size() > 1) { autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor); if (autowiredBeanName == null) { if (isRequired(descriptor) || !indicatesMultipleBeans(type)) { return descriptor.resolveNotUnique(type, matchingBeans); } else { // In case of an optional Collection/Map, silently ignore a non-unique case: // possibly it was meant to be an empty collection of multiple regular beans // (before 4.3 in particular when we didn't even look for collection beans). return null; } } instanceCandidate = matchingBeans.get(autowiredBeanName); } else { //If only the unique bean type matches, this bean is returned // We have exactly one match. 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); } }
As can be seen from the above code, spring injection can be divided into three categories:
1. If the @ Value annotation exists, the Value value of the annotation is directly parsed, and then the desired Value is obtained after type conversion through the type converter;
2. If you need to inject array, List and other types, find the beans with matching types and return them;
3. If it is a non array type, you need to make some judgment after finding all matching beans;
resolveMultipleBeans method for handling array types
//Processing of array types private Object resolveMultipleBeans(DependencyDescriptor descriptor, String beanName, Set<String> autowiredBeanNames, TypeConverter typeConverter) { Class<?> type = descriptor.getDependencyType(); //For arrays, collections, and maps, spring is handled separately, but the general logic is very detailed //You can see that no matter what type, the search for bean s is placed in the findAutowireCandidates function if (type.isArray()) { Class<?> componentType = type.getComponentType(); ResolvableType resolvableType = descriptor.getResolvableType(); Class<?> resolvedArrayType = resolvableType.resolve(); if (resolvedArrayType != null && resolvedArrayType != type) { type = resolvedArrayType; componentType = resolvableType.getComponentType().resolve(); } if (componentType == null) { return null; } //Find all types of matching beans in beanFactory according to the attribute type, //The returned value is composed of: key = the matched beanName, value = the instantiated bean corresponding to beanName 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(), type); if (getDependencyComparator() != null && result instanceof Object[]) { Arrays.sort((Object[]) result, adaptDependencyComparator(matchingBeans)); } return result; } else if (Collection.class.isAssignableFrom(type) && type.isInterface()) { 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 (getDependencyComparator() != null && result instanceof List) { Collections.sort((List<?>) result, adaptDependencyComparator(matchingBeans)); } return result; } else if (Map.class == type) { 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; } }
Because it is processed by classification, the above code is relatively long, but the search for beans is placed in the findAutowireCandidates function. If you look carefully, in fact, for non array types, the search for beans is also in this function, but the input parameters are slightly different:
//Array type Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType, new MultiElementDescriptor(descriptor)); //Non array type Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
findAutowireCandidates
protected Map<String, Object> findAutowireCandidates( String beanName, Class<?> requiredType, DependencyDescriptor descriptor) { //Get the beanName list of candidate beans String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors( this, requiredType, true, descriptor.isEager()); Map<String, Object> result = new LinkedHashMap<String, Object>(candidateNames.length); for (Class<?> autowiringType : this.resolvableDependencies.keySet()) { if (autowiringType.isAssignableFrom(requiredType)) { Object autowiringValue = this.resolvableDependencies.get(autowiringType); autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType); if (requiredType.isInstance(autowiringValue)) { result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue); break; } } } //Add the matched beanName (ensure that the bean corresponding to the beanName lock is not itself) and the bean instance to the result for (String candidate : candidateNames) { if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) { addCandidateEntry(result, candidate, descriptor, requiredType); } } //... return result; }
applyPropertyValues
When the program runs here, it has completed the acquisition of all injected properties, but the obtained properties exist in the form of PropertyValues and have not been applied to the instantiated bean
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; if (pvs instanceof MutablePropertyValues) { mpvs = (MutablePropertyValues) pvs; //If the value in mpvs has been converted to the corresponding type, it can be set directly to bean wapper if (mpvs.isConverted()) { // Shortcut: use the pre-converted values as-is. try { bw.setPropertyValues(mpvs); return; } catch (BeansException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Error setting property values", ex); } } original = mpvs.getPropertyValueList(); } else { //If ps is not the type encapsulated by MutablePropertyValues, the original property acquisition method is used directly original = Arrays.asList(pvs.getPropertyValues()); } TypeConverter converter = getCustomTypeConverter(); if (converter == null) { converter = bw; } //Get the corresponding parser BeanDefinitionValueResolver valueResolver = new BeanDefinitionValueResolver(this, beanName, mbd, converter); // Create a deep copy, resolving any references for values. List<PropertyValue> deepCopy = new ArrayList<>(original.size()); boolean resolveNecessary = false; //Traverse the attribute and convert the attribute to the type of the corresponding attribute of the corresponding class for (PropertyValue pv : original) { if (pv.isConverted()) { deepCopy.add(pv); } else { String propertyName = pv.getName(); Object originalValue = pv.getValue(); 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); } // Possibly store converted value in merged bean definition, // in order to avoid re-conversion for every created bean instance. 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(); } // Set our (possibly massaged) deep copy. try { bw.setPropertyValues(new MutablePropertyValues(deepCopy)); } catch (BeansException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Error setting property values", ex); } }
Initialize bean
When bean is configured, there is an attribute of init-method in bean. The function of this attribute is to invoke the method specified by init-mehtod before bean instantiation to make corresponding instantiation according to user business.
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) { if (System.getSecurityManager() != null) { AccessController.doPrivileged((PrivilegedAction<Object>) () -> { invokeAwareMethods(beanName, bean); return null; }, getAccessControlContext()); } else { //Special bean processing: Aware, BeanClassLoaderAware, beanfactory Aware invokeAwareMethods(beanName, bean); } Object wrappedBean = bean; if (mbd == null || !mbd.isSynthetic()) { //Application post processor wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName); } try { //Activate user-defined init method invokeInitMethods(beanName, wrappedBean, mbd); } catch (Throwable ex) { throw new BeanCreationException( (mbd != null ? mbd.getResourceDescription() : null), beanName, "Invocation of init method failed", ex); } if (mbd == null || !mbd.isSynthetic()) { //Post processor application wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName); } return wrappedBean; }
Activate Aware method
Spring provides some Aware related interfaces, such as BeanFactory Aware, ApplicationContextAware, ResourceLoaderAware, ServletContextAware, etc. after the beans implementing these Aware interfaces are initialized, they can obtain some corresponding resources. For example, after the beans implementing BeanFactory Aware are initialized, the spring container will inject BeanFactory instances, The bean that implements ApplicationContextAware will be injected into the instance of ApplicationContext after the bean is initialized.
Let's first learn about the use of Aware
(1) Define ordinary bean s
public class Hello{ public void say(){ System.out.println("hello"); } }
(2) bean defining BeanFactoryAware type
class Test implements BeanFactoryAware{ private BeanFactory beanFactory; @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException{ this.beanFactory=beanFactory; } public void testAware(){ //Get the instance from beanFactory through the bean id of hello Hello hello =(Hello) beanFactory.getBean("hello"); hello.say(); } }
(3) Use the main method to test
public static void main(String[] args) { ApplicationContext ctx = new ClassPathAplicationContext("applicationContext.xml"); Test test = (Test) ctx.getBean("test"); test.testAware(); }
Console output: hello
According to the above method, you can obtain the BeanFactory in Spring, and you can obtain all beans according to the BeanFactory and make relevant settings. Of course, other ways of using Aware are similar. Take a look at the implementation of Spring.
private void invokeAwareMethods(String beanName, Object bean) { if (bean instanceof Aware) { if (bean instanceof BeanNameAware) { ((BeanNameAware) bean).setBeanName(beanName); } if (bean instanceof BeanClassLoaderAware) { ClassLoader bcl = getBeanClassLoader(); if (bcl != null) { ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl); } } if (bean instanceof BeanFactoryAware) { ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this); } } }
There's nothing to say. It's simple
Application of processor
BeanPostProcessor gives users sufficient permissions to change or extend Spring, and there are many postprocessors out of BeanPostProcessor. Of course, most of them are based on this and inherited from BeanPostProcessor. This is where the BeanPostProcessor is used. Before calling the custom initialization method and after calling the custom initialization method, the postProcessBeforeInitialization and postProcessAfterInitialization methods of BeanPostProcessor will be called respectively, so that users can respond according to business requirements
Activate the custom init method
In addition to the well-known init method, the customized initialization method also enables the customized bean to implement the InitializingBean interface and implement its own initialization business logic in the afterpropertieset.
Both init method and afterPropertiesSet are executed when initializing bean s. The execution order is afterPropertiesSet - > init method
The two-step initialization method call is implemented in the invokeinitialmethods method
protected void invokeInitMethods(String beanName, Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { //Check whether it is an initializing bean. If so, you need to call afterpropertieset boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isDebugEnabled()) { logger.debug("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 (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { //Call custom initialization method invokeCustomInitMethod(beanName, bean, mbd); } } }
Register DisposableBean
For the extension of the destroy method, in addition to the destroy method method, users can also register the post processor DestructionAwareBeanPostProcessor to uniformly handle the bean destruction method
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()) { /** * Register the beans that need to be destroyed in the singleton mode. This method will process the beans that implement DisposableBean, * And use DestructionAwareBeanPostProcessors for all beans */ registerDisposableBean(beanName, new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc)); } else { //Processing of custom scope // A bean with a custom scope... 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)); } } }