手写一个Spring IOC框架

目录

一,Spring IOC

 二,流程图设计

三,设计思路解析

三,开始写代码

1.准备工作:

2.扫描并加载类信息

3.初始化bean

4.测试一下


一,Spring IOC

Spring IoC容器是Spring框架的核心,它通过读取配置信息来自动创建对象(bean)、配置对象属性以及管理对象的生命周期。IoC容器利用依赖注入(DI)自动将对象的依赖关系注入到需要它们的其他对象中,从而减少代码间的耦合度。

容器支持多种bean作用域和生命周期管理,提供了事件处理、国际化消息和资源访问等高级功能。此外,Spring IoC与AOP(面向切面编程)紧密结合,支持通过切面来实现如日志、事务等横切关注点的处理。

上面是IOC的介绍,接下来我将去完成一个简单的IOC容器,剖析底层。

 二,流程图设计

三,设计思路解析

  1. 扫描类路径下所有以Java结尾的文件: 在Spring框架中,通过使用ClassPathScanningCandidateComponentProvider类,可以扫描指定的类路径下所有的.class文件。这个类通常与AnnotationConfigApplicationContext一起使用,后者是Spring中用于处理注解配置的上下文。

  2. 获得需要被IoC管理的类: 通过类路径扫描,Spring容器会筛选出那些需要被IoC容器管理的类。这些类通常带有特定的注解,如@Component@Service@Repository@Controller等,这些注解表明了一个类将作为Spring容器中的一个bean。

  3. 全类名加入到beanNames集合中: 将筛选出的类全限定名(即包名+类名)加入到一个名为beanNames的集合中。这个集合是Spring容器内部用于跟踪所有候选bean的一个列表。

  4. 反射为实例属性赋值: 对于每个候选的bean类,Spring容器会通过反射创建其实例,并通过反射机制为其属性赋值。这一过程涉及到处理@Autowired注解,以及根据类型或名称自动装配依赖关系。

  5. 完成依赖注入的过程: 依赖注入是Spring IoC容器的核心功能之一。Spring会根据bean的定义,解析和注入所有需要的依赖。这可能涉及到查找其他bean、处理复杂的依赖关系(如循环依赖)以及使用BeanFactory来获取和注入依赖。

  6. 反射获取类上的注解: 在处理每个bean时,Spring容器会通过反射获取类上的注解信息。这些注解可能会影响bean的创建、作用域、生命周期等。例如,@Scope注解定义了bean的作用域,@PostConstruct注解指定了在构造之后执行的方法等。

  7. 容器启动: 在所有bean都被创建并注入依赖之后,Spring容器会启动。这通常涉及到调用ApplicationContextrefresh()方法,该方法会触发容器的启动过程,包括初始化所有的singleton beans、处理@PostConstruct注解的方法、以及发送ContextRefreshedEvent事件。

  8. 启动结束: 一旦容器启动完成,Spring会发送一个ContextClosedEvent事件,表明容器已经准备就绪,可以开始处理请求和执行业务逻辑。

三,开始写代码

我们将一步步按照流程图中所述的将IOC底层实现出来。

1.准备工作

编写注解和测试类(包含@Autowired和@Component)

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {
}
@Component
public class UserService {public void addUser(String name, int age) {System.out.println(name);System.out.println(age);}
}
@Component
public class TestController {@Autowiredprivate UserService userService;public void test(){userService.addUser("zhangsan",18);}
}

2.扫描并加载类信息

(1)扫描类路径下的所有文件将其加入到集合中。

(2)将.java结尾的文件筛选出来

    //构造函数public SpringIOC() {initPath();try {scan();} catch (FileNotFoundException e) {e.printStackTrace();}beanNames= new ArrayList<>();initBeanNames();}private void initPath(){basePath="类路径自己指定";basePackage="自己指定扫描包";}/*** 将类路径下所有文件提取出来放到集合中*/private void scan() throws FileNotFoundException {File file = new File(basePath);filePaths = new ArrayList<>();if(file.exists()){Queue<File> queue = new LinkedList<>();queue.add(file);while(!queue.isEmpty()){File poll = queue.poll();if(poll == null){continue;}if(poll.isDirectory()){//目录下面的所有文件夹File[] files = poll.listFiles();for (File f : files) {queue.add(f);}}else {filePaths.add(poll.getPath());//将单个文件放到filePaths当中}}}else {throw new FileNotFoundException(basePath+" not found");}}/*** 将所有的.java结尾的 全限定名放到 beanNames*/public void  initBeanNames(){for (String s : filePaths) {//遍历刚才文件路径String replace = s.replace(basePath, "");if(replace.endsWith(".java")) {replace = replace.substring(0, replace.length()-5);}char[] chars = replace.toCharArray();for (int i = 0; i < chars.length; i++) {if(chars[i]=='\\'){chars[i] = '.';}}beanNames.add(basePackage+"."+new String(chars));}}

3.初始化bean

在2中我们已经得到了所有java类的类名,这一步可以根据全类名来获取类的注解等各种信息。

(1)反射获取类上注解,筛选出被IOC管理的类并创建实例,封装到ioc容器中。

(2)为ioc容器中的bean反射设置属性(依赖注入)

    //非懒加载public void initBeans(){for (String beanName : beanNames) {System.out.println(beanName);try {Class<?> aClass = Class.forName(beanName);Annotation[] declaredAnnotations = aClass.getDeclaredAnnotations();//获取类上的所有注解for (Annotation declaredAnnotation : declaredAnnotations) {if(declaredAnnotation instanceof Component){//反射获取带@Component的类Object o = aClass.newInstance();//创建实例beans.put(aClass.getName(),o);//将类名-实例放到beans容器当中}}} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {e.printStackTrace();}}System.out.println(beans);for (Map.Entry<String, Object> entry : beans.entrySet()) {//将实例从map中拿出来Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();//获取实例的所有字段for (Field field : declaredFields) {Annotation[] declaredAnnotations = field.getDeclaredAnnotations();//字段上所有的注解for (Annotation annotation : declaredAnnotations) {if (annotation instanceof Autowired) {//带@Autowired的注解String name = field.getType().getName();//com.heaboy.springioc.entity.UserService// 从beans 中获得对应的对象Object o = beans.get(name);//beans是个map 只要里面有这样的类型 就能拿到field.setAccessible(true);//设置可访问的权限try {field.set(entry.getValue(), o);//反射将o设置给字段} catch (IllegalAccessException e) {e.printStackTrace();}}}}}}

4.测试一下

3完成之后,此时加@Autowired和@Component注解的类已经在集合中了,属性也已经注入。我们来编写测试代码debug一下。

public class SpringIOCTest {@Testpublic void testScan() throws FileNotFoundException {SpringIOC springIOC = new SpringIOC();//获取java类的全类名springIOC.initBeans();TestController instance = (TestController)springIOC.getInstance(TestController.class.getName());instance.test();}
}

可以看到两个bean实例以及他们之间的依赖关系。

完整代码获取可以去我的git: https://gitee.com/code0321/ioc

拜托可以给它点点赞哦~

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

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

相关文章

【每日刷题】Day20

【每日刷题】Day20 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 面试题 17.04. 消失的数字 - 力扣&#xff08;LeetCode&#xff09; 2. 189. 轮转数组 - 力扣&#…

随机森林(Random Forests)

通过5个条件判定一件事情是否会发生&#xff0c;5个条件对这件事情是否发生的影响力不同&#xff0c;计算每个条件对这件事情发生的影响力多大&#xff0c;写一个随机森林&#xff08;Random Forests&#xff09;模型程序,最后打印5个条件分别的影响力。 ChatGPT 下面是一个使…

后端获取请求体Body,将请求体进行解密放回Request请求,并能通过@RequestBody获取

目前系统发送的post和put请求都是没有加密数据。客户需要将请求体加密。而系统已经基本开发完成&#xff0c;不可能一个一个去修改发送的请求。就需要在发送请求时候在拦截器中将body进行加密。并且在后端进行请求过滤解密&#xff0c;并且能通过RequestBody继续获取对象。 1.…

matlab学习004-使用matlab绘制两个序列及信号的卷积波形图

目录 1&#xff0c;序列&#xff1a;x(n)u(n-2)-u(n-10)&#xff0c;h(n)((0.9)^n)*u(n) 1&#xff09;前期基础 ①conv函数 ②离散单位阶跃信号 2&#xff09;波形图 3&#xff09;代码 ①使用input方法 ②代码改进【推荐使用】 2&#xff0c;信号&#xff1a;xu(…

C语言趣味代码(二)

1.珠玑妙算 1.1 介绍 《珠玑妙算》(Mastermind)是英国Invicta公司于1973年开始销售的一款益智游戏&#xff0c;据说迄今为止已经在全世界销售了5000万套。《珠玑妙算》于1974年获奖后&#xff0c;在1975年传入美国&#xff0c;1976年leslieH.Autl博士甚至还出版了一本名为The…

去雾笔记01-SRKTDN: Applying Super Resolution Method to Dehazing Task

文章目录 Abstract1. Introduction2. Related Work3. Method3.1. Network Architecture Abstract 们提出了一种结合超分辨方法和知识转移方法的模型。我们的模型由一个教师网络、一个去雾网络和一个超分辨率网络组成。 1. Introduction ECNU KT团队提出了一个知识蒸馏[20]模…

智慧养老平台|基于SprinBoot+vue的智慧养老平台系统(源码+数据库+文档)

智慧养老平台目录 基于SprinBootvue的外贸平台系统 一、前言 二、系统设计 三、系统功能设计 前台 后台 管理员功能 老人功能 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题推荐 八、源码获取&#xff1a; 博主介绍&#xff1a;✌️大厂码农…

控制台程序设置

该篇博客主要是对下一篇博客《贪吃蛇游戏》的设计做铺垫&#xff0c;大家可以持续关注(点个关注哦&#x1f60a;)等待更新&#xff0c;以下是个人主页&#xff1a; 敲上瘾-CSDN博客 在我们写的程序运行起来后弹出的框就是控制台程序。而这个窗口我们是可以对它进行设置的&…

终于找到恢复照片的绝佳方法了!手机照片恢复的看这里!

当今手机已经彻底离不开我们的生活&#xff0c;手机里面的数据也成为了我们高质量生活的重要构成&#xff0c;当手机内存爆满&#xff0c;我们就不得不采用清除部分手机数据的方法来释放空间。有一些数据删除后往往才发现还另有用处&#xff0c;比如手机里存储着的大量照片。 …

帆软报表实现通过js查询数据库设置表格数据

最近做的一直在做报表相关的需求&#xff0c;自己也是一边学一边做。有一个有意思的需求是在表格中某个单元格在编辑完以后其它的表格中的数据自动填充&#xff0c;当也是根据一定的规则与数据来源才能填充的。 先来点基础概念&#xff0c;就是帆软给我们提供了这个编辑后的事件…

学习笔记:Vue2高级篇

Vue2 学习笔记&#xff1a;Vue2基础篇_ljtxy.love的博客-CSDN博客学习笔记&#xff1a;Vue2中级篇_ljtxy.love的博客-CSDN博客学习笔记&#xff1a;Vue2高级篇_ljtxy.love的博客-CSDN博客 Vue3 学习笔记&#xff1a;Vue3_ljtxy.love的博客&#xff09;-CSDN博客 文章目录 7.…

《Spring》系列文章目录

Spring Framework是一个为基于Java的现代企业应用程序提供全面编程和配置模型的开源框架。它集成了控制反转&#xff08;IOC&#xff09;、依赖注入&#xff08;DI&#xff09;和面向切面编程&#xff08;AOP&#xff09;等容器技术。Spring框架的设计理念是面向Bean编程&#…

现代图形API综合比较:Vulkan | DirectX | Metal | WebGPU

Vulkan、DirectX、Metal 和 WebGPU 等低级图形 API 正在融合为类似于当前 GPU 构建方式的模型。 图形处理单元 (GPU) 是异步计算单元&#xff0c;可以处理大量数据&#xff0c;例如复杂的网格几何形状、图像纹理、输出帧缓冲区、变换矩阵或你想要计算的任何数据。 NSDT工具推荐…

早期javeweb技术 JSP JDBC JSTJ Servlet BooStrap

你好,我是Qiuner. 为记录自己编程学习过程和帮助别人少走弯路而写博客 这是我的 github gitee 如果本篇文章帮到了你 不妨点个赞吧~ 我会很高兴的 &#x1f604; (^ ~ ^) 想看更多 那就点个关注吧 我会尽力带来有趣的内容 jar包放在web-web-inf中 Boot strap框架 container是两…

【Go语言快速上手(三)】数组, 切片与映射

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Go语言专栏⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多Go语言知识   &#x1f51d;&#x1f51d; GO快速上手 1. 前言2. 数组详解3. 切…

平面设计简介:快速了解

在互联网飞速发展的时代&#xff0c;平面设计仍然是很多设计师的职业选择&#xff0c;平面设计也被称为设计的启蒙星。平面设计是什么&#xff0c;无论是想转行进入平面设计行业还是平面设计新秀&#xff1f;什么是平面设计&#xff1f;只有了解这一点&#xff0c;我们才能在未…

制造业信息安全实践——企业信息安全运营规划

前言 制造业作为一个庞大的传统产业&#xff0c;涵盖了汽车、船舶、飞机、家电、新能源等众多领域。当前&#xff0c;无论是国内还是国外的制造业都面临着一个共同的挑战&#xff1a;在计算机和信息时代的背景下&#xff0c;如何跟上IT技术的发展步伐&#xff1f;如何让传统产…

4月21日Linux运维用户相关的添加,分组,修改权限等shell脚本开发第一天

4月21日运维用户相关的添加&#xff0c;分组&#xff0c;修改权限等shell脚本开发第一天 第一天主要实现前2个功能 ​ 主要卡在了&#xff1a; 正确的写法如下&#xff0c;注意[]中的空格&#xff0c;要求很严格&#xff01;&#xff01;&#xff01; #!/bin/bash # 先查看已…

抖音ip地址怎么换位置

抖音&#xff0c;作为一款短视频分享平台&#xff0c;已经成为了许多人展示生活、分享才艺的重要舞台。然而&#xff0c;在抖音的使用过程中&#xff0c;你是否想过更换自己的IP地址位置呢&#xff1f;更换IP地址不仅可以帮助你访问一些地域限制的内容&#xff0c;还可以为你的…

micro-app的css样式隔离

手写微前端micro-app-CSS隔离 子应用的CSS可能会对基座应用或者其他子应用产生的影响 首先现在我们把react页面放入到vue2的页面大家也能看到一些问题了&#xff0c;在react中的index.css中对body的一些css样式&#xff0c;已经影响了基座应用的css。 为了看的更明显&#x…