Spring框架自定义实现IOC基础功能/IDEA如何手动实现IOC功能
继续整理记录这段时间来的收获,详细代码可在我的Gitee仓库Java设计模式克隆下载学习使用!
7.4 自定义Spring IOC
创建新模块,结构如图![[Pasted image 20230210173222.png]]
7.4.1 定义bean相关POJO类
7.4.1.1 定义propertyValue类
/** * @Author:Phil * @ClassName: PropertyValue * @Description: * 用来封装bean标签下的property标签属性 * name属性 * ref属性 * value属性:给基本数据类型及String类型赋值 * @Date 2023/2/8 21:45 * @Version: 1.0 **/public class propertyValue { private String name; private String ref; private String value; public propertyValue() { } public propertyValue(String name, String ref, String value) { this.name = name; this.ref = ref; this.value = value; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getRef() { return ref; } public void setRef(String ref) { this.ref = ref; } public String getValue() { return value; } public void setValue(String value) { this.value = value; }
}
7.4.1.2 定义MultiplePropertyValue类
一个bean 标签可以有多个property子标签,故用multiplePropertyValue类来存储PropertyValue对象
public class MultiplePropertyValues implements Iterable<PropertyValue>{
// 定义list集合对象,用来存储PropertyValue对象 private final List<PropertyValue> propertyValueList; public MultiplePropertyValues() { this.propertyValueList = new ArrayList<PropertyValue> (); } public MultiplePropertyValues(List<PropertyValue> propertyValueList) { if(propertyValueList == null) this.propertyValueList = new ArrayList<PropertyValue>(); else this.propertyValueList = propertyValueList; }
// 获取所有propertyValue对象,以数组形式返回 public PropertyValue[] getPropertyValues(){ return propertyValueList.toArray(new PropertyValue[0]); }
// 根据name属性值返回对应PropertyValue对象 public PropertyValue getPropertyValues(String propertyName){
// 遍历集合返回 for (PropertyValue propertyValue : propertyValueList) { if(propertyValue.getName().equals(propertyName)) return propertyValue; } return null; }
// 判断集合是否为空 public boolean isEmpty(){ return propertyValueList.isEmpty(); }
// 添加PropertyValue对象 public MultiplePropertyValues addPropertyValue(PropertyValue pv){
// 若有则进行覆盖 for (int i = 0; i < propertyValueList.size(); i++) { if(propertyValueList.get(i).getName().equals(pv.getName())){ propertyValueList.set(i,pv); return this;//目的是链式编程 } }
// 添加新的 this.propertyValueList.add(pv); return this; }
// 判断是否有指定name的PropertyValue对象 public boolean contains(String propertyName){ return getPropertyValues(propertyName) != null; }
// 获取迭代器对象 @Override public Iterator<PropertyValue> iterator() { return propertyValueList.iterator(); }
}
7.1.4.3 BeanDefinition类
BeanDefinition类用来封装bean信息,主要包含id(bean 名称),class(bean全类名)及子标签property对象数据
public class BeanDefinition { private String id; private String className; private MultiplePropertyValues multiplePropertyValues; public BeanDefinition() { multiplePropertyValues = new MultiplePropertyValues(); } 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; } public MultiplePropertyValues getMultiplePropertyValues() { return multiplePropertyValues; } public void setMultiplePropertyValues(MultiplePropertyValues multiplePropertyValues) { this.multiplePropertyValues = multiplePropertyValues; }
}
7.4.2 定义注册表类
7.4.2.1 定义BeanDefinitionRegistry接口
BeanDefinitionRegistry接口定义了注册表相关操作,定义如下功能:
- 注册BeanDefinition对象到注册表中
- 根据名称从注册表中获取后去BeanDefinition对象
- 从注册表中删除指定名称的BeanDefinition对象
- 判断注册表中是否包含指定名称的BeanDefinition对象
- 获取注册表中BeanDefinition对象个数
- 获取注册表中所有的Bean
public interface BeanDefinitionRegistry { //往注册表中注册bean void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) ; //从注册表删掉指定名称bean void removeBeanDefinition(String beanName) throws Exception; //获取指定名称bean BeanDefinition getBeanDefinition(String beanName) throws Exception; //判断是否包含指定名称bean boolean containsBeanDefinition(String beanName); //获取所有bean String[] getBeanDefinitionNames(); int getBeanDefinitionCount(); boolean isBeanNameInUse(String var1);
}
7.4.2.2 SimpleBeanDefinitionRegistry类
public class SimpleBeanDefinitionRegistry implements BeanDefinitionRegistry{
// 创建容器,用于存储 Map<String,BeanDefinition> beanDefinitionMap = new HashMap<String,BeanDefinition>(); @Override public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) { beanDefinitionMap.put(beanName,beanDefinition); } @Override public void removeBeanDefinition(String beanName) throws Exception { beanDefinitionMap.remove(beanName); } @Override public BeanDefinition getBeanDefinition(String beanName) throws Exception { return beanDefinitionMap.get(beanName); } @Override public boolean containsBeanDefinition(String beanName) { return beanDefinitionMap.containsKey(beanName); } @Override public String[] getBeanDefinitionNames() { return beanDefinitionMap.keySet().toArray(new String[0]); } @Override public int getBeanDefinitionCount() { return beanDefinitionMap.size(); } @Override public boolean isBeanNameInUse(String var1) { return beanDefinitionMap.containsKey(var1); }
}
7.4.3 定义解析器类
7.4.3.1 BeanDefinitionReader接口
BeanDefinitionReader用来解析配置文件并在注册表中注册bean的信息,定义了两规范:
- 获取注册表功能,让外界可通过该对象获取注册表对象
- 加载配置文件,并注册bean数据
public interface BeanDefinitionReader{//获取注册表对象BeanDefinitionRegistry getRegistry();//加载配置文件斌在注册表中进行注册void loadBeanDefinitions(String configuration);
}
7.4.3.2 XmlBeanDefinitionReader类
XmlBeanDefinitionReader类是专门来解析xml配置文件,实现了BeanDefinitionReader接口的两个功能。
public class XmlBeanDefinitionReader implements BeanDefinitionReader {
// 声明注册表对象 private BeanDefinitionRegistry registry; public XmlBeanDefinitionReader() { this.registry = new SimpleBeanDefinitionRegistry(); } @Override public BeanDefinitionRegistry getRegistry() { return registry; } @Override public void loadBeanDefinitions(String configuration) throws Exception{
// 使用dom4j进行xml配置文件的解析 SAXReader saxReader = new SAXReader();
// 后去类路径下的配置文件 InputStream resourceAsStream = XmlBeanDefinitionReader.class.getClassLoader().getResourceAsStream(configuration); Document document = saxReader.read(resourceAsStream);
// 根据Document对象获取根标签对象(beans) Element rootElement = document.getRootElement();
// 获取根标签下所有的bean标签对象 List<Element> elements = rootElement.elements("bean");
// 遍历集合 for (Element element : elements) {
// 获取id属性 String id = element.attributeValue("id");
// 获取className String className = element.attributeValue("class");
// 将id和className封装到BeanDefinition对象中
// 创建BeanDefinition对象 BeanDefinition beanDefinition = new BeanDefinition(); beanDefinition.setId(id); beanDefinition.setClassName(className);
// 创建MultiplePropertyValue对象 MultiplePropertyValues multiplePropertyValues = new MultiplePropertyValues();
// 获取bean标签下的所有property标签对象 List<Element> propertyElements = element.elements("property"); for (Element propertyElement : propertyElements) { String name = propertyElement.attributeValue("name"); String ref = propertyElement.attributeValue("ref"); String value = propertyElement.attributeValue("value"); PropertyValue propertyValue = new PropertyValue(name, ref, value); multiplePropertyValues.addPropertyValue(propertyValue); }
// 将multiplePropertyValues封装到BeanDefinition中 beanDefinition.setMultiplePropertyValues(multiplePropertyValues);
// 将BeanDefinition注册到注册表中 registry.registerBeanDefinition(id,beanDefinition); } }
}
7.4.4 容器相关类
7.4.4.1 BeanFactory接口
该接口定义IOC容器的统一规范即获取bean对象
public interface BeanFactory{//根据bean对象的名称获取bean对象Object getBean(String name) throws Exception;//根据bean对象的名称获取bean对象,并进行类型转换<T> T getBean(String name,Class<? extends T> clazz) throws Exception;
}
7.4.4.2 ApplicationContext接口
该接口的子实现类对bean 对象的创建都是非延时的,所以该接口定义refresh方法,主要有两功能:
- 加载配置文件
- 根据注册表中BeanDefinition对象封装的数据进行bean对象的创建
public interface ApplicationContext extends BeanFactory{ void refresh()throws Exception;
}
7.4.4.3 AbstractApplicationContext接口
- 作为ApplicationContext接口的子类,故该类是非延时加载,故需要在该类中定义Map集合,作为bean对象存储容器
- 声明BeanDefinition类型变量,用来进行xml配置文件解析,符合单一职责原则
- BeanDefinition类型对象创建交由子类实现,子类明确创建BeanDefinitionReader
public abstract class AbstractApplicationContext implements ApplicationContext {
// 声明解析器对象 protected BeanDefinitionReader beanDefinitionReader;
// 存储bean容器,key存储的bean的id,value是bean对象 protected Map<String,Object> singletonObject = new HashMap<String,Object>();;
// 存储配置文件路径 String configLocation; public void refresh() throws Exception{
// 加载BeanDefinition beanDefinitionReader.loadBeanDefinitions(configLocation);
// 初始化bean finishBeanInitialization(); } public void finishBeanInitialization() throws Exception{
// 获取注册表对象 BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry();
// 获取BeanDefinition对象 String [] beanNames = registry.getBeanDefinitionNames();
// 初始化bean for (String beanName : beanNames) { getBean(beanName); } }
}
7.4.4.4 ClassPathXmlApplicationContext接口
该类主要是加载类路径下的配置文件,并进行bean对象的创建,主要有以下功能:
- 在构造方法中,创建BeanDefinitionReader对象
- 在构造方法中,调用refresh方法,用于进行配置文件加载,创建bean对象并存储到容器中
- 重写父类中的getBean方法,并实现依赖注入
public class ClassPathXmlApplicationContext extends AbstractApplicationContext{ public ClassPathXmlApplicationContext(String configLocation){ this.configLocation = configLocation;
// 构建解析器对象 beanDefinitionReader = new XmlBeanDefinitionReader(); try { this.refresh(); }catch (Exception exception){ exception.printStackTrace(); } }
// 根据bean对象的名称获取bean对象 @Override public Object getBean(String name) throws Exception {
// 判断对象容器中是否包含指定bean对象,若包含则返回,否则创建 Object object = singletonObject.get(name); if(object != null) return object;
// 获取BeanDefinition对象 BeanDefinitionRegistry registry = beanDefinitionReader.getRegistry(); BeanDefinition beanDefinition = registry.getBeanDefinition(name);
// 获取bean信息中的className String className = beanDefinition.getClassName();
// 通过反射获取对象 Class<?> clazz = Class.forName(className); Object instance = clazz.newInstance();
// 进行依赖注入操作 MultiplePropertyValues multiplePropertyValues = beanDefinition.getMultiplePropertyValues(); for (PropertyValue propertyValue : multiplePropertyValues) {
// 获取name属性值 String propertyValueName = propertyValue.getName();
// 获取value值 String value = propertyValue.getValue();
// 获取ref值 String ref = propertyValue.getRef(); if(ref != null && !"".equals(ref)){
// 获取依赖的对象 Object bean = getBean(ref);
// 拼接方法名 String setterMethodByField = StringUtils.getSetterMethodByField(propertyValueName);
// 获取所有方法 Method[] methods = clazz.getMethods(); for (Method method : methods) { if(method.getName().equals(setterMethodByField))
// 执行setter方法 method.invoke(instance,bean); } } if(value != null && !"".equals(value)){
// 拼接方法名 String methodName = StringUtils.getSetterMethodByField(propertyValueName);
// 获取method对象 Method method = clazz.getMethod(methodName, String.class); method.invoke(instance, value); } }
// 在返回instance对象之前,将该对象存储到map容器中 singletonObject.put(name,instance); return instance; } @Override public <T> T getBean(String name, Class<? extends T> clazz) throws Exception { Object bean = getBean(name); if(bean == null) return null; return clazz.cast(bean); }
}
7.4.4.5 测试
将前文回顾Spring框架项目中的pom文件的spring-context依赖换为上述新建项目依赖,如图
运行后如图
7.4.5 总结
7.4.5.1 使用到的设计模式
- 工厂模式:工厂模式+ 配置文件
- 单例模式。Spring IOC管理的bean都是单例的,此处单例不是通过构造器进行单例构建,且框架对每个bean只创建一个对象。
- 模板方法模式。AbstractApplicationContext类中的finishInitialization方法调用getBean方法,因为getBean实现和环境有关。
- 迭代器模式。其中MultiplePropertyValyes类使用了迭代器模式,因为此类存储并管理PropertyValue对象,也属于一个容器。
- 还使用了很多设计模式,如AOP使用到了代理模式,选择JDK代理或CGLIB代理使用了策略模式,还有适配器模式,装饰者模式,观察者模式等。
7.4.5.2 符合大部分设计原则
7.4.5.3 整个设计和Spring设计还有一定出入
Spring框架底层是很复杂的,进行了很深入的封装,并对外提供了很好的扩展性,自定义Spring IOC容器有两目的:
- 了解Spring底层对对象的大体管理机制
- 了解设计模式在具体开发中的使用
- 以后学习Spring源码,通过该案例实现,可以降低Spring学习入门成本
相关文章:

Spring框架自定义实现IOC基础功能/IDEA如何手动实现IOC功能
继续整理记录这段时间来的收获,详细代码可在我的Gitee仓库Java设计模式克隆下载学习使用! 7.4 自定义Spring IOC 创建新模块,结构如图![[Pasted image 20230210173222.png]] 7.4.1 定义bean相关POJO类 7.4.1.1 定义propertyValue类 /** …...

pip离线安装windows版torch
文章目录前言conda创建虚拟环境安装torchtorch官网在线安装离线手动安装测试是否安装成功后记前言 学习的时候遇到几个机器学习相关的项目,由于不同的项目之间用到的依赖库不太一样,于是想利用conda为不同的项目创建不同的环境方便管理和运行࿰…...

Redis核心知识点
Redis核心知识点Redis核心知识点大全五种数据类型redis整合SpringBoot序列化问题渐进式扫描慢查询缓存相关问题数据库和缓存谁先更新缓存穿透缓存雪崩缓存击穿实际应用超卖问题分布式锁全局唯一ID充当消息队列Feed流附近商户签到HyperLogLog实现UV统计持久化RDBAOF持久化小结事…...

14. 最长公共前缀
14. 最长公共前缀 一、题目描述: 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀,返回空字符串 “”。 示例 1: 输入:strs [“flower”,“flow”,“flight”] 输出:“fl” 示例 2: …...

SignalR注册成Windows后台服务,并实现web前端断线重连
注意下文里面的 SignalR 不是 Core 版本,而是 Framework 下的 本文使用的方式是把 SignalR 写在控制台项目里,再用 Topshelf 注册成 Windows 服务 这样做有两点好处 传统 Window 服务项目调试时需要“附加到进程”,开发体验比较差…...

【前端笔试题二】从一个指定数组中,每次随机取一个数,且不能与上次取数相同,即避免相邻取数重复
前言 本篇文章记录下我在笔试过程中遇到的真实题目,供大家参考。 1、题目 系统给定一个数组,需要我们编写一个函数,该函数每次调用,随机从该数组中获取一个数,且不能与上一次的取数相同。 2、思路解析 数组已经有了…...
专栏关注学习
Node学习专栏(全网最细的教程) 【spring系列】 SpringCloud 前端框架Vue java学习过程 RocketMQ Spring Tomcat websocket 从头开始学Redisson 从头开始学Oracle 跟着大宇学Shiro 吃透Shiro源代码 Git基础与进阶 Java并发编程 Spring系列 手写…...

【手写 Vuex 源码】第八篇 - Vuex 的 State 状态安装
一,前言 上一篇,主要介绍了 Vuex 模块安装的实现,针对 action、mutation、getter 的收集与处理,主要涉及以下几个点: Vuex 模块安装的逻辑;Vuex 代码优化;Vuex 模块安装的实现;Vue…...

Mac下拉式终端的安装与配置 (iTerm2)
Mac下拉式终端的安装与配置 使用效果如图所示 安装前置软件 iTerm2 很可惜,如此炫酷的功能在原终端中并不能实现,我们需要借助iTerm2这个软件来实现。 官网链接:iTerm2 - macOS Terminal Replacement 我们点击download下载即可 配置 当我…...
使用 Spring 框架结合阿里云 OSS 实现文件上传的代码示例
使用 Spring 框架结合阿里云 OSS 实现文件上传的代码示例POM文件配置文件上传工具类控制层使用yaml配置文件(第二种用法,看公司要求)注入 OSSClient 对象及工具类(第二种用法,看公司要求)使用 Vue 前端代码…...

神经网络基础知识
神经网络基础知识 文章目录神经网络基础知识一、人工神经网络1.激活函数sigmod函数Tanh函数Leaky Relu函数分析2.过拟合和欠拟合二、学习与感知机1.损失函数与代价函数2. 线性回归和逻辑回归3. 监督学习与无监督学习三、优化1.梯度下降法2.随机梯度下降法(SGD)3. 批量梯度下降法…...

SpringBoot开发规范部分通用模板+idea配置【项目通用-1】
SpringBoot开发规范通用模板 1 分页插件使用 通过MybatisPlus配置分页插件拦截器 Configuration MapperScan("com.xuecheng.content.mapper") //拦截的mapper层 public class MybatisPlusConfig {//定义分页的拦截器Beanpublic MybatisPlusInterceptor getMybatisPl…...

程序的机器级表示part3——算术和逻辑操作
目录 1.加载有效地址 2. 整数运算指令 2.1 INC 和 DEC 2.2 NEG 2.3 ADD、SUB 和 IMUL 3. 布尔指令 3.1 AND 3.2 OR 3.3 XOR 3.4 NOT 4. 移位操作 4.1 算术左移和逻辑左移 4.2 算术右移和逻辑右移 5. 特殊的算术操作 1.加载有效地址 指令效果描述leaq S, DD…...

基于YOLOV5的钢材缺陷检测
数据和源码见文末 1.任务概述 数据集使用的是东北大学收集的一个钢材缺陷检测数据集,需要检测出钢材表面的6种划痕。同时,数据集格式是VOC格式,需要进行转化,上传的源码中的数据集是经过转换格式的版本。 2.数据与标签配置方法 在数据集目录下,train文件夹下有训练集数据…...

Session与Cookie的区别(三)
中场休息 让我们先从比喻回到网络世界里,HTTP 是无状态的,所以每一个 Request 都是不相关的,就像是对小明来说每一位客人都是新的客人一样,他根本不知道谁是谁。 既然你没办法把他们关联,就代表状态这件事情也不存在。…...

七大设计原则之接口隔离原则应用
目录1 接口隔离原则介绍2 接口隔离原则应用1 接口隔离原则介绍 接口隔离原则(Interface Segregation Principle, ISP)是指用多个专门的接口,而不使用单一的总接口,客户端不应该依赖它不需要的接口。这个原则指导我们在设计接口时…...

【Shell1】shell语法,ssh/build/scp/upgrade,环境变量,自动升级bmc
文章目录1.shell语法:shell是用C语言编写的程序,是用户使用Linux的桥梁,硬件>内核(os)>shell>文件系统1.1 变量:readonly定义只读变量,unset删除变量1.2 函数:shell脚本传递的参数中包含空格&…...
JavaScript HTML DOM - 改变CSS
JavaScript 是一种动态语言,它可以动态地修改网页的外观,并且使用HTML DOM(文档对象模型)可以更方便地控制HTML元素的样式。 JavaScript 通过在HTML DOM中更改CSS属性来更改样式,这些CSS属性包括颜色、位置、字体大小…...

mycat连接mysql 简单配置
mycat三个配置文件位于conf下 可通过Notepad操作 首先配置service.xml中的user标签,设置用户名,密码,查询权限,是否只读等 只是设置了root用户,有所有权限 配置schema.xml <?xml version"1.0"?&g…...
Spring常用注解
文章目录一、Bean交给Spring管理1、Component2、Bean3、Controller4、Service5、Repository6、Configuration7、ComponentScan二、作用域1、Lazy(false)Scope三、依赖注入1、Autowired2、Resource3、Qualifier四、读取配置文件值1、Value一、Bean交给Spring管理 1、Component …...
golang循环变量捕获问题
在 Go 语言中,当在循环中启动协程(goroutine)时,如果在协程闭包中直接引用循环变量,可能会遇到一个常见的陷阱 - 循环变量捕获问题。让我详细解释一下: 问题背景 看这个代码片段: fo…...

【网络安全产品大调研系列】2. 体验漏洞扫描
前言 2023 年漏洞扫描服务市场规模预计为 3.06(十亿美元)。漏洞扫描服务市场行业预计将从 2024 年的 3.48(十亿美元)增长到 2032 年的 9.54(十亿美元)。预测期内漏洞扫描服务市场 CAGR(增长率&…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放
简介 前面两期文章我们介绍了I2S的读取和写入,一个是通过INMP441麦克风模块采集音频,一个是通过PCM5102A模块播放音频,那如果我们将两者结合起来,将麦克风采集到的音频通过PCM5102A播放,是不是就可以做一个扩音器了呢…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
AspectJ 在 Android 中的完整使用指南
一、环境配置(Gradle 7.0 适配) 1. 项目级 build.gradle // 注意:沪江插件已停更,推荐官方兼容方案 buildscript {dependencies {classpath org.aspectj:aspectjtools:1.9.9.1 // AspectJ 工具} } 2. 模块级 build.gradle plu…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...

技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析
Java求职者面试指南:Spring、Spring Boot、MyBatis框架与计算机基础问题解析 一、第一轮提问(基础概念问题) 1. 请解释Spring框架的核心容器是什么?它在Spring中起到什么作用? Spring框架的核心容器是IoC容器&#…...
C++.OpenGL (20/64)混合(Blending)
混合(Blending) 透明效果核心原理 #mermaid-svg-SWG0UzVfJms7Sm3e {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-icon{fill:#552222;}#mermaid-svg-SWG0UzVfJms7Sm3e .error-text{fill…...