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, realize 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 implement a class instantiation strategy with constructor
- [] Chapter 5: Filling Bean Objects with Attribute Information
- [] Chapter 6: To be archived...
I. Introduction
growth of technology is the continuous engraving of the details of the scene design!
When do you think your technology has been rapidly improved? After writing more CRUD? Don't even think about it, it's absolutely impossible! No matter how much CRUD writes, it can only satisfy you as a brick-moving tool, and the speed of hitting less logical pipeline code, and the programming ability, except for the initial from unskilled to proficient, there are very few other things. Promoted.
Then you might think what is the improvement of programming ability? In fact, the improvement of more programming ability is your architectural control of complex scenarios and the continuous use of large-scale traffic impact verification for each technical implementation detail. Whether you can ensure the stable operation of the system and determine how much you have seen , How much has been learned, how much has been improved!
Finally, when you receive a product request, start thinking about the design of the program data structure, the algorithm logic realization of the
core functions, the use of the
overall service design pattern, the
system architecture, the
application cluster deployment structure, then That is when your programming ability is really improved!
2. Goal
The goal of this chapter is mainly to solve the pit buried by previous chapter What of pit is that? In fact, it is a pit about the instantiation of Bean objects with constructors.
In the previous chapter, we expanded the function of the Bean container and handed the instantiated object to the container for unified processing, but we did not consider whether the object class contains a constructor in the code of our instantiated object, which means that if we go to the instance If an object with a constructor is used, then an exception will be thrown.
How to verify? In fact, it is enough to add UserService with a constructor containing parameter information, as follows:
public class UserService {
private String name;
public UserService(String name) {
this.name = name;
}
// ...
}
The error is as follows:
java.lang.InstantiationException: cn.bugstack.springframework.test.bean.UserService
at java.lang.Class.newInstance(Class.java:427)
at cn.bugstack.springframework.test.ApiTest.test_newInstance(ApiTest.java:51)
...
The main reason for this phenomenon is that the beanDefinition.getBeanClass().newInstance();
does not consider the parameters of the constructor, so this pit is waiting for you here! Then our goal is obvious, to fill this hole!
Three, design
The technical design of filling this hole mainly considers two parts. One is how to transfer the input parameter information of the constructor to the instantiation operation in a reasonable way from the string process, and the other is how to instantiate the object containing the constructor.
- Refer to the implementation of the Spring Bean container source code, add the
Object getBean(String name, Object... args)
interface to the BeanFactory, so that the input parameter information of the constructor can be passed in when the Bean is obtained. - Another core content is what method is used to create a Bean object with a constructor? There are two ways to choose, one is based on Java's own method
DeclaredConstructor
, and the other is to use Cglib to dynamically create Bean objects. Cglib is implemented based on the bytecode framework ASM, so you can also directly create objects through the ASM operation instruction code
Fourth, realize
1. Engineering structure
small-spring-step-03
└── src
├── main
│ └── java
│ └── cn.bugstack.springframework.beans
│ ├── factory
│ │ ├── factory
│ │ │ ├── BeanDefinition.java
│ │ │ └── SingletonBeanRegistry.java
│ │ ├── support
│ │ │ ├── AbstractAutowireCapableBeanFactory.java
│ │ │ ├── AbstractBeanFactory.java
│ │ │ ├── BeanDefinitionRegistry.java
│ │ │ ├── CglibSubclassingInstantiationStrategy.java
│ │ │ ├── DefaultListableBeanFactory.java
│ │ │ ├── DefaultSingletonBeanRegistry.java
│ │ │ ├── InstantiationStrategy.java
│ │ │ └── SimpleInstantiationStrategy.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 complete source code
Spring Bean container class relationship, as shown in Figure 4-2
This chapter "filling the hole" is mainly to add the InstantiationStrategy instantiation strategy interface to the existing project, and supplement the corresponding getBean input parameter information, so that the input parameters of the constructor can be passed and instantiated smoothly when external calls are made.
2. Add the getBean interface
cn.bugstack.springframework.beans.factory.BeanFactory
public interface BeanFactory {
Object getBean(String name) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
}
- In BeanFactory, we overload a getBean method that contains the input parameter information args, so that it is convenient to pass the input parameters to the constructor for instantiation.
3. Define the instantiation strategy interface
cn.bugstack.springframework.beans.factory.support.InstantiationStrategy
public interface InstantiationStrategy {
Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException;
}
- Add the necessary input parameter information in the instantiate method of the instantiated interface, including: beanDefinition, beanName, ctor, args
- The Constructor may be a little unfamiliar to you. It is the Constructor class under the java.lang.reflect package, which contains some necessary class information. The purpose of this parameter is to get the constructor corresponding to the input parameter information.
- And args is a specific input parameter information, which will be used in the final instantiation.
4. JDK instantiation
cn.bugstack.springframework.beans.factory.support.SimpleInstantiationStrategy
public class SimpleInstantiationStrategy implements InstantiationStrategy {
@Override
public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
Class clazz = beanDefinition.getBeanClass();
try {
if (null != ctor) {
return clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);
} else {
return clazz.getDeclaredConstructor().newInstance();
}
} catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException e) {
throw new BeansException("Failed to instantiate [" + clazz.getName() + "]", e);
}
}
}
- First obtain the Class information through beanDefinition, which is passed in when the Bean is defined.
- Next, determine whether the ctor is empty, if it is empty, it means instantiation without a constructor, otherwise, it needs instantiation with a constructor.
- Here we focus on instantiation with a constructor, the instantiation method is
clazz.getDeclaredConstructor(ctor.getParameterTypes()).newInstance(args);
, and the input parameter information is passed to newInstance for instantiation.
5. Cglib instantiation
cn.bugstack.springframework.beans.factory.support.CglibSubclassingInstantiationStrategy
public class CglibSubclassingInstantiationStrategy implements InstantiationStrategy {
@Override
public Object instantiate(BeanDefinition beanDefinition, String beanName, Constructor ctor, Object[] args) throws BeansException {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(beanDefinition.getBeanClass());
enhancer.setCallback(new NoOp() {
@Override
public int hashCode() {
return super.hashCode();
}
});
if (null == ctor) return enhancer.create();
return enhancer.create(ctor.getParameterTypes(), args);
}
}
- In fact, Cglib is also very convenient to create a bean with a constructor. Here we have simplified the processing. If you read the Spring source code, you will also see implementations such as CallbackFilter, but our current method will not affect the creation.
6. Create a policy call
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);
} 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);
}
}
InstantiationStrategy instantiationStrategy
creating objects is defined in the AbstractAutowireCapableBeanFactory abstract class. Here we choose the Cglib implementation class.- Next, extract the
createBeanInstance
method. In this method, you need to pay attention to how many constructors you have. You can get all your constructors through beanClass.getDeclaredConstructors(), which is a collection. - Next, we need to cyclically compare the match between the constructor set and the
args
parameter information 060b6ed380f3b7. Here we compare the method is relatively simple, just a quantitative comparison, and the actual Spring
The source code also needs to compare the input parameter types, otherwise an exception will be thrown in the case of the same number of different input parameter types.
Five, test
1. Prepare in advance
cn.bugstack.springframework.test.bean.UserService
public class UserService {
private String name;
public UserService(String name) {
this.name = name;
}
public void queryUserInfo() {
System.out.println("查询用户信息:" + name);
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("");
sb.append("").append(name);
return sb.toString();
}
}
- The only thing that is added to UserService here is a constructor with name as a parameter, so that we can verify whether such an object can be instantiated.
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();
}
- 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
- From the test results, the biggest change is that objects with constructors can be instantiated.
- You can try to use two different instantiation strategies to instantiate.
SimpleInstantiationStrategy
,CglibSubclassingInstantiationStrategy
4. Operation case
Here we put several different methods of instantiation operations into the unit test, so that everyone can compare and learn.
4.1 No constructor
@Test
public void test_newInstance() throws IllegalAccessException, InstantiationException {
UserService userService = UserService.class.newInstance();
System.out.println(userService);
}
- This method of instantiation is also the method we used directly when we implemented the Spring Bean container in the previous chapter.
4.2 Verify that the constructor is instantiated
@Test
public void test_constructor() throws Exception {
Class<UserService> userServiceClass = UserService.class;
Constructor<UserService> declaredConstructor = userServiceClass.getDeclaredConstructor(String.class);
UserService userService = declaredConstructor.newInstance("小傅哥");
System.out.println(userService);
}
- From the simplest operation point of view, if a class with a constructor function needs to be instantiated, you need to use
getDeclaredConstructor
obtain the constructor function, and then instantiate it by passing parameters.
4.3 Get constructor information
@Test
public void test_parameterTypes() throws Exception {
Class<UserService> beanClass = UserService.class;
Constructor<?>[] declaredConstructors = beanClass.getDeclaredConstructors();
Constructor<?> constructor = declaredConstructors[0];
Constructor<UserService> declaredConstructor = beanClass.getDeclaredConstructor(constructor.getParameterTypes());
UserService userService = declaredConstructor.newInstance("小傅哥");
System.out.println(userService);
- In fact, the core point in this case is to get all the constructors in a class, which is actually the use of this method
beanClass.getDeclaredConstructors()
4.4 Cglib instantiation
@Test
public void test_cglib() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(UserService.class);
enhancer.setCallback(new NoOp() {
@Override
public int hashCode() {
return super.hashCode();
}
});
Object obj = enhancer.create(new Class[]{String.class}, new Object[]{"小傅哥"});
System.out.println(obj);
}
- This case demonstrates the use of very simple, but about the use of Cglib in the Spring container is very much, you can also learn about the extended knowledge of Cglib.
Six, summary
- The main purpose of this chapter is to improve the instantiation operation, increase the InstantiationStrategy instantiation strategy interface, and add two new instantiation classes. The name and implementation of this part of the class is basically a reduced version of the Spring framework. You can also find the corresponding code from the Spring source code during the learning process.
- As we can see from our continuous improvement and increasing requirements, when your code structure is designed more reasonable, it is very easy and convenient to extend the class responsibilities of different attributes without causing confusion in the class structure due to the increase in requirements. . Therefore, in the process of realizing our own business requirements, we must also consider a good scalability and split the responsibilities of good classes as much as possible.
- Hands-on is the fastest way to learn. Don't let your eyes feel like you can see it, but the hands-on operation is useless. I also hope that readers in need can do it with their hands, incorporate your ideas into the code that can be implemented, and see if what they think is consistent with what they do.
Seven, series recommendation
- Face-to-face Manual · Part 1 " of Technology Stack" 160b6ed380f8e3)
- The middle stage that just got fired turned around and was dismantled, and a large wave of companies couldn't put it down or pick it up!
- working for two or three years, and I don’t understand what the architecture diagram is all about.
- understanding of domain-driven design DDD landing
- Interview scene: sharing and analysis of the small partner
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。