前言
开个新坑,来整点儿大项目。有这个想法是因为用了那么久的Spring,但是某一天突然冒出来一个念头:Spring到底是怎么实现这些功能的。发现脑子一片空白。在我抽出纸巾擦干脑门儿上的汗之后,我决定去网上搜索Spring源码解读。但是找来找去,要么讲的乱七八糟,要么就是过于庞大,一下子堆到面前,看不下去。终于,找到了一个还不错的教程,作者基于Spring框架,自己手写了一个简化版的Spring,而且代码的命名、类之间的关系,都是根据真正的Spring来的,只不过有所简化。我想,这就是我要找的教程了。所以我也跟着一边学习,一边在这里记录自己的学习理解,也算是有所沉淀。
在这里说一下原作者的博客地址:https://bugstack.cn
作者叫小傅哥,教程的名字叫《手撸Spring》,也算个版权声明以及宣传吧~
教程的一开始,还是非常简单的。这也是吸引我学习下去的原因,由浅入深,逐步深入,才是正确的学习路线。在这一章,我们要搭建起一个简单的Bean容器,实现Bean的注册和获取。
工程结构
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─akitsuki
│ │ │ └─springframework
│ │ │ BeanDefinition.java
│ │ │ BeanFactory.java
│ │ │
│ │ └─resources
│ └─test
│ └─java
│ └─com
│ └─akitsuki
│ └─springframework
│ └─test
│ │ ApiTest.java
│ │
│ └─bean
│ UserService.java
看这里的工程目录,好像只有两个类。没错,我们一开始要做的就是这么多,两个类,就可以实现一个简单的Bean容器。
Bean定义
首先是我们两个类中的第一个类:BeanDefinition。
package com.akitsuki.springframework;
/**
* @author ziling.wang@hand-china.com
* @date 2022/11/7 9:28
*/
public class BeanDefinition {
private Object bean;
public BeanDefinition(Object bean) {
this.bean = bean;
}
public Object getBean() {
return bean;
}
}
这里可能会觉得这个BeanDefinition有些莫名其妙,里面只有一个Object类型的属性bean。顾名思义,这里应该是用来放置我们实例化好的bean的。但实际上,这里是因为过于简化的结果。在后面的学习中,我们会逐渐扩充BeanDefinition的内容,比如加入bean的class、bean的属性和值等等内容。同时也会将现在的实例化bean进行移除。
所以BeanDefinition到底是用来做什么的呢,我理解,这个类是用来包装bean的一些“参数”的。bean的class是什么,有哪些属性,属性的初始化值是哪些,有没有初始化方法、销毁方法之类。就像它的名字一样,bean的定义,这个bean应该是什么样子的,由这个类来规定。
Bean工厂
看到这个名字,大家肯定都很清楚了,生产bean的工厂,还用到了工厂模式。
package com.akitsuki.springframework;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author ziling.wang@hand-china.com
* @date 2022/11/7 9:30
*/
public class BeanFactory {
private Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();
public Object getBean(String beanName) {
return beanDefinitionMap.get(beanName).getBean();
}
public void registerBeanDefinition(String name, BeanDefinition beanDefinition) {
beanDefinitionMap.put(name, beanDefinition);
}
}
这个类的内容就比刚才的要复杂一些了,首先有这么两个方法:getBean
和 registerBeanDefinition
。而且,内部维护了一个Map,用来存放bean定义。
既然是Bean的工厂,那么自然是要用来生产bean了。这里有个很有意思的地方,就是内部维护的map,不是存放bean的,而是存放bean定义的。从这里我们也可以看出,bean定义是我们实例化bean的一个模子的概念。我们先要把bean定义好,才能去实现它。
首先我们看 getBean
方法,这个方法是用来获取bean的。方法的内容则是从map中通过bean的名称,来获取相应的bean定义。这里是用bean的名称作为map的key来与bean定义进行关联。一开始我是有一些疑惑的,为什么bean的名称不是bean定义中的一个属性。但后来我考虑,bean定义中的内容,都是模板的内容,只有最终实例化好了的bean,才能有一个名称来与之对应,一个模板可以生产多个产品,也就是bean,所以bean的名称不能作为bean定义中的一个属性
然后我们看 registerBeanDefinition
。这个方法是用来注册bean定义的。我们要先把一个bean定义注册到Spring中,才能根据这个定义来实例化具体的bean。这个注册具体的体现方式,自然就是我们的各种bean配置文件以及注解了。当然我们现在还没有发展到那一步,在后面会去继续完善的。
测试!
首先,我们要有一个等待交给Spring管理的bean
package com.akitsuki.springframework.test.bean;
/**
* @author ziling.wang@hand-china.com
* @date 2022/11/7 9:36
*/
public class UserService {
public void queryUserInfo() {
System.out.println("查询用户信息");
}
}
非常简单,但它确实可以作为一个bean,而且有一个方法可以对外提供服务,嗯,你已经是一个合格的bean了。
然后,我们就要来编写具体的测试方法了
package com.akitsuki.springframework.test;
import com.akitsuki.springframework.BeanDefinition;
import com.akitsuki.springframework.BeanFactory;
import com.akitsuki.springframework.test.bean.UserService;
import org.junit.Test;
/**
* @author ziling.wang@hand-china.com
* @date 2022/11/7 9:37
*/
public class ApiTest {
@Test
public void testBeanFactory() {
//初始化BeanFactory
BeanFactory beanFactory = new BeanFactory();
//注册Bean
BeanDefinition beanDefinition = new BeanDefinition(new UserService());
beanFactory.registerBeanDefinition("userService", beanDefinition);
//获取Bean
UserService userService = (UserService) beanFactory.getBean("userService");
userService.queryUserInfo();
}
}
可以看到,这里我们先初始化了BeanFactory,然后新建了一个bean定义,并将其注册到了factory中。最后再由factory生产出这个bean。
可以看到,这时候我们的Spring已经初具雏形了,虽然是各方面都经过简化的结果,但我们已经完成了bean的注册、生产、获取等操作。在后面,我们会逐步完善,把这些操作变成自动化、读取配置文件来进行,让我们的小demo逐渐的更加接近真正的Spring。
相关源码可以参考我的gitee:https://gitee.com/akitsuki-kouzou/mini-spring
,这里对应的代码是mini-spring-01