Small knowledge, big challenge! This paper is participating in theEssentials for programmers”Creative activities.

To clarify, this article discusses what an IoC container is and its design and implementation. The more important initialization process of the Spring IoC container has not been explored yet, both because there is too much to cover and because there is not enough time.

IoC overview in Spring

Our Spring IoC is simply an application of IoC ideas in Java. There are also applications in other languages that we don’t know about. DI (Dependency Injection) is the specific technology. How to implement the IoC container in Spring depends on DI.

So what is the IoC philosophy up to? What do you think? Simply said is the unified management system used in the object, as well as the dependency between objects. The benefits of doing this are obvious: there is sufficient decoupling between objects. We would have used the new Object() form to create objects in the class, but we don’t need to use the IoC container to manage them.

IoC container series design and implementation

We can compare the IoC container to a bucket, just as there are different buckets in the shop, and the implementation or standards of our containers are also different. In contrast, Spring IoC is divided into two types of containers in the design. One is a simple container series that implements the BeanFactory interface. The functions it defines are the most basic functions of the IoC container.

One is a complex container that implements the ApplicationContext interface, which provides many advanced features and adds many frame-oriented features. I’ll talk about the differences later. Buckets need to hold water, while IoC containers need to manage objects and dependencies, which are represented by BeanDefinition objects. The BeanDefinition is the data abstraction of the dependency of the object in reverse control, which is as important as the water in a bucket.

The interface design diagram of IoC container is as follows:

From BeanFactory to HierarchicalBeanFactory to ConfigurableBeanFactory this is a major BeanFactory design path. In this design path, the BeanFactory defines the basic IoC container specification.

The second main line of design is the interface design with the ApplicationContext application as the context interface as the core, From BeanFactory to ListableBeanfactory to ApplicationContext to the WebApplicationContext interface that we all use, Or ConfigurableApplicationContext interface, both the implementation of the interface is we often say that the application of context. The ApplicationContext adds advanced features to the BeanFactory simple container by inherits interfaces such as MessageSource and ResourceLoader.

Design and implementation of BeanFactory

Here’s a look at the methods in the BeanFactory interface, which is what basic IoC does.

Public interface BeanFactory {// Here is the escape definition of FactoryBean, Because if you retrieve a FactoryBean by its name, the object you get is the factory-generated object String FACTORY_BEAN_PREFIX = "&"; // Based on the bean's name, we get the bean instance in the IOC container, which is a large abstract factory. Object getBean(String name) throws BeansException; // Get the bean instance based on the bean name and Class type. The difference is that this method will throw an exception if the bean instance based on the name has a different Class type than the required one. <T> T getBean(String name, Class<T> requiredType); <T> T getBean(Class<T> requiredType) throws BeansException; Object getBean(String name, Object... args) throws BeansException; Boolean containsBean(String name); Boolean containsBean(String name); / / here according to the bean name get bean instance, and figure out whether this bean sheet Boolean isSingleton (String name) throws NoSuchBeanDefinitionException; / / here according to the bean name get bean instance, and figure out whether this bean prototype Boolean isPrototype (String name) throws NoSuchBeanDefinitionException; // The Class type of the bean instance is Class<? > getType(String name) throws NoSuchBeanDefinitionException; String[] getAliases(String name); String[] getAliases(String name);Copy the code

In every implementation of the BeanFactory XmlBeanFactory is a bottom implementation, XmlBeanFactory inherited from DefaultListableBeanFactory, And DefaultListableBeanFactory is a very important to the realization of the IoC container. In other containers, for example ApplicationContext, the basic principle of its implementation and XmlBeanFactory, through holding or inheritance extension DefaultListableBeanFactory to obtain the basic function of the IoC container.

Take a look at the implementation of XmlBeanFactory

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

Looking at the code implementation above, we know that when constructing the XmlBeanFactory, we need to specify the source of the BeanDifiniton (analogous to a water source) and that this information source needs to be passed in as the Resource that the Spring framework allows. Resource is the class Spring uses to encapsulate I/O operations. Once we have a water source, we need to parse the water source, which is why we need XmlBeanDefinitionReader.

In the constructor, we can see that we use the parser Reader to load the resource, so that we can load the object into a container in the form of a DeanDifiniton, which is actually a Map.

Similarly, we can try to implement a simple IoC container ourselves, and we need to prepare the following.

1. Create a water source that contains information about DeanDifinitiaon.

2, create a the BeanFactory, we can use the basic implementation class DefaultListableBeanFactory

Create a DeanDifinitiaon reader to load beans as XML files using XmlDeanDifinitiaonReader.

4. Use reader to load resources.

Code demo:

ClassPathResource resource = new ClassPathResource("bean.xml"); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory); reader.loadBeanDefinitions(resource); factory.getBean("user");Copy the code

The design and implementation of ApplicationContext

The BeanFactory above is the basic implementation, and we can customize some features as needed. Spring also provides the ApplicationContext container. ApplicationContext adds the following functionality to the BeanFactory.

1. Support different information sources through MessageSource interface.

2, access to different resources, can be different ways to get Bean definition information. Through the ResourceLoader interface.

3, support application events, more convenient management Bean. Through interfaces ApplicationEventPublisher implementation.

4. Other frame-oriented features, so we recommend using ApplicationContext as the container implementation.

With our FileSystemXmlApplicationContext implementation to look at the design principle of ApplicationContext.

public FileSystemXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent) throws BeansException { super(parent); setConfigLocations(configLocations); if (refresh) { refresh(); }}@Overrideprotected Resource getResourceByPath(String path) { if (path ! = null && path.startsWith("/")) { path = path.substring(1); } return new FileSystemResource(path); }Copy the code

In the constructor, it is easier to call refresh directly. This method is more important, and it is designed to start the IoC container, which will be discussed later. The resource is created using FileSystemResource class, which can handle both URL and File resources.

The IoC container initialization process

The initialization process includes three basic processes: Resource location, loading and registration of BeanDefinition. This is the basic logic we used to implement the IoC container above. Spring specifically implements these parts in separate modules, making it easier for users to tailor or extend these three processes.

The first process Resource locating refers to the Resource locating of BeanDefinition, which is done by ResourceLoader through the unified Resource interface. This Resource provides a unified interface for the use of all forms of BeanDefinitions, which typically exist in FileSystemResource and ClassPtahResource. The process is like trying to fill a bucket with water and finding water.

The second process is the loading of the BeanDefinition, which represents the user-defined Bean as a data structure inside the IoC container, known as the BeanDefinition. The BeanDefinition is essentially an abstraction of POJO objects in the IoC container. The data structure defined by the BeanDefinition enables the IoC container to easily manage beans. The container holds the BeanDefinition through a Map.

The third process is the process of registering these Beandefinitions with the IoC container. This is done by calling the implementation of the BeanDefinitionRegistry interface, which registers the beanDefinitions resolved during loading with the IoC container, Inject the BeanDefinition into a HashMap inside the IoC container. It is through this HashMap that the IoC container holds the BeanDefinition data.

At this point, those of you who can see here must be wondering what happens in the refresh method. This article is already too long and I have dug a hole for myself.

Finally, I don’t know if you have this kind of feeling, but I always feel who is so strong. Anyway, I watched others analyze Spring source code before, so I hurried to be really cool, in fact, it is really cool, cool and patient to sum up after reading the book.