Discussion on the implementation of spring IOC container

In the implementation of the spring IOC container, I was buried by complex interfaces and classes at the beginning. I couldn't see the overall idea and design clearly, wandered in the code jungle, and couldn't figure out the direction of progress. At the beginning, I decided to only study the specific implementation of XmlFactoryBean with xml files as configuration files as the main goal. Gradually, I got a feeling that it would be clearer to show the bean factory system in spring with UML, so you have to Admire the ingenuity and complexity of the design. This article is just my personal understanding of the implementation of spring IOC. If there are any mistakes, please let me know, thank you.
    First of all, what needs to be understood is the life cycle of beans in the spring container. The picture in "spring in action" is the best explanation. Combining this picture and the source code to interpret the implementation of IOC in spring will be very easy to understand. This picture fully shows the entire life cycle of a bean in the spring container from creation to destruction.
   
1. The container looks for the definition information of the bean and instantiates it.
2. Using dependency injection, Spring configures all properties of the Bean according to the Bean definition information.
3. If the Bean implements the BeanNameAware interface, the factory calls the Bean's setBeanName() method to pass the Bean's ID.
4. If the Bean implements the BeanFactoryAware interface, the factory calls the setBeanFactory() method to pass in the factory itself.
5. If the BeanPostProcessor is associated with the Bean, their postProcessBeforeInitialzation() method will be called.
6. If the Bean specifies an init-method, it will be called.
7. Finally, if there is a BeanPsotProcessor associated with the Bean, their postProcessAfterInitialization() method will be called.
    By this time, the bean is ready to be used by the application system and will be kept in the Bean Factory until it is no longer needed. There are two ways to remove it from the Bean Factory.
1. If the Bean implements the DisposableBean interface, the destroy() method is called.
2. If a custom destruction method is specified, call this method.
   
    Below we will see how each stage of these bean creation and destruction is implemented in the source code.
    Looking at the complete system of bean factories in spring, it is more complicated, but we only need to pay attention to a few core factories.
    
    (I can’t see clearly, please download the picture to see it, it’s clearer)
    These factory classes are not in the same package, but are distributed in org.springframework.beans and its sub-packages, and it’s clearer to put them together. We need to pay attention to these two classes: org.springframework.beans.factory.support.AbstractBeanFactory
org.springframework.beans.factory.support.DefaultListableBeanFactory
and interface:
org.springframework.beans.factory.support.BeanDefinitionRegistry

    AbstractBeanFactory, as the abstract implementation class of the BeanFactory interface, is the parent class of other factory classes, providing bean singleton cache, singleton/prototype decision, bean alias, and bean and its subclass bean definition merging (bean definition merging for child bean definitions) and destruction. Here are 3 overloaded getBean methods (implementing the getBean method defined by BeanFactory), we focus on the most important method:

 

public Object getBean(String name, Class requiredType, Object[] args) throws BeansException {
        String beanName 
= transformedBeanName(name);
        Object bean 
= null;

        
// Eagerly check singleton cache for manually registered singletons.
        Object sharedInstance = null;
        
//从单例缓存中获取
        synchronized (this.singletonCache) {
            sharedInstance 
= this.singletonCache.get(beanName);
        }
        
if (sharedInstance != null) {
            
if (isSingletonCurrentlyInCreation(beanName)) {
                
if (logger.isDebugEnabled()) {
                    logger.debug(
"Returning eagerly cached instance of singleton bean '" + beanName +
                            
"' that is not fully initialized yet - a consequence of a circular reference");
                }
            }
            
else {
                
if (logger.isDebugEnabled()) {
                    logger.debug(
"Returning cached instance of singleton bean '" + beanName + "'");
                }
            }
            bean 
= getObjectForSharedInstance(name, sharedInstance);
        }

        
else {
            
// Fail if we're already creating this singleton instance:
            
// We're assumably within a circular reference.
            if (isSingletonCurrentlyInCreation(beanName)) {
                
throw new BeanCurrentlyInCreationException(beanName);
            }

            
// Check if bean definition exists in this factory.
            
//检测bean是否定义在父工厂
            if (getParentBeanFactory() != null && !containsBeanDefinition(beanName)) {
                
// Not found -> check parent.
                if (getParentBeanFactory() instanceof AbstractBeanFactory) {
                    
// Delegation to parent with args only possible for AbstractBeanFactory.
                    return ((AbstractBeanFactory) getParentBeanFactory()).getBean(name, requiredType, args);
                }
                
else if (args == null) {
                    
// No args -> delegate to standard getBean method.
                    return getParentBeanFactory().getBean(name, requiredType);
                }
                
else {
                    
throw new NoSuchBeanDefinitionException(beanName,
                            
"Cannot delegate to parent BeanFactory because it does not supported passed-in arguments");
                }
            }

            
//获取BeanDefinition
            RootBeanDefinition mergedBeanDefinition = getMergedBeanDefinition(beanName, false);
            checkMergedBeanDefinition(mergedBeanDefinition, beanName, requiredType, args);

            
// Create bean instance.
            
//创建bean,如果为为singleton
            if (mergedBeanDefinition.isSingleton()) {
                
synchronized (this.singletonCache) {
                    
// Re-check singleton cache within synchronized block.
                    sharedInstance = this.singletonCache.get(beanName);
                    
if (sharedInstance == null) {
                        
if (logger.isDebugEnabled()) {
                            logger.debug(
"Creating shared instance of singleton bean '" + beanName + "'");
                        }
                        
this.currentlyInCreation.add(beanName);
                        
try {
                            sharedInstance 
= createBean(beanName, mergedBeanDefinition, args);
                            //加进单例缓存
                            addSingleton(beanName, sharedInstance);
                        }
                        
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.
                            destroyDisposableBean(beanName);
                            
throw ex;
                        }
                        
finally {
                            
this.currentlyInCreation.remove(beanName);
                        }
                    }
                }
                bean 
= getObjectForSharedInstance(name, sharedInstance);
            }
            
//如果是prototype
            else {
                
// It's a prototype -> create a new instance.
                bean = createBean(beanName, mergedBeanDefinition, args);
            }
        }

        
// Check if required type matches the type of the actual bean instance.
        if (requiredType != null && !requiredType.isAssignableFrom(bean.getClass())) {
            
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
        
return bean;
    }

 

When you call getBean to get a bean, spring first checks whether the bean already exists in the singleton cache, and returns it if there is one (it will first determine whether it has been created), if not, spring starts to create the bean: get the bean first The definition of BeanDefinition (BeanDefinition), check whether the bean is defined in the parent factory, if so, call the getBean method of the parent factory; if not, check whether the bean is a singleton or a prototype, if it is a singleton, create a bean and add it to the cache for the next direct call, if Is the prototype, just recreate a bean instance every time it is called. Pay attention to createBean(beanName, mergedBeanDefinition, args); This method, which is the core method of creating beans, and is an abstract method, will be implemented by subclasses.
    See how to get the bean definition,

protected RootBeanDefinition getMergedBeanDefinition(String name, boolean includingAncestors)
        
throws BeansException {

        String beanName 
= transformedBeanName(name);

        
// Efficiently check whether bean definition exists in this factory.
        if (includingAncestors && !containsBeanDefinition(beanName) &&
                getParentBeanFactory() 
instanceof AbstractBeanFactory) {
            
return ((AbstractBeanFactory) getParentBeanFactory()).getMergedBeanDefinition(beanName, true);
        }

        
// Resolve merged bean definition locally. return  getMergedBeanDefinition(beanName, getBeanDefinition(beanName));     }     Call the overloaded getMergedBeanDefinition to merge the bean definitions in the parent factory and the child factory. Note that getBeanDefinition(beanName), which is an abstract method, is delayed until the implementation of the subclass In order to provide a specific method for obtaining bean definitions, this method is an application of the template method pattern like createBean. Look at their definitions: protected abstract  BeanDefinition getBeanDefinition(String beanName)  throws  BeansException; protected abstract  Object createBean(             String beanName, RootBeanDefinition mergedBeanDefinition, Object[] args)  throws  BeanCreationException; subclass The AbstractAutowireCapableBeanFactory, this class implements createBean(), in this method we will see the same as
        


 

 

The above bean life cycle diagram corresponds to the bean creation process , and the English notes are very clear:

protected Object createBean(String beanName, RootBeanDefinition mergedBeanDefinition, Object[] args)
            
throws BeanCreationException {

        
if (logger.isDebugEnabled()) {
            logger.debug(
"Creating instance of bean '" + beanName +
                    
"' with merged definition [" + mergedBeanDefinition + "]");
        }

        Object bean 
= null;

        
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
        if (mergedBeanDefinition.hasBeanClass()) {
            bean 
= applyBeanPostProcessorsBeforeInstantiation(mergedBeanDefinition.getBeanClass(), beanName);
            
if (bean != null) {
                
return bean;
            }
        }

        
// Guarantee initialization of beans that the current one depends on.
        if (mergedBeanDefinition.getDependsOn() != null) {
            
for (int i = 0; i < mergedBeanDefinition.getDependsOn().length; i++) {
                getBean(mergedBeanDefinition.getDependsOn()[i]);
            }
        }

        BeanWrapper instanceWrapper 
= null;
        Object originalBean 
= null;
        String errorMessage 
= null;

        
try {
            
// Instantiate the bean.
            errorMessage = "Instantiation of bean failed";

            
if (mergedBeanDefinition.getFactoryMethodName() != null)  {
                instanceWrapper 
= instantiateUsingFactoryMethod(beanName, mergedBeanDefinition, args);
            }
            
else if (mergedBeanDefinition.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
                    mergedBeanDefinition.hasConstructorArgumentValues() )  {
                instanceWrapper 
= autowireConstructor(beanName, mergedBeanDefinition);
            }
            
else {
                
// No special handling: simply use no-arg constructor.
                instanceWrapper = instantiateBean(beanName, mergedBeanDefinition);
            }
            bean 
= instanceWrapper.getWrappedInstance();

            
// Eagerly cache singletons to be able to resolve circular references
            
// even when triggered by lifecycle interfaces like BeanFactoryAware.
            if (isAllowCircularReferences() && isSingletonCurrentlyInCreation(beanName)) {
                
if (logger.isDebugEnabled()) {
                    logger.debug(
"Eagerly caching bean with name '" + beanName +
                            
"' to allow for resolving potential circular references");
                }
                addSingleton(beanName, bean);
            }

            
// Initialize the bean instance.
            errorMessage = "Initialization of bean failed";
            populateBean(beanName, mergedBeanDefinition, instanceWrapper);

            
if (bean instanceof BeanNameAware) {
                
if (logger.isDebugEnabled()) {
                    logger.debug(
"Invoking setBeanName on BeanNameAware bean '" + beanName + "'");
                }
                ((BeanNameAware) bean).setBeanName(beanName);
            }

            
if (bean instanceof BeanFactoryAware) {
                
if (logger.isDebugEnabled()) {
                    logger.debug(
"Invoking setBeanFactory on BeanFactoryAware bean '" + beanName + "'");
                }
                ((BeanFactoryAware) bean).setBeanFactory(
this);
            }

            originalBean 
= bean;
            bean 
= applyBeanPostProcessorsBeforeInitialization(bean, beanName);
            invokeInitMethods(beanName, bean, mergedBeanDefinition);
            bean 
= applyBeanPostProcessorsAfterInitialization(bean, beanName);
        }
        
catch (BeanCreationException ex) {
            
throw ex;
        }
        
catch (Throwable ex) {
            
throw new BeanCreationException(
                    mergedBeanDefinition.getResourceDescription(), beanName, errorMessage, ex);
        }

        
// Register bean as disposable, and also as dependent on specified "dependsOn" beans.
        registerDisposableBeanIfNecessary(beanName, originalBean, mergedBeanDefinition);

        
return bean;
    }

 

Use the instanceof operator to judge whether the bean implements the interface for lifecycle callback, and then call the corresponding callback method. You can see that spring has fully practiced the principle of programming for the interface. These interfaces are also used as mark interfaces to mark callbacks. Looking at this code in conjunction with the above life cycle diagram will be easy to understand. With the bean creation method, how to obtain the definition information of the bean in the configuration file? That is, where is the getBeanDefinition method implemented? The answer lies in the DefaultListableBeanFactory subclass of AbstractAutowireCapableBeanFactory.

    DefaultListableBeanFactory not only inherits AbstractAutowireCapableBeanFactory, but also implements BeanDefinitionRegistry and ConfigurableListableBeanFactory interfaces. Among them, the ConfigurableListableBeanFactory interface defines a callback method for analyzing and modifying bean definition information, so leave it alone for now. Take a look at the BeanDefinitionRegistry interface:

public interface BeanDefinitionRegistry {
  
int getBeanDefinitionCount();
  String[] getBeanDefinitionNames();
  BeanDefinition getBeanDefinition(String beanName) 
throws NoSuchBeanDefinitionException;
  String[] getAliases(String beanName) 
throws NoSuchBeanDefinitionException;
  
void registerAlias(String beanName, String alias) throws BeansException;
  
void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            
throws BeansException;
  ......
}

A series of methods used to obtain bean definition information, this interface we will see in the following code, as a mark inteface. Note that a very critical method in this interface is registerBeanDefinition, which is used to register each bean definition parsed with the bean factory. We will see that calling this method to register bean definition information in the parsing of the xml file.

    DefaultListableBeanFactory implements the BeanDefinitionRegistry interface. You can see that it implements the key registerBeanDefinition to register the bean definition into the factory class, which maintains a Map for storing bean definition information:

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
            
throws BeanDefinitionStoreException {

        Assert.hasText(beanName, 
"Bean name must not be empty");
        Assert.notNull(beanDefinition, 
"Bean definition must not be null");

        
if (beanDefinition instanceof AbstractBeanDefinition) {
            
try {
                ((AbstractBeanDefinition) beanDefinition).validate();
            }
            
catch (BeanDefinitionValidationException ex) {
                
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        
"Validation of bean definition failed", ex);
            }
        }

        Object oldBeanDefinition 
= this.beanDefinitionMap.get(beanName);
        
if (oldBeanDefinition != null) {
            
if (!isAllowBeanDefinitionOverriding()) {
                
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
                        
"Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName +
                        
"': there's already [" + oldBeanDefinition + "] bound");
            }
            
else {
                
if (logger.isInfoEnabled()) {
                    logger.info(
"Overriding bean definition for bean '" + beanName +
                            
"': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]");
                }
            }
        }
        
else {
            
this.beanDefinitionNames.add(beanName);
        }
        
this.beanDefinitionMap.put(beanName, beanDefinition);

        
// Remove corresponding bean from singleton cache, if any.
        
// Shouldn't usually be necessary, rather just meant for overriding
        
// a context's default beans (e.g. the default StaticMessageSource
        
// in a StaticApplicationContext).
        removeSingleton(beanName);
    }

beanDefinitionMap is where we store the bean information registered in the factory class, using the bean name as the key:

/**  Map of bean definition objects, keyed by bean name  */ private final  Map beanDefinitionMap  = new  HashMap();     DefaultListableBeanFactory rewrites protected abstract  BeanDefinition getBeanDefinition(String beanName)  throws  BeansException; template method is used to provide bean definition information to getBean method for creating bean instances. GetBeanDefinition can find the bean definition from beanDefinitionMap:
    
   

 

public BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException {
        BeanDefinition bd 
= (BeanDefinition) this.beanDefinitionMap.get(beanName);
        
if (bd == null) {
            
if (logger.isDebugEnabled()) {
                logger.debug(
"No bean named '" + beanName + "' found in " + toString());
            }
            
throw new NoSuchBeanDefinitionException(beanName);
        }
        
return bd;
    }

Do you feel a little confused after seeing this? I still suggest that you find the source code of spring 1.2 if you are interested, and it will be very clear. Make full use of the F3 jump of eclipse combined with the UML diagram above.
    OK, there is a method of registering bean definition information (registerBeanDefinition), a method of obtaining bean definition information (getBeanDefinition), and a method of creating bean (createBean), so where to call these methods? We already know that createBean is called in the getBean method of BeanFactory, and getBeanDefinition is called in creatBean to obtain bean definition information, so the method of registering bean definition information must be called in the xml file parsing stage.
   XmlBeanFactory inherits DefaultListableBeanFactory, does not define new methods, but has two overloaded constructors:

public class XmlBeanFactory extends DefaultListableBeanFactory {
  
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
  
public XmlBeanFactory(Resource resource) throws BeansException {
        
this(resource, null);
    }
  
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        
super(parentBeanFactory);
        
this.reader.loadBeanDefinitions(resource);
    }
}

 When initializing XmlBeanFactory, call XmlBeanDefinitionReader to read the xml configuration file, and the loadBeanDefinitions method of XmlBeanDefinitionReader loads the xml file and calls XmlBeanDefinitionParser to parse the xml file and register the bean definition information to the bean factory. The collaboration of these classes and their inheritance system are also very clear. No more details here (tired), XmlBeanDefinitionParser is an interface, the specific implementation class is org.springframework.beans.factory.xml.DefaultXmlBeanDefinitionParser, we focus on how it registers bean information to the bean factory:

protected int parseBeanDefinitions(Element root) throws BeanDefinitionStoreException {

    
    
if (IMPORT_ELEMENT.equals(node.getNodeName())) {
                    importBeanDefinitionResource(ele);
                }
                
else if (ALIAS_ELEMENT.equals(node.getNodeName())) {
                    String name 
= ele.getAttribute(NAME_ATTRIBUTE);
                    String alias 
= ele.getAttribute(ALIAS_ATTRIBUTE);
                    
this.beanDefinitionReader.getBeanFactory().registerAlias(name, alias);
                }
                
else if (BEAN_ELEMENT.equals(node.getNodeName())) {                     beanDefinitionCount
++ ;
                    BeanDefinitionHolder bdHolder 
=  parseBeanDefinitionElement(ele,  false );
                    
// Key code, call tool class to register bean definition to beanFactory BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder,  this .beanDefinitionReader .getBeanFactory());                 } }
                    

   

 

The registration method in the tool class BeanDefinitionReaderUtils:
 public static void  registerBeanDefinition( BeanDefinitionHolder bdHolder, BeanDefinitionRegistry         beanFactory             )  throws  BeansException { //  Register bean definition under primary name., register bean beanFactory.registerBeanDefinition (bdHolder.getBeanName() , bdHolder. getBeanDefinition()) ; //  Register aliases for bean name, if any., register aliases if  (bdHolder.getAliases()  != null ) { for  ( int  i  = 0 ; i  <  bdHolder.getAliases().length; i  


        



        

         
            
 ++) {
                beanFactory.registerAlias(bdHolder.getBeanName(), bdHolder.getAliases()[i]);
            }
        }
    }

 

Note that the beanFactory here is of type BeanDefinitionRegistry, and DefaultListableBeanFactory implements this interface.
    More content, such as how does spring inject bean properties? The answer is in a series of setPropertyXXX methods in BeanWrapperImpl. How does BeanFactory instantiate beans? For details, please see the org.springframework.beans.factory.support.InstantiationStrategy interface, a typical strategy pattern application, two implementation classes: SimpleInstantiationStrategy and CglibSubclassingInstantiationStrategy. We know that spring has 3 injection methods: setter, constructor and Method Inject. The CglibSubclassingInstantiationStrategy here uses the cglib library to implement method inject. Such a small article cannot accommodate so much content, so if you are interested, you can find the source code and read it yourself.

    As I write this, I have doubts about my ability to express myself. Can I explain all the information clearly? I feel very dissatisfied, let's just let it go, and it can be regarded as my own study summary.
    During the period of reading the spring source code, the deepest experience:
1) Unit testing is very important, and I fully feel the advantages of unit testing as project documentation. If you don't understand a class, find the test class of this class and it's OK!
2) For the principle of interface programming, spring embodies this principle everywhere. The various interfaces bring a loosely coupled, high cohesive and easy-to-extend architecture, but it also brings a certain degree of difficulty in understanding. As a general-purpose framework, it may be necessary to consider this way, and the choice in actual projects still needs to be grasped by yourself.
3) The use of design patterns, many patterns are applied in spring, such as template method, such as strategy and factory method, visitor, singleton, etc., it is simply a good teaching material for pattern practice.

 
Reprint address: http://www.blogjava.net/killme2008/archive/2007/04/20/112160.html


Guess you like

Origin blog.csdn.net/u012411159/article/details/12918577