手撸Spring:定义一个简单的 Spring 容器(Day1)

一、目标:定义一个简单的 Spring 容器

Spring Bean 容器是什么?

Spring 包含并管理应用对象的配置和生命周期,在这个意义上它是一种用于承载对象的容器,你可以配置你的每个 Bean 对象是如何被创建的,这些 Bean 可以创建一个单独的实例或者每次需要时都生成一个新的实例,以及它们是如何相互关联构建和使用的。

当一个 Bean 对象被定义存放以后,再由 Spring 统一进行装配,这个过程包括 Bean 的初始化、属性填充等,最终我们就可以完整的使用一个 Bean 实例化后的对象了。

二、设计:使用HashMap作为 Spring Bean 容器

一个简单的 Spring Bean 容器实现,还需 Bean 的定义、注册、获取三个基本步骤,简化设计如下;

image-20231230030219258

  • 定义:BeanDefinition,可能这是你在查阅 Spring 源码时经常看到的一个类,例如它会包括 singleton、prototype、BeanClassName 等。但目前我们初步实现会更加简单的处理,只定义一个 Object 类型用于存放对象。
  • 注册:这个过程就相当于我们把数据存放到 HashMap 中,只不过现在 HashMap 存放的是定义了的 Bean 的对象信息。
  • 获取:最后就是获取对象,Bean 的名字就是key,Spring 容器初始化好 Bean 以后,就可以直接获取了。

三、实现

1. 工程结构

1
2
3
4
5
6
7
8
9
10
11
12
13
small-spring-step-01
└── src
├── main
│ └── java
│ └── cn.chaos.springframework
│ ├── BeanDefinition.java
│ └── BeanFactory.java
└── test
└── java
└── cn.chaos.springframework.test
├── bean
│ └── UserService.java
└── ApiTest.java

Spring Bean 容器类关系,如图 2-2

image-20231230030342035

Spring Bean 容器的整个实现内容非常简单,也仅仅是包括了一个简单的 BeanFactory 和 BeanDefinition,这里的类名称是与 Spring 源码中一致,只不过现在的类实现会相对来说更简化一些,在后续的实现过程中再不断的添加内容。

  1. BeanDefinition,用于定义 Bean 实例化信息,现在的实现是以一个 Object 存放对象
  2. BeanFactory,代表了 Bean 对象的工厂,可以存放 Bean 定义到 Map 中以及获取。

2. Bean 定义

cn.chaos.springframework.BeanDefinition

1
2
3
4
5
6
7
8
9
10
11
12
13
public class BeanDefinition {

private Object bean;

public BeanDefinition(Object bean) {
this.bean = bean;
}

public Object getBean() {
return bean;
}

}
  • 目前的 Bean 定义中,只有一个 Object 用于存放 Bean 对象。如果感兴趣可以参考 Spring 源码中这个类的信息,名称都是一样的。
  • 不过在后面陆续的实现中会逐步完善 BeanDefinition 相关属性的填充,例如:SCOPE_SINGLETON、SCOPE_PROTOTYPE、ROLE_APPLICATION、ROLE_SUPPORT、ROLE_INFRASTRUCTURE 以及 Bean Class 信息。

3. Bean 工厂

cn.chaos.springframework.BeanFactory

1
2
3
4
5
6
7
8
9
10
11
12
13
public class BeanFactory {

private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

public Object getBean(String name) {
return beanDefinitionMap.get(name).getBean();
}

public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
beanDefinitionMap.put(name, beanDefinition);
}

}
  • 在 Bean 工厂的实现中,包括了 Bean 的注册,这里注册的是 Bean 的定义信息。同时在这个类中还包括了获取 Bean 的操作。
  • 目前的 BeanFactory 仍然是非常简化的实现,但这种简化的实现内容也是整个 Spring 容器中关于 Bean 使用的最终体现结果,只不过实现过程只展示出基本的核心原理。在后续的补充实现中,这个会不断变得庞大。

四、测试

1. 事先准备

cn.chaos.springframework.test.bean.UserService

1
2
3
4
5
6
7
public class UserService {

public void queryUserInfo(){
System.out.println("查询用户信息");
}

}
  • 这里简单定义了一个 UserService 对象,方便我们后续对 Spring 容器测试。

2. 测试用例

cn.chaos.springframework.test.ApiTest

1
2
3
4
5
6
7
8
9
10
11
12
13
@Test
public void test_BeanFactory(){
// 1.初始化 BeanFactory
BeanFactory beanFactory = new BeanFactory();

// 2.注册 bean
BeanDefinition beanDefinition = new BeanDefinition(new UserService());
beanFactory.registerBeanDefinition("userService", beanDefinition);

// 3.获取 bean
UserService userService = (UserService) beanFactory.getBean("userService");
userService.queryUserInfo();
}
  • 在单测中主要包括初始化 Bean 工厂、注册 Bean、获取 Bean,三个步骤,使用效果上贴近与 Spring,但显得会更简化。
  • 在 Bean 的注册中,这里是直接把 UserService 实例化后作为入参传递给 BeanDefinition 的,在后续的陆续实现中,我们会把这部分内容放入 Bean 工厂中实现。

3. 测试结果

1
2
3
查询用户信息

Process finished with exit code 0
  • 通过测试结果可以看到,目前的 Spring Bean 容器案例,已经稍有雏形。

[文章来源]: https://mp.weixin.qq.com/s?__biz=MzIxMDAwMDAxMw==&mid=2650730551&idx=1&sn=47cfff26ce11cc40c7cdc6495e409c91&chksm=8f6111d5b81698c36470c3413a8343c9e28b0494 “《Spring 手撸专栏》第 2 章:小试牛刀,实现一个简单的Bean容器!”


手撸Spring:定义一个简单的 Spring 容器(Day1)
https://xsinxcos.github.io/2023/12/30/手撸Spring:Day1/
作者
xsinxcos(涿)
发布于
2023年12月30日
许可协议