(一)实现一个简易版IoC容器【手撸Spring】

一、前言

相信大家在看本篇文章的时候,对IoC应该有一个比较清晰的理解,我在这里再重新描述下:它的作用就是实现一个容器将一个个的Bean(这里的Bean可以是一个Java的业务对象,也可以是一个配置对象)统一管理起来。在Java中,我们创建一个对象最简单的方法是使用new关键字。Spring框架的IoC容器则是将创建Bean的动作与使用Bean解耦,将应用层程序员无需关注底层对象的构建以及其生命周期,以便更好的专注于业务开发。

本节我们则开始进入手写Spring框架的第一步:实现一个最简易的IoC容器。

二、一个最简易的IoC容器

1、简易流程

IoC简易流程

我们在面向Spring框架开发时,想要使用一个Bean时,通常会将bean的一些元信息配置在xml文件中(也可以通过注解),Spring IoC容器会加载指定路径的xml文件,将其进一步解析成BeanDefinition并存储到IoC容器中,当我们(应用层)去获取Bean实例(通过getBean)时,如果该Bean没有被初始化,则会触发Bean实例创建的动作,创建实例由反射实现。

2、简易功能下UML图

简易IoC-UML图

3、相关代码

BeanDefinition
package com.tiny.spring.beans.factory.config;/*** @author: markus* @date: 2023/10/7 8:14 PM* @Description: Bean配置元信息* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class BeanDefinition {private String id;private String className;public BeanDefinition() {}public BeanDefinition(String id, String className) {this.id = id;this.className = className;}public String getId() {return id;}public void setId(String id) {this.id = id;}public String getClassName() {return className;}public void setClassName(String className) {this.className = className;}
}
ClassPathXmlApplicationContext
package com.tiny.spring.context.support;import com.tiny.spring.beans.factory.config.BeanDefinition;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author: markus* @date: 2023/10/7 8:16 PM* @Description: 基于xml的Spring应用上下文* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class ClassPathXmlApplicationContext {private List<BeanDefinition> beanDefinitions = new ArrayList<>();private Map<String, Object> singletons = new HashMap<>();public ClassPathXmlApplicationContext(String pathname) {this.readXml(pathname);this.instanceBeans();}private void readXml(String pathname) {SAXReader saxReader = new SAXReader();try {URL xmlPath = this.getClass().getClassLoader().getResource(pathname);Document document = saxReader.read(xmlPath);Element rootElement = document.getRootElement();// 对配置文件的每一个<bean>标签进行处理for (Element element : rootElement.elements()) {// 获取Bean的基本信息String beanId = element.attributeValue("id");String beanClassName = element.attributeValue("class");BeanDefinition beanDefinition = new BeanDefinition(beanId, beanClassName);// 将Bean的定义存放到BeanDefinitionbeanDefinitions.add(beanDefinition);}} catch (DocumentException e) {e.printStackTrace();}}/*** 利用反射创建Bean实例,并存储在singletons中*/private void instanceBeans() {for (BeanDefinition beanDefinition : beanDefinitions) {try {singletons.put(beanDefinition.getId(), Class.forName(beanDefinition.getClassName()).newInstance());} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {e.printStackTrace();}}}/*** 对外提供的方法,让外部程序获取Bean实例* @param beanName* @return*/public Object getBean(String beanName) {return singletons.get(beanName);}
}
测试类
package com.tiny.spring.test;import com.tiny.spring.beans.BeansException;
import com.tiny.spring.context.support.ClassPathXmlApplicationContext;
import com.tiny.spring.test.service.AService;/*** @author: markus* @date: 2023/10/7 8:37 PM* @Description: 最原始的IoC容器功能测试* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class OriginalIoCContainerTest {public static void main(String[] args) throws BeansException {ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("beans.xml");AService aService = (AService) classPathXmlApplicationContext.getBean("aService");aService.sayHello();}
}

控制台

三、单一职责原则

我们可以看到,上面的ClassPathXmlApplicationContext承担了太多的功能,这不符合对象单一职责原则。

原本ClassPathXmlApplicationContext既承担了对外提供Bean实例访问,对内进行配置文件的加载并解析成BeanDefinition存储起来以及进行Bean实例的创建操作。

我们需要对此进行优化,将IoC容器的核心功能(Bean实例访问+BeanDefinition注册)和外部信息的访问剥离出来。

1、优化后的UML类图

单一职责优化后的UML图

2、相关代码

Resource
package com.tiny.spring.core.io;import java.util.Iterator;/*** @author: markus* @date: 2023/10/7 8:45 PM* @Description: 外部的配置信息抽象* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public interface Resource extends Iterator<Object> {
}
ClassPathXmlResource
package com.tiny.spring.core.io;import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.net.URL;
import java.util.Iterator;/*** @author: markus* @date: 2023/10/7 8:47 PM* @Description:* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class ClassPathXmlResource implements Resource {Document document;Element rootElement;Iterator<Element> elementIterator;public ClassPathXmlResource(String pathname) {SAXReader saxReader = new SAXReader();URL xmlPath = this.getClass().getClassLoader().getResource(pathname);try {this.document = saxReader.read(xmlPath);this.rootElement = document.getRootElement();this.elementIterator = this.rootElement.elementIterator();} catch (DocumentException e) {e.printStackTrace();}}@Overridepublic boolean hasNext() {return this.elementIterator.hasNext();}@Overridepublic Object next() {return this.elementIterator.next();}
}
XmlBeanDefinitionReader
package com.tiny.spring.beans.factory.xml;import com.tiny.spring.beans.factory.BeanFactory;
import com.tiny.spring.beans.factory.config.BeanDefinition;
import com.tiny.spring.core.io.Resource;
import org.dom4j.Element;/*** @author: markus* @date: 2023/10/7 8:50 PM* @Description:* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class XmlBeanDefinitionReader {BeanFactory beanFactory;public XmlBeanDefinitionReader(BeanFactory beanFactory) {this.beanFactory = beanFactory;}public void loadBeanDefinitions(Resource resource) {while (resource.hasNext()) {Element element = (Element) resource.next();String beanId = element.attributeValue("id");String className = element.attributeValue("class");BeanDefinition beanDefinition = new BeanDefinition(beanId, className);this.beanFactory.registerBeanDefinition(beanDefinition);}}
}
BeanFactory
package com.tiny.spring.beans.factory;import com.tiny.spring.beans.BeansException;
import com.tiny.spring.beans.factory.config.BeanDefinition;/*** @author: markus* @date: 2023/10/7 8:43 PM* @Description: IoC底层容器的根类* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public interface BeanFactory {/*** 根据beanName获取Bean实例* @param beanName* @return* @throws BeansException*/Object getBean(String beanName) throws BeansException;/*** 注册Bean配置元信息* @param beanDefinition*/void registerBeanDefinition(BeanDefinition beanDefinition);
}
ClassPathXmlApplicationContext
package com.tiny.spring.context.support;import com.tiny.spring.beans.BeansException;
import com.tiny.spring.beans.factory.BeanFactory;
import com.tiny.spring.beans.factory.config.BeanDefinition;
import com.tiny.spring.beans.factory.support.SimpleBeanFactory;
import com.tiny.spring.beans.factory.xml.XmlBeanDefinitionReader;
import com.tiny.spring.core.io.ClassPathXmlResource;
import com.tiny.spring.core.io.Resource;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;/*** @author: markus* @date: 2023/10/7 8:16 PM* @Description: 基于xml的Spring应用上下文* @Blog: https://markuszhang.com* It's my honor to share what I've learned with you!*/
public class ClassPathXmlApplicationContext implements BeanFactory {BeanFactory beanFactory;public ClassPathXmlApplicationContext(String pathname) {Resource resource = new ClassPathXmlResource(pathname);BeanFactory beanFactory = new SimpleBeanFactory();XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);reader.loadBeanDefinitions(resource);this.beanFactory = beanFactory;}/*** 对外提供的方法,让外部程序获取Bean实例** @param beanName* @return*/public Object getBean(String beanName) throws BeansException {return this.beanFactory.getBean(beanName);}@Overridepublic void registerBeanDefinition(BeanDefinition beanDefinition) {this.beanFactory.registerBeanDefinition(beanDefinition);}
}
功能验证

四、本文总结

可以看到,经过上述构建,我们在使用一个对象时不需要再去手动new一个了,只需要进行一些简单的配置将其交给框架容器去管理就可以获取我们所需的对象。通过功能解耦,我们定义出以下几个核心类:

  • BeanFactory : 底层根容器
  • SimpleBeanFactory : 容器的实现类
  • ClassPathXmlApplicationContext : 应用层容器,交付给上层程序调用
  • Resource : 外部资源对象抽象
  • ClassPathXmlResource : 外部Xml资源对象
  • XmlBeanDefinitionLoader : xml文件加载,并解析为BeanDefinition

通过功能解耦,我们后续对容器进行扩展时就会更方便,适配更多的场景。

就这样,一个简易的IoC容器就实现了,接下来我们就一步一步的将其他功能添加上去,让一颗小树苗发育成一个参天大树。

完整代码参见:https://github.com/markuszcl99/tiny-spring

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/97082.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JetPack系列:001-JetPack概要介绍

文章目录 1. 概念介绍2. 主要内容2.1 框架库2.2 UI界面库 3. 核心思想4. 内容总结 本章回是一起Talk AndroidJetpack吧专栏的第一章回&#xff0c;本章回中主要介绍JetPack的基本概念和编程思想&#xff0c;同时也会介绍它的基础知识。闲话休提&#xff0c;请我们一起Talk Andr…

趣味工具箱小程序源码

趣味工具箱小程序源码&#xff0c;支持功能去水印&#xff0c;精选壁纸&#xff0c;图片压缩&#xff0c;文字生成二维码&#xff0c;图片加水印&#xff0c;模拟来电&#xff0c;手持弹幕&#xff0c;掷骰子…等 使用小工具&#xff0c;一个小程序有几十个功能。 源码下载&am…

面试打底稿⑥ 项目一的第二部分

简历原文 抽查部分 计算运费模块板块扩展性优化&#xff0c;采用责任链模式&#xff0c;实现不同地区间寄件的运费模板扩展的优化&#xff0c;为模块解耦&#xff0c;提高了系统的扩展性 短信模块设计&#xff0c;设计了短信发送数据模板的数据化存储&#xff0c;规范了发送短…

服务器or虚拟机安装SSH和虚拟机or服务器设置远程服务权限

第一步 服务器/虚拟机安装SSH工具,这是外部SSH终端连接服务器/虚拟机的第一步! sudo apt update && sudo apt upgrade#更新apt sudo apt install openssh-server#安装SSH工具 service ssh status#查看SSh运行状态 sudo systemctl enable --now ssh#运行SSH工具第二步…

Pytorch-学习记录-1-Tensor

1. 张量 (Tensor): 数学中指的是多维数组&#xff1b; torch.Tensor data: 被封装的 Tensor dtype: 张量的数据类型 shape: 张量的形状 device: 张量所在的设备&#xff0c;GPU/CPU requires_grad: 指示是否需要计算梯度 grad: data 的梯度 grad_fn: 创建 Tensor 的 Functio…

[python 刷题] 3 Longest Substring Without Repeating Characters

[python 刷题] 3 Longest Substring Without Repeating Characters 题目&#xff1a; Given a string s, find the length of the longest substring without repeating characters. 这到提要求找的是最长的&#xff0c;没有重复符号的子字符串 解题思路是用双指针哈希表&…

mysql MVCC(多版本并发控制)理解

最近看MVCC相关资料&#xff0c;这边做一个记录总结&#xff0c;方便后续理解。 目录 一、MVCC相关概念 二、MVCC实现原理 1.隐藏字段 2.undo log 3.Read View 4.MVCC的整体处理流程 5. RC&#xff0c;RR级级别下的innoDB快照读有什么不同 6.总结 一、MVCC相关概念 1…

华为云云耀云服务器L实例评测|部署私有网盘 Nextcloud

华为云云耀云服务器L实例评测&#xff5c;部署私有网盘 Nextcloud 一、云耀云服务器L实例介绍1.1 云服务器介绍1.2 产品规格1.3 应用场景 二、云耀云服务器L实例配置2.1 重置密码2.2 服务器连接2.3 安全组配置 三、部署 Nextcloud3.1 Nextcloud 介绍3.2 Docker 环境搭建3.3 Nex…

mysql面试题23:如果某个表有近千万数据,CRUD比较慢,如何优化?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:如果某个表有近千万数据,CRUD比较慢,如何优化? 当某个表存在近千万数据且CRUD(增删改查)操作比较慢时,可以考虑以下优化策略: 使用索引:索…

数百个下载能够传播 Rootkit 的恶意 NPM 软件包

供应链安全公司 ReversingLabs 警告称&#xff0c;最近观察到的一次恶意活动依靠拼写错误来诱骗用户下载恶意 NPM 软件包&#xff0c;该软件包会通过 rootkit 感染他们的系统。 该恶意软件包名为“node-hide-console-windows”&#xff0c;旨在模仿 NPM 存储库上合法的“node-…

Java中使用正则表达式

正则表达式 正则表达式&#xff08;Regular Expression&#xff09;是一种用于匹配、查找和替换文本的强大工具。它由一系列字符和特殊字符组成&#xff0c;可以用来描述字符串的模式。在编程和文本处理中&#xff0c;正则表达式常被用于验证输入、提取信息、搜索和替换文本等…

2.5 数字传输系统

笔记&#xff1a; 针对这一节的内容&#xff0c;我为您提供一个笔记的整理方法。将内容按重要性、逻辑关系进行组织&#xff0c;再进行简化。 ## 2.5 数字传输系统 ### 背景介绍&#xff1a; 1. **早期电话网**&#xff1a;市话局到用户采用双绞线电缆&#xff0c;长途干线采…

nodejs+vue快递管理服务系统elementui

电子商务改变了人们的传统经济活动中的交易方式和流通技术&#xff0c; 复杂的物流快递信息需要有效的进行处理&#xff0c;构建一个快递管理服务系统可以确保物流信息的一致性、员工登录&#xff1a;通过用户名和密码登录。这也间接带动了物流快递行业的高速发展。 &#xff0…

KylinOSv10系统k8s集群启动mysql5.7占用内存高的问题

问题现象 麒麟系统搭建k8s集群 mysql的pod启动失败 describe查看ommkill&#xff0c;放大limit资源限制到30G依旧启动失败 系统 报错信息 原因 内存占用太高 open_files_limit初始化太高 解决&#xff1a; 1、更换镜像 链接: https://pan.baidu.com/s/1b9uJLcc5Os0uDqD1e…

3. 无重复字符的最长子串(枚举+滑动窗口)

目录 一、题目 二、代码 一、题目 力扣&#xff08;LeetCode&#xff09;官网 - 全球极客挚爱的技术成长平台 二、代码 class Solution { public:int lengthOfLongestSubstring(string s) {int _MaxLength 0;int left 0, right 0;vector<int>hash(128, 0);//ASCII…

Spring AOP(JavaEE进阶系列5)

目录 前言&#xff1a; 1.什么是Spring AOP 2.为什么要使用AOP呢&#xff1f; 3.AOP的组成 3.1切面 3.2切点 3.3通知 3.4连接点 4.Spring AOP的实现 4.1添加依赖 4.2定义切面 4.3定义切点 4.4实现通知 5.AOP的实现原理 结束语&#xff1a; 前言&#xff1a; 在…

NOSQL Redis 数据持久化 RDB、AOF(二) 恢复

redis 执行flushall 或 flushdb 也会产生dump.rdb文件&#xff0c;但里面是空的。 注意&#xff1a;千万执行&#xff0c;不然rdb文件会被覆盖的。 dump.rdb 文件如何恢复数据 讲备份文件 dump.rdb 移动到redis安装目录并启动服务即可。 dump.rdb 自动触发 和手动触发 自…

IDEA 2023.1.3图文安装教程及下载

IDEA 2023.1 最新变化是在 IDEA 2023.1 中&#xff0c;对新 UI 做出了大量改进。实现了性能增强&#xff0c;从而更快导入 Maven&#xff0c;以及在打开项目时更早提供 IDE 功能。 新版本通过后台提交检查提供了简化的提交流程。 IntelliJ IDEA Ultimate 现在支持 Spring Secur…

智慧电力物联网系统引领电力行业数字化发展

智慧电力物联网系统是以提高用户侧电力运行安全、降低运维成本为目的的一套电力运维管理系统。综合分析采用智慧物联网、人工智能等现代化经济信息网络技术&#xff0c;配置智能采集终端、小安神童值班机器人或边缘网关&#xff0c;实现对企事业用户供配电系统的数字化远程监控…

Elasticsearch:使用 ELSER 文本扩展进行语义搜索

在今天的文章里&#xff0c;我来详细地介绍如何使用 ELSER 进行文本扩展驱动的语义搜索。 安装 Elasticsearch 及 Kibana 如果你还没有安装好自己的 Elasticsearch 及 Kibana&#xff0c;请参考如下的链接来进行安装&#xff1a; 如何在 Linux&#xff0c;MacOS 及 Windows 上…