五分钟,手撸一个简单的Spring容器
工厂和Spring容器
Spring是一个成熟的框架,为了满足扩展性、实现各种功能,所以它的实现如同枝节交错的大树一样,现在让我们把视线从Spring本身移开,来看看一个萌芽版的Spring容器怎么实现。
Spring的IOC本质就是一个大工厂,我们想想一个工厂是怎么运行的呢?
————————————————
生产产品:一个工厂最核心的功能就是生产产品。在Spring里,不用Bean自己来实例化,而是交给Spring,应该怎么实现呢?——答案毫无疑问,反射。
那么这个厂子的生产管理是怎么做的?你应该也知道——工厂模式。
库存产品:工厂一般都是有库房的,用来库存产品,毕竟生产的产品不能立马就拉走。Spring我们都知道是一个容器,这个容器里存的就是对象,不能每次来取对象,都得现场来反射创建对象,得把创建出的对象存起来。
订单处理:还有最重要的一点,工厂根据什么来提供产品呢?订单。这些订单可能五花八门,有线上签签的、有到工厂签的、还有工厂销售上门签的……最后经过处理,指导工厂的出货。
在Spring里,也有这样的订单,它就是我们bean的定义和依赖关系,可以是xml形式,也可以是我们最熟悉的注解形式。
————————————————
涉及到需要的事物有:
订单:Bean定义
Bean可以通过一个配置文件定义,我们会把它解析成一个类型。
userDao = com.example.apidemo.spring.UserDao
BeanConfig.java
bean定义类,配置文件中bean定义对应的实体
package com.example.apidemo.spring; import lombok.Data; /** * bean定义类,配置文件中bean定义对应的实体 */ @Data public class BeanConfig { private String beanName; private Class beanClass; //省略getter、setter }
获取订单:资源加载
接下订单之后,就要由销售向生产部门交接,让生产部门知道商品的规格、数量之类。
资源加载器,就是来完成这个工作的,由它来完成配置文件中配置的加载。
/** * 获取订单:资源加载 * @return */ public class ResourceLoader { public static Map<String, BeanConfig> getResource() { Map<String, BeanConfig> beanDefinitionMap = new HashMap<>(16); Properties properties = new Properties(); try { //配置beans.properties文件: userDao = com.example.apidemo.spring.UserDao 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); BeanConfig beanConfig = new BeanConfig(); beanConfig.setBeanName(key); Class clazz = Class.forName(className); beanConfig.setBeanClass(clazz); beanDefinitionMap.put(key, beanConfig); } inputStream.close(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } return beanDefinitionMap; } }
订单分配:Bean注册
对象注册器,这里用于单例bean的缓存,我们大幅简化,默认所有bean都是单例的。可以看到所谓单例注册,也很简单,不过是往HashMap里存对象。
/** * 仓库:订单分配:Bean注册 */ 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); } }
生产车间:对象工厂
好了,到了我们最关键的生产部门了,在工厂里,生产产品的是车间,在IOC容器里,生产对象的是BeanFactory。
-
对象工厂,我们最核心的一个类,在它初始化的时候,创建了bean注册器,完成了资源的加载。
-
获取bean的时候,先从单例缓存中取,如果没有取到,就创建并注册一个bean
/** * 生产车间:对象工厂 * 流程:《生产车间:对象工厂》==执行BeanFactory()初始化方法,ResourceLoader().getResource()去==《获取订单:资源加载》 * 返回一个BeanFactory,里面包含配置的bean实例。 如果第一次调用,就是==《仓库:订单分配:Bean注册》,用BeanRegister去注册一个单例的bean, * 并且存储到BeanRegister,如果第二次调用,先从缓存beanRegister取,获取不到再注册。 */ public class BeanFactory { private Map<String, BeanConfig> 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 beanConfig bean定义 * @return */ private Object createBean(BeanConfig beanConfig) { try { Object bean = beanConfig.getBeanClass().newInstance(); //缓存bean beanRegister.registerSingletonBean(beanConfig.getBeanName(), bean); return bean; } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } return null; } }
生产销售:测试
-
UserDao.java
我们的Bean类,很简单
/** * 生产销售:测试 */ public class UserDao { public void queryUserInfo(){ System.out.println("new A good man."); } }
单元测试:
import org.junit.Test; public class ApiTest { /** * 演示一个简单的spring容器框架 */ @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(); } }
结果:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南