1.概述
你有自己实现过简单的 Spring 吗?
Spring 的 IOC 本质就是一个大工厂,我们想想一个工厂是怎么运行的呢?
生产产品
:一个工厂最核心的功能就是生产产品。在 Spring 里,不用 Bean 自己来实例化,而是交给 Spring,应该怎么实现呢?——答案毫无疑问,反射。
那么这个厂子的生产管理是怎么做的?你应该也知道——工厂模式。
库存产品
:工厂一般都是有库房的,用来库存产品,毕竟生产的产品不能立马就拉走。Spring 我们都知道是一个容器,这个容器里存的就是对象,不能每次来取对象,都得现场来反射创建对象,得把创建出的对象存起来。
订单处理
:还有最重要的一点,工厂根据什么来提供产品呢?订单。这些订单可能五花八门,有线上签签的、有到工厂签的、还有工厂销售上门签的……最后经过处理,指导工厂的出货。
在 Spring 里,也有这样的订单,它就是我们 bean 的定义和依赖关系,可以是 xml 形式,也可以是我们最熟悉的注解形式。
我们简单地实现一个 mini 版的 Spring IOC:
Bean 定义:
Bean 通过一个配置文件定义,把它解析成一个类型。
beans.properties
偷懒,这里直接用了最方便解析的 properties,这里直接用一个<key,value>
类型的配置来代表 Bean 的定义,其中 key 是 beanName,value 是 class
userDao:cn.fighter3.bean.UserDao
BeanDefinition.java
bean 定义类,配置文件中 bean 定义对应的实体
public class BeanDefinition {
private String beanName;
private Class beanClass;
//省略getter、setter
}
ResourceLoader.java
资源加载器,用来完成配置文件中配置的加载
public class ResourceLoader {
public static Map<String, BeanDefinition> getResource() {
Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>(16);
Properties properties = new Properties();
try {
InputStream inputStream = ResourceLoader.class.getResourceAsStream("/beans.properties");
properties.load(inputStream);
Iterator<String> it = properties.stringPropertyNames().iterator();
while (it.hasNext()) {
String key = it.next();
String className = properties.getProperty(key);
BeanDefinition beanDefinition = new BeanDefinition();
beanDefinition.setBeanName(key);
Class clazz = Class.forName(className);
beanDefinition.setBeanClass(clazz);
beanDefinitionMap.put(key, beanDefinition);
}
inputStream.close();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return beanDefinitionMap;
}
}
BeanRegister.java
对象注册器,这里用于单例 bean 的缓存,我们大幅简化,默认所有 bean 都是单例的。可以看到所谓单例注册,也很简单,不过是往 HashMap 里存对象。
public class BeanRegister {
//单例Bean缓存
private Map<String, Object> singletonMap = new HashMap<>(32);
/**
* 获取单例Bean
*
* @param beanName bean名称
* @return
*/
public Object getSingletonBean(String beanName) {
return singletonMap.get(beanName);
}
/**
* 注册单例bean
*
* @param beanName
* @param bean
*/
public void registerSingletonBean(String beanName, Object bean) {
if (singletonMap.containsKey(beanName)) {
return;
}
singletonMap.put(beanName, bean);
}
}
BeanFactory.java
对象工厂,我们最核心的一个类,在它初始化的时候,创建了 bean 注册器,完成了资源的加载。
获取 bean 的时候,先从单例缓存中取,如果没有取到,就创建并注册一个 bean
public class BeanFactory {
private Map<String, BeanDefinition> beanDefinitionMap = new HashMap<>();
private BeanRegister beanRegister;
public BeanFactory() {
//创建bean注册器
beanRegister = new BeanRegister();
//加载资源
this.beanDefinitionMap = new ResourceLoader().getResource();
}
/**
* 获取bean
*
* @param beanName bean名称
* @return
*/
public Object getBean(String beanName) {
//从bean缓存中取
Object bean = beanRegister.getSingletonBean(beanName);
if (bean != null) {
return bean;
}
//根据bean定义,创建bean
return createBean(beanDefinitionMap.get(beanName));
}
/**
* 创建Bean
*
* @param beanDefinition bean定义
* @return
*/
private Object createBean(BeanDefinition beanDefinition) {
try {
Object bean = beanDefinition.getBeanClass().newInstance();
//缓存bean
beanRegister.registerSingletonBean(beanDefinition.getBeanName(), bean);
return bean;
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
2.测试
UserDao.java
我们的 Bean 类,很简单
public class UserDao {
public void queryUserInfo(){
System.out.println("A good man.");
}
}
单元测试
public class ApiTest {
@Test
public void test_BeanFactory() {
//1.创建bean工厂(同时完成了加载资源、创建注册单例bean注册器的操作)
BeanFactory beanFactory = new BeanFactory();
//2.第一次获取bean(通过反射创建bean,缓存bean)
UserDao userDao1 = (UserDao) beanFactory.getBean("userDao");
userDao1.queryUserInfo();
//3.第二次获取bean(从缓存中获取bean)
UserDao userDao2 = (UserDao) beanFactory.getBean("userDao");
userDao2.queryUserInfo();
}
}
运行结果
A good man.
A good man.
至此,我们一个乞丐+破船版的 Spring 就完成了,代码也比较完整,有条件的可以跑一下。