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"
- [x] Chapter 1: opening introduction, I want to show you Spring!
- [x] Chapter 2: small test, implement a simple Bean container
- [x] Chapter 3: skills, using design patterns to realize the definition, registration, and acquisition of
- [x] Chapter 4: , based on Cglib to realize the class instantiation strategy with constructor
- [x] Chapter 5: blockbuster, injecting attributes and dependent Bean functions into Bean objects to achieve
- [] Chapter 6: To be archived...
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
operationAbstractAutowireCapableBeanFactory
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
- 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 attributenew 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
- Chapter 1 of "Spring Handbook Column": opening introduction, I want to show you Spring!
- Brother Fu, a code farmer with a "sideline"!
- You said, how do you stuff the Bean into the Spring container?
- How many lines of code do I need to write to get a development job without spending money on training?
- Mathematics, how close is it to a programmer?
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。