Hello everyone, I’m my third brother. Spring is our most commonly used open source framework. After years of development, Spring has grown into a leafy tree, which makes it difficult for us to see the whole picture.

In this section, we return to the essence of Spring, and lift a Spring container in five minutes to uncover the mystery of Spring!

Start with what is IOC?

The Spring in the Java programming world was ushered in by a musician, Rod Johnson.

Rod Johnson has written two major books, Expert One-on-one J2EE Design and Development and Expert One-on-one J2EE Development Without EJB. Raised the flag to challenge EJB, the orthodox Java EE framework.

Rod Johnson was not only a standard-bearer, but also the man who developed Spring, a lightweight framework, like a brave dragoon-trooper who charged EJB and ultimately defeated IT, making Spring the de facto standard for Java EE.

The two main cores of Spring are IOC and AOP, of which IOC is the most central.

So-called IOC (Inversion of Control) : The container is responsible for controlling the life cycle and relationships between objects. It used to be that we built what we wanted, and now the container delivers what we need.

That is, the object’s lifecycle is no longer controlled by the object that references it, but by the container. For a specific object, it used to control other objects, but now all objects are controlled by the container, so this is called inversion of control.

Another concept you may have heard of is DI (dependency injection), which means that a container injects dependent classes into an object when it instantiates it. We can also think of DI as a complement to IOC.

Factory and Spring container

Spring is a mature framework for extensibility and functionality, so its implementation is like a tree with interleaved branches. Now let’s look away from Spring itself and see how a budding version of the Spring container can be implemented.

The IOC nature of Spring is a big factory. How does a factory work?

  • Making products: The core function of a factory is to make products. In Spring, instead of instantiating the Bean itself, you hand it to Spring. The answer, of course, is reflection.

    So how does the factory’s production management work? You know that too — the factory model.

  • Inventory products: Factories generally have warehouses to stock products, after all, the products can not be pulled away immediately. Spring as we all know is a container, this container is stored in the object, you can’t come to fetch the object, you have to create the object on the spot, you have to create the object to store.

  • Order processing: And most importantly, on what basis does the factory supply the product? The order. These orders may be multifarious, wired signed, signed to the factory, and factory sales door-to-door signed…… Finally, after processing, direct factory shipment.

    In Spring, there are orders as well, which are our bean definitions and dependencies, either in XML form or in the most familiar form of annotations.

So what does our embryonic version of the Spring container look like?

Order: Bean definition

Beans can be defined through a configuration file, which we resolve to a type.

  • beans.properties

    For the sake of laziness, the most parsed properties are used here, with a <key,value> configuration representing the Bean definition, where key is beanName and value is class

    userDao:cn.fighter3.bean.UserDao
    Copy the code
  • BeanDefinition.java

    The bean defines the class, and the bean in the configuration file defines the corresponding entity

    public class BeanDefinition {
    
        private String beanName;
    
        private Class beanClass;
         // omit the getter and setter
     }   
    Copy the code

Get order: resource load

After taking the order, it is necessary to hand over the goods from the sales department to the production department, so that the production department knows the specifications, quantity and so on.

The resource loader does this by loading the configuration in the configuration file.

public class ResourceLoader {

    public static Map<String, BeanDefinition> getResource(a) {
        Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>(16);
        Properties properties = new Properties();
        try {
            InputStream inputStream = ResourceLoader.class.getResourceAsStream("/beans.properties");
            properties.load(inputStream);
            Iterator<String> it = properties.stringPropertyNames().iterator();
            while (it.hasNext()) {
                String key = it.next();
                String className = properties.getProperty(key);
                BeanDefinition beanDefinition = new BeanDefinition();
                beanDefinition.setBeanName(key);
                Class clazz = Class.forName(className);
                beanDefinition.setBeanClass(clazz);
                beanDefinitionMap.put(key, beanDefinition);
            }
            inputStream.close();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        returnbeanDefinitionMap; }}Copy the code

Order allocation: Bean registration

The object registry, which is used here to cache singleton beans, is greatly simplified and all beans are singleton by default. You can see that singleton registration is also very simple, just storing objects in a HashMap.

public class BeanRegister {

    // Singleton Bean caching
    private Map<String, Object> singletonMap = new HashMap<>(32);

    /** * get the singleton Bean **@paramBeanName bean name *@return* /
    public Object getSingletonBean(String beanName) {
        return singletonMap.get(beanName);
    }

    /** * Register singleton bean **@param beanName
     * @param bean
     */
    public void registerSingletonBean(String beanName, Object bean) {
        if (singletonMap.containsKey(beanName)) {
            return; } singletonMap.put(beanName, bean); }}Copy the code

Production workshop: Object factory

Ok, here comes our most critical production department. In the factory, the workshop produces the products, and in the IOC container, the BeanFactory produces the objects.

  • The object factory, one of our core classes, creates the bean registry and loads the resources when it initializes.

  • When a bean is fetched, it is first fetched from the singleton cache, and if not, a bean is created and registered

    public class BeanFactory {
    
        private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
    
        private BeanRegister beanRegister;
    
        public BeanFactory(a) {
            // Create the bean registry
            beanRegister = new BeanRegister();
            // Load resources
            this.beanDefinitionMap = new ResourceLoader().getResource();
        }
    
        /** * get bean **@paramBeanName bean name *@return* /
        public Object getBean(String beanName) {
            // fetch from the bean cache
            Object bean = beanRegister.getSingletonBean(beanName);
            if(bean ! =null) {
                return bean;
            }
            // Create the bean according to the bean definition
            return createBean(beanDefinitionMap.get(beanName));
        }
    
        /** * create Bean **@paramBeanDefinition bean definition *@return* /
        private Object createBean(BeanDefinition beanDefinition) {
            try {
                Object bean = beanDefinition.getBeanClass().newInstance();
                / / cache the bean
                beanRegister.registerSingletonBean(beanDefinition.getBeanName(), bean);
                return bean;
            } catch (InstantiationException | IllegalAccessException e) {
                e.printStackTrace();
            }
            return null; }}Copy the code

Production and sales: testing

  • UserDao.java

    Our Bean class is very simple

    public class UserDao {
    
        public void queryUserInfo(a){
            System.out.println("A good man."); }}Copy the code
  • Unit testing

    public class ApiTest {
        @Test
        public void test_BeanFactory(a) {
            //1. Create the bean factory (load the resource and create the registered singleton bean registry)
            BeanFactory beanFactory = new BeanFactory();
    
            //2. Get bean for the first time (create bean by reflection, cache bean)
            UserDao userDao1 = (UserDao) beanFactory.getBean("userDao");
            userDao1.queryUserInfo();
    
            //3. Get the bean a second time (get the bean from the cache)
            UserDao userDao2 = (UserDao) beanFactory.getBean("userDao"); userDao2.queryUserInfo(); }}Copy the code
  • The results

    A good man.
    A good man.
    Copy the code

At this point, we have a budding Version of the Spring container.

Think about it, what are its disadvantages? Can you still abstract, extend, decouple…

Thinking about it, do you have any idea why a real Spring IOC container is so complex?






Reference:

[1]. Spring Revealed

[2]. Xiao Fu Ge “Hand Off Spring”

[3]. Proficient in Spring4.x Enterprise Application Development Practice