In the process of Spring Bean instance, how to use Bean properties of reflection and recursive processing to fill in?


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

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

Catalogue of Spring hand roll column

1, Foreword

Oversold, lost orders, idempotent, your program is always not resistant to beating!

Think about it. The operation has publicized the activity for seven or eight days, waiting for the page to go online on the last day with joy. Suddenly, there are a lot of abnormalities, asset losses and flashbacks, and the user traffic is fleeting. Finally, people want to die!

As far as programming development is concerned, there are three mistakes and seven mess codes. Maybe this is the true portrayal of the daily development of most junior programmers. Even if there are testers to verify, there will be the phenomenon of going online with bugs, but it was not found at that time! Because people write code, there will be mistakes, even the old code farmers

As far as program bugs are concerned, they will include bugs in product PRD process, bugs in operation configuration activities, bugs in function realization during R & D and development, bugs that miss the process during test and verification, and bugs related to operation and maintenance service configuration during online. In fact, these can be gradually reduced as much as possible through the formulation of process specifications and the accumulation of certain R & D experience.

The other is the Bug left by communication. Usually, business needs, product plans and R & D are implemented. Finally, personnel in UI, testing, operation, architecture and other links need to participate in the undertaking, development and online operation of a project. In fact, it is difficult for this group of people to maintain a unified information dissemination. For example, in the middle of the project development, the operation said a new demand to the product, and the product felt that the function was not large. Then it found the corresponding front-end R & D and added a logic, but it didn't expect that it might also affect the back-end development and test cases. Although the final function is on-line, it is not within the demand coverage of the whole production, research and testing, so it has buried a hole invisibly.

Therefore, if you want your program to be very anti beating and catch the farmer's three punches, then you need to do more than just a simple brick yard farmer!

2, Target

First, let's review what these chapters have accomplished, including: Implement a container,Define and register beans,Instantiate Bean,Implement different instantiation strategies according to whether constructors are included or not So what are we missing in creating object instantiation? In fact, there is still a problem about whether there are attributes in a class. If a class contains attributes, you need to fill in the attribute information when instantiating, so as to create a complete object.

For the filling of attributes, not only int, Long and String, but also the object attributes that have not been instantiated, the filling operation needs to be carried out when the Bean is created. However, we will not consider the cyclic dependence of Bean for the time being, otherwise we will expand the implementation of the whole function, so that newcomers can't grasp it when learning. After the core functions are implemented in succession, they will be gradually improved

3, Design

Since the attribute filling starts to complete the attribute information after the Bean is created using newInstance or Cglib, you can add the completion attribute method to the createBean method of class AbstractAutowireCapableBeanFactory. In this part, you can also learn from the Spring source code during the internship. The implementation here is also a simplified version of Spring, and the follow-up comparative learning will be easier to understand

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

4, 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: official account "bugstack wormhole stack", reply: Spring column, get the complete source code.

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

  • Three new classes need to be added in this chapter, beanreference (class reference), propertyvalue (property value) and propertyvalues (property collection), which are respectively used for class and other types of property filling operations.
  • In addition, the changed class is 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 transmitting attribute information in the class. Because there may be many attributes, you need to define a collection wrapper.

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
}
  • In the process of Bean registration, it is necessary to transfer Bean information, which is reflected in the tests in several previous chapters. new BeanDefinition(UserService.class, propertyValues);
  • Therefore, in order to give the attribute to the Bean definition, the PropertyValues attribute is filled in here, and the two constructors are simply optimized to avoid judging whether the attribute filling is empty in the subsequent for loop.

4. Bean attribute 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);
            // Populate the Bean with attributes
            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 Attribute filling
     */
    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 depends on B and gets the instantiation of B
                    BeanReference beanReference = (BeanReference) value;
                    value = getBean(beanReference.getBeanName());
                }
                // Attribute filling
                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 slightly longer, mainly including three methods: createBean, createBeanInstance, applyPropertyValues, where we mainly focus on the applyPropertyValues method invoked in createBean's method.
  • In applyPropertyValues, by obtaining beandefinition Getpropertyvalues() loop performs the property filling operation. If a BeanReference is encountered, you need to recursively obtain the Bean instance and call the getBean method.
  • When the dependent Bean object is created, it will be returned to the current attribute filling. It should be noted here that we did not deal with the problem of circular dependency. This part is relatively large and will be supplemented later. BeanUtil.setFieldValue(bean, name, value) is a method in hutool all tool class. You can also implement it yourself

5, Testing

1. Preparation in advance

cn.bugstack.springframework.test.bean.UserDao

public class UserDao {

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

    static {
        hashMap.put("10001", "Little brother Fu");
        hashMap.put("10002", "Eight cups of water");
        hashMap.put("10003", "A Mao");
    }

    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("Query user information:" + userDao.queryUserName(uId));
    }

    // ...get/set
}
  • Dao and Service are often used in our daily development. Inject UserDao into UserService to reflect the dependency of Bean attribute.

2. Test cases

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

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

    // 3. UserService setting attribute [uId, userDao]
    PropertyValues propertyValues = new PropertyValues();
    propertyValues.addPropertyValue(new PropertyValue("uId", "10001"));
    propertyValues.addPropertyValue(new PropertyValue("userDao",new BeanReference("userDao")));  

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

    // 5. UserService get bean
    UserService userService = (UserService) beanFactory.getBean("userService");
    userService.queryUserInfo();
}
  • Different from directly obtaining the Bean object, this time we also need to inject userdao into the Bean container. beanFactory.registerBeanDefinition("userDao", new BeanDefinition(UserDao.class));
  • Next is the operation of property filling. One is the general property new PropertyValue("uId", "10001"), and the other is the object property new PropertyValue("userDao",new BeanReference("userDao"))
  • The next operation is simple, just get the userService object normally and call the method.

3. Test results

Query user information: little brother Fu

Process finished with exit code 0
  • From the test results, our property filling has worked, because Dao method can be called only after the property is filled, such as userdao queryUserName(uId)

  • Then, let's check whether the Bean attribute filling of the implementation is entered in the case of Debug debugging, as follows:

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

6, Summary

  • In this chapter, we extend the function of creating objects in the AbstractAutowireCapableBeanFactory class, which depends on whether there is a constructor. After the instantiation strategy is completed, we begin to supplement the Bean attribute information. When the Bean attribute is a Bean object, recursive processing is required. Finally, reflection operation is required 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 new people can better accept the design ideas in Spring. Especially in some developed classes, how to expand new functions is more important. When learning programming, sometimes learning thinking design can improve programming thinking better than just doing simple implementation.
  • In this chapter, the creation of beans is completed. Next, we need to load the resource attributes based on the whole framework, that is, we need to move the Xml configuration to make our small framework more and more like Spring. In addition, in the process of framework implementation, all class names will refer to the Spring source code, and the corresponding design and implementation steps are also corresponding to the Spring source code, which will only simplify some processes, but you can take the same class name to find the implementation of each function in the Spring source code.

7, Series recommendation

Keywords: Spring reflection recursion

Added by steve@MRS on Wed, 02 Feb 2022 04:08:37 +0200