3
头图

Author: Xiao Fu Ge
Blog: https://bugstack.cn

Precipitate, share, and grow, so that you and others can gain something! 😄

I. Introduction

Can you foresee the design problems of complex content?

To be fair, no matter whether the product function is complex or not, a large part of programmers will write a bunch of if...else to complete the development and goes online smoothly. This is mainly because it is impossible to foresee the current needs, whether the development is long-term, whether the flow is huge, and whether the iteration is rapid, so in the case of being urged to go online, it is impossible not to write if...else!

Then you said, since if...else is implemented so fast, do you still consider data structure, algorithm logic, design pattern, and system architecture? Of course, this basically depends on how long your project can live in the foreseeable future. If a project survives for at least one year, it will continue to iterate during this year. It's like; you have created a marketing coupon system that issues various types of coupons under various conditions. If you did not consider the system design and architecture model at the beginning, then when activities frequently occur, traffic surges, Under the iteration of requirements, you may end up with system accidents in the end!

We are focusing the system design perspective on specific code implementation. What means will you have to achieve the design pattern you want? In fact, the coding method mainly relies on: interface definition, class implementation interface, abstract class implementation interface, inherited class, inherited abstract class, and these operation methods can well isolate the basic functions, general functions and business functions of each class. After the responsibilities of the class are clear, your entire design will become easy to expand and iterate.

Next, in this chapter, continue to improve the functional development of the Spring Bean container framework. In this development process, more interfaces, classes, and abstract classes will be used, and there will be class implementation and class inheritance between them. You can refer to the development and implementation of this part of the content carefully. Although it is not very complicated, this design idea can be reused in our own business system development.

2. Goal

In the previous chapter "A small test, implement a simple Bean container" we initially implemented a rough version of the code implementation based on the concept of the Spring Bean container. Then in this chapter, we need to combine the implemented Spring Bean container to complete the function and realize the registration and acquisition of Bean objects by the Bean container.

This time we give the creation of the Bean to the container instead of passing an instantiated Bean object when calling. In addition, we also need to consider the singleton object. The object can be obtained from memory during the second acquisition of the object. In addition, not only to realize the functions, but also to improve the class structure of the basic container framework, otherwise it will be difficult to expand into other functions in the future.

Three, design

In view of the case goals of this chapter, we need to complete the Spring Bean container. The first very important point is that only one class information is registered when the Bean is registered, and the instantiation information is not directly registered in the Spring container. Then you need to modify the attribute Object in the BeanDefinition to Class. The next thing you need to do is to process the instantiation operation of the Bean object and determine whether the current singleton object has been cached in the container when obtaining the Bean object. The overall design is shown in Figure 3-1

  • First of all, we need to define a Bean factory such as BeanFactory and provide a method of obtaining Bean getBean(String name) . After that, this Bean factory interface is implemented by the abstract class AbstractBeanFactory. In this way, the use of the template pattern design method can unify the calling logic and standard definitions of the common core methods, and it is well controlled that subsequent implementers do not need to care about the calling logic and execute in a unified manner. Then the inheritors of the class only need to care about the logical implementation of the specific method.
  • Then the AbstractAutowireCapableBeanFactory that inherits the abstract class AbstractBeanFactory can implement the corresponding abstract methods, because AbstractAutowireCapableBeanFactory itself is also an abstract class, so it only implements its own abstract methods, and other abstract methods are implemented by classes that inherit AbstractAutowireCapableBeanFactory. Here is the performance of each role in the class implementation process, you only need to care about the content that belongs to you, not your content, don't participate. This part of the content we will have a specific reflection in the code
  • In addition, there is a very important point of knowledge here, which is about the implementation of the interface definition of the singleton SingletonBeanRegistry, and after the DefaultSingletonBeanRegistry implements the interface, it will be inherited by the abstract class AbstractBeanFactory. Now AbstractBeanFactory is a very complete and powerful abstract class, and it can also very well reflect its abstract definition of the template pattern. Next, we will take these design-level thinking to see the specific implementation results of the code

Fourth, realize

1. Engineering structure

small-spring-step-02
└── src
    ├── main
    │   └── java
    │       └── cn.bugstack.springframework.beans
    │           ├── factory
    │           │   ├── factory
    │           │   │   ├── BeanDefinition.java
    │           │   │   └── SingletonBeanRegistry.java
    │           │   ├── support
    │           │   │   ├── AbstractAutowireCapableBeanFactory.java
    │           │   │   ├── AbstractBeanFactory.java
    │           │   │   ├── BeanDefinitionRegistry.java
    │           │   │   ├── DefaultListableBeanFactory.java
    │           │   │   └── DefaultSingletonBeanRegistry.java
    │           │   └── BeanFactory.java
    │           └── BeansException.java
    └── test
        └── java
            └── cn.bugstack.springframework.test
                ├── bean
                │   └── UserService.java
                └── ApiTest.java

project source code : public account "bugstack wormhole stack", reply: Spring column, get the source code

Spring Bean container class relationship, as shown in Figure 3-2

图 3-2

Although there are still many gaps between the functional implementation of the Spring Bean container and the Spring source code in this chapter, judging from the class relationship diagram of the current implementation results, it actually has a certain design complexity. These complex classes The relationship design is reflected in the definition and implementation of various interfaces and in the inheritance of abstract classes, for example:

  • The definition of BeanFactory is implemented by the abstract class of AbstractBeanFactory to implement the getBean method of the interface
  • AbstractBeanFactory inherits the DefaultSingletonBeanRegistry class that implements SingletonBeanRegistry. In this way, the AbstractBeanFactory abstract class has the registration function of a singleton bean.
  • AbstractBeanFactory defines two abstract methods: getBeanDefinition(String beanName), createBean(String beanName, BeanDefinition beanDefinition), and these two abstract methods are implemented by DefaultListableBeanFactory and AbstractAutowireCapableBeanFactory respectively.
  • In the end, DefaultListableBeanFactory will inherit the abstract class AbstractAutowireCapableBeanFactory so that it can call the createBean method in the abstract class.

In summary, the class relationship and implementation process of this part will still be somewhat complicated, because all implementations are based on the division of responsibilities, the separation of commonality, and the definition of the calling relationship as standard class relationships. this part of 160aefd2e3f487 may enrich your design ideas in the development of complex business systems.

2. BeanDefinition definition

cn.bugstack.springframework.beans.factory.config.BeanDefinition

public class BeanDefinition {

    private Class beanClass;

    public BeanDefinition(Class beanClass) {
        this.beanClass = beanClass;
    }
        // ...get/set
}
  • In the Bean definition class, the Object bean in the previous chapter has been replaced with Class, so that the instantiation operation of the Bean can be processed in the container. If you have read the previous chapter carefully and did the corresponding test, then you will find that the instantiation operation of the Bean is passed to the BeanDefinition constructor in the initialization call phase.

3. Singleton registration interface definition and implementation

cn.bugstack.springframework.beans.factory.config.SingletonBeanRegistry

public interface SingletonBeanRegistry {

    Object getSingleton(String beanName);

}
  • This class is relatively simple and mainly defines an interface for obtaining singleton objects.

cn.bugstack.springframework.beans.factory.config.DefaultSingletonBeanRegistry

public class DefaultSingletonBeanRegistry implements SingletonBeanRegistry {

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

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

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

}
  • In DefaultSingletonBeanRegistry, the getSingleton method is mainly implemented, and a protected addSingleton method is also implemented. This method can be called by other classes that inherit this class. Including: AbstractBeanFactory and inherited DefaultListableBeanFactory calls.

4. Abstract class definition template method (AbstractBeanFactory)

cn.bugstack.springframework.beans.factory.support.AbstractBeanFactory

public abstract class AbstractBeanFactory extends DefaultSingletonBeanRegistry implements BeanFactory {

    @Override
    public Object getBean(String name) throws BeansException {
        Object bean = getSingleton(name);
        if (bean != null) {
            return bean;
        }

        BeanDefinition beanDefinition = getBeanDefinition(name);
        return createBean(name, beanDefinition);
    }

    protected abstract BeanDefinition getBeanDefinition(String beanName) throws BeansException;

    protected abstract Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException;

}
  • AbstractBeanFactory first inherited DefaultSingletonBeanRegistry, and also has the method of using singleton registration class.
  • The next very important point is about the implementation of the interface BeanFactory, which can be seen in the implementation of the method getBean, mainly for the acquisition of the singleton Bean object and the definition of the Bean when it is not available.

Bean instantiation operation. Then getBean does not implement these methods by itself, but only defines the calling process and provides abstract methods, which are implemented by other classes that implement this abstract class.

  • There are two subsequent classes that inherit the abstract class AbstractBeanFactory, including: AbstractAutowireCapableBeanFactory and DefaultListableBeanFactory. These two classes have been implemented correspondingly, and then look down.

5. Instantiate the Bean class (AbstractAutowireCapableBeanFactory)

cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition) throws BeansException {
        Object bean = null;
        try {
            bean = beanDefinition.getBeanClass().newInstance();
        } catch (InstantiationException | IllegalAccessException e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        addSingleton(beanName, bean);
        return bean;
    }

}
  • newInstance is implemented in the AbstractAutowireCapableBeanFactory class. In fact, this block will bury a hole. How to deal with objects with constructor parameters? can think ahead
  • After processing the instantiation of the Bean object, directly call the addSingleton method to store it in the cache of the singleton object.

6. Core class implementation (DefaultSingletonBeanRegistry)

cn.bugstack.springframework.beans.factory.support.DefaultSingletonBeanRegistry

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory implements BeanDefinitionRegistry {

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

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

    @Override
    public 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;
    }

}
  • DefaultListableBeanFactory is also a very core class in the Spring source code. In our current implementation, it is gradually getting closer to the source code and keeping the same with the source code class name.
  • DefaultListableBeanFactory inherits the AbstractAutowireCapableBeanFactory class, and also has a series of functional implementations such as the interface BeanFactory and AbstractBeanFactory. So sometimes you will see some classes forced to transfer, call certain methods, it is also because the class you forced to implement interfaces or inherit some classes.
  • In addition, this class also implements the registerBeanDefinition(String beanName, BeanDefinition beanDefinition) method in the interface BeanDefinitionRegistry. Of course, you will also see an implementation of getBeanDefinition, which we mentioned in the article is an abstraction defined in the abstract class AbstractBeanFactory method. Now that you can register Bean definitions and get Bean definitions at the same time, you don't feel that this routine is quite deep. The interface defines the registration, and the abstract class defines the acquisition, all concentrated in the beanDefinitionMap 160aefd2e3f75c in

Five, test

1. Prepare in advance

cn.bugstack.springframework.test.bean.UserService

public class UserService {

    public void queryUserInfo(){
        System.out.println("查询用户信息");
    }

}
  • Here is a simple definition of a UserService object to facilitate our subsequent testing of the Spring container.

2. Test cases

cn.bugstack.springframework.test.ApiTest

@Test
public void test_BeanFactory(){
    // 1.初始化 BeanFactory
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
    // 2.注册 bean
    BeanDefinition beanDefinition = new BeanDefinition(UserService.class);
    beanFactory.registerBeanDefinition("userService", beanDefinition);
    // 3.第一次获取 bean
    UserService userService = (UserService) beanFactory.getBean("userService");
    userService.queryUserInfo();
    // 4.第二次获取 bean from Singleton
    UserService userService_singleton = (UserService) beanFactory.getBean("userService");
    userService_singleton.queryUserInfo();
}
  • In this unit test, in addition to the three steps: Bean Factory, Registering Bean, and Obtaining Bean, it also adds an additional object acquisition and call. The main test here is to verify whether the singleton object is correctly stored in the cache.
  • In addition, unlike the test process in the previous chapter, we passed UserService.class to BeanDefinition instead of directly using the new UserService() operation as in the previous chapter.

3. Test results

查询用户信息
查询用户信息

Process finished with exit code 0
  • There will be two test information here, one is the object created directly when the Bean is obtained, and the other is the instantiated object obtained from the cache.
  • In addition, you can also see from the screenshots of debugging that the singleton object is obtained for the second time, which can already be obtained from the memory, as shown in Figure 3-3.
    图 3-3
  • By this chapter, the function implementation and test verification are completed, and you can go to breakpoints to debug the calls of each stage class during the test process, and be familiar with the call relationships.

Six, summary

  • Compared with the simple concept implementation of the Spring Bean container in the previous chapter, this chapter has strengthened the improvement of functions. In the process of implementation, you can also see that the relationship between classes has become more and more. If you have not done some slightly complicated system class system, then even now such a container factory built out of 9 classes can give you a stunner. .
  • In the implementation classes of the Spring Bean container, we should focus on the responsibilities and relationships between classes. Almost all program function designs are inseparable from interfaces, abstract classes, implementation, and inheritance, and the use of these different characteristic classes can be very good Isolate the functional responsibilities and scope of the class. And such knowledge points are also very important knowledge in the process of learning handwritten Spring Bean container framework.
  • Finally, I want to emphasize the learning of the entire series. In the process of learning, you may encounter very simple code implementations like the second chapter, but to be a growing programmer, remember that the code implementation is only the final landing. As a result, those design considerations are the most valuable place. like whether you have ever encountered it. Someone asks you to describe, document, and explain a content. You always feel that it is too simple and nothing to write. Even if you want to write, you don't know where to start! In fact, these knowledge content comes from your understanding of the overall function, which is not only code development, but also includes procedural content such as requirement goals, program design, technical implementation, and logic verification. So, don't just ignore the overall view of the seemingly simple content, learn to let go of your vision and open your learning perspective.

Seven, series recommendation


小傅哥
4.7k 声望28.4k 粉丝

CodeGuide | 程序员编码指南 - 原创文章、案例源码、资料书籍、简历模版等下载。