学习写简单Spring源码demo

时间:2023-03-09 04:29:15
学习写简单Spring源码demo

最近在研究怎么实现简单的Spring的源码,通过注解的方式来实现对bean的加载管理。

首先先来看下我的工程结构:

学习写简单Spring源码demo

(1)spring-common:定义了常用的枚举常量,工具类(如FileUtils提供了递归找到某个目录下所有文件的具体实现)

(2)spring-frame:是整个框架的具体实现,依赖spring-common

(3)spring-test:提供了一个简单的测试demo

我们先看测试类,这个应该更熟悉,简单得到bean实例的代码:

public static void main(String[] args) {

        // 1. 启动Spring
MyApplicationContext context = new MyApplicationContext(AppConfig.class);
// 2. getBean
OrderService orderService = (OrderService) context.getBean("orderService");
orderService.getUserInfo(); }

其中我们引用了自己定义的MyApplicationContext来启动Spring去装载bean,并对bean进行初始化和实例化。

针对MyApplicationContext, 核心构造方法:

public MyApplicationContext(Class configClass) {
// Spring启动要做什么事情?
// 扫描类 --> 创建非懒加载的单例的bean --> 放入单例池
// 1. 单纯扫描包
List<Class> classList = scanSpecifiedPath(configClass);
// 2. 解析出文件中的bean
initialBeanDefinition(classList);
// 3. 实例化单例的bean
instantiateSingletonBean();
}

再上述看到的三个方法中,主要是使用了我们自己定义的@ComponentScan, @Component, @Scope, @Autowired注解来实现bean的扫描,识别,是否单例,依赖注入;

针对bean的初始化和后置处理,我们定义了同Spring原生的接口:InitializingBean,BeanPostProcessor来实现。

其中,我们特意构造了getBean的方法,核心代码如下:

public Object getBean(String beanName) {
BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);
if (Objects.equals(beanDefinition.getScope(), BaseConstant.Scope.PROTOTYPE)) {
// 重新创建
return doCreateBean(beanName, beanDefinition);
} else if (Objects.equals(beanDefinition.getScope(), BaseConstant.Scope.SINGLETON)) {
Object obj = singletonObjectPool.get(beanName);
if (Objects.isNull(obj)) {
// 创建单例bean
obj = doCreateBean(beanName, beanDefinition);
singletonObjectPool.put(beanName, obj);
}
return obj;
}
return null;
}

可以简单看到,针对Scope为原型的bean,直接创建了一个bean的实例;单例模式的bean会先从单例池中直接获取,否则才会创建之后再加入单例池。

简单描述到这里,更多实现细节和demo测试,见gitee源码:

 https://gitee.com/leijisong/myspring-demo