当前位置: 首页 > news >正文

实现自己的IOC容器——Winter (一)Bean加载

做了几年 Java 开发,每天和 Spring系列框架打交道。虽然各种注解、中间件用的飞起,但说实话,心里挺虚的。框架帮我们屏蔽了太多细节,舒服是舒服了,可久而久之,感觉自己像个被惯坏的孩子——离了脚手架就不会盖房了。
为了治好这份“技术焦虑症”,也为了满足那点该死的好奇心,我决定自己动手,从零开始撸一个简易版的 Spring 容器。不求功能强大,只求把那些神秘的面纱亲手揭开。

项目地址:https://gitee.com/sheevg/winter
目前还在持续更新中....

我是从《Spring源码深度解析第二版》这本书开始了解Spring框架,所以Winter容器也是从解析Xml配置实现Bean注册(后面会陆续更新注解驱动、AOP等等)。

一、解析Xml配置、注册BeanDefinition

参考方法:XmlBeanDefinitionReader.loadBeanDefinitions(resource)

Winter容器是Xml配置文件驱动,对配置的解析流程如下:

1. 加载xml文件成Resource对象

2. 将Resource转成Document,解析Document,将Bean的配置解析成BeanDefinition

3. 将解析完成的BeanDefinition放入容器WinterBeanFactory中

1. 加载xml文件,转成Document对象

定义ClassPathResource作为类路径下文件抽象,定义getInputStream方法用于读取。定义ResourceLoader用于统一加载文件。

import java.io.FileNotFoundException; import java.io.InputStream; /** * 用于解析 classpath 路径下的文件 */ public class ClassPathResource implements Resource{ // 文件路径 private final String path; // 用于加载文件的 private final ClassLoader classLoader; public ClassPathResource(String path){ this(path,null); } public ClassPathResource(String path,ClassLoader classLoader){ // classloader加载时,不能以/ 为开头,比如/config/xx.xml if(path.startsWith("/")){ path = path.substring(1); } this.path = path; this.classLoader = classLoader != null ? classLoader : ClassLoader.getSystemClassLoader() ; } @Override public InputStream getInputStream() throws FileNotFoundException { InputStream inputStream = this.classLoader.getResourceAsStream(this.path); if(inputStream == null){ throw new FileNotFoundException("描述: 类路径下的资源 [" + this.path + "] 不存在,无法打开"); } return inputStream; } }

2. 解析Document成BeanDefintion

定义BeanDefinitionReader用于将Resource的输入流转成Document对象,使用dom4j转换。

public int loadBeanDefinition(Resource resource) throws FileNotFoundException, DocumentException { // 获取输入流 InputStream is = resource.getInputStream(); SAXReader reader = new SAXReader(); // 将输入流转成 document Document document = reader.read(is); // 计算注册了多少 int countBefore = beanDefinitionRegistry.getBeanDefinitionCount(); doRegisterBeanDefinition(document.getRootElement()); return beanDefinitionRegistry.getBeanDefinitionCount() - countBefore; }

在doRegisterBeanDefinition方法中,遍历bean element,在用解析配置的委托类BeanDefinitionParserDelegate进行具体解析。最终将bean配置解析为BeanDefinition对象。

public void doRegisterBeanDefinition(Element rootElement){ System.out.println(rootElement.getName()); BeanDefinitionParserDelegate parserDelegate = new BeanDefinitionParserDelegate(); // 获取所有 bean标签 List<Element> beanElements = rootElement.elements("bean"); beanElements.forEach(e -> { BeanDefinitionHolder bdHolder = parserDelegate.parseBeanDefinition(e); beanDefinitionRegistry.registerBeanDefinition(bdHolder.getBeanName(),bdHolder.getBeanDefinition()); }); }
public BeanDefinitionHolder parseBeanDefinition(Element element) { String id = element.attributeValue("id"); String className = element.attributeValue("class"); String initMethod = element.attributeValue("init-method"); // 构造函数 List<ConstructorArg> argList = new ArrayList<>(); List<Element> constructorArgs = element.elements("constructor-arg"); for(int i=0; i<constructorArgs.size();i++){ Element constructorArg = constructorArgs.get(i); String argName = constructorArg.attributeValue("name"); String ref = constructorArg.attributeValue("ref"); String value = constructorArg.attributeValue("value"); ConstructorArg ca = ConstructorArg.builder() .index(i) .name(argName) .type(StringUtils.isNotBlank(ref)?ConstructorArgEnum.REF:ConstructorArgEnum.VALUE) .value(StringUtils.isNotBlank(ref)?ref:value) .build(); argList.add(ca); } BeanDefinition bd = BeanDefinition.builder() .id(id) .beanName(id) .className(className) .initMethodName(initMethod) .argList(argList) // 默认单例 .isSingleton(true) .build(); return new BeanDefinitionHolder(id,bd); }

3. 注册BeanDefinition

需要在WinterBeanFactory中创建一个本地缓存ConcurrentHashMap,将创建好的BeanDefinition放进去即可。

private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(64); @Override public void registerBeanDefinition(String beanName, BeanDefinition bd) { this.beanDefinitionMap.put(beanName,bd); }

明天更新如何将配置中的Bean注册进容器....

http://www.jsqmd.com/news/891185/

相关文章:

  • 3步解锁ThinkPad黑苹果:让T480笔记本完美运行macOS
  • AIfES:嵌入式AI框架解析与实战,实现MCU端完整训练
  • 英雄联盟智能助手:你的排位赛数据决策专家
  • 2026年国产在线ORP仪品牌TOP10权威排行榜:技术参数、市场表现与实战选型全解析 - 水质仪表品牌排行榜
  • Steam挂刀行情站:24小时自动化交易监控系统的完整技术实现指南
  • Linux 后台任务详解:、nohup、jobs、systemd 的区别
  • 《多智能体系统实战:我用10个智能体搭建了一个自动赚钱的AI公司》
  • 别再手动改时间了!用timedatectl一条命令搞定Linux时区与NTP同步(Ubuntu/CentOS通用)
  • 2026高口碑普拉提培训机构推荐:优质机构选择指南 - 品牌2025
  • JMeter接口测试中Cookie会话保持的七步实战法
  • 记录一次claude配置知乎mcp经历
  • 佛山黄金回收行业综合实力排名TOP5,2026年5月权威测评榜单 - 生活测评君
  • 简单好用!WinRAR的三种密码保护方式
  • av1编码--编码块的预测约束条件
  • 单图扩散模型实战:多尺度与提示学习实现精准图像编辑
  • 物理生物学研究报告【20260018】
  • Linux 环境变量详解:PATH、export、source 到底是什么?
  • CDR标准体系再添三件套:组网、业务、工程同步落地
  • 百度网盘下载加速终极指南:使用Python工具实现满速下载的完整教程
  • 如何利用组策略精准管控USB与可移动存储设备
  • 系统辨识选最小二乘还是最大似然?一个传感器噪声的例子讲明白
  • 从“飞起来”到“管得好”:2026工程进度低空管理系统供应商推荐 - 品牌2025
  • 3大智能特性重塑象棋辅助体验:视觉识别+实时分析+多平台适配
  • OpenCode + oh-my-openagent 实践全记录
  • 软件测试专栏(10/20):安全测试实战:OWASP Top 10漏洞检测与防护
  • 新鲜出炉!2026高级PDF编辑器推荐排行 专业实测榜单 - 极欧测评
  • 3分钟快速上手:NCBI基因组下载终极指南,让数据获取从未如此简单
  • 2026年5月欧米茄“非官方售后”陷阱深度起底报告 - 资讯纵览
  • 2026 年 5 月在线考试系统哪家靠谱?从功能题库实测推荐 - 讲清楚了
  • PaCE-RL:基于强化学习的ICU患者个性化血糖管理框架解析