Spring框架自定义实现IOC基础功能/IDEA如何手动实现IOC功能

news2024/11/15 4:22:28

继续整理记录这段时间来的收获,详细代码可在我的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]]
运行后如图
![[Pasted image 20230210173447.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学习入门成本

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

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

相关文章

Linux--POSIX信号量--基于环形队列的生产消费模型-0208

1. 什么是信号量 共享资源由于原子性的原则&#xff0c;任何时刻都只有一个执行流在进行访问。表现为互斥&#xff0c;也就代表共享资源起始是被当做整体来访问的。 那如果有一个共享资源&#xff0c;不当成一个整体&#xff0c;让不同的执行流访问不同的资源区域代码&#x…

67 自注意力【动手学深度学习v2】

67 自注意力【动手学深度学习v2】 深度学习学习笔记 学习视频&#xff1a;https://www.bilibili.com/video/BV19o4y1m7mo/?spm_id_fromautoNext&vd_source75dce036dc8244310435eaf03de4e330 给定长为n 的序列&#xff0c;每个xi为长为d的向量&#xff0c;自注意力将xi 既当…

Java中的异常处理

1.概述 在 Java 中&#xff0c;所有的异常都有一个共同的祖先java.lang包中的 Throwable类。 异常是程序中的一些错误&#xff0c;但并不是所有的错误都是异常&#xff0c;并且错误有时候是可以避免的。 比如说&#xff0c;你的代码少了一个分号&#xff0c;那么运行出来结果…

二叉树和堆的讲解和实现(图解+代码/C语言)

今天和大家分享的是二叉树的实现&#xff0c;关于遍历二叉树部分均采用递归的方式实现&#xff0c;最后附上部分OJ题供大家练习。 文章目录一、树的概念及结构1.1 树的概念1.2 树的相关概念1.3 树的表示二、二叉树的概念及结构2.1 概念2.2 二叉树的性质2.3 二叉树的存储结构2.…

proxy代理与reflect反射

proxy代理与reflect 在这之前插入一个知识点arguments&#xff0c;每个函数里面都有一个arguments&#xff0c;执行时候不传默认是所有参数&#xff0c;如果传了就是按顺序匹配&#xff0c;箭头函数没有 代理函数 代理对象也就是生成一个替身&#xff0c;然后这个替身处理一切的…

【深度学习】认识神经网络

上一章——过拟合与正则化 从本章开始我们将学习深度学习的内容&#xff1a;包括神经网络和决策树等高级算法 文章目录神经网络的生物学原理神经网络的算法结构案例——图像感知神经网络的前向传播神经网络的生物学原理 在最开始&#xff0c;人们想要构建一个能够模拟生物大脑…

Python __doc__属性:查看文档

在使用 dir() 函数和 __all__ 变量的基础上&#xff0c;虽然我们能知晓指定模块&#xff08;或包&#xff09;中所有可用的成员&#xff08;变量、函数和类&#xff09;&#xff0c;比如&#xff1a;import string print(string.__all__)程序执行结果为&#xff1a;[ascii_lett…

Zabbix 构建监控告警平台(六)

监控TCP连接监控MySQL监控php-fpm监控 Apache监控 MySQL A-B监控磁盘I/O1.监控TCP连接 1.1 tcp状态简介 netstat中的各种状态&#xff1a; CLOSED 初始&#xff08;无连接&#xff09;状态。 LISTEN 侦听状态&#xff0c;等待远程机器的连接…

自动驾驶规控课程学习——决策规划

行为决策系统的规划1 行为决策基础1.1 基本概念与任务行为类型&#xff1a;系统输入输出&#xff1a;输入&#xff1a;定位、感知、地图等输出&#xff1a;决策意图小例子&#xff1a;1.2决策系统的评价与挑战评价指标挑战&#xff08;1&#xff09;决策密度&#xff08;2&…

卡尔曼滤波器与DSP实现

卡尔曼滤波器是利用系统状态方程&#xff0c;结合测量结果对系统状态进行进行最优估计的算法。本文介绍它的主要公式&#xff0c;并举例在C6000 DSP上实现。 推荐资料 KalmanFilter.NETUnderstanding Kalman Filters卡尔曼滤波与组合导航原理 “If you can’t explain it sim…

rust 程序设计语言入门(1)

本文是阅读《Rust程序设计语言》的学习记录&#xff0c;配合视频《Rust编程语言入门教程》食用更佳 环境搭建 windows下载rustup_init.exe&#xff0c;点击安装&#xff0c;默认选择msvc的toolchain&#xff0c;一路default即可 解决下载慢的问题&#xff0c;在powershell中修…

libxlsxwriter条件格式

今天来看一个libxlsxwriter的高级用法&#xff1a;一个条件格式的示例。 说它“高级”&#xff0c;也是基于非Excel专家的小白们的视角。对&#xff0c;没错&#xff0c;本小白正是这样的小白。 1 一个简单的问题 来看我们今天的场景问题&#xff1a;有一列数据&#xff0c;有…

操作系统(一): 进程和线程,进程的多种状态以及进程的调度算法

文章目录前言一、进程和线程1. 进程2. 线程二、进程和线程的区别(面试常问)三、进程调度算法3.1. 批处理系统3.2. 交互式系统3.2.1 时间片轮转3.2.2 优先级调度3.2.3 多级别反馈队列3.3. 实时系统四、进程的状态五、进程同步5.1 什么是进程同步5.2 进程同步应该遵循的几点原则前…

Qt 学习(四) —— QGridLayout栅格布局

目录一、QGridLayout布局规则二、创建QGridLayout三、成员函数1. 控件间距2. 可拉伸控件&#xff08;弹簧&#xff09;3. 最小行高/列宽4. 行数和列数5. 锁定纵横比6. 添加控件7. 添加布局8. 设置栅格布局原点位置9. 操作布局项9.1 访问布局项9.2 删除布局项9.3 通过索引获取布…

Git教程个人分享:如何将一个本地项目上传至远程仓库的流程

前言&#xff1a; 今天来分享一下&#xff0c;关于Git的一些教程&#xff0c;同时这也是我自己曾今学习Git时候的笔记&#xff0c;之所以更&#xff0c;也是方便后期自己可以去回顾&#xff0c;当然后面也会出一部分关于Git其他操作方面的内容。 这次我们分享的是&#xff0c…

基于JavaScript的Web端股票价格查看器——大道

&#x1f436; 基于JavaScript的Web端股票价格查看器——大道 一、项目背景 当下互联网发展迅速&#xff0c;互联网已经不断向传统金融领域渗透。在互联网上有大量金融领域的数据&#xff0c;如何利用好这些数据&#xff0c;对于投资者来说是十分重要的一件事情。股票价格实时…

JavaSE学习day4_01 循环for,while,do...while

1. 循环高级 1.1 无限循环 for、while、do...while都有无限循环的写法。 最为常用的是while格式的。 因为无限循环是不知道循环次数的&#xff0c;所以用while格式的 代码示例&#xff1a; while(true){} 1.2 跳转控制语句&#xff08;掌握&#xff09; 跳转控制语句&…

MySQL 插入数据

数据库与表创建成功以后&#xff0c;需要向数据库的表中插入数据。在 MySQL 中可以使用 INSERT 语句向数据库已有的表中插入一行或者多行元组数据。 你可以通过 mysql> 命令提示窗口中向数据表中插入数据&#xff0c;或者通过PHP脚本来插入数据。 语法 以下为向MySQL数据表…

51单片机——步进电机实验,小白讲解,相互学习

步进电机简介&#xff1a; 步进电机是将电脉冲信号转变为角位移或多线位移的开源控制元件。在非超载的情况下&#xff0c;电机的转速&#xff0c;停止的位置只取决于脉冲信号的频率和脉冲数&#xff0c;而不受负载变化的影响&#xff0c;即给电机加一个脉冲信号&#xff0c;电机…

Android - 自动系统签名

一、系统签名 以下是两类应用开发场景&#xff1a; 普通应用开发&#xff1a;使用公司自定义 keystore 进行签名&#xff0c;如&#xff1a;微信、支付宝系统应用开发&#xff1a;使用 AOSP 系统签名或厂商自定义 keystore 进行签名&#xff0c;如&#xff1a;设置、录音 系…