Today’s IOC, do not intend to analyze the source code, on the one hand feel that Spring source code is very large, step by step with words easy to fall into a deep branch, and easy to forget. So the plan is simple to say the principle, we can first write down the simple principle, and then look at the big guy’s blog, and then step by step debug source code, so it is almost estimated. Then let’s talk briefly about the principle of IOC.

IOC means inversion of control in a broad sense. What is inversion of control? We all know that in the past, when we wanted to use a method or property in a class, we would first use the new class and then use that object to call the method in the class. This is the traditional way of using methods and properties in a class, but there is a lot of coupling.

To reduce coupling, Spring comes up with IOC inversion of control, which means that Spring helps us create objects that we call beans, and manages bean objects, and provides them to us when we need them. So instead of actively creating objects, we’re going to have Spring control over creating objects for us to use, which is inversion of control.

For IOC, its design includes:

Dependency injection, dependency checking, automatic configuration, support for collections, specifying initialization and destruction methods, support for callbacks to some methods.

It has two main interfaces designed to represent containers

  • One is the BeanFactory

  • One is the ApplicationContext

For the BeanFactory container, it is a HashMap, with the BeanName as the key and the value as the bean’s instantiation object. It usually provides both registration (PUT) and get (get) functions. This implementation uses the factory pattern + reflection technique to instantiate the Bean and store the instantiated object in the HashMap. Here is a simple example to show how BeanFactory uses the factory pattern + reflection technique to create an object.

1. Create an interface class

public interface Shape {
    public void draw(a);
}
Copy the code

2. Implement this interface class

public class Circle implements Shape {
    public void draw(a){
        System.out.println("Draw circles"); }}Copy the code
public class Square implements Shape {
    public void draw(a){
        System.out.println("Draw a square"); }}Copy the code

Create a factory class

public class ReflectFactory {
    public Shape getInstance(String className) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
        Shape shape =null;
        shape =(Shape)Class.forName(className).newInstance();
        returnshape; }}Copy the code

4. Create a Client class to test

public class Client {
   public static void main(String[] args){
       ShapeFactory shapeFactory = new ShapeFactory();
       Shape shape= shapeFactory.getInstance("Circle"); shape.draw(); }}Copy the code

5. The output is

Draw roundCopy the code

This simple example is a simple way to create objects using factory patterns and reflection in BeanFactory. The source code for BeanFactory is much more complex than this. And that’s just for you to understand.

As for ApplicationContext, it inherits the BeanFactory interface and adds more functionality, including callback methods. The main thing is that there is a refresh() method, which basically refreshes the entire container, that is, reloads or refreshes all beans. Here’s the source code for the refresh() method.

public void refresh(a) throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        Synchronized refresh(); synchronized refresh(); synchronized refresh()
        synchronized(this.startupShutdownMonitor) {
         // This method does preparatory work, recording the startup time of the container, marking the "started" status, and handling placeholders in the configuration file
            this.prepareRefresh();
            // This step is to parse the configuration file into beans and register them with the BeanFactory. Note that this is only registered, not initialized
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            // Set up the BeanFactory classloader, add a few BeanPostProcessors, and manually register a few special beans. These are all special processing in Spring
            this.prepareBeanFactory(beanFactory);
 
            try {
                // Specific subclasses can add special BeanFactoryPostProcessor implementation classes at this stage to perform other operations
                this.postProcessBeanFactory(beanFactory);
                // This method calls the postProcessBeanFactory(factory) of each BeanFactoryPostProcessor implementation class.
                this.invokeBeanFactoryPostProcessors(beanFactory);
                This method registers the implementation class of BeanPostProcessor
                this.registerBeanPostProcessors(beanFactory);
                // This method initializes the MessageSource of the current ApplicationContext
                this.initMessageSource();
                This method initializes the event announcer for the current ApplicationContext
                this.initApplicationEventMulticaster();
                // This method initializes some special beans (before initializing Singleton beans)
                this.onRefresh();
                // This method registers event listeners that need to implement the ApplicationListener interface
                this.registerListeners();
 
                // Initialize all Singleton beans except non-lazy-init ones
                this.finishBeanFactoryInitialization(beanFactory);
                // The method is the last step, broadcasting the event
                this.finishRefresh();
            } catch (BeansException var9) {
                if(this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }
                // Call the bean destruction method
                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches(); }}}Copy the code

That’s what the refresh() method in ApplicationContext does, Need to see the key method is to ConfigurableListableBeanFactory inside the beanFactory = this. ObtainFreshBeanFactory (); And enclosing finishBeanFactoryInitialization (the beanFactory); These two methods, you can have a look at the source code.

After the completion of the analysis of the above two large, then the IOC container startup process is what kind, is said to be more straightforward ClassPathXmlApplicationContext did what the class start?

The first access is to the “high-level container” refresh method, which loads all of the BeanDefinitions and properties into IOC’s container using the low-level container. Once the low-level container has been successfully loaded, the high-level container starts to handle some of the callbacks. Examples are Bean post-handlers, callbacks to the setBeanFactory method, and registering listeners, publishing events, and instantiating singleton beans.

What the IOC does is essentially load the configuration file from the low-level container (BeanFactory), parse it into a BeanDefinition, and place it in a map with the BeanName as the key, the Bean instantiated object as the value, and call the getBean method when used. Dependency injection is complete.

As for the high-level container (ApplicationContext), it contains the functionality of the low-level container, which refreshes the beans of the entire container when it executes the Refresh template method. Also, as a high-level container, it contains too much functionality. In short, he is not just the IoC. It supports different information sources, BeanFactory utility classes, hierarchical containers, access to file resources, event notification, interface callbacks, and more.