preface

In the Spring framework, we are familiar with nothing more than IOC, DI, Spring MVC, AOP, these are the most basic core functions of Spring, and then there are data access modules (JDBC, ORM, transaction, etc.). The extensibility of Spring itself is also very good, the source code is also the use of a large number of design patterns to achieve, to understand the Spring source code for a Java developer is very necessary, from the source code we can also learn a lot of good design ideas, Now let’s start our Spring source tour with Spring IOC.

IOCJust aMapA collection of

When it comes to IOC, people who come into contact with it for the first time may think it is very lofty and sophisticated, but what is the truth? The truth is that IOC is really just a collection of maps, not some fancy new technology. Please sit down and have a cup of tea and listen to me.

IOC: Inversion of Control. The basic concept of inversion of control is that you don’t need to create an object, but you need to describe how the object is created.

To put it simply, we used to create an object in code with the new keyword. With Spring, we no longer need to create an object ourselves. Instead, we need to fetch it directly from the container and automatically inject it into the object we need: dependency injection.

In other words, the control of object creation is no longer in the hands of the programmers, and all the control is in the hands of Spring.

In fact, the IOC is also known as the IOC container. Since it is a container, it must be used to store things. So what does the IOC container store? If you are familiar with Spring, you know that in Spring, you can say that everything is bean-oriented programming, and beans refer to the objects we give Spring to manage. The IOC container that we are going to learn today is a container for storing all beans.

IOC has three core interfaces

As an excellent framework, Spring also supports many kinds of Bean sources. Therefore, in order to unify the standard, it is natural to define a configuration file interface, which is BeanDefinition. With configuration standards, you need to define classes for converting different configuration files, so BeanDefinitionReader; After the Bean is finally parsed, you need to manipulate the Bean, hence the BeanFactory. These three interfaces form the core of IOC:

  • BeanDefinition: Defines oneBeanVarious information about relevant configuration files, such as currentBeanConstructor parameters, attributes, and other information. This interface also spawns other implementation classes, such as
  • BeanDefinitionReader: Defines some methods for reading configuration filesResourceStringThe positional argument specifies the loading method, and you can extend your own specific methods as you go. This class simply provides a suggested standard and does not require all parses to implement the interface.
  • The BeanFactory: accessBeanThe top-level interface of a container, the one we use most oftenApplicationContextInterfaces are also implementedBeanFactory.

There are three steps to IOC initialization

Above we know roughly what IOC container is, also know what IOC container is used to store, but also to the three core IOC interfaces mixed with a familiar, so next we should understand how beans come, stored in IOC container is only Bean itself or further encapsulation?

With these two questions in mind, let’s take a closer look at the entire IOC initialization process.

The entire IOC initialization process can be summarized into three steps: location, loading, and registration.

  1. Locate: Find what needs to be initializedBean.
  2. Load: will find what needs to be initializedBeanParse encapsulation.
  3. Register: This step is after the second step has been loadedBeanIn theIOCContainer, which means put inMapIn the set.

positioning

The beans we use most often come from XML configurations or annotations, so where are these configuration files stored? Configuration files in Spring support the following six sources:

  • classpath
  • network
  • filesystem
  • servletContext
  • annotation

Let’s look at the localization process as an entry point in one of our most common ways (one of the top interfaces of the ApplicationContext implementation is BeanFactory, so it has BeanFactory operation Bean capabilities) :

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
applicationContext.getBean("myBean");
applicationContext.getBean(MyBean.class);
Copy the code

When using traditional Spring, before the above are the means by which we are to get Bean, positioning of the we will start with the entry of the ClassPathXmlApplicationContext entrance.

The logic here is very simple, you call the setConfigLocations method to set the configuration file, and then the core is the refresh method, which is implemented by its parent class, The backbone of the refresh method in the parent class is to get a beanFactory at line 522, with all subsequent operations doing some extension around the beanFactory.

Actually see 522 lines can also know that eventually it will call back to subclass is AbstractRefreshableApplicationContext to perform load bean operation:

The core logic is in line 623, and line 624 actually gets the beanFactory from the global variable:

While the global variable the beanFactory here is the beanFactory DefaultListableBeanFactory a default implementation. With that in mind, let’s go back to the refreshBeanFactory above:

This method is also very simple, is to create a default DefaultListableBeanFactory, And then began to call its subclasses AbstractXmlApplicationContext (at the same time it is ClassPathXmlApplicationContext superclass) loadBeanDefinitions method:

loading

If we go to the above method, we see that a BeanDefinitionReader object XmlBeanDefinitionReader has been created, which means it’s almost time to load the configuration file, So for the main trunk, just follow the BeanDefinitionReader object, and then go to the loadBeanDefinitions method:

There are two cases, one is based on the Resource type, one is based on the String type, because we pass a String path, so the following logic will be executed, but although the following logic is executed, But we will eventually convert our passed spring.xml to Resource and invoke the above parsing method.

There will be a few detour steps, then the loadBeanDefinitions method for the XmlBeanDefinitionReader object again:

Here we finally see doLoadBeanDefinitions, a method that surprises us because in Spring, the do method is essentially the core processing logic method:

The first method is to convert the resource into a Document object, and then another method is called to prepare to register the bean. Of course, how to parse our XML configuration file is not analyzed here. We will continue to look at the logic of the trunk registration bean.

registered

After the above call registration method, will eventually by its subclasses DefaultBeanDefinitionDocumentReader to perform:

At this point we’re back to the method that starts with do, indicating that we’re going to start registering.

Here we create a delegate. If we enter the delegate, we can see that all the nodes in the XML file are defined:

With the delegator created, you can then call parseBeanDefinitions for parsing:

ParseDefaultElement parseDefaultElement parseDefaultElement parseDefaultElement parseDefaultElement parseDefaultElement parseDefaultElement

Here we break down the parsing of import, Alias, and bean nodes into different cases, including recursive processing of nested nodes, and proceed to the processBeanDefinition method:

Here basically is about to end the registration process, called the BeanDefinitionReaderUtils tool in the class as a way to register:

Three things are done here:

  1. Access to thebeanName.
  2. Let’s go back to the beginningDefaultListableBeanFactory, the callregisterBeanDefinitionmethods
  3. Register an alias if one exists.

The key here is the second, we found that the full circle back to the us eventually DefaultListableBeanFactory in load steps in front of the class (the following this method in order to facilitate screenshots, I deleted some abnormal judgment) :

This method is the final logic to register the bean. It checks whether the current bean has been registered, and if so, whether overwriting is allowed. If all conditions are met, it overwrites the bean directly (line 795). You will need to determine whether the current the ioc container has created good beans, but in the end is enclosing beanDefinitionMap. Put (beanDefinition beanName); This line completes the registration, and beanDefinitionMap is really just a ConcurrentHashMap collection.

This is the end of the analysis of the whole IOC loading process. In fact, the whole logic is very simple, and the reason why we think Spring is complicated and difficult is that Spring has been carefully designed for scalability and readability, and many design patterns and design principles have been used in the whole framework. Cause us to look at the source code feel very around, but as long as seize the core trunk, read the source code is not difficult.

conclusion

This paper mainly describes the ioc initialization process, the whole process is actually very complicated, it is very easy to get lost when looking at it for the first time, so we need to grasp the main process, understand the core of IOC is three steps: Locating (finding the configuration file), loading (parsing the configuration file), and registering (adding the bean to the IOC container) are key steps, and by grasping these three steps, we can move on. So if we change the way we get beans to an annotation implementation, we are simply changing the process of parsing XML configuration files to the process of parsing annotations, and the core flow is still the same.