Handwritten spring Chapter 2 - writing extensible containers using design patterns

preface

Based on the previous chapter Handwritten spring Chapter 1 - Basic container construction
, we have made a simple spring container to complete bean injection, but we still have many deficiencies in function and architecture design.

requirement analysis

In terms of framework design, we believe that the architecture must ensure that the project can be easily extended within the foreseeable software cycle. For example, in the field demand scenario, our framework only needs to manage annotated beans. Of course, we only need to write an AutowireCapableBeanFactory, But one day in the future, we need to deal with configuration beans, beans for network transmission, etc. do we have to rewrite a container, and then copy and paste some of the same code segments as AutowireCapableBeanFactory?
Obviously, this situation is the lack of architecture design. Such an approach will lead to a large number of redundant code.

Next, there is the second problem. In the previous chapter, when we stored beans in BeanDefinition, we stored instantiated objects, which is obviously a waste of memory. Therefore, we also need to optimize this problem in this chapter.

Design

Train of thought analysis

To solve the first problem, beans need to be easy to expand and reuse common logic. We can design as follows. Since we may have various bean containers in the future, we will design an interface and name it BeanFactory. This class has only one method, getBean(), which is used to restrict the behavior of subsequent bean container classes.

With such a behavior, considering that subsequent containers may take singleton objects or multiple objects, and the methods of obtaining singleton objects are basically the same, we can design a singleton object container class, so we can use the template method to design an abstract container class AbstractBeanFactory, which inherits BeanFactory and implements getBean(), In order to ensure universality, we need to extract common logic, that is, first obtain the singleton object through the singleton object container. If the singleton object does not exist in the singleton object container, go to the BeanDefinition container to obtain the corresponding BeanDefinition, and then create a bean object. However, we are not sure how to obtain BeanDefinition and create beans in different containers. What should we do? Then we will design these two actions as abstract classes, which will be implemented by the subsequent container classes that inherit this class.

As mentioned above, the methods of obtaining singleton beans are basically the same, but considering the scalability and the principle of single responsibility, we can design a singleton object registration interface BeanDefinitionRegistry to restrict the required behavior of all singleton object managers, and then write a defaultsingleton beanregistry to achieve a method of obtaining singleton objects Two methods for registering singleton objects.

Next, we need to analyze the design of bean container based on annotations. Considering the diversity of annotations, our methods of getBeanDefinition may be different, so we have to write an AbstractAutowireCapableBeanFactory to implement createBean and leave getBeanDefinition to subsequent successors.

Having said so much, we have forgotten an important question. Where does BeanDefinition come from? Where do we store BeanDefinition? Therefore, we also need to design an interface to store BeanDefinition. BeanDefinitionRegistry gives the class that implements the bean container to complete this behavior.

Class diagram

Code structure

code

BeanFactory

package cn.shark.springframework.beans.factory;

import cn.shark.springframework.beans.BeansException;

/**
 * Set the bean factory as the interface to facilitate various subsequent extensions
 */
public interface BeanFactory {

    Object getBean(String name) throws BeansException;


}

AbstractBeanFactory

package cn.shark.springframework.beans.factory.support;


import cn.shark.springframework.beans.BeansException;
import cn.shark.springframework.beans.factory.BeanFactory;
import cn.shark.springframework.beans.factory.config.BeanDefinition;

public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {

    /**
     * Using the template method pattern ensures that the later bean container only needs to focus on its own responsibilities and unify the bean container specification
     * @param name
     * @return
     * @throws BeansException
     */
    public Object getBean(String name) throws BeansException {
        Object singleton = getSingleton(name);
        if (singleton!=null){
            return singleton;
        }
        BeanDefinition beanDefinition = getBeanDefinition(name);
        return createBean(name,beanDefinition);
    }

    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

    /**
     * Create the bean and put it in the singleton container
     * @param beanName
     * @param beanDefinition
     * @return
     * @throws BeansException
     */
    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition)throws BeansException;




}

SingletonBeanRegistry

package cn.shark.springframework.beans.factory.config;

/**
 * Singleton Registry 
 */
public interface SingletonBeanRegistry {

    Object getSingleton(String beanName);
}

DefaultSingletonBeanRegistry

package cn.shark.springframework.beans.factory.support;

import cn.shark.springframework.beans.factory.config.SingletonBeanRegistry;

import java.util.HashMap;
import java.util.Map;

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

    private Map<String, Object> singletonObjects = new HashMap<String, Object>();

    public Object getSingleton(String beanName) {
        return singletonObjects.get(beanName);
    }

    protected void addSingleton(String beanName, Object singletonObject) {
        singletonObjects.put(beanName, singletonObject);
    }
}

AbstractAutowireCapableBeanFactory

package cn.shark.springframework.beans.factory.support;

import cn.shark.springframework.beans.BeansException;
import cn.shark.springframework.beans.factory.config.BeanDefinition;

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

    protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {
        Object bean = null;

        try {
            bean = getBeanDefinition(beanName).getBeanClass().newInstance();
        } catch (InstantiationException e) {
            throw new BeansException("Instantiation of bean failed", e);
        } catch (IllegalAccessException e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        return bean;
    }
}

DefaultListableBeanFactory

package cn.shark.springframework.beans.factory.support;

import cn.shark.springframework.beans.BeansException;
import cn.shark.springframework.beans.factory.config.BeanDefinition;

import java.util.HashMap;
import java.util.Map;

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {

    private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();

    @Override
    protected BeanDefinition getBeanDefinition(String beanName) throws BeansException {
        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
        if (beanDefinition==null){
            throw new BeansException("No bean named '" + beanName + "' is defined");
        }
        return beanDefinition;
    }

    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) {
        beanDefinitionMap.put(beanName, beanDefinition);
    }
}

BeanDefinitionRegistry

package cn.shark.springframework.beans.factory.support;

import cn.shark.springframework.beans.factory.config.BeanDefinition;

public interface BeanDefinitionRegistry {

    void registerBeanDefinition(String beanName, BeanDefinition beanDefinition);
}

test

reference

Chapter 3 - initial skills - Application of design patterns - Implementation - definition of Bean - Registration - acquisition. html

Single responsibility principle

UML Foundation (with drawing tutorial)

Keywords: Java Spring source code mvc

Added by TEENFRONT on Fri, 03 Sep 2021 07:34:44 +0300