Source code analysis of Spring IOC container

The most important concepts of Spring are IOC and AOP. This article is actually to lead you to analyze the IOC container of Spring. Since we usually use Spring, how can we not have a good understanding of Spring? Reading this article will not make you a Spring expert, but it will help you understand many concepts of Spring and help you troubleshoot some Spring related problems in your application.

The source code version used in this paper is 4.3 11. Release is 5.0 A relatively new version before X. In order to reduce the difficulty, all the contents mentioned in this paper are xml based configuration. Few people have done so in actual use, at least not pure xml configuration. However, from the perspective of understanding the source code, this method is undoubtedly the most appropriate.

Reading suggestions: readers should at least know how to configure Spring and understand various concepts in Spring. I also assume that readers have used Spring MVC. Generally speaking, the IOC to be mentioned in this article has two most important places: one is to create a Bean container and the other is to initialize a Bean. If readers feel a little pressure after reading this article at one time, they can digest it twice according to this idea. Readers may not be interested in the source code of the Spring container. Perhaps the knowledge introduced in the appendix will help readers.

I hope that through this article, readers will not be afraid to read the Spring source code, and I hope you can feed back the wrong or unreasonable expression.

introduction

Let's take a look at the most basic example of starting the Spring container:

public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationfile.xml");
}

The above code can use the configuration file to start a Spring container. Please use maven's small partner to directly add the following dependencies to the dependencies. Personally, I am opposed to those who do not know what dependencies to add, and then add all related things of Spring.

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>4.3.11.RELEASE</version>
</dependency>

Spring context will automatically bring in the basic jar packages of spring core, spring beans, spring AOP and spring expression.

To add a word, many developers directly contact spring MVC when they get started. In fact, they don't know much about spring. Spring is a progressive tool, not very intrusive, and its modules are reasonably divided. Even if your application is not a web application, or you haven't used spring at all before, and you want to inject this function with spring's dependency, In fact, it is completely possible. Its introduction will not conflict with other components.

With that nonsense, let's go on. ApplicationContext context = new ClassPathXmlApplicationContext(...) In fact, it's easy to understand. One or two guesses from the name are to find the xml configuration file in ClassPath and build the ApplicationContext according to the content of the xml file. Of course, in addition to classpathxmlapplicationcontext, we also have other options for building ApplicationContext. Let's take a look at the general inheritance structure:

Readers can take a general look at the class name. When analyzing the source code, they won't be unable to find which class to look at, because each interface provided by Spring may have many implementation classes in order to adapt to various use scenarios. For us, it is to grasp a complete branch.

Of course, when reading this article, readers don't have to worry too much. When analyzing each code block, I will tell readers which class and line we are talking about.

We can see that ClassPathXmlApplicationContext took a long time to get to the ApplicationContext interface. Similarly, we can also use the green FileSystemXmlApplicationContext and AnnotationConfigApplicationContext classes.

1. The constructor of FileSystemXmlApplicationContext requires the path of an xml configuration file in the system, which is basically the same as ClassPathXmlApplicationContext.

2. AnnotationConfigApplicationContext is used based on annotations. It does not need configuration files. It is a relatively simple way to configure it by using java configuration classes and various annotations. It is also the general trend.

However, this article is intended to help you understand the whole construction process, so we decided to use ClassPathXmlApplicationContext for analysis.

Let's start with a simple example to see how to instantiate ApplicationContext.

First, define an interface:

public interface MessageService {
    String getMessage();
}

Define interface implementation classes:

public class MessageServiceImpl implements MessageService {

    public String getMessage() {
        return "hello world";
    }
}

Next, we create a new configuration file in the resources directory. The file name is arbitrary, usually called application XML or application XXX XML is OK:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd" default-autowire="byName">

    <bean id="messageService" class="com.javadoop.example.MessageServiceImpl"/>
</beans>

So we can run:

public class App {
    public static void main(String[] args) {
        // Start an ApplicationContext with our configuration file
        ApplicationContext context = new ClassPathXmlApplicationContext("classpath:application.xml");
      
        System.out.println("context Start successful");
      
        // Take out our Bean from the context instead of using new MessageServiceImpl()
        MessageService messageService = context.getBean(MessageService.class);
        // This sentence will output: hello world
        System.out.println(messageService.getMessage());
    }
}

The above example is very simple, but it is enough to lead to the topic of this article, that is, how to start Spring's ApplicationContext through the configuration file? That is the core of the IOC we are going to analyze today. During the startup of ApplicationContext, it will be responsible for creating instance beans and injecting dependencies into each Bean.

Introduction to BeanFactory

BeanFactory is also well understood from the name. It is the factory that produces beans. It is responsible for producing and managing various bean instances.

Beginners should not think that what I said before has nothing to do with BeanFactory. The ApplicationContext mentioned earlier is actually a BeanFactory. Let's take a look at the main inheritance structures related to the BeanFactory interface:

I think everyone may not be very happy after reading this picture. The inheritance structure under ApplicationContext has been described in the previous figure, which will not be repeated here. It's definitely unnecessary to memorize this picture. Just explain a few key points to you.

  1. ApplicationContext inherits ListableBeanFactory. This Listable means that through this interface, we can obtain multiple beans. Looking at the source code, we will find that the method of the top-level BeanFactory interface is to obtain a single Bean.
  2. ApplicationContext inherits the Hierarchical BeanFactory. The Hierarchical word itself can explain the problem, that is, we can create multiple beanfactories in the application, and then set each BeanFactory as a parent-child relationship.
  3. Autowire in the name of AutowireCapableBeanFactory is very familiar to everyone. It is used to automatically assemble beans. However, looking carefully at the figure above, ApplicationContext does not inherit it, but don't worry. If inheritance is not used, it does not mean that combination can not be used, If you see the last method getAutowireCapableBeanFactory() in the ApplicationContext interface definition, you will know.
  4. ConfigurableListableBeanFactory is also a special interface. See the figure. The difference is that it inherits all three interfaces of layer 2, but ApplicationContext does not. This will be used later.
  5. Please don't spend time on other interfaces and classes. Just understand what I said first.

Then, please open the editor and look through the codes of BeanFactory, ListableBeanFactory, hierarchalbeanfactory, AutowireCapableBeanFactory and ApplicationContext. Let's have a general idea of the methods in each interface. I won't post the code introduction due to space limitation.

Start up process analysis

The following will be a lengthy code analysis. Remember, you must open the source code yourself, otherwise you will be very tired.

In the first step, we must start with the construction method of ClassPathXmlApplicationContext.

public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
  private Resource[] configResources;
  
  // If you already have ApplicationContext and need to configure it as a parent-child relationship, call this constructor
  public ClassPathXmlApplicationContext(ApplicationContext parent) {
    super(parent);
  }
  ...
  public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
      throws BeansException {

    super(parent);
    // According to the provided path, it is processed into a configuration file array (separated by semicolon, comma, space, tab and newline character)
    setConfigLocations(configLocations);
    if (refresh) {
      refresh(); // Core method
    }
  }
    ...
}

Next, we'll talk about refresh(). Here's why it's a method with the name refresh() instead of init(). After the ApplicationContext is established, we can actually rebuild it by calling the refresh() method. refresh() will destroy the original ApplicationContext and then perform the initialization operation again.

Looking down, you can see that the refresh() method calls so many methods. It must be not simple. Please read the details first and then talk about them in detail.

@Override
public void refresh() throws BeansException, IllegalStateException {
   // Give me a lock. Otherwise, refresh() is not over yet. If you start or destroy the container again, it will be a mess
   synchronized (this.startupShutdownMonitor) {

      // Prepare the work, record the start time of the container, mark the "started" status, and process the placeholder in the configuration file
      prepareRefresh();
     
      // This is a key step. After this step is completed, the configuration file will be parsed into Bean definitions and registered in BeanFactory,
      // Of course, the Bean mentioned here has not been initialized, but the configuration information has been extracted,
      // Registration only saves these information to the registration center (in the final analysis, the core is a beanname - > beandefinition map)
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Set the class loader of BeanFactory, add several beanpostprocessors, and manually register several special beans
      // We'll talk about this later
      prepareBeanFactory(beanFactory);

      try {
         // [here you need to know the knowledge of beanfactoryprocessor. If a Bean implements this interface,
         // After the container is initialized, Spring will be responsible for calling the postProcessBeanFactory method inside.]
        
         // Here is the extension point for subclasses. When you get here, all beans have been loaded and registered, but they have not been initialized
         // For specific subclasses, you can add some special beanfactoryprocessor implementation classes or do something at this step
         postProcessBeanFactory(beanFactory);
         // Call the postProcessBeanFactory(factory) method of each implementation class of beanfactoryprocessor
         invokeBeanFactoryPostProcessors(beanFactory);

         // Register the implementation class of BeanPostProcessor. Pay attention to the difference between BeanPostProcessor and beanfactoryprocessor
         // This interface has two methods: postProcessBeforeInitialization and postProcessAfterInitialization
         // The two methods are executed before and after Bean initialization. Note that the Bean has not been initialized
         registerBeanPostProcessors(beanFactory);

         // Initialize the MessageSource of the current ApplicationContext. Internationalization will not be expanded here, otherwise it will be endless
         initMessageSource();

         // Initialize the event broadcaster of the current ApplicationContext, which will not be expanded here
         initApplicationEventMulticaster();

         // You can know from the method name that a typical template method (hook method),
         // Specific subclasses can initialize some special beans here (before initializing singleton beans)
         onRefresh();

         // Register the event listener. The listener needs to implement the ApplicationListener interface. That's not our point, too
         registerListeners();

         // Focus, focus, focus
         // Initialize all singleton beans
         //(except for lazy init)
         finishBeanFactoryInitialization(beanFactory);

         // Finally, broadcast the event, and the ApplicationContext initialization is completed
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         // Destroy the initialized singleton Beans to prevent some Beans from occupying resources all the time
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Throw the anomaly out
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

Next, let's start dismembering the refresh() method step by step.

Preparations before creating Bean container

This is relatively simple. Just look at a few comments in the code.

protected void prepareRefresh() {
   // Record the start time,
   // Set the active property to true and the closed property to false, both of which are AtomicBoolean types
   this.startupDate = System.currentTimeMillis();
   this.closed.set(false);
   this.active.set(true);

   if (logger.isInfoEnabled()) {
      logger.info("Refreshing " + this);
   }

   // Initialize any placeholder property sources in the context environment
   initPropertySources();

   // Validate xml configuration file
   getEnvironment().validateRequiredProperties();

   this.earlyApplicationEvents = new LinkedHashSet<ApplicationEvent>();
}

Create a Bean container, load and register beans

Let's go back to the next line in the refresh() method, obtainFreshBeanFactory().

Note that this method is one of the most important parts of the full text. It will initialize BeanFactory, load beans, register beans, and so on.

Of course, after this step, the Bean did not complete initialization. This means that the Bean instance is not generated in this step.

// AbstractApplicationContext.java

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
   // Close the old BeanFactory (if any), create a new BeanFactory, load Bean definitions, register beans, and so on
   refreshBeanFactory();
  
   // Return to the BeanFactory you just created
   ConfigurableListableBeanFactory beanFactory = getBeanFactory();
   if (logger.isDebugEnabled()) {
      logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
   }
   return beanFactory;
}

// AbstractRefreshableApplicationContext.java 120

@Override
protected final void refreshBeanFactory() throws BeansException {
   // If BeanFactory has been loaded in ApplicationContext, destroy all beans and close BeanFactory
   // Note that there can be more than one BeanFactory in an application. This does not mean whether there is a BeanFactory in the global application, but the current
   // Does the ApplicationContext have a BeanFactory
   if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
   }
   try {
      // Initialize a DefaultListableBeanFactory. Why use this? Let's talk about it right away.
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      // I don't think some people can use the serialization for BeanFactory
      beanFactory.setSerializationId(getId());
     
      // The following two methods are very important. Don't lose them. Let's talk about the details later
      // Set two configuration properties of BeanFactory: whether to allow Bean overwrite and whether to allow circular reference
      customizeBeanFactory(beanFactory);
     
      // Load beans into BeanFactory
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
         this.beanFactory = beanFactory;
      }
   }
   catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
   }
}

When I see this, I think readers should stand high and look at ApplicationContext. ApplicationContext inherits from BeanFactory, but it should not be understood as the implementation class of BeanFactory, Instead, it holds an instantiated BeanFactory (DefaultListableBeanFactory). All future BeanFactory related operations are actually delegated to this instance.

Let's talk about why we chose to instantiate DefaultListableBeanFactory? As mentioned earlier, there is a very important interface ConfigurableListableBeanFactory, which implements all three interfaces in the lower layer of BeanFactory. I'll take the previous inheritance diagram and have a closer look:

We can see that ConfigurableListableBeanFactory has only one implementation class DefaultListableBeanFactory, and the implementation class DefaultListableBeanFactory also eats the right way by implementing AbstractAutowireCapableBeanFactory on the right. So the conclusion is that the DefaultListableBeanFactory at the bottom is basically the best BeanFactory, which is why this class is used to instantiate.

If you want to dynamically register new bean s with the Spring IOC container when the program is running, you will use this class. How do we get this instance at runtime?

As we said before, the ApplicationContext interface can get the AutowireCapableBeanFactory, which is the one in the top right corner, and then it can get the DefaultListableBeanFactory by downward transformation.

How do I get the ApplicationContext instance? If you can't, you haven't used Spring.

Before moving on, we need to understand BeanDefinition. We say that BeanFactory is a Bean container, so what is a Bean?

The BeanDefinition here is what we call Spring beans. In fact, each Bean defined by ourselves will be converted into BeanDefinition, which exists in the BeanFactory of Spring.

Therefore, if someone asks you what a Bean is, you should know that a Bean can be simply regarded as an instance of BeanDefinition at the code level.

BeanDefinition stores our Bean information, such as which class the Bean points to, whether it is a singleton, whether it is lazy to load, which beans the Bean depends on, and so on.

BeanDefinition interface definition

Let's look at the interface definition of BeanDefinition:

public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

   // We can see that only sington and prototype are provided by default,
   // Many readers may know that there are also requests, sessions, global sessions, applications and WebSockets,
   // However, they are web-based extensions.
   String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
   String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

   // It's not important. Just skip it
   int ROLE_APPLICATION = 0;
   int ROLE_SUPPORT = 1;
   int ROLE_INFRASTRUCTURE = 2;

   // Setting the parent bean involves bean inheritance, not java inheritance. Please refer to the appendix for details
   // In a word: inherit the configuration information of the parent Bean
   void setParentName(String parentName);
  
   // Get parent Bean
   String getParentName();
  
   // Setting the class name of the Bean will generate instances through reflection in the future
   void setBeanClassName(String beanClassName);
   
   // Gets the class name of the Bean
   String getBeanClassName();

 
   // Set the scope of the bean
   void setScope(String scope);

   String getScope();

   // Set lazy loading
   void setLazyInit(boolean lazyInit);
   
   boolean isLazyInit();

   // Set all beans that the Bean depends on. Note that the dependency here does not refer to attribute dependency (such as @ Autowire tag),
   // Is the value set by the dependencies on = "" property.
   void setDependsOn(String... dependsOn);

   // Returns all dependencies of the Bean
   String[] getDependsOn();

   // Set whether the Bean can be injected into other beans, which is only valid for injection according to type,
   // If you inject by name, even if false is set here, it is OK
   void setAutowireCandidate(boolean autowireCandidate);

   // Can this Bean be injected into other beans
   boolean isAutowireCandidate();

   // major. For multiple implementations of the same interface, if no name is specified, Spring will preferentially select the bean whose primary is set to true
   void setPrimary(boolean primary);

   // Is it primary
   boolean isPrimary();

   // If the Bean is generated using the factory method, specify the factory name. For readers unfamiliar with the factory, please refer to the appendix
   // In a word, some instances are not generated by reflection, but by factory mode
   void setFactoryBeanName(String factoryBeanName);
   // Get factory name
   String getFactoryBeanName();
   // Specifies the factory method name in the factory class
   void setFactoryMethodName(String factoryMethodName);
   // Gets the factory method name in the factory class
   String getFactoryMethodName();

   // Constructor parameters
   ConstructorArgumentValues getConstructorArgumentValues();

   // The attribute value in the bean, which will be mentioned later when injecting the attribute value into the bean
   MutablePropertyValues getPropertyValues();

   // singleton
   boolean isSingleton();

   // prototype
   boolean isPrototype();

   // If the Bean is set to abstract, it cannot be instantiated,
   // It is often used as a parent bean for inheritance. In fact, it is rarely used
   boolean isAbstract();

   int getRole();
   String getDescription();
   String getResourceDescription();
   BeanDefinition getOriginatingBeanDefinition();
}

In fact, this BeanDefinition already contains a lot of information. It doesn't matter what all methods correspond to for the time being. I hope readers can thoroughly understand everything after reading this article.

There are so many interfaces here, but there is no method like getInstance() to obtain the instance of the class we defined. Where is the real instance generated by the class we defined? Don't worry, this will be discussed later.

After having the concept of BeanDefinition, let's move on to the rest of the refreshBeanFactory() method:

customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);

Although there are only two ways, there is still a long way to go...

customizeBeanFactory

customizeBeanFactory(beanFactory) is relatively simple, which is to configure whether BeanDefinition coverage and circular reference are allowed.

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
   if (this.allowBeanDefinitionOverriding != null) {
      // Allow Bean definition overrides
      beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
   }
   if (this.allowCircularReferences != null) {
      // Whether to allow circular dependency between beans
      beanFactory.setAllowCircularReferences(this.allowCircularReferences);
   }
}

Some developers may encounter the problem of overwriting BeanDefinition, that is, the same id or name is used when defining beans in the configuration file. By default, allowBeanDefinitionOverriding property is null. If it is repeated in the same configuration file, an error will be thrown, but if it is not in the same configuration file, overwriting will occur.

Circular references are also well understood: A depends on B, and B depends on A. Or A depends on B, B depends on C, and C depends on A.

By default, Spring allows circular dependency. Of course, if you rely on B in the construction method of A, you can't rely on A in the construction method of B.

How are these two properties configured? I introduced it in the appendix. Especially for coverage, many people want to prohibit Bean coverage, but Spring can override different files by default.

These two attributes will appear in the source code later. Just give the reader an impression. They are not very important.

Load Bean: loadBeanDefinitions

Next is the most important loadBeanDefinitions(beanFactory) method. This method will load each Bean according to the configuration, and then put it into BeanFactory.

The operation of reading configuration is in XmlBeanDefinitionReader, which is responsible for loading configuration and parsing.

// AbstractXmlApplicationContext.java 80

/** We can see that this method will load each Bean through an XmlBeanDefinitionReader instance.*/
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
   // Instantiate an XmlBeanDefinitionReader for this BeanFactory
   XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);

   // Configure the bean definition reader with this context's
   // resource loading environment.
   beanDefinitionReader.setEnvironment(this.getEnvironment());
   beanDefinitionReader.setResourceLoader(this);
   beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));

   // Initialize BeanDefinitionReader. In fact, this is provided for subclass override,
   // I saw that there is no class override method. Let's take it as unimportant
   initBeanDefinitionReader(beanDefinitionReader);
   // Here's the point. Go on
   loadBeanDefinitions(beanDefinitionReader);
}

Now it is still in this class. Next, use the Reader just initialized to load the xml configuration. This code can be skipped by readers, which is not very important. In other words, readers can easily skip the following code block.

// AbstractXmlApplicationContext.java 120

protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
   Resource[] configResources = getConfigResources();
   if (configResources != null) {
      // Look down
      reader.loadBeanDefinitions(configResources);
   }
   String[] configLocations = getConfigLocations();
   if (configLocations != null) {
      // 2
      reader.loadBeanDefinitions(configLocations);
   }
}

// Although there are two branches above, the second branch will soon be converted to a Resource through the resolution path, and will come here in the future
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
   Assert.notNull(resources, "Resource array must not be null");
   int counter = 0;
   // Note that this is a for loop, that is, each file is a resource
   for (Resource resource : resources) {
      // Keep looking down
      counter += loadBeanDefinitions(resource);
   }
   // Finally, counter is returned, indicating how many beandefinitions are loaded in total
   return counter;
}

// XmlBeanDefinitionReader 303
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
   return loadBeanDefinitions(new EncodedResource(resource));
}

// XmlBeanDefinitionReader 314
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
   Assert.notNull(encodedResource, "EncodedResource must not be null");
   if (logger.isInfoEnabled()) {
      logger.info("Loading XML bean definitions from " + encodedResource.getResource());
   }
   // A ThreadLocal is used to store configuration file resources
   Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
   if (currentResources == null) {
      currentResources = new HashSet<EncodedResource>(4);
      this.resourcesCurrentlyBeingLoaded.set(currentResources);
   }
   if (!currentResources.add(encodedResource)) {
      throw new BeanDefinitionStoreException(
            "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
   }
   try {
      InputStream inputStream = encodedResource.getResource().getInputStream();
      try {
         InputSource inputSource = new InputSource(inputStream);
         if (encodedResource.getEncoding() != null) {
            inputSource.setEncoding(encodedResource.getEncoding());
         }
         // The core part is here. Look down
         return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
      }
      finally {
         inputStream.close();
      }
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException(
            "IOException parsing XML document from " + encodedResource.getResource(), ex);
   }
   finally {
      currentResources.remove(encodedResource);
      if (currentResources.isEmpty()) {
         this.resourcesCurrentlyBeingLoaded.remove();
      }
   }
}

// Also in this file, line 388
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
      throws BeanDefinitionStoreException {
   try {
      // Don't look at it here. Convert the xml file into a Document object
      Document doc = doLoadDocument(inputSource, resource);
      // continue
      return registerBeanDefinitions(doc, resource);
   }
   catch (...
}
// Also in this file, line 505
// Return value: returns the number of beans loaded from the current configuration file
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
   BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
   int countBefore = getRegistry().getBeanDefinitionCount();
   // here
   documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
   return getRegistry().getBeanDefinitionCount() - countBefore;
}
// DefaultBeanDefinitionDocumentReader 90
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
   this.readerContext = readerContext;
   logger.debug("Loading bean definitions");
   Element root = doc.getDocumentElement();
   // Parse the file from the xml root node
   doRegisterBeanDefinitions(root);
}         

After a long link, a configuration file is finally converted into a DOM tree. Note that this refers to one of the configuration files, not all of them. Readers can see that there is a for loop on it. Start parsing from the root node:

doRegisterBeanDefinitions:
// DefaultBeanDefinitionDocumentReader 116
protected void doRegisterBeanDefinitions(Element root) {
   // We can see from the name that BeanDefinitionParserDelegate must be an important class, which is responsible for parsing Bean definitions,
   // Why define a parent here? I'll see later. It's a recursive problem,
   // Because < beans / > can be defined internally, the root of this method is not necessarily the root node of xml, but also the embedded < beans / > node. From the perspective of source code analysis, we should just act as the root node
   BeanDefinitionParserDelegate parent = this.delegate;
   this.delegate = createDelegate(getReaderContext(), root, parent);

   if (this.delegate.isDefaultNamespace(root)) {
      // This is about the root node < beans Whether the profile in profile = "dev" / > is required by the current environment,
      // If the profile configured in the current environment does not contain this profile, it will be return ed directly. This < beans / > will not be parsed
      // If you are not familiar with what a profile is or how to configure a profile, please move to the appendix area
      String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
      if (StringUtils.hasText(profileSpec)) {
         String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
               profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
         if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
            if (logger.isInfoEnabled()) {
               logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
                     "] not matching: " + getReaderContext().getResource());
            }
            return;
         }
      }
   }

   preProcessXml(root); // hook
   // Look down
   parseBeanDefinitions(root, this.delegate);
   postProcessXml(root); // hook

   this.delegate = parent;
}

preProcessXml(root) and postProcessXml(root) are hook methods for subclasses. Since they are not used and are not our focus, we skip them directly.

This involves the issue of profile. For readers who don't understand it, I have made a simple explanation of profile in the appendix, which readers can refer to.

Next, look at the core parsing method parsebean definitions (root, this. Delegate):

// The default namespace involves four tags < import / >, < alias / >, < bean / > and < beans / >,
// Others belong to custom
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
   if (delegate.isDefaultNamespace(root)) {
      NodeList nl = root.getChildNodes();
      for (int i = 0; i < nl.getLength(); i++) {
         Node node = nl.item(i);
         if (node instanceof Element) {
            Element ele = (Element) node;
            if (delegate.isDefaultNamespace(ele)) {
               // Parse several elements under the default namespace
               parseDefaultElement(ele, delegate);
            }
            else {
               // Resolve elements of other namespace s
               delegate.parseCustomElement(ele);
            }
         }
      }
   }
   else {
      delegate.parseCustomElement(root);
   }
}

From the above code, we can see that for each configuration, enter parseDefaultElement(ele, delegate); And delegate parseCustomElement(ele); These two branches.

parseDefaultElement(ele, delegate) means that the resolved nodes are < import / >, < alias / >, < bean / >, < beans / >.

The four tags here are default because they are defined in this namespace:

http://www.springframework.org/schema/beans

It's time for beginners to popularize science. Readers who are not familiar with namespace please see the xml posted below. The second line here is xmlns.

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns="http://www.springframework.org/schema/beans"
    xsi:schemaLocation="
         http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd"
    default-autowire="byName">

For other tags, go to delegate Parsecustomelement (element) is the branch. For example, we often use < MVC / >, < task / >, < context / >, < AOP / >, etc.

These are extensions. If the above "non default" tags need to be used, the corresponding namespace and. xsd file paths should also be introduced in the above xml header, as shown below. At the same time, corresponding parser s should be provided in the code to parse, such as MvcNamespaceHandler, TaskNamespaceHandler, ContextNamespaceHandler, AopNamespaceHandler, etc.

If readers want to analyze the implementation principle of < context: Property placeholder location = "classpath: XX. Properties" / /, they should find the answer in ContextNamespaceHandler.

<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xmlns="http://www.springframework.org/schema/beans"
   xmlns:context="http://www.springframework.org/schema/context"
   xmlns:mvc="http://www.springframework.org/schema/mvc"
   xsi:schemaLocation="
        http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/mvc   
        http://www.springframework.org/schema/mvc/spring-mvc.xsd  
    "
   default-autowire="byName">

Similarly, if you encounter a < Dubbo / > tag in the future, you should search whether there is a dubbnamespacehandler processing class.

Come back and see how to handle the default tag:

private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
   if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
      // Process < import / > tags
      importBeanDefinitionResource(ele);
   }
   else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
      // Processing < alias / > tag definitions
      // <alias name="fromName" alias="toName"/>
      processAliasRegistration(ele);
   }
   else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
      // Handling < bean / > tag definitions is our focus
      processBeanDefinition(ele, delegate);
   }
   else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
      // If you encounter nested < beans / > tags, recursion is required
      doRegisterBeanDefinitions(ele);
   }
}

If every label says, I don't spit blood, you'll spit blood. Let's pick our focus < bean / > tag and say it.

processBeanDefinition resolves bean tags

The following is the < bean / > tag parsed by processBeanDefinition:

// DefaultBeanDefinitionDocumentReader 298

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // Extract the information from the < bean / > node and package it into a BeanDefinitionHolder. See the details below
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
  
   // Don't look at the following lines first. Skip first, skip first, skip first. I'll continue later
  
   if (bdHolder != null) {
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // Register the final decorated instance.
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }
      // Send registration event.
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

Before moving on to how to parse, let's take a look at the attributes that can be defined in the < bean / > tag:

Property
classFully qualified name of class
nameid and name can be specified (separated by commas, semicolons and spaces)
scopeScope
constructor argumentsSpecify construction parameters
propertiesSet the value of the property
autowiring modeNo (default), byName, byType, constructor
lazy-initialization modeWhether to load lazily (if it is dependent on non lazily loaded bean s, it cannot be loaded lazily)
initialization methodThis method will be called after the bean property is set
destruction methodCallback method after bean destruction

I think everyone is very familiar with the contents in the above table. If you are not familiar with it, you don't know enough about Spring configuration.

Simply put, it's like this:

<bean id="exampleBean" name="name1, name2, name3" class="com.javadoop.ExampleBean"
      scope="singleton" lazy-init="true" init-method="init" destroy-method="cleanup">
  
    <!-- Construction parameters can be specified in the following three forms -->
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg name="years" value="7500000"/>
    <constructor-arg index="0" value="7500000"/>
  
    <!-- property Several cases of -->
    <property name="beanOne">
        <ref bean="anotherExampleBean"/>
    </property>
    <property name="beanTwo" ref="yetAnotherBean"/>
    <property name="integerProperty" value="1"/>
</bean>

Of course, in addition to the above examples, there are also factory bean, factory method, < lock method / >, < replaced method / >, < method / >, < qualifier / >. Are you familiar with them? Check your understanding of beans in Spring.

With the above knowledge, we will continue to look at how to parse bean elements and how to convert them to BeanDefinitionHolder.

// BeanDefinitionParserDelegate 428

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
    return parseBeanDefinitionElement(ele, null);
}

public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
   String id = ele.getAttribute(ID_ATTRIBUTE);
   String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

   List<String> aliases = new ArrayList<String>();
      
   // Divide the definition of name attribute according to "comma, semicolon and space" to form an alias list array,
   // Of course, if you do not define the name attribute, it will be empty
   // In the appendix, I briefly introduce the configuration of id and name. You can have a look. It takes 20 seconds
   if (StringUtils.hasLength(nameAttr)) {
      String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
      aliases.addAll(Arrays.asList(nameArr));
   }

   String beanName = id;
   // If no id is specified, the first name in the alias list is used as the beanName
   if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
      beanName = aliases.remove(0);
      if (logger.isDebugEnabled()) {
         logger.debug("No XML 'id' specified - using '" + beanName +
               "' as bean name and " + aliases + " as aliases");
      }
   }

   if (containingBean == null) {
      checkNameUniqueness(beanName, aliases, ele);
   }
  
   // According to < bean... ></ Create a BeanDefinition according to the configuration in bean >, and then set all the information in the configuration to the instance,
   // The details will be described later. After the following line is completed, a BeanDefinition instance will come out.
   AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
   
   // Here, even if the parsing of the entire < bean / > tag is completed, a BeanDefinition is formed.
   if (beanDefinition != null) {
      // If neither id nor name is set, the beanName will be null at this time. Enter the following code to generate
      // If the reader is not interested, I don't think we need to care about this code. For the source code analysis of this article, these things are not important
      if (!StringUtils.hasText(beanName)) {
         try {
            if (containingBean != null) {// According to our thinking, the containingBean here is null
               beanName = BeanDefinitionReaderUtils.generateBeanName(
                     beanDefinition, this.readerContext.getRegistry(), true);
            }
            else {
               // If we do not define id and name, the example in our introduction:
               //   1. beanName: com javadoop. example. MessageServiceImpl#0
               //   2. beanClassName: com javadoop. example. MessageServiceImpl
              
               beanName = this.readerContext.generateBeanName(beanDefinition);
               
               String beanClassName = beanDefinition.getBeanClassName();
               if (beanClassName != null &&
                     beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
                     !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
                  // Set beanClassName as the alias of the Bean
                  aliases.add(beanClassName);
               }
            }
            if (logger.isDebugEnabled()) {
               logger.debug("Neither XML 'id' nor 'name' specified - " +
                     "using generated bean name [" + beanName + "]");
            }
         }
         catch (Exception ex) {
            error(ex.getMessage(), ele);
            return null;
         }
      }
      String[] aliasesArray = StringUtils.toStringArray(aliases);
      // Return BeanDefinitionHolder
      return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
   }

   return null;
}

Then, let's look at how to create a BeanDefinition instance according to the configuration:

public AbstractBeanDefinition parseBeanDefinitionElement(
      Element ele, String beanName, BeanDefinition containingBean) {

   this.parseState.push(new BeanEntry(beanName));

   String className = null;
   if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
      className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
   }

   try {
      String parent = null;
      if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
         parent = ele.getAttribute(PARENT_ATTRIBUTE);
      }
      // Create a BeanDefinition and set the class information. It's very simple. Don't post the code
      AbstractBeanDefinition bd = createBeanDefinition(className, parent);

      // Set a bunch of properties of BeanDefinition, which are defined in AbstractBeanDefinition
      parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
      bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
    
      /**
       * The following pile is parsing < bean ></ Bean > internal child elements,
       * The parsed information is put into the bd attribute
       */
     
      // Parse < meta / >
      parseMetaElements(ele, bd);
      // Parse < lookup method / >
      parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
      // Parse < replaced method / >
      parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
    // Parse < constructor Arg / >
      parseConstructorArgElements(ele, bd);
      // Resolve < property / >
      parsePropertyElements(ele, bd);
      // Parse < qualifier / >
      parseQualifierElements(ele, bd);

      bd.setResource(this.readerContext.getResource());
      bd.setSource(extractSource(ele));

      return bd;
   }
   catch (ClassNotFoundException ex) {
      error("Bean class [" + className + "] not found", ele, ex);
   }
   catch (NoClassDefFoundError err) {
      error("Class that bean class [" + className + "] depends on not found", ele, err);
   }
   catch (Throwable ex) {
      error("Unexpected failure during bean definition parsing", ele, ex);
   }
   finally {
      this.parseState.pop();
   }

   return null;
}

Here, we have finished creating a BeanDefinitionHolder instance according to the < bean / > configuration. Note that it's a.

Let's go back to the entry method for parsing < bean / >:

protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
   // Convert the < bean / > node into a BeanDefinitionHolder, which is the pile mentioned above
   BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
   if (bdHolder != null) {
      // If there are custom attributes, perform corresponding parsing and ignore them first
      bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
      try {
         // Let's call this step registering beans
         BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
      }
      catch (BeanDefinitionStoreException ex) {
         getReaderContext().error("Failed to register bean definition with name '" +
               bdHolder.getBeanName() + "'", ele, ex);
      }
      // After the registration is completed, send the event. This article will not expand on this
      getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
   }
}

Let's take a closer look at this one. We won't talk about this later. Here, an instance of BeanDefinitionHolder has been generated according to a < bean / > tag. In this instance, there is an instance of BeanDefinition and its beanName and aliases. Note that our focus is always on BeanDefinition:

public class BeanDefinitionHolder implements BeanMetadataElement {

  private final BeanDefinition beanDefinition;

  private final String beanName;

  private final String[] aliases;
...

Then we are ready to register the BeanDefinition. Finally, we send the registration event.

Now, let's start talking about registering beans.

Register Bean

// BeanDefinitionReaderUtils 143

public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

   String beanName = definitionHolder.getBeanName();
   // Register this Bean
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // If there are aliases, you should also register them all according to the alias, otherwise you will not find the Bean according to the alias
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         // Alias - > beanname saves their alias information. This is very simple. Just save it with a map,
         // When obtaining, the alias will be converted to beanName first, and then searched
         registry.registerAlias(beanName, alias);
      }
   }
}

Put aside the alias registration. After all, it's very simple. Let's see how to register beans.

// DefaultListableBeanFactory 793

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

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

   if (beanDefinition instanceof AbstractBeanDefinition) {
      try {
         ((AbstractBeanDefinition) beanDefinition).validate();
      }
      catch (BeanDefinitionValidationException ex) {
         throw new BeanDefinitionStoreException(...);
      }
   }

   // old?  Remember the "allow bean overrides" configuration? allowBeanDefinitionOverriding
   BeanDefinition oldBeanDefinition;
  
   // After that, you will see that all beans will be put into the Bean definition map after registration
   oldBeanDefinition = this.beanDefinitionMap.get(beanName);
  
   // Handling Bean definitions with duplicate names
   if (oldBeanDefinition != null) {
      if (!isAllowBeanDefinitionOverriding()) {
         // Throw an exception if overwrite is not allowed
         throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription()...
      }
      else if (oldBeanDefinition.getRole() < beanDefinition.getRole()) {
         // log... Overwrite user-defined beans with framework defined beans 
      }
      else if (!beanDefinition.equals(oldBeanDefinition)) {
         // log... Overwrite the old Bean with the new Bean
      }
      else {
         // log... Overwrite the old Bean with the same Bean. This refers to the Bean whose equals method returns true
      }
      // cover
      this.beanDefinitionMap.put(beanName, beanDefinition);
   }
   else {
      // Determine whether other beans have started initialization
      // Note that after the "register Bean" action, the Bean is still not initialized. We will talk about the initialization process later,
      // At the end of the Spring container startup, all singleton beans will be pre initialized
      if (hasBeanCreationStarted()) {
         // Cannot modify startup-time collection elements anymore (for stable iteration)
         synchronized (this.beanDefinitionMap) {
            this.beanDefinitionMap.put(beanName, beanDefinition);
            List<String> updatedDefinitions = new ArrayList<String>(this.beanDefinitionNames.size() + 1);
            updatedDefinitions.addAll(this.beanDefinitionNames);
            updatedDefinitions.add(beanName);
            this.beanDefinitionNames = updatedDefinitions;
            if (this.manualSingletonNames.contains(beanName)) {
               Set<String> updatedSingletons = new LinkedHashSet<String>(this.manualSingletonNames);
               updatedSingletons.remove(beanName);
               this.manualSingletonNames = updatedSingletons;
            }
         }
      }
      else {
         // The most normal thing is to go into this branch.
        
         // Put the BeanDefinition into this map, which saves all beandefinitions
         this.beanDefinitionMap.put(beanName, beanDefinition);
         // This is an ArrayList, so the name of each registered bean will be saved in the order of bean configuration
         this.beanDefinitionNames.add(beanName);
         // This is a LinkedHashSet, which represents a manually registered singleton bean,
         // Note that this is the remove method. Of course, the Bean here is not registered manually
         // Manual refers to bean s registered by calling the following methods:
         //     registerSingleton(String beanName, Object singletonObject)
         // This is not the point. The explanation is just to avoid confusion. Spring will "manually" register some beans later,
         // For beans such as "environment" and "systemProperties", we can also register beans in the container at runtime
         this.manualSingletonNames.remove(beanName);
      }
      // This is not important. It will be used during pre initialization. Don't worry about it.
      this.frozenBeanDefinitionNames = null;
   }

   if (oldBeanDefinition != null || containsSingleton(beanName)) {
      resetBeanDefinition(beanName);
   }
}

To sum up, the Bean container has been initialized here, and the < Bean / > configuration has been transformed into beandefinitions accordingly. Then, each BeanDefinition has been registered to the registry, and the registration event has been sent.

---------Split line---------

Here is a watershed. The contents in front are relatively simple, but they should also be cumbersome. We should clearly know what we have done in front.

After Bean container instantiation is completed

At this point, let's go back to the refresh() method, and I'll paste the code again to see where we're talking. Yes, we have just finished the obtainFreshBeanFactory() method.

Considering the length, we begin to greatly reduce the parts that do not need to be introduced in detail. Just look at the comments in the code below.

@Override
public void refresh() throws BeansException, IllegalStateException {
   // Give me a lock. Otherwise, refresh() is not over yet. If you start or destroy the container again, it will be a mess
   synchronized (this.startupShutdownMonitor) {

      // Prepare the work, record the start time of the container, mark the "started" status, and process the placeholder in the configuration file
      prepareRefresh();
     
      // This is a key step. After this step is completed, the configuration file will be parsed into Bean definitions and registered in BeanFactory,
      // Of course, the Bean mentioned here has not been initialized, but the configuration information has been extracted,
      // Registration only saves these information to the registration center (in the final analysis, the core is a beanname - > beandefinition map)
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

      // Set the class loader of BeanFactory, add several beanpostprocessors, and manually register several special beans
      // We'll talk about this later
      prepareBeanFactory(beanFactory);

      try {
         // [here you need to know the knowledge of beanfactoryprocessor. If a Bean implements this interface,
         // After the container is initialized, Spring will be responsible for calling the postProcessBeanFactory method inside.]
        
         // Here is the extension point for subclasses. When you get here, all beans have been loaded and registered, but they have not been initialized
         // For specific subclasses, you can add some special beanfactoryprocessor implementation classes or do something at this step
         postProcessBeanFactory(beanFactory);
         // Call the postProcessBeanFactory(factory) callback method of each implementation class of beanfactoryprocessor
         invokeBeanFactoryPostProcessors(beanFactory);          
         
          

         // Register the implementation class of BeanPostProcessor. Pay attention to the difference between BeanPostProcessor and beanfactoryprocessor
         // This interface has two methods: postProcessBeforeInitialization and postProcessAfterInitialization
         // The two methods are executed before and after Bean initialization. This is just registration, and then you will see the timing of callback of these two methods
         registerBeanPostProcessors(beanFactory);

         // Initialize the MessageSource of the current ApplicationContext. Internationalization will not be expanded here, otherwise it will be endless
         initMessageSource();

         // Initialize the event broadcaster of the current ApplicationContext, which will not be expanded here
         initApplicationEventMulticaster();

         // You can know from the method name that a typical template method (hook method) is not expanded
         // Specific subclasses can initialize some special beans here (before initializing singleton beans)
         onRefresh();

         // Register the event listener. The listener needs to implement the ApplicationListener interface. That's not our point, too
         registerListeners();

         // Focus, focus, focus
         // Initialize all singleton beans
         //(except for lazy init)
         finishBeanFactoryInitialization(beanFactory);

         // Finally, broadcast the event. The ApplicationContext is initialized and does not expand
         finishRefresh();
      }

      catch (BeansException ex) {
         if (logger.isWarnEnabled()) {
            logger.warn("Exception encountered during context initialization - " +
                  "cancelling refresh attempt: " + ex);
         }

         // Destroy already created singletons to avoid dangling resources.
         // Destroy the initialized singleton Beans to prevent some Beans from occupying resources all the time
         destroyBeans();

         // Reset 'active' flag.
         cancelRefresh(ex);

         // Throw the anomaly out
         throw ex;
      }

      finally {
         // Reset common introspection caches in Spring's core, since we
         // might not ever need metadata for singleton beans anymore...
         resetCommonCaches();
      }
   }
}

Prepare Bean container: prepareBeanFactory

As we said before, Spring will "manually" register some special beans after registering all our xml configured beans.

Here is a brief introduction to the prepareBeanFactory(factory) method:

/**
 * Configure the factory's standard context characteristics,
 * such as the context's ClassLoader and post-processors.
 * @param beanFactory the BeanFactory to configure
 */
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
   // Set the class loader of BeanFactory. We know that BeanFactory needs to load classes, so it needs class loader,
   // This is set as the class loader that loads the current ApplicationContext class
   beanFactory.setBeanClassLoader(getClassLoader());
    
   // Set BeanExpressionResolver
   beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
   // 
   beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));

   // Add a beanpost processor, which is relatively simple:
   // During initialization of beans that implement Aware interface, this processor is responsible for callback,
   // We often use this. For example, we will implement ApplicationContextAware to obtain ApplicationContext
   // Note: it does not just call back ApplicationContextAware,
   //   It will also be responsible for callback of EnvironmentAware, ResourceLoaderAware, etc. just look at the source code
   beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
  
   // The following lines mean that if a bean depends on the implementation classes of the following interfaces, ignore them during automatic assembly,
   // Spring handles these dependencies in other ways.
   beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
   beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
   beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
   beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
   beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
   beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

   /**
    * The following lines assign values to special beans. If a bean depends on the following, the corresponding values will be injected here,
    * As we said before, "the current ApplicationContext holds a BeanFactory", the first line is explained here.
    * ApplicationContext It also inherits ResourceLoader, ApplicationEventPublisher and MessageSource
    * Therefore, these dependencies can be assigned this. Note that this is an ApplicationContext
    * Then why don't you see the assignment for MessageSource here? That's because MessageSource is registered as an ordinary bean
    */
   beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
   beanFactory.registerResolvableDependency(ResourceLoader.class, this);
   beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
   beanFactory.registerResolvableDependency(ApplicationContext.class, this);

   // The BeanPostProcessor is also very simple. After the bean is instantiated, if it is a subclass of ApplicationListener,
   // Adding it to the listener list can be understood as: registering an event listener
   beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));

   // This involves a special bean called loadTimeWeaver, which is not our focus. Ignore it
   // tips: ltw is the concept of AspectJ, which refers to weaving during runtime, which is different from Spring AOP,
   //    For interested readers, please refer to another article I wrote about AspectJ https://www.javadoop.com/post/aspectj
   if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      // Set a temporary ClassLoader for type matching.
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
   }

   /**
    * From the following lines of code, we can know that Spring is often "smart" because it will help us register some useful bean s by default,
    * We can also choose to override
    */
  
   // If the bean "environment" is not defined, Spring will "manually" register one
   if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
      beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
   }
   // If the bean "systemProperties" is not defined, Spring will "manually" register one
   if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
      beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
   }
   // If the bean "systemEnvironment" is not defined, Spring will "manually" register one
   if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
      beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
   }
}

In the above code, Spring handles some special bean s. It doesn't matter if the reader can't digest them for the time being. Look down slowly.

Initialize all singleton beans

Of course, our focus is finish bean factory initialization (bean factory); This giant will be responsible for initializing all singleton beans.

Note that in the following description, I will use initialization or pre initialization to represent this stage. Spring will complete the instantiation of all singleton beans in this stage.

Let's summarize. So far, it should be said that the BeanFactory has been created, and all beans that implement the BeanFactory postprocessor interface have been initialized, and the postProcessBeanFactory(factory) method has been callback executed. Moreover, Spring has "manually" registered some special beans, such as environment, system properties, etc.

The rest is to initialize singleton beans. We know that they are singletons. If lazy loading is not set, Spring will initialize all singleton beans next.

// AbstractApplicationContext.java 834

// Initialize the remaining singleton beans
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {

   // First, initialize the Bean named ConversionService. In the spirit of sending Buddha to the west, I briefly introduce ConversionService in the appendix because it is too practical
   // What? Look at the code. There is no initialization Bean here!
   // Note that the initialization action is wrapped in beanfactory getBean(...)  Let's not talk about the details here, let's look down first
   if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
         beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
      beanFactory.setConversionService(
            beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
   }

   // Register a default embedded value resolver if no bean post-processor
   // (such as a PropertyPlaceholderConfigurer bean) registered any before:
   // at this point, primarily for resolution in annotation attribute values.
   if (!beanFactory.hasEmbeddedValueResolver()) {
      beanFactory.addEmbeddedValueResolver(new StringValueResolver() {
         @Override
         public String resolveStringValue(String strVal) {
            return getEnvironment().resolvePlaceholders(strVal);
         }
      });
   }

   // Initialize the Bean of loadtimeweaveaware type first
   // As I said before, this is related to AspectJ. Don't worry about skipping
   String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
   for (String weaverAwareName : weaverAwareNames) {
      getBean(weaverAwareName);
   }

   // Stop using the temporary ClassLoader for type matching.
   beanFactory.setTempClassLoader(null);

   // There is no other purpose, because by this step, Spring has started pre initializing singleton beans,
   // I certainly don't want bean definition parsing, loading and registration at this time.
   beanFactory.freezeConfiguration();

   // Start initialization
   beanFactory.preInstantiateSingletons();
}

From the last line above, we will return to the DefaultListableBeanFactory class, which should be familiar to everyone.

preInstantiateSingletons

// DefaultListableBeanFactory 728

@Override
public void preInstantiateSingletons() throws BeansException {
   if (this.logger.isDebugEnabled()) {
      this.logger.debug("Pre-instantiating singletons in " + this);
   }
   // this.beanDefinitionNames saves all beanNames
   List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);

   // The following loop triggers the initialization of all non lazy loaded singleton beans
   for (String beanName : beanNames) {
     
      // Merge the configuration in the parent Bean. Note that the parent in < Bean id = "" class = "" parent = "" / > is not used much,
      // Considering that this may affect your understanding, I explained "Bean inheritance" in the appendix. If you don't understand it, please go to the appendix
      RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
     
      // Non abstract, non lazy loaded singletons. If 'abstract = true' is configured, initialization is not required
      if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
         // Processing factorybeans (if readers are not familiar with factorybeans, please move to the appendix area to understand)
         if (isFactoryBean(beanName)) {
            // For FactoryBean, add '&' before beanName. Call getBean again. Don't worry about the getBean method
            final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
            // Judge whether the current FactoryBean is the implementation of SmartFactoryBean. Ignore it here and skip it directly
            boolean isEagerInit;
            if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
               isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
                  @Override
                  public Boolean run() {
                     return ((SmartFactoryBean<?>) factory).isEagerInit();
                  }
               }, getAccessControlContext());
            }
            else {
               isEagerInit = (factory instanceof SmartFactoryBean &&
                     ((SmartFactoryBean<?>) factory).isEagerInit());
            }
            if (isEagerInit) {
               
               getBean(beanName);
            }
         }
         else {
            // For an ordinary Bean, you can initialize it by calling the getBean(beanName) method
            getBean(beanName);
         }
      }
   }

   // This shows that all non lazy loaded singleton beans have been initialized
   // If the bean we defined implements the smartinitializingsingsingleton interface, we will get a callback here and ignore it
   for (String beanName : beanNames) {
      Object singletonInstance = getSingleton(beanName);
      if (singletonInstance instanceof SmartInitializingSingleton) {
         final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
         if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(new PrivilegedAction<Object>() {
               @Override
               public Object run() {
                  smartSingleton.afterSingletonsInstantiated();
                  return null;
               }
            }, getAccessControlContext());
         }
         else {
            smartSingleton.afterSingletonsInstantiated();
         }
      }
   }
}

Next, we will enter the getBean(beanName) method, which is often used to obtain a Bean from BeanFactory, and the initialization process is encapsulated in this method.

getBean

Before moving on, readers should have the knowledge of FactoryBean. If they are not familiar with FactoryBean, please move to the appendix to understand FactoryBean.

// AbstractBeanFactory 196

@Override
public Object getBean(String name) throws BeansException {
   return doGetBean(name, null, null, false);
}

// We are analyzing the process of initializing beans, but the get Bean method is often used to obtain beans from containers. Pay attention to switching ideas,
// After initialization, it will be returned directly from the container. Otherwise, it will be initialized first and then returned
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
      final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
      throws BeansException {
   // Get an "orthodox" beanName and handle two situations. One is the factorybean (with '&' in front),
   // One is the alias problem, because this method is used to get beans. If you pass in an alias, it is entirely possible
   final String beanName = transformedBeanName(name);
  
   // Note that this is followed by the return value
   Object bean; 

   // Check if it has been created
   Object sharedInstance = getSingleton(beanName);
  
   // Let's talk about args here, although it doesn't seem important at all. When we came in all the way, we were all getBean(beanName),
   // Therefore, the args parameter is actually null, but if args is not empty, it means that the caller does not want to obtain the Bean, but creates the Bean
   if (sharedInstance != null && args == null) {
      if (logger.isDebugEnabled()) {
         if (isSingletonCurrentlyInCreation(beanName)) {
            logger.debug("...");
         }
         else {
            logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
         }
      }
      // The following method: if it is an ordinary Bean, return sharedInstance directly,
      // If it is a FactoryBean, return the instance object it creates
      // (for FactoryBean knowledge, if the reader is not clear, please move to the appendix)
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
   }

   else {
      if (isPrototypeCurrentlyInCreation(beanName)) {
         // If you have created a bean of the prototype type of this beanName, throw an exception,
         // Often because of falling into a circular reference
         throw new BeanCurrentlyInCreationException(beanName);
      }

      // Check whether the BeanDefinition exists in the container
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
         // If the BeanDefinition does not exist in the current container, try whether it exists in the parent container
         String nameToLookup = originalBeanName(name);
         if (args != null) {
            // Returns the query result of the parent container
            return (T) parentBeanFactory.getBean(nameToLookup, args);
         }
         else {
            // No args -> delegate to standard getBean method.
            return parentBeanFactory.getBean(nameToLookup, requiredType);
         }
      }

      if (!typeCheckOnly) {
         // typeCheckOnly is false. Put the current beanName into an alreadyCreated Set collection.
         markBeanAsCreated(beanName);
      }

      /*
       * To sum up:
       * If you are here, you should be ready to create a Bean. For singleton beans, this Bean has not been created in the container;
       * For a prototype Bean, it is necessary to create a new Bean.
       */
      try {
         final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
         checkMergedBeanDefinition(mbd, beanName, args);

         // Initialize all dependent beans first, which is well understood.
         // Note that dependencies here refer to dependencies defined in dependencies on
         String[] dependsOn = mbd.getDependsOn();
         if (dependsOn != null) {
            for (String dep : dependsOn) {
               // Check whether there is circular dependency. The circular dependency here is different from the circular dependency we mentioned earlier. It is definitely not allowed here, otherwise it will be chaotic. Readers will know it after thinking about it
               if (isDependent(beanName, dep)) {
                  throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                        "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
               }
               // Register dependencies
               registerDependentBean(dep, beanName);
               // Initialize dependent items first
               getBean(dep);
            }
         }

         // If it is a singleton scope, create an instance of singleton
         if (mbd.isSingleton()) {
            sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
               @Override
               public Object getObject() throws BeansException {
                  try {
                     // Execute to create Bean, details will be explained later
                     return createBean(beanName, mbd, args);
                  }
                  catch (BeansException ex) {
                     destroySingleton(beanName);
                     throw ex;
                  }
               }
            });
            bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
         }

         // If it is a prototype scope, create an instance of prototype
         else if (mbd.isPrototype()) {
            // It's a prototype -> create a new instance.
            Object prototypeInstance = null;
            try {
               beforePrototypeCreation(beanName);
               // Execute create Bean
               prototypeInstance = createBean(beanName, mbd, args);
            }
            finally {
               afterPrototypeCreation(beanName);
            }
            bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
         }

         // If it is not singleton or prototype, it needs to be delegated to the corresponding implementation class
         else {
            String scopeName = mbd.getScope();
            final 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, new ObjectFactory<Object>() {
                  @Override
                  public Object getObject() throws BeansException {
                     beforePrototypeCreation(beanName);
                     try {
                        // Execute create Bean
                        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;
      }
   }

   // Finally, check whether the type is correct, throw an exception if it is wrong, and return if it is right
   if (requiredType != null && bean != null && !requiredType.isInstance(bean)) {
      try {
         return getTypeConverter().convertIfNecessary(bean, requiredType);
      }
      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;
}

As you can guess, the next step is to analyze the createBean method:

protected abstract Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException;

The third parameter args array represents the parameters required to create an instance, which is not the parameters used for the construction method or the parameters of the factory Bean. However, it should be noted that args is null in our initialization phase.

This time we are going to a new class AbstractAutowireCapableBeanFactory. Look at the class name, AutowireCapable? Does the class name also explain some problems.

The @ Autowired annotation is mainly used to inject attribute values for the following scenarios:

public class MessageServiceImpl implements MessageService {
    @Autowired
    private UserService userService;
  
    public String getMessage() {
        return userService.getMessage();
    }
}
<bean id="messageService" class="com.javadoop.example.MessageServiceImpl" />

The above configuration method is a combination of xml and annotation. Spring will handle this situation.

Well, readers need to know that. Just move on.

// AbstractAutowireCapableBeanFactory 447

/**
 * Central method of this class: creates a bean instance,
 * populates the bean instance, applies post-processors, etc.
 * @see #doCreateBean
 */
@Override
protected Object createBean(String beanName, RootBeanDefinition mbd, Object[] args) throws BeanCreationException {
   if (logger.isDebugEnabled()) {
      logger.debug("Creating instance of bean '" + beanName + "'");
   }
   RootBeanDefinition mbdToUse = mbd;

   // Ensure that the Class in the BeanDefinition is loaded
   Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
   if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
      mbdToUse = new RootBeanDefinition(mbd);
      mbdToUse.setBeanClass(resolvedClass);
   }

   // Preparing method overrides involves another concept: MethodOverrides, which comes from < lookup method / > in the bean definition 
   // And < replaced method / >. If the reader is interested, go back to the bean parsing place to see the parsing of these two tags.
   // I also introduced the relevant knowledge points of these two labels in the appendix. Readers can take a step-by-step look
   try {
      mbdToUse.prepareMethodOverrides();
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
            beanName, "Validation of method overrides failed", ex);
   }

   try {
      // Let the instantiaawarebeanpostprocessor have the opportunity to return to the agent at this step,
      // It is explained in the article "Spring AOP source code analysis", which is skipped here
      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);
   }
   // Play, creating bean s
   Object beanInstance = doCreateBean(beanName, mbdToUse, args);
   if (logger.isDebugEnabled()) {
      logger.debug("Finished creating instance of bean '" + beanName + "'");
   }
   return beanInstance;
}

Create Bean

Let's continue to look at the doCreateBean method:

/**
 * Actually create the specified bean. Pre-creation processing has already happened
 * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks.
 * <p>Differentiates between default bean instantiation, use of a
 * factory method, and autowiring a constructor.
 * @param beanName the name of the bean
 * @param mbd the merged bean definition for the bean
 * @param args explicit arguments to use for constructor or factory method invocation
 * @return a new instance of the bean
 * @throws BeanCreationException if the bean could not be created
 * @see #instantiateBean
 * @see #instantiateUsingFactoryMethod
 * @see #autowireConstructor
 */
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args)
      throws BeanCreationException {

   // Instantiate the bean.
   BeanWrapper instanceWrapper = null;
   if (mbd.isSingleton()) {
      instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
   }
   if (instanceWrapper == null) {
      // The description is not a FactoryBean. Instantiating a Bean here is very important. We'll talk about the details later
      instanceWrapper = createBeanInstance(beanName, mbd, args);
   }
   // This is the instance of the class we defined in the bean. In many places, I directly describe it as "bean instance"
   final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);
   // type
   Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);
   mbd.resolvedTargetType = beanType;

   // It is recommended to skip the interface: MergedBeanDefinitionPostProcessor
   synchronized (mbd.postProcessingLock) {
      if (!mbd.postProcessed) {
         try {
            // MergedBeanDefinitionPostProcessor, I really don't want to expand it. Just skip it. It's rarely used
            applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
         }
         catch (Throwable ex) {
            throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                  "Post-processing of merged bean definition failed", ex);
         }
         mbd.postProcessed = true;
      }
   }

   // Eagerly cache singletons to be able to resolve circular references
   // even when triggered by lifecycle interfaces like BeanFactoryAware.
   // The following code is to solve the problem of circular dependency. I'll analyze the problem of circular dependency later
   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");
      }
      addSingletonFactory(beanName, new ObjectFactory<Object>() {
         @Override
         public Object getObject() throws BeansException {
            return getEarlyBeanReference(beanName, mbd, bean);
         }
      });
   }

   // Initialize the bean instance.
   Object exposedObject = bean;
   try {
      // This step is also very critical. This step is responsible for attribute assembly, because the previous instance is only instantiated and has no set value. Here is the set value
      populateBean(beanName, mbd, instanceWrapper);
      if (exposedObject != null) {
         // Remember the init method? And the InitializingBean interface? And the BeanPostProcessor interface?
         // Here are various callbacks after bean initialization
         exposedObject = initializeBean(beanName, exposedObject, mbd);
      }
   }
   catch (Throwable ex) {
      if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
         throw (BeanCreationException) ex;
      }
      else {
         throw new BeanCreationException(
               mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
      }
   }

   if (earlySingletonExposure) {
      // 
      Object earlySingletonReference = getSingleton(beanName, false);
      if (earlySingletonReference != null) {
         if (exposedObject == bean) {
            exposedObject = earlySingletonReference;
         }
         else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
            String[] dependentBeans = getDependentBeans(beanName);
            Set<String> actualDependentBeans = new LinkedHashSet<String>(dependentBeans.length);
            for (String dependentBean : dependentBeans) {
               if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
                  actualDependentBeans.add(dependentBean);
               }
            }
            if (!actualDependentBeans.isEmpty()) {
               throw new BeanCurrentlyInCreationException(beanName,
                     "Bean with name '" + beanName + "' has been injected into other beans [" +
                     StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
                     "] in its raw version as part of a circular reference, but has eventually been " +
                     "wrapped. This means that said other beans do not use the final version of the " +
                     "bean. This is often the result of over-eager type matching - consider using " +
                     "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
            }
         }
      }
   }

   // Register bean as disposable.
   try {
      registerDisposableBeanIfNecessary(beanName, bean, mbd);
   }
   catch (BeanDefinitionValidationException ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
   }

   return exposedObject;
}

So far, we have analyzed the doCreateBean method. In general, we have finished the whole initialization process.

Next, let's pick out three details in doCreateBean. One is the createBeanInstance method for creating Bean instances, the other is the populateBean method for dependency injection, and the other is the callback method initializeBean.

Note that the next three methods are extremely complex. I'll stop at many places. Interested readers can look inside by themselves. It's best to write their own code to debug what they don't understand.

Create Bean instance

Let's first look at the createBeanInstance method. It should be noted that if each branch is analyzed, this method must be extremely complex and lengthy. Let's pick the point. The purpose of this method is to instantiate the class we specify.

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
   // Make sure this class is loaded
   Class<?> beanClass = resolveBeanClass(mbd, beanName);

   // Check the access permissions of this class
   if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
            "Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
   }

   if (mbd.getFactoryMethodName() != null)  {
      // The factory method is used for instantiation. Readers who are not familiar with this concept please see the appendix. Note that it is not FactoryBean
      return instantiateUsingFactoryMethod(beanName, mbd, args);
   }

   // If it is not created for the first time, for example, the prototype bean is created for the second time.
   // In this case, we can know from the first creation whether to use parameterless constructor or constructor dependency injection to complete instantiation
   boolean resolved = false;
   boolean autowireNecessary = false;
   if (args == null) {
      synchronized (mbd.constructorArgumentLock) {
         if (mbd.resolvedConstructorOrFactoryMethod != null) {
            resolved = true;
            autowireNecessary = mbd.constructorArgumentsResolved;
         }
      }
   }
   if (resolved) {
      if (autowireNecessary) {
         // Constructor Dependency Injection 
         return autowireConstructor(beanName, mbd, null, null);
      }
      else {
         // non-parameter constructor 
         return instantiateBean(beanName, mbd);
      }
   }

   // Determine whether to use a parametric constructor
   Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
   if (ctors != null ||
         mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
         mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {
      // Constructor Dependency Injection 
      return autowireConstructor(beanName, mbd, ctors, args);
   }

   // Call parameterless constructor
   return instantiateBean(beanName, mbd);
}

Take a simple nonparametric constructor construction example to see:

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
   try {
      Object beanInstance;
      final BeanFactory parent = this;
      if (System.getSecurityManager() != null) {
         beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
               
               return getInstantiationStrategy().instantiate(mbd, beanName, parent);
            }
         }, getAccessControlContext());
      }
      else {
         // instantiation 
         beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
      }
      // Wrap it up and return
      BeanWrapper bw = new BeanWrapperImpl(beanInstance);
      initBeanWrapper(bw);
      return bw;
   }
   catch (Throwable ex) {
      throw new BeanCreationException(
            mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
   }
}

We can see that the key points are:

beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);

The actual instantiation process will be carried out here. Let's go in and have a look:

// SimpleInstantiationStrategy 59

@Override
public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {

   // If there is no method override, use java reflection for instantiation, otherwise use CGLIB,
   // For method override, see the introduction of lookup method and replaced method in the appendix "method injection"
   if (bd.getMethodOverrides().isEmpty()) {
      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(new PrivilegedExceptionAction<Constructor<?>>() {
                     @Override
                     public Constructor<?> run() throws Exception {
                        return clazz.getDeclaredConstructor((Class[]) null);
                     }
                  });
               }
               else {
                  constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
               }
               bd.resolvedConstructorOrFactoryMethod = constructorToUse;
            }
            catch (Throwable ex) {
               throw new BeanInstantiationException(clazz, "No default constructor found", ex);
            }
         }
      }
      // Instantiation using construction method
      return BeanUtils.instantiateClass(constructorToUse);
   }
   else {
      // There is method override, and CGLIB is used to complete instantiation. It needs to rely on CGLIB to generate subclasses, which will not be expanded here.
      // tips: because if CGLIB is not used, the JDK does not provide corresponding instantiation support
      return instantiateWithMethodInjection(bd, beanName, owner);
   }
}

At this point, we're done instantiating. Let's start with how to inject attributes.

bean attribute injection

After reading the createBeanInstance(...) method, let's take a look at the populateBean(...) method, which is responsible for setting attribute values and handling dependencies.

// AbstractAutowireCapableBeanFactory 1203

protected void populateBean(String beanName, RootBeanDefinition mbd, BeanWrapper bw) {
   // All the properties of the bean instance are here
   PropertyValues pvs = mbd.getPropertyValues();

   if (bw == null) {
      if (!pvs.isEmpty()) {
         throw new BeanCreationException(
               mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
      }
      else {
         // Skip property population phase for null instance.
         return;
      }
   }

   // At this step, the bean instantiation is completed (through the factory method or constructor method), but the property setting has not been started,
   // The implementation class of instantiawarebeanpostprocessor can modify the state of beans here,
   // I haven't found any practical use, so let's ignore this one for the time being
   boolean continueWithPropertyPopulation = true;
   if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
         if (bp instanceof InstantiationAwareBeanPostProcessor) {
            InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
            // If false is returned, it means that subsequent property setting and processing by other beanpostprocessors are not required
            if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
               continueWithPropertyPopulation = false;
               break;
            }
         }
      }
   }

   if (!continueWithPropertyPopulation) {
      return;
   }

   if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
         mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
      MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

      // Find all attribute values by name. If it is a bean dependency, initialize the dependent bean first. Record dependencies
      if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
         autowireByName(beanName, mbd, bw, newPvs);
      }

      // Assembly by type. More complicated
      if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
         autowireByType(beanName, mbd, bw, newPvs);
      }

      pvs = newPvs;
   }

   boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
   boolean needsDepCheck = (mbd.getDependencyCheck() != RootBeanDefinition.DEPENDENCY_CHECK_NONE);

   if (hasInstAwareBpps || needsDepCheck) {
      PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
      if (hasInstAwareBpps) {
         for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
               InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
               // There is a very useful BeanPostProcessor here: Autowired annotation BeanPostProcessor
               // The content here is also very rich for setting the dependency using @ Autowired and @ Value annotations. However, this article will not expand. Interested readers should study it by themselves
               pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
               if (pvs == null) {
                  return;
               }
            }
         }
      }
      if (needsDepCheck) {
         checkDependencies(beanName, mbd, filteredPds, pvs);
      }
   }
   // Set the property value of the bean instance
   applyPropertyValues(beanName, mbd, bw, pvs);
}
initializeBean

After the property injection is completed, this step is actually dealing with various callbacks. This code is relatively simple.

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
   if (System.getSecurityManager() != null) {
      AccessController.doPrivileged(new PrivilegedAction<Object>() {
         @Override
         public Object run() {
            invokeAwareMethods(beanName, bean);
            return null;
         }
      }, getAccessControlContext());
   }
   else {
      // If the bean implements the BeanNameAware, BeanClassLoaderAware, or beanfactory aware interfaces, the callback
      invokeAwareMethods(beanName, bean);
   }

   Object wrappedBean = bean;
   if (mbd == null || !mbd.isSynthetic()) {
      // postProcessBeforeInitialization callback for BeanPostProcessor
      wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
   }

   try {
      // Handle the init method defined in the bean,
      // Or, if the bean implements the InitializingBean interface, call the afterpropertieset () 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()) {
      // postProcessAfterInitialization callback for BeanPostProcessor
      wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
   }
   return wrappedBean;
}

Have you found that the two callbacks of the BeanPostProcessor occur here, but the init method is processed in the middle. Is it a little different from the original cognition of the readers?

appendix

id and name

Each Bean has a unique name and 0 or more aliases in the Spring container.

When we get beans from the Spring container, we can use beanName or alias.

beanFactory.getBean("beanName or alias");

In the process of configuring < bean / >, we can configure id and name. We can see what's going on by looking at a few examples.

<bean id="messageService" name="m1, m2, m3" class="com.javadoop.example.MessageServiceImpl">

The result of the above configuration is that beanName is messageService and there are three aliases, m1, m2 and m3 respectively.

<bean name="m1, m2, m3" class="com.javadoop.example.MessageServiceImpl" />

The result of the above configuration is that beanName is m1 and there are 2 aliases, m2 and m3 respectively.

<bean class="com.javadoop.example.MessageServiceImpl">

beanName: com javadoop. example. MessageServiceImpl#0,

1 alias: com javadoop. example. MessageServiceImpl

<bean id="messageService" class="com.javadoop.example.MessageServiceImpl">

The result of the above configuration is that beanName is messageService and there is no alias.

Configure whether Bean overrides and circular dependencies are allowed

As we said, by default, the allowBeanDefinitionOverriding property is null. If the Bean id or name is repeated in the same configuration file, an error will be thrown, but if it is not in the same configuration file, an overwrite will occur.

However, sometimes we want to strictly prevent Bean coverage during system startup, because in case of such a situation, it will increase the cost of troubleshooting.

Circular dependency means that A depends on B, and B depends on A. Or A depends on B, B depends on C, and C depends on A. The default allowCircularReferences is also null.

The two attributes appear together and can be configured together in the same place.

The author Juergen Hoeller who added these two properties in this jira How to configure these two properties is explained in the discussion of.

public class NoBeanOverridingContextLoader extends ContextLoader {
 
  @Override
  protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
    super.customizeContext(servletContext, applicationContext);
    AbstractRefreshableApplicationContext arac = (AbstractRefreshableApplicationContext) applicationContext;
    arac.setAllowBeanDefinitionOverriding(false);
  }
}
public class MyContextLoaderListener extends org.springframework.web.context.ContextLoaderListener {
 
  @Override
  protected ContextLoader createContextLoader() {
    return new NoBeanOverridingContextLoader();
  }
  
}
<listener>
    <listener-class>com.javadoop.MyContextLoaderListener</listener-class>  
</listener>

If the above methods can not meet your needs, please refer to this link: Solve the problems that may be caused by bean s with the same name or id in different configuration files in spring

profile

We can configure the configurations of different environments into separate files, for example:

<beans profile="development"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xsi:schemaLocation="...">

    <jdbc:embedded-database id="dataSource">
        <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
        <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
    </jdbc:embedded-database>
</beans>
<beans profile="production"
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="...">

    <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
</beans>

There should be no need to explain too much. Look at the profile = "" in the first line of each file.

Of course, we can also use it in a configuration file:

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:jee="http://www.springframework.org/schema/jee"
    xsi:schemaLocation="...">

    <beans profile="development">
        <jdbc:embedded-database id="dataSource">
            <jdbc:script location="classpath:com/bank/config/sql/schema.sql"/>
            <jdbc:script location="classpath:com/bank/config/sql/test-data.sql"/>
        </jdbc:embedded-database>
    </beans>

    <beans profile="production">
        <jee:jndi-lookup id="dataSource" jndi-name="java:comp/env/jdbc/datasource"/>
    </beans>
</beans>

It's easy to understand.

The next question is, how to use a specific profile? During the startup process, Spring will look for the attribute value of "spring.profiles.active", and it will be based on this attribute value. How do you configure this value?

Spring will look for spring in these places profiles. Active attribute values: operating system environment variable, JVM system variable, web Parameters defined in XML, JNDI.

The simplest way is to specify when the program starts:

-Dspring.profiles.active="profile1,profile2"

profile can activate multiple

Of course, we can also set the profile from the Environment in the form of code:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.getEnvironment().setActiveProfiles("development");
ctx.register(SomeConfig.class, StandaloneDataConfig.class, JndiDataConfig.class);
ctx.refresh(); // restart

If it's Spring Boot, it's easier. We usually create an application Properties, application-dev.properties, application-prod.properties, etc., where application Properties configure the configuration common to each environment, application - {profile} Configure the configuration of a specific environment in properties, and then specify the profile at startup:

java -Dspring.profiles.active=prod -jar JavaDoop.jar

If it is used in unit testing, use @ ActiveProfiles to specify it in the test class. It will not be expanded here.

Factory pattern generation Bean

Please pay attention to the difference between factory Bean and FactoryBean. This section refers to the former, which refers to static factory or instance factory, while the latter is a special interface in Spring and represents a special class of beans. The following section of the appendix will introduce factorybeans.

In the design pattern, the factory method pattern is divided into static factory and instance factory. Let's take a look at how to configure these two in Spring, and we'll know everything by a code example.

Static plant:

<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>
public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    // Static method
    public static ClientService createInstance() {
        return clientService;
    }
}

Example factory:

<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

<bean id="accountService"
    factory-bean="serviceLocator"
    factory-method="createAccountServiceInstance"/>
public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    private static AccountService accountService = new AccountServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public AccountService createAccountServiceInstance() {
        return accountService;
    }
}

FactoryBean

FactoryBean is applicable to scenes where the creation process of Bean is complex, such as the creation of database connection pool.

public interface FactoryBean<T> {
    T getObject() throws Exception;
    Class<T> getObjectType();
    boolean isSingleton();
}
public class Person { 
    private Car car ;
    private void setCar(Car car){ this.car = car;  }  
}

Let's assume that we need to create a Person Bean. First, we need an instance of Car. Let's assume that the creation of Car instance is troublesome, so we can package the complex process of creating Car:

public class MyCarFactoryBean implements FactoryBean<Car>{
    private String make; 
    private int year ;
    
    public void setMake(String m){ this.make =m ; }
    
    public void setYear(int y){ this.year = y; }
    
    public Car getObject(){ 
      // Here, we assume that the instantiation process of Car is very complex. Anyway, it is not the kind that can be written in a few lines of code
      CarBuilder cb = CarBuilder.car();
      
      if(year!=0) cb.setYear(this.year);
      if(StringUtils.hasText(this.make)) cb.setMake( this.make ); 
      return cb.factory(); 
    }
    
    public Class<Car> getObjectType() { return Car.class ; } 
    
    public boolean isSingleton() { return false; }
}

Let's see how it is configured during assembly:

<bean class = "com.javadoop.MyCarFactoryBean" id = "car">
  <property name = "make" value ="Honda"/>
  <property name = "year" value ="1984"/>
</bean>
<bean class = "com.javadoop.Person" id = "josh">
  <property name = "car" ref = "car"/>
</bean>

See the difference? The bean with id "car" actually specifies a FactoryBean, but when configuring, we can directly make the bean configuring Person directly depend on this FactoryBean. The middle process has been encapsulated by Spring.

At this point, let's have some dry goods. We know that there are fewer and fewer dependencies on configuring beans with xml. More often, we may use java config to configure them. What's the difference here?

@Configuration 
public class CarConfiguration { 

    @Bean 
    public MyCarFactoryBean carFactoryBean(){ 
      MyCarFactoryBean cfb = new MyCarFactoryBean();
      cfb.setMake("Honda");
      cfb.setYear(1984);
      return cfb;
    }

    @Bean
    public Person aPerson(){ 
    Person person = new Person();
      // Notice the difference here
    person.setCar(carFactoryBean().getObject());
    return person; 
    } 
}

At this time, in fact, our idea is also very simple. Just regard MyCarFactoryBean as a simple Bean. We don't have to pay attention to any FactoryBean. Whether it is a FactoryBean has nothing to do with us.

Initialize callback for Bean

There are four options:

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class AnotherExampleBean implements InitializingBean {

    public void afterPropertiesSet() {
        // do some initialization work
    }
}
@Bean(initMethod = "init")
public Foo foo() {
    return new Foo();
}
@PostConstruct
public void init() {
    
}

Callback to destroy Bean

<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class AnotherExampleBean implements DisposableBean {

    public void destroy() {
        // do some destruction work (like releasing pooled connections)
    }
}
@Bean(destroyMethod = "cleanup")
public Bar bar() {
    return new Bar();
}
@PreDestroy
public void cleanup() {
    
}

ConversionService

Since this is mentioned in the article, by the way.

The most useful scenario is when it is used to bind the parameters passed from the front end to the parameters on the back-end controller method.

For example, it is easy to convert strings and integers passed from the front end to strings and integers from the back end. However, if the controller method needs an enumeration value or non basic type (including basic type wrapper class) values such as Date, we can consider using ConversionService for conversion.

<bean id="conversionService"
  class="org.springframework.context.support.ConversionServiceFactoryBean">
  <property name="converters">
    <list>
      <bean class="com.javadoop.learning.utils.StringToEnumConverterFactory"/>
    </list>
  </property>
</bean>

The ConversionService interface is very simple, so it is also very simple to customize a convert.

Another simple way to implement this conversion is to implement the Converter interface.

Let's take a very simple example. It works better than anything.

public class StringToDateConverter implements Converter<String, Date> {

    @Override
    public Date convert(String source) {
        try {
            return DateUtils.parseDate(source, "yyyy-MM-dd", "yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd HH:mm", "HH:mm:ss", "HH:mm");
        } catch (ParseException e) {
            return null;
        }
    }
}

Just register the Bean. In this way, the time description string passed from the front end to the back end can be easily bound into Date type without any other operation.

Bean inheritance

We said this when initializing the Bean:

RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);

The parent attribute in < bean parent = "" / > is involved here. Let's see what Spring uses this for.

First of all, we should understand that inheritance here has nothing to do with inheritance in java syntax, but the ideas are the same. The child bean inherits all the configurations of the parent bean, and can override some configurations. Of course, additional configurations can also be added.

Spring provides ChildBeanDefinition inherited from AbstractBeanDefinition to represent child bean s.

Take the following example:

<bean id="inheritedTestBean" abstract="true" class="org.springframework.beans.TestBean">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>

<bean id="inheritsWithDifferentClass" class="org.springframework.beans.DerivedTestBean"
        parent="inheritedTestBean" init-method="initialize">
        
    <property name="name" value="override"/>
</bean>

The parent bean is set with abstract="true", so it will not be instantiated. The child bean inherits the two properties of the parent bean, but overrides the name property.

Child beans inherit scope, constructor parameter values, attribute values, init method, destroy method, and so on.

Of course, I'm not saying that abstract = true in the parent bean is necessary here, but that if it is added, Spring will ignore this bean when instantiating singleton beans in the future.

For example, the following extreme parent bean does not specify a class, so there is no doubt that the function of this bean is to act as a parent bean for templates. Here, abstract = true must be added.

<bean id="inheritedTestBeanWithoutClass" abstract="true">
    <property name="name" value="parent"/>
    <property name="age" value="1"/>
</bean>

Method injection

Generally speaking, most beans in our applications are singleton. Singleton depends on singleton, or prototype depends on prototype. You can directly set the attribute dependency.

But what if singleton relies on prototype? Attribute dependency cannot be used at this time, because if attribute dependency is used, we actually get the bean at the first initialization every time.

One solution is not to use attribute dependency. Each time you get the dependent bean, you get it from BeanFactory. This is also the most commonly used way. I won't introduce how to get it. Most Spring projects will define such a tool class.

Another solution is introduced here by using the Lookup method.

lookup-method

Let's take a look at an example provided in the Spring Reference:

package fiona.apple;

// no more Spring imports!

public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

xml configuration < lookup method / >:

<!-- a stateful bean deployed as a prototype (non-singleton) -->
<bean id="myCommand" class="fiona.apple.AsyncCommand" scope="prototype">
    <!-- inject dependencies here as required -->
</bean>

<!-- commandProcessor uses statefulCommandHelper -->
<bean id="commandManager" class="fiona.apple.CommandManager">
    <lookup-method name="createCommand" bean="myCommand"/>
</bean>

Spring uses CGLIB to generate bytecode to generate a subclass. The class we define cannot be defined as final class, and final cannot be added to abstract methods.

The configuration on the lookup method can also be completed by annotation, so that the < lookup method / > configuration is not required, and the others remain unchanged:

public abstract class CommandManager {

    public Object process(Object commandState) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup("myCommand")
    protected abstract Command createCommand();
}

Note that since annotations are used, annotation scanning should be configured: < context: component scan base package = "com. Javadoop" / >

Even, we can do something like this:

public abstract class CommandManager {

    public Object process(Object commandState) {
        MyCommand command = createCommand();
        command.setState(commandState);
        return command.execute();
    }

    @Lookup
    protected abstract MyCommand createCommand();
}

The above return value uses MyCommand. Of course, if Command has only one implementation class, the return value can also be written to Command.

replaced-method

Remember that its function is to replace some methods in the bean.

public class MyValueCalculator {

    public String computeValue(String input) {
        // some real code...
    }

    // some other methods...
}

For method override, pay attention to implementing the methodreplace interface:

public class ReplacementComputeValue implements org.springframework.beans.factory.support.MethodReplacer {

    public Object reimplement(Object o, Method m, Object[] args) throws Throwable {
        // get the input value, work with it, and return a computed result
        String input = (String) args[0];
        ...
        return ...;
    }
}

The configuration is also simple:

<bean id="myValueCalculator" class="x.y.z.MyValueCalculator">
    <!-- definition computeValue This method needs to be replaced -->
    <replaced-method name="computeValue" replacer="replacementComputeValue">
        <arg-type>String</arg-type>
    </replaced-method>
</bean>

<bean id="replacementComputeValue" class="a.b.c.ReplacementComputeValue"/>

Arg type is obviously not necessary, unless there is a method overload, so you must judge which method to override here through the parameter type list.

BeanPostProcessor

It should be said that the concept of BeanPostProcessor is also important in Spring. Let's look at the interface definition:

public interface BeanPostProcessor {

   Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

   Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}

Looking at the names of the two methods in this interface, we can guess that the bean will execute the postProcessBeforeInitialization method before initialization, and the postProcessAfterInitialization method after initialization. However, this understanding is very one-sided.

First of all, we should understand that in addition to our own defined BeanPostProcessor implementation, the Spring container automatically adds several to us at startup. For example, in prepareBeanFactory(factory) after obtaining the obtainFactory() method of BeanFactory, you will find that Spring has added these two beanpostprocessors to the container: ApplicationContextAwareProcessor and ApplicationListenerDetector.

Let's go back to the interface itself. Please look at the first method. The first parameter accepted by this method is the bean instance, and the second parameter is the bean name. The focus is that the return value will be used as a new bean instance. Therefore, if it's okay, you can't return null here.

What does that mean? It's easy to think that we can do something here for some bean instances we want to modify. However, for the Spring framework, it will decide whether to return the proxy of the bean instance in this method, so there is more room for imagination.

Finally, if we define a bean to implement BeanPostProcessor, when will it be executed?

If you look at the code analysis carefully, it is easy to know that the callback method will be executed after bean instantiation and attribute injection. For details, see class AbstractAutowireCapableBeanFactory#initBean method.

First, several beans implementing Aware interface will be called back, and then the postProcessBeforeInitialization method of BeanPostProcessor will be called back, followed by init method, and then the postProcessAfterInitialization method of BeanPostProcessor.

summary

Normally, the summary should be written in front of the appendix, so I don't pay attention to it.

After spending so much time, this article has finally been basically completed. When everyone is amazed that Spring has done so many things for us, we should look at the essence through the phenomenon, understand what Spring has written well, and understand its design idea.

The defect of this paper lies in the insufficient analysis of the process of Spring pre initializing singleton beans, mainly because the amount of code is really large and there are many branch bypasses. At the same time, although there are many appendix items, the huge Spring really leads to many concepts. I hope I can add some slowly in the future.

(end of full text)

Keywords: Java Spring Container

Added by anth0ny on Thu, 30 Dec 2021 21:16:22 +0200