The Spring framework is very large and complex. In order to better understand its implementation, I try to imitate its functions and write a simple version. For the time being, it only includes a few simple functions and will be updated based on the situation.
1. Contains features
- @ComponentScan package body scan
- @Autowire dependency injection
- @Scope singleton implementation
- BeanPostProcessor post processor
- InitializingBean initializes the bean interface
- BeanNameAware name resource settings
- BeanDefinition
- SingletonObjects singleton pool
- BeanDefinitionMap pool
- ApplicationContext based on annotation configuration
2. Logic
public class main {
public static void main(String[] args) {
EndwasApplicationContext context = new EndwasApplicationContext(AppConfig.class);
EndwasService endwasService = (EndwasService) context.getBean("endwasService");
endwasService.execute();
}
}
Just like using a normal Spring container, you need to create the container and pass in the configuration class, and then get the beans.
public EndwasApplicationContext(Class<?> configClass) {
if (configClass == null) {
throw new EndwasException("configClass can not null!");
}
this.configClass = configClass;
init();
}
The initialization of the container is divided into three steps:
- Get the package body scanned by the configuration class @ComponentScan, such as com.endwas.context. If it is not set, scan the package body where the class is located.
- Scan the current folder and subfolders for BeanDefinition creation and save metadata.
- The singleton bean creation is saved into the singleton pool singletonObjects.
private void init() {
// 初始化 1、扫描 -> beanDefinitionMap 2、创建到singletonObjects
String packageName = getPackagePath();
// 扫描
scan(packageName);
// 创建bean
createBean();
}
To get the bean, if it is a singleton bean, you only need to get it directly from the singleton pool. If it is not a singleton bean, you can create it when you get it. Both call the createBean method when creating.
private Object createBean(String beanName, BeanDefinition beanDefinition) {
Class<?> clazz = beanDefinition.getClazz();
Object bean = null;
try {
// 1.实例bean
bean = clazz.getDeclaredConstructor().newInstance();
// 2.依赖注入
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Autowired.class)) {
Object value = getBean(field.getName());
field.setAccessible(true);
field.set(bean, value);
}
}
// 3.Aware回调
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
// 如果是BeanPostProcessor则不调用before/after
if (bean instanceof BeanPostProcessor) {
initBean(clazz, bean);
return bean;
}
// 4.beanPostProcessor::postProcessBeforeInitialization
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessBeforeInitialization(bean, beanName);
}
initBean(clazz, bean);
// 6.beanPostProcessor::postProcessAfterInitialization aop动态代理
for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
bean = beanPostProcessor.postProcessAfterInitialization(bean, beanName);
}
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
e.printStackTrace();
}
return bean;
}
The method includes
- Create by calling the default constructor.
- dependency injection
- If aware callback is implemented
- 调用beanPostProcessor::postProcessBeforeInitialization
- Initialize the bean (execute postConstruct, InitializingBean)
- 调用beanPostProcessor::postProcessAfterInitialization
It should be noted here that postConstruct is executed before the post-processor is initialized, and is executed together with afterPropertiesSet.
3.Source code
It has been uploaded to github and is still being improved from time to time.
https://github.com/Endwas/EndwasApplicationContext
4.next
Follow-up plan
- Supports resource, inject and other annotations.
- PostConstruct modifies the implementation location.
- Supports ComponentScan multipath.