手写Spring框架---IOC容器实现

news2024/11/24 14:46:31

目录

框架具备的最基本功能

实现容器前奏

创建注解

提取标记对象

extractPacakgeClass里面需要完成的事情

获取项目类加载器的目的

为什么不让用户传入绝对路径

类加载器ClassLoader

统一资源定位符URL

ClassUtil提取标记类

获取包下类集合

装载目标类的集合

获取Class对象

获取类加载器

测试成功

实现容器

容器的组成部分

实现容器的加载

容器的操作方式

多种作用域

实现容器的依赖注入


  • 框架具备的最基本功能

  • 解析配置
  • 定位与注册对象, 定位就是标记(注解),定位到对象之后,需要将对象注册到容器中
  • 注入对象, 在用户需要时将对象精确的返回给用户
  • 提供通用的工具类, 方便用户和框架根据自身需求进行调用
  • 实现容器前奏

  • 创建注解

  • 先创建常用注解@Controller(注入服务)(需要将该类被spring容器管理起来,方便对前端请求进行管理)
  • @Service(注入dao)(表示service层的实现类)、
  • @Repository(实现dao访问)(进行数据库的操作)
  • @Component(它的作用就是实现bean的注入,其余三个不同的注解是其衍生注解(功能与@Component一样),能够在逻辑上划分不同的模块,针对不同的使用场景)
  • 提取标记对象

  • 实现思路
    • 指定范围,获取范围内的所有类
    • 遍历所有类,获取被注解标记的类并加载进容器里
  • 将第一步的实现抽取出来,整成工具类(根据用户传入的业务项目的package,去扫描该package里面的所有类)
  • 根据传入的package获取下面的所有类
  • extractPacakgeClass里面需要完成的事情

    • 获取到类的加载器
    • 通过类加载器获取到加载的资源信息
    • 依据不同的资源类型,采用不同的方式获取资源的集合
  • 获取项目类加载器的目的

  • 获取项目发布的实际路径
  • com.icpc,我们是无法定位到它的具体路径,所以必须要先得到具体路径,才能拿到该路径下的所有class文件
  • 为什么不让用户传入绝对路径

  • 不够友好:不同机器之间的路径可能不相同
  • 如果打的是war包或者jar包,根本找不到路径
  • 因此通用的做法是通过项目的类加载器来获取
  • 类加载器ClassLoader

  • 根据一个指定的类的名称,找到或者生成其对应的字节码
  • 加载Java应用所需的资源(图片文件、配置文件、资源目录等)
  • 程序都是线程来执行的,获得当前执行的线程所属的加载器即可

  • 统一资源定位符URL

  • 某个资源的唯一地址
  • 通过获取java.net.URL实例获取协议名、资源名路径等信息

  • ClassUtil提取标记类

  • 获取包下类集合

  • 装载目标类的集合

  • 获取Class对象

  • 获取类加载器

  • 测试成功

  • 实现容器

  • 容器的组成部分

  • 保存Class对象及其实例的载体
  • 容器的加载 (需要定义配置获取并过滤目标对象的方法)
  • 容器的操作方式
  • 并非所有class的对象都是容器管理的对象 ,而是从中选取配置里指定的class对象,也就是注解标记的对象,才将其存入载体并管理
  • 将类对象以及对应的实例以键值对的形式存入map里面
  • 实现容器的加载

  • 实现思路:
  • 配置的管理与获取(如何将注解管理起来,随时读取目标注解,得到被它标记的类)
  • 获取指定范围内的Class对象
  • 依据配置提取Class对象,连同实例一并存入容器
  • 解决配置的问题:创建一个配置的载体用来保存相关的配置
  • 利用asList去转换成相应的list实例

  • 创建class对象实例通用的方法,让用户来决定是否生成修饰符为private的实例

  • 枚举的私有函数是可以抵御反射的攻击的,可以通过这种方式来创建较为安全的容器
  • 反编译:javap 编译:javac
  • 装备了枚举的饿汉模式能抵御反射与序列化的进攻使满足容器需求
  • 通过序列化将创建的单例写到文件里,再通过该文件逆序列化创建单例,很有可能会出现两个不同的实例
  • 通过class的newInstance方法创建的对象被称为bean,因此将Ioc容器命名为BeanContainer
  • 没有参数的构造方法,并设置修饰符,设置私有的枚举的成员变量
  • HOLDER用来盛放BeanContainer实例
  • 再定义私有的成员变量,定义枚举的私有构造函数,在里面new实例
  • 解决单例输出,方便外界获取单例对象

  • 将判断集合是否为空的逻辑抽取出来作为工具类,还有判断字符串、数组、Map是否为空

  • 由于容器的加载是一个比较耗时的过程,为了避免重复加载,可以定义一个私有的布尔类型的成员变量用来判断容器是否被加载过

  • 当被这些注解修饰后就需要被beancontainer容器管理
  • 加载bean和获取容器的分离,所以不需要返回值
  • 传入加载的范围,返回该范围内的bean
  • 1---获取package所有的class
  • 2---判断集合空值,日志记录,并返回;得到了就循环遍历classset根据定义好的注解得到对应的class对象
  • 在容器加载完成后,修改变量的值
  • 为了线程安全(两个线程修改变量的值),将方法定义为同步的
  • 测试:
  • 单元测试,创建一样的目录
  • @BeforeAll:只进行一次初始化,在所有测试开始之前进行的初始化;并不是每执行一次测试方法都初始化一次
  • 先判断是否被加载过
  • 将所有被core下面的annotation修饰了的类找出来并创建该类对应的实例,将其转载到容器里面

  • 容器的操作方式

  • 根据某个service接口来获取该接口的实现类
  • 实现容器的操作方式:
  • 涉及到容器的增删改查
  • 增加、删除操作
  • 通过注解来获取被注解标注的Class
  • 根据Class获取对应实例
  • 通过超类获取对应的子类Class
  • 获取所有的Class和实例
  • 获取容器载体保存Class的数量
  • 增加bean

  • 删除:通过key删除

  • 获取bean实例

  • 获取所有bean管理的对象,即key的集合

  • 获取所有bean实例,即v的集合,和key保持一致强转为set

  • 根据注解获得容器里所有Class对象,即所有的key,对返回的classSet集合进行空值判断(null或者为空),统一返回结果

  • 通过接口或者父类获取实现类或者子类的Class集合,不包括本身

  • 测试:
  • 对容器的操作都是基于容器被加载之后,所以测试时要指定顺序
  • 用instanceof来判断controller实例是否是MainPageController创建出来的
  • dispatcherServlet没有加注解没有被bean容器管理,所以使用bean容器获得的实例对象应该为null
  • 多种作用域

  • singleton:
  • 在初始化配置文件时生成单例bean对象,单例模式指的是无状态bean,spring IOC容器中只会有一个共享的Bean,每次getBean时获取的都是同一个对象,是线程不安全的
  • prototype:
  • 每次通过容器的getBean方法都会获得一个新的实例
  • request:
  • 对于每次http请求使用request定义的bean都会产生一个新的bean实例(即每次http请求都会产生不同的bean实例),只有在web应用中使用spring时该作用域才有效
  • session:
  • 对于每次http请求使用session定义的bean都会产生一个新的bean实例(即在session有效的时候返回的bean是一个,失效再去访问返回的就是新的),只有在web应用中使用spring时该作用域才有效
  • globalsession:
  • 每个全局的http session,使用session定义的bean都会产生一个新实例
  • 实现容器的依赖注入

  • 目前容器里面管理的Bean实例仍可能是不完备的
  • 上面虽然将bean实例交给了容器管理,但是从容器中获取bean对象没有注入到相对应的成员变量对象上
  • 此时如果调用对象封装的方法,一定会报空指针异常(相当于取到的对象没有赋值给对应的对象变量)
  • 即接下来需要解决把取到的对象给到对应的主人,让主人充实起来,不是null,就是依赖注入
  • 实现思路:
  • 定义相关的注解标签
  • 实现创建被注解标记的成员变量实例,并将其注入到成员变量里
  • 依赖注入的使用(只支持成员变量级别的注入,不像spring框架还支持构造函数、工厂注入)

  • 定义相关的注解标签
  • 因为Autowired是依据成员变量的类型注入的
  • 多个实现类的情况,增加了Qualifier,指定具体的实现类名称
  • 采用最简单的实现方式,在Autowired里面加上value属性
  • 提供依赖注入的服务,需要先获取容器中的实例
  • 为了在DependencyInjector实例被创建时就能获得beanContainer实例,可以在构造方法里面编写获得容器实例的逻辑

  • 先判空
  • 给实例变量赋值是一个通用的方法

  • 得到的bean实例不为空就说明在容器中直接找到了(说明Class是类),直接返回
  • 如果为空,需要判断是不是接口(返回对应的实现类)
  • 找它对应的实现类后,再在容器里找实现类,有实现类就返回
  • 没有就返回null

  • 重构注入ioc方法(先去获得Autowired实例,再去获取其value)、根据Class在beanContainer里获取其实例或者实现类(就传递个参数)
  • 首先判断autowiredValue的值是否为默认值
  • 为空表示用户没有告诉框架返回哪个具体的实现类,需要处理两种情况:1、只有一个实现类 2、多个实现类,抛出异常
  • 如果value设置上值,需要去遍历classSet,class的名字是去除掉package的名字,所以用getSimpleName

  • 测试:
  • 创建同样的目录
  • 1、获取容器实例
  • 2、指定范围将被注解标记的类交给容器管理加载
  • 3、先判断容器是否被加载,加载后就可以获得bean实例
  • 4、判断是否是MainPageController创建的实例
  • 5、给controller设置get方法获得私有成员变量
  • 6、因为没调用doIoc,所以成员变量的实例应该为null
  • 7、调用doIoc,进行依赖注入

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

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

相关文章

Git相关使用

私人博客 许小墨のBlog —— 菜鸡博客直通车 系列文章完整版,配图更多,CSDN博文图片需要手动上传,因此文章配图较少,看不懂的可以去菜鸡博客参考一下配图! 系列文章目录 前端系列文章——传送门 后端系列文章——传送…

APP UI自动化测试思路总结 ,教你彻底学会APP自动化测试

目录 一,开发语言选择 二,UI测试框架选择 1,Appium 2,Airtest 3,选择框架 三,单元测试框架选择 四,测试环境搭建 1,测试电脑选择 2,测试手机选择 3,…

【Redis】Redis管道

面试题 如何优化频繁命令往返造成的性能瓶颈? Redis是一种基于客户端-服务端模型以及请求/响应协议的TCP服务。一个请求会遵循以下步骤: 客户端向服务端发送命令分四步(发送命令→命令排队→命令执行→返回结果),并监听Socket返回&#xf…

基于matlab仿真相控天线阵列在波束成形MIMO-OFDM系统中的使用

一、前言 本例显示了相控阵在采用波束成形的MIMO-OFDM通信系统中的使用。它使用通信工具箱和相控阵系统工具箱中的组件,对组成发射器和前端接收器组件的辐射元件进行建模,用于MIMO-OFDM通信系统。使用用户指定的参数,您可以根据不同空间位置和…

Django自动化测试平台搭建落地全过程(附视频教程+源码)

目录 前言 一、平台基础架构设计 二、自动化测试工具选择与集成 三、平台功能开发 四、总结 前言 自动化测试在现代软件开发中扮演着至关重要的角色,它可以显著提高测试效率和准确性,并帮助开发团队更快地发布高质量的软件产品。Django作为一种流行…

Hudi数据湖技术之集成Spark

目录 1 环境准备1.1 安装MySQL 5.7.311.2 安装Hive 2.11.3 安装Zookeeper 3.4.61.4 安装Kafka 2.4.1 2 滴滴运营分析2.1 需求说明2.2 环境准备2.2.1 工具类SparkUtils2.2.2 日期转换星期 2.3 数据ETL保存2.3.1 开发步骤2.3.2 加载CSV数据2.3.3 数据ETL转换2.3.4 保存数据至Hudi…

Flink的DataStreamApi

1.源算子 SourceFunction 并行度只能为1 ParallelSourceFunction 并行度 2.转换算子 (1)基本类型 所有 Java 基本类型及其包装类,再加上 Void、String、Date、BigDecimal 和 BigInteger。 (2)数组类型 包括基…

【算法】【算法杂谈】判断点是否在三角形内部(面积法和向量法)

目录 前言问题介绍解决方案代码编写java语言版本c语言版本c语言版本 思考感悟写在最后 前言 当前所有算法都使用测试用例运行过,但是不保证100%的测试用例,如果存在问题务必联系批评指正~ 在此感谢左大神让我对算法有了新的感悟认识! 问题介…

netcore工程在linux下调用linux动态库

文章的内容可能看着枯燥,排版也存在一些问题,但是如果你遇到相关问题,真的无法解决的时候,不妨沉下心来好好阅读一下这篇文章,你会有所收获,也可以先跳到文章最后,看看是不是对你的问题有价值。…

Day955.到底是重构,还是重写? -遗留系统现代化实战

到底是重构,还是重写? Hi,我是阿昌,今天学习记录的是关于到底是重构,还是重写?的内容。 到底是重构,还是重写?这是一个困扰着很多团队的问题。 重构吧,遗留系统积重难…

神经网络模型入门及蠓虫分类问题简单实战

学习知识要实时简单回顾,我把学习的神经网络模型简单梳理一下,方便入门与复习。 神经网络模型 神经网络简介 人工神经网络是在现代神经科学的基础上提出和发展起来的,旨在反映人脑结构及功能的一种抽象数学模型。自 1943 年美国心理学家W.M…

【分段DP】ABC275 F

一万年没写DP了 这么简单的DP我居然没写出来 F - Erase Subarrays (atcoder.jp) 题意: 思路: 原本的思路是这样的: 看到3000的数据范围就是n^2的DP了 看到删子串,那么留下来的就是子序列,要使得剩下来的子序列的…

剑指Offer--05替换空格58左旋字符串

文章目录 一、剑指Offer--05.替换空格二、剑指Offer--58.左旋字符串 一、剑指Offer–05.替换空格 题目是这样的 意思是将字符串s中的空格替换为字符串"%20",如果只是替换一个字符还好,可以在原数组直接替换,但是是将空格替换为字符串&#xf…

Vue+Echarts 项目演练(下)收尾工作图表绘制

设置销售总量图表 中心容器地图设置 产品库存统计图 产品类别图表 项目可视化完结-整体展示 设置销售总量图表 在第一个容器中进行图表设置 <template><div><h2>A</h2><div class"chart" id"oneChart">容纳后期的图表…

shell编程规范与变量

shell脚本编程规范 shell脚本概述 将要执行的命令按顺序保存到一个文本文件给该文件可执行权限可结合各种Shell控制语句以完成更复杂的操作 Shell脚本应用场景 重复性操作交互性任务批量事务处理服务运行状态监控定时任务执行 什么是Shell 就是与内核沟通的界面、应用程序等…

[JAVA数据结构]顺序表ArrayList

目录 1.线性表 2.顺序表 3.ArrayList简介 4.ArrayList的使用 4.1ArrayList的构造方法 4.2ArrayList的常用操作 4.3ArrayList的遍历方法 4.4ArrayList的扩容机制 5.ArrayList的具体运用 ArrayList是一种基于数组的数据结构&#xff0c;是线性表的一种&#xff0c;也是…

[NLP]如何训练自己的大型语言模型

简介 大型语言模型&#xff0c;如OpenAI的GPT-4或谷歌的PaLM&#xff0c;已经在人工智能领域掀起了一场风暴。然而&#xff0c;大多数公司目前没有能力训练这些模型&#xff0c;而且完全依赖少数几家大型科技公司作为技术提供者。 在Replit&#xff0c;我们已经大量投资于所需…

linux-01-基础回顾-虚拟机安装linux(centos7)、linux常用命令

文章目录 Linux-Day01课程内容1. 前言1.1 什么是Linux1.2 为什么要学Linux1.3 学完Linux能干什么 2. Linux简介2.1 主流操作系统2.2 Linux发展历史2.3 Linux系统版本 3. Linux安装3.1 安装方式介绍3.2 安装VMware3.3 安装Linux3.4 网卡设置3.5 安装SSH连接工具3.5.1 SSH连接工具…

Neural ODE 神经常微分方程

Neural ODE ODE常微分方程 欧拉法求解&#xff1a;欧拉法求解过程是一个递归的过程&#xff0c;这个思想和牛顿法、梯度下降法是相似的。并且它将函数离散化&#xff0c;分割成一个个小段来求解。欧拉法求解的常微分方程的形式通常为 图片来自知乎Neural ODE&#xff0c;这个…

EventBus源码解析

文章目录 前言一、EventBus使用二、EventBus事件流程分析1.注册订阅者2.发布事件Event3.接收事件Event4.取消注册订阅者 三、发送粘性事件问答EventBus 以及它的优点EventBus原理 EventBus中设计模式为什么要使用 EventBus 来替代广播呢&#xff1f;说下 5 种线程模式的区别Eve…