I am kite, the public account “ancient time kite”, a not only technology public account, I have been in the app community for many years, mainly Java, Python, React also play 6 slash developer. The Spring Cloud series is complete, and you can check out the full series on my Github. You can also reply “PDF” in the public account to get the full PDF version of my elaborate tutorial.

If you want to understand Spring IoC, you need to know how to extend Spring custom beans. For example, if you want to extend Spring custom beans, you need to know how to extend Spring custom beans. How do we do that? How can we extend a tag that has similar functionality, but different attributes?

Now that you know about custom extension beans, it will be much clearer to understand the Spring IoC process.

All right, here we go.

Spring IoC is an Inversion of Control. It is also called Dependency Injection (DI). It can also be said that inversion of control is the end goal, and dependency injection is the concrete way to achieve this goal.

What is inversion of control

Why is it called inversion of control.

In traditional mode, what do I do when I want to use another non-static object? The answer is to create a new instance.

For example, suppose you have a Logger class that prints logs. The definition is as follows:

public class Logger {

    public void log(String text){
        System.out.println("log:"+ text); }}Copy the code

So now I’m going to call this log method, what’s going to happen.

Logger logger = new Logger();
logger.log("Log content");
Copy the code

Right? So that’s a traditional invocation pattern. It’s up to the caller to control when an instance of the object is new, or rather, it’s up to us developers to control when we use it.

When we used Spring IoC, things changed. In short, the result is that the developer does not need to care about the operation of the new object. Once again, the Logger class, how will we use it after we introduce Spring IoC?

public class UserController {

    @Autowired
    private Logger logger;
    public void log(){ logger.log("please write a log"); }
}
Copy the code

Developers don’t create objects, but it’s impossible to ensure that objects are used properly without the action new, which doesn’t make sense. In this case, someone must have done it for us, and that is the Spring framework, or rather the Spring IoC Container. In this way, control is transferred from the developer to the third-party framework, which is called inversion of control.

What is dependency injection

The subject-verb-object completion of dependency injection is to inject the class instance object that the caller depends on into the caller class. In the previous example, the UserController class is the caller that wants to call the log method that Logger instantiates the object out of. Logger is the instantiated (new) object that UserController depends on. We don’t actively use the new keyword in our code because the Spring IoC Container does it for us, and this transparent operation for developers is called injection.

There are three methods of injection: constructor injection, setter injection, and annotation injection. The first two methods are rarely used nowadays, and annotations are more commonly used in development, especially as Spring Boot becomes more common today. When developing with the Spring framework, we usually use @AutoWired, although sometimes we can use @Resource

@Autowired
private IUserService userService;

@Autowired
private Logger logger;
Copy the code

Spring IoC Container

The Spring IoC Container does this for us. What is the Spring IoC Container?


This is the Core Container section in the figure above, which includes Beans, Core, Context, and SpEL.

The Container is responsible for instantiating, configuring, and assembling the Bean, and injecting it into the dependent caller class. Container is the manager that manages the entire life cycle of beans in a Spring project, including Bean creation, registration, storage, retrieval, destruction, and so on.

Let’s start with a basic example. The @bean in the previous example was implemented as an annotation, but more on that later. Although Spring Boot is now used, you can see the dependency injection process more clearly through the original XML method. Remember, before Spring Boot, XML can be said to be the link between Spring projects. Most of the configuration information comes from XML configuration files.

Start by adding a bean declaration file in XML format, assuming the name application.xml, which in most cases will be familiar to you if you’ve used Spring MVC before.


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

    <bean id="logger" class="org.kite.spring.bean.Logger" />
</beans>

Copy the code

Declaring a bean object through the

element, specifying the ID and class, is the standard way to declare bean objects in XML, and is worth knowing if you’ve been using Spring Boot since Java.

Then test it by calling the log method of the Logger class through a console program.

public class IocTest {

    public static void main(String[] args){
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        Logger logger = (Logger) ac.getBean("logger");
        logger.log("hello log"); }}Copy the code

ApplicationContext is the interface to realize the Container classes, including ClassPathXmlApplicationContext is a Container of the specific implementation, Similarly FileSystemXmlApplicationContext, both are is to parse the XML format configuration of the container. Let’s take a look at the ClassPathXmlApplicationContext inheritance diagram.


If it seems too complicated, there are several layers just to get to the ApplicationContext layer.

This is our active call ClassPathXmlApplicationContext in the console, in our project is generally don’t need to care about the ApplicationContext, such as the Spring we use the Boot program, only need a few lines below.

@SpringBootApplication
public class Application {
    public static void main(String[] args) { SpringApplication.run(Application.class, args); }}Copy the code

However, these lines do not mean that Spring Boot does not do dependency injection. As such, ApplicationContext is also implemented internally. Specific implementation is called AnnotationConfigServletWebServerApplicationContext, take a look at the implementation class inheritance diagram below, it is very complicated, need not care about the details, it is ok to look at.


Injection process analysis

Go ahead and pick up the base code above, and we’ll start with it.

public class IocTest {
    public static void main(String[] args){
        ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
        Logger logger = (Logger) ac.getBean("logger");
        logger.log("hello log"); }}Copy the code

There are many articles about the injection process that have done source analysis, so I won’t focus on the source code here.

Brief introduction, if we only analyze the simple container ClassPathXmlApplicationContext, in fact, the whole injection process source is very easy to read, have to say that the Spring source of writing is very neat. Step by step, from ClassPathXmlApplicationContext constructor, we find the refresh () method, and then follow the read on to understand the basis of a Spring IoC process. The following code is the core method of the refresh method:

@Override
 public void refresh(a) throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {
   // Prepare this context for refreshing.
   prepareRefresh();
   // Tell the subclass to refresh the internal bean factory.
   ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
   // Prepare the bean factory for use in this context.
   prepareBeanFactory(beanFactory);
   try {
    // Allows post-processing of the bean factory in context subclasses.
    postProcessBeanFactory(beanFactory);
    // Invoke factory processors registered as beans in the context.
    invokeBeanFactoryPostProcessors(beanFactory);
    // Register bean processors that intercept bean creation.
    registerBeanPostProcessors(beanFactory);
    // Initialize message source for this context.
    initMessageSource();
    // Initialize event multicaster for this context.
    initApplicationEventMulticaster();
    // Initialize other special beans in specific context subclasses.
    onRefresh();
    // Check for listener beans and register them.
    registerListeners();
    // Instantiate all remaining (non-lazy-init) singletons.
    finishBeanFactoryInitialization(beanFactory);
    // Last step: publish corresponding event.
    finishRefresh();
   }

   catch (BeansException ex) {
    }
    destroyBeans();
    cancelRefresh(ex);
    throw ex;
   }

   finally{ resetCommonCaches(); }}}Copy the code

The comments are very clear, and the core injection process is actually in this line:

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
Copy the code

I have drawn a swimlane diagram of the logical calls to this core part, which lists only the core methods, but clearly shows the process. (To obtain the vector format can be in the public number to reply to “vector map” to obtain)


On the other hand, most people are not able to read the source code, including myself, let alone such a huge open source framework, even my own new project can not read much. Said the key to read the source code is details, here is not to let you pick the details of the details, on the contrary, don’t be too dig details, who also can’t put all the source code for a frame line not fall all through, find the key of the logical relationship is ok, otherwise, you are likely a detail to headache, upset, and then give up reading.

Some students a look at the diagram or source code will find, how to involve so many classes ah, this call chain is really long enough. It doesn’t matter, you just think of them as a whole. As you can see from the previous class diagram, inheritance is very complicated, with various inheritances and implementations, so at the end of the call chain becomes very complicated.

Simple generalization

To summarize, the core of injection is to parse the contents of an XML file, find the elements, then go through a series of processes, and finally store these processed objects in a public space for the caller to retrieve and use.

For annotated beans, such as @Bean, @Service, @Component, etc., the parsing step is different. The rest of the operation is basically the same.

So, as long as we clear up some of the core issues here, it’s ok.

The relationship between BeanFactory and ApplicationContext

The above the line of the core code, finally return is a ConfigurableListableBeanFactory object, and multiple methods with the return of the beanFactory behind as a parameter.

BeanFactory is an interface, ApplicationContext is an interface, and BeanFactory is the parent interface of ApplicationContext. BeanFactory is the Spring IoC container. In the early days, there was only the BeanFactory, which was really the Spring IoC container at the time, but later, as version updates extended more functionality, the ApplicationContext was added. The biggest difference between the two is that the ApplicationContext instantiates all beans when it is initialized, while the BeanFactory instantiates beans when it is used, so early versions of Spring used lazy loading by default. The new version, by default, instantiates all beans at initialization, so Spring’s startup process is not as fast, which is one reason.

Where is BeanDefinition kept

Where is the public space, as outlined above? It is actually a Map, and a ConcurrentHashMap, to ensure concurrency security. In its statement as follows, DefaultListableBeanFactory.

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256)
Copy the code

Where beanName is the key, which is logger in this example, and value is the BeanDefinition type, which describes the definition of a Bean, and the attributes of the elements we defined in the XML file are init, It also includes other necessary attributes.

Add elements to beanDefinitionMap, called Bean registrations. Only registered beans can be used.

Where is the Bean instance stored

In addition, there is a Map called singletonObjects, which is declared as follows:

/** Cache of singleton objects: bean name to bean instance. */
private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);
Copy the code

The refresh () in the process, also will store the Bean here a copy of the stored procedure in finishBeanFactoryInitialization (the beanFactory) method, It is used to place non-lazy-init beans into singletonObjects.

In addition to storing our defined beans, we also include several system beans.


For example, we call this in our code:

ApplicationContext ac = new ClassPathXmlApplicationContext("application.xml");
StandardEnvironment env = (StandardEnvironment) ac.getBean("environment");
Copy the code

Use registered beans

In this case, we are fetching the registered Bean displayed through the getBean() method of the ApplicationContext. For beanDefinitionMap, we have a copy of our defined Bean stored in singletonObjects. SingletonObjects is a cache that will be retrieved when we call the getBean method. If you don’t find it (for those beans that actively set lazy-init), go beanDefinitionMap and add it to singletonObjects.

The call flow chart for obtaining the Bean is as follows.


The following is an example of a Bean set in lazy-init mode.

<bean id="lazyBean" lazy-init="true" class="org.kite.spring.bean.lazyBean" />
Copy the code

If not, it is registered at initialization by default.

Way of annotating

There are very few projects that use XML configuration now. Most of them are Spring Boot, and if not, they are registered and used with annotations in Spring MVC. In fact, the whole process is similar, but there are more annotations involved in registration and retrieval. In addition to the BeanFactory and ApplicationContext interfaces in Srping, there are a number of abstract classes that allow you to customize your own registration and invocation processes. Consider the annotation approach as one of these customizations. Just find the time to parse the corresponding annotation notation.

However, Spring Boot registration and invocation are not as smooth as XML, due to the nature of annotations. Annotations are simple, convenient and have many benefits. But at the same time, annotations break up the traditional process, which is called step by step and you just follow the code down, and the way annotations break up the process, so it requires extra methods to read.

IoC process in Spring Boot, we will talk about it next time.

Please reply “Vector picture” to get hd swimming lane picture of this article.

Feel good to give it a thumbs up, always be white whoring, body can’t bear!

Reference Documents:

https://docs.spring.io/spring/docs

I am kite, public id “ancient Time kite”, I have been working in the application circles for many years, mainly Java, Python, React also play very 6 slash developer. Can add my friends in the public account, into the group of small partners exchange learning, a lot of dachang students are also in the group yo.

Technical exchange can also add group or directly add my wechat.