1
头图

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

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

Catalogue of "Spring Handbook Column"

I. Introduction

oversold, dropped orders, idempotent, your program is always not resistant to beating!

Think about it, the operation has been publicizing the activities for seven or eight days, and I am happily waiting for the page to go online on the last day. Suddenly there are a bunch of abnormalities, capital losses, and flashbacks, and the user traffic is fleeting, and I finally want to die. Have your heart!

As far as programming development is concerned, it’s a mess and messy code. Maybe this is the true portrayal of most junior programmers’ daily development. Even with testers’ verification, there will be bugs going online, but it’s just not there at the time. Found it! Because people write the code, there will be errors, even the old code farmer

In terms of program bugs, it will include bugs in the product PRD process, bugs in operational configuration activities, bugs in function implementation during R&D and development, bugs in the process of missing during testing and verification, and bugs in the configuration of operation and maintenance services during the online process. In fact, these can be gradually reduced as much as possible through the development of process specifications and certain R&D experience accumulation.

The other type is the bugs left by communication. Normally, business requirements are raised, product plans are determined, and R&D is implemented. Eventually, UI, testing, operation, architecture, etc. personnel are required to participate in the undertaking of a project. From development to online operation, it is actually difficult to maintain a unified information dissemination among this group of people. For example, in the middle of the project development, the operation stated a new requirement for the product, and the product felt that the function was not large, and then found the corresponding front-end development and logic, but I did not expect that it might also affect the back-end development and test use cases. Although the final function is online, it is not in the range of demand coverage of the entire production, research and testing, and it is invisible to bury a hole.

So, if you want to make your program very resistant to beatings and catch the farmer's three punches, then what you have to do is not just a simple brick-moving farmer!

2. Goal

First of all, let’s review what has been accomplished in these chapters, including: implements a container , defines and registers Bean , instantiated Bean , and implements the creation What else do we lack in object instantiation? In fact, there is still a lack of a question about whether there are attributes in the class. If there are attributes in the class, then the attribute information needs to be filled in when instantiating, so that a complete object is created.

The filling of attributes is not only int, Long, String, but also object attributes that have not yet been instantiated. All of them need to be filled when the Bean is created. But here we will not consider the circular dependency of Bean for the time being, otherwise the entire function will be expanded, so that newcomers will not be able to grasp it when they learn. After the core functions are realized in the follow-up,

Three, design

In view of the attribute filling is to complete the attribute information after the newInstance or Cglib , then you can add the completion attribute method in the createBean method of the AbstractAutowireCapableBeanFactory This part of you can also learn from the Spring source code during the internship. The implementation here is also a simplified version of Spring. The follow-up comparison study will make it easier to understand

  • Attribute filling must be created after the class is instantiated, that is applyPropertyValues operation AbstractAutowireCapableBeanFactory the createBean method of 060bd7daedd2c1.
  • Since we need to fill in property operations when creating a Bean, we need to add PropertyValues information in the bean definition BeanDefinition class.
  • In addition, the filling attribute information also includes the object type of the Bean, that is, you need to define a BeanReference, which is actually a simple Bean name, which is created and filled recursively during the specific instantiation operation, which is the same as the Spring source code implementation. Spring source code BeanReference is an interface

Fourth, realize

1. Engineering structure

small-spring-step-04
└── src
    ├── main
    │   └── java
    │       └── cn.bugstack.springframework.beans
    │           ├── factory
    │           │   ├── factory
    │           │   │   ├── BeanDefinition.java
    │           │   │   ├── BeanReference.java
    │           │   │   └── SingletonBeanRegistry.java
    │           │   ├── support
    │           │   │   ├── AbstractAutowireCapableBeanFactory.java
    │           │   │   ├── AbstractBeanFactory.java
    │           │   │   ├── BeanDefinitionRegistry.java
    │           │   │   ├── CglibSubclassingInstantiationStrategy.java
    │           │   │   ├── DefaultListableBeanFactory.java
    │           │   │   ├── DefaultSingletonBeanRegistry.java
    │           │   │   ├── InstantiationStrategy.java
    │           │   │   └── SimpleInstantiationStrategy.java
    │           │   └── BeanFactory.java
    │           ├── BeansException.java
    │           ├── PropertyValue.java
    │           └── PropertyValues.java
    └── test
        └── java
            └── cn.bugstack.springframework.test
                ├── bean
                │   ├── UserDao.java
                │   └── UserService.java
                └── ApiTest.java

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

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

图 5-2

  • In this chapter, we need to add 3 new classes, BeanReference (class reference), PropertyValue (attribute value), PropertyValues (attribute collection), which are used for class and other types of attribute filling operations.
  • In addition, the modified class is mainly AbstractAutowireCapableBeanFactory , and the attribute filling part is completed in createBean.

2. Define attributes

cn.bugstack.springframework.beans.PropertyValue

public class PropertyValue {

    private final String name;

    private final Object value;

    public PropertyValue(String name, Object value) {
        this.name = name;
        this.value = value;
    }
    
    // ...get/set
}

cn.bugstack.springframework.beans.PropertyValues

public class PropertyValues {

    private final List<PropertyValue> propertyValueList = new ArrayList<>();

    public void addPropertyValue(PropertyValue pv) {
        this.propertyValueList.add(pv);
    }

    public PropertyValue[] getPropertyValues() {
        return this.propertyValueList.toArray(new PropertyValue[0]);
    }

    public PropertyValue getPropertyValue(String propertyName) {
        for (PropertyValue pv : this.propertyValueList) {
            if (pv.getName().equals(propertyName)) {
                return pv;
            }
        }
        return null;
    }

}
  • The function of these two classes is to create a class for transferring attribute information in the class. Because there may be many attributes, it is also necessary to define a collection package.

3. Bean definition completion

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

public class BeanDefinition {

    private Class beanClass;

    private PropertyValues propertyValues;

    public BeanDefinition(Class beanClass) {
        this.beanClass = beanClass;
        this.propertyValues = new PropertyValues();
    }

    public BeanDefinition(Class beanClass, PropertyValues propertyValues) {
        this.beanClass = beanClass;
        this.propertyValues = propertyValues != null ? propertyValues : new PropertyValues();
    }
    
    // ...get/set
}
  • Bean information needs to be passed in the process of Bean registration, which is reflected in the tests in several previous chapters. new BeanDefinition(UserService.class, propertyValues);
  • So in order to give the attribute to the Bean definition, the PropertyValues attribute is filled here, and some simple optimizations are made to the two constructors to avoid the need to judge whether the attribute filling is empty in the for loop later.

4. Bean property filling

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

public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory {

    private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();

    @Override
    protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
        Object bean = null;
        try {
            bean = createBeanInstance(beanDefinition, beanName, args);
            // 给 Bean 填充属性
            applyPropertyValues(beanName, bean, beanDefinition);
        } catch (Exception e) {
            throw new BeansException("Instantiation of bean failed", e);
        }

        addSingleton(beanName, bean);
        return bean;
    }

    protected Object createBeanInstance(BeanDefinition beanDefinition, String beanName, Object[] args) {
        Constructor constructorToUse = null;
        Class<?> beanClass = beanDefinition.getBeanClass();
        Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
        for (Constructor ctor : declaredConstructors) {
            if (null != args && ctor.getParameterTypes().length == args.length) {
                constructorToUse = ctor;
                break;
            }
        }
        return getInstantiationStrategy().instantiate(beanDefinition, beanName, constructorToUse, args);
    }

    /**
     * Bean 属性填充
     */
    protected void applyPropertyValues(String beanName, Object bean, BeanDefinition beanDefinition) {
        try {
            PropertyValues propertyValues = beanDefinition.getPropertyValues();
            for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {

                String name = propertyValue.getName();
                Object value = propertyValue.getValue();

                if (value instanceof BeanReference) {
                    // A 依赖 B,获取 B 的实例化
                    BeanReference beanReference = (BeanReference) value;
                    value = getBean(beanReference.getBeanName());
                }
                // 属性填充
                BeanUtil.setFieldValue(bean, name, value);
            }
        } catch (Exception e) {
            throw new BeansException("Error setting property values:" + beanName);
        }
    }

    public InstantiationStrategy getInstantiationStrategy() {
        return instantiationStrategy;
    }

    public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
        this.instantiationStrategy = instantiationStrategy;
    }

}
  • The content of this class is a bit long, mainly including three methods: createBean, createBeanInstance, applyPropertyValues, here we mainly focus on the applyPropertyValues method called in the createBean method.
  • In applyPropertyValues, the beanDefinition.getPropertyValues() . If you encounter a BeanReference, you need to recursively obtain the Bean instance and call the getBean method.
  • When the dependent Bean object is created, it will recursively return to the current attribute filling. It should be noted here that we have not dealt with the problem of circular dependencies. This part is relatively large and will be added later. BeanUtil.setFieldValue(bean, name, value) is a method in the hutool-all tool class, you can also implement

Five, test

1. Prepare in advance

cn.bugstack.springframework.test.bean.UserDao

public class UserDao {

    private static Map<String, String> hashMap = new HashMap<>();

    static {
        hashMap.put("10001", "小傅哥");
        hashMap.put("10002", "八杯水");
        hashMap.put("10003", "阿毛");
    }

    public String queryUserName(String uId) {
        return hashMap.get(uId);
    }

}

cn.bugstack.springframework.test.bean.UserService

public class UserService {

    private String uId;

    private UserDao userDao;

    public void queryUserInfo() {
        System.out.println("查询用户信息:" + userDao.queryUserName(uId));
    }

    // ...get/set
}
  • Dao and Service are the scenes we often use in our usual development. Inject UserDao into UserService, so that the dependency of Bean attributes can be reflected.

2. Test cases

@Test
public void test_BeanFactory() {
    // 1.初始化 BeanFactory
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();  

    // 2. UserDao 注册
    beanFactory.registerBeanDefinition("userDao", new BeanDefinition(UserDao.class));   

    // 3. UserService 设置属性[uId、userDao]
    PropertyValues propertyValues = new PropertyValues();
    propertyValues.addPropertyValue(new PropertyValue("uId", "10001"));
    propertyValues.addPropertyValue(new PropertyValue("userDao",new BeanReference("userDao")));  

    // 4. UserService 注入bean
    BeanDefinition beanDefinition = new BeanDefinition(UserService.class, propertyValues);
    beanFactory.registerBeanDefinition("userService", beanDefinition);    

    // 5. UserService 获取bean
    UserService userService = (UserService) beanFactory.getBean("userService");
    userService.queryUserInfo();
}
  • Unlike getting the Bean object directly, this time we also need to inject userDao into the Bean container first. beanFactory.registerBeanDefinition("userDao", new BeanDefinition(UserDao.class));
  • Next is the operation of attribute filling, one is the ordinary attribute new PropertyValue("uId", "10001") , and the other is the object attribute new PropertyValue("userDao",new BeanReference("userDao"))
  • The next operation is simple, just get the userService object normally and call the method.

3. Test results

查询用户信息:小傅哥

Process finished with exit code 0
  • From the test results, we can see that our attribute filling has worked, because the Dao method can only be called after the attribute filling, such as: userDao.queryUserName(uId)
  • Then we look at the Debug debugging, whether it has entered the implementation of Bean property filling, as follows:

    • Okay, here is the screenshot. We see that the property filling operation has started. When the property is found to be a BeanReference, you need to obtain and create a Bean instance.

Six, summary

  • In this chapter, we expand the object creation function in the AbstractAutowireCapableBeanFactory class. After the instantiation strategy that depends on whether there is a constructor is completed, we start to supplement the Bean attribute information. When the Bean attribute is a Bean object, recursive processing is required. Finally, reflection operations are needed when filling attributes, and some tool classes can also be used for processing.
  • We are implementing the function points of each chapter step by step, so that newcomers can better accept the design ideas in Spring. Especially in some already developed classes, the design of how to expand new functions is more important. Sometimes when learning programming, learning ideas and design can improve programming thinking more than just simple implementation.
  • In this chapter, the development of the Bean creation operation is completed. Next, we need to complete the loading of resource attributes on the basis of the entire framework. That is, we need to move the Xml configuration, so that our small framework is more and more like Spring. In addition, all class names in the framework implementation process will refer to the Spring source code, and the corresponding design and implementation steps are also corresponding to the Spring source code, but it will simplify some processes, but you can use the same class name to search for each one The realization of the function in the Spring source code.

Seven, series recommendation


小傅哥
4.7k 声望28.4k 粉丝

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