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依赖换为上述新建项目依赖,如图
![![[Pasted image 20230210173346.png]]](https://img-blog.csdnimg.cn/d9e3da1a2d324f51b74cb3d59c3c9773.png)
运行后如图
![![[Pasted image 20230210173447.png]]](https://img-blog.csdnimg.cn/e7f6f555a68848d6b18187b454c2de5e.png)
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 …...
RestClient
什么是RestClient RestClient 是 Elasticsearch 官方提供的 Java 低级 REST 客户端,它允许HTTP与Elasticsearch 集群通信,而无需处理 JSON 序列化/反序列化等底层细节。它是 Elasticsearch Java API 客户端的基础。 RestClient 主要特点 轻量级ÿ…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
Python爬虫实战:研究feedparser库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
2021-03-15 iview一些问题
1.iview 在使用tree组件时,发现没有set类的方法,只有get,那么要改变tree值,只能遍历treeData,递归修改treeData的checked,发现无法更改,原因在于check模式下,子元素的勾选状态跟父节…...
掌握 HTTP 请求:理解 cURL GET 语法
cURL 是一个强大的命令行工具,用于发送 HTTP 请求和与 Web 服务器交互。在 Web 开发和测试中,cURL 经常用于发送 GET 请求来获取服务器资源。本文将详细介绍 cURL GET 请求的语法和使用方法。 一、cURL 基本概念 cURL 是 "Client URL" 的缩写…...
Pydantic + Function Calling的结合
1、Pydantic Pydantic 是一个 Python 库,用于数据验证和设置管理,通过 Python 类型注解强制执行数据类型。它广泛用于 API 开发(如 FastAPI)、配置管理和数据解析,核心功能包括: 数据验证:通过…...
DAY 26 函数专题1
函数定义与参数知识点回顾:1. 函数的定义2. 变量作用域:局部变量和全局变量3. 函数的参数类型:位置参数、默认参数、不定参数4. 传递参数的手段:关键词参数5 题目1:计算圆的面积 任务: 编写一…...
《信号与系统》第 6 章 信号与系统的时域和频域特性
目录 6.0 引言 6.1 傅里叶变换的模和相位表示 6.2 线性时不变系统频率响应的模和相位表示 6.2.1 线性与非线性相位 6.2.2 群时延 6.2.3 对数模和相位图 6.3 理想频率选择性滤波器的时域特性 6.4 非理想滤波器的时域和频域特性讨论 6.5 一阶与二阶连续时间系统 6.5.1 …...
