手写一个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 {

    @Autowired
    private 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 {
    @Test
    public 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

拜托可以给它点点赞哦~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【C语言】万字详讲操作符

目录 前言 一、操作符分类 二、算数操作符 三、移位操作符 四、位操作符 五、赋值操作符 六、单目操作符 6.1 逻辑反操作 6.2 负值与正值 6.3 取地址 6.4 sizeof 6.5 取反操作符 6.6 --和操作符 6.7 间接访问操作符&#xff08;解引用操作符&#xff09; 6.8 强…

java导出数据到excel表中

java导出数据到excel表中 环境说明项目结构1.controller层2.service层3.实现层4.工具类&#xff1a;ExcelUtil.java5.ProductModel.java类 使用的Maven依赖postman请求展示&#xff0c;返回内容需要前端接收浏览器接收说明&#xff08;如果下载下来的为zip类型&#xff0c;记得…

矽塔SA8321 单通道 2.7-12.0V 持续电流 3.0A H 桥驱动芯片

描述 SA8321是为消费类产品&#xff0c;玩具和其他低压或者电池供电的运动控制类应用提供了一个集成的电机驱动器解决方案。此器件能够驱动一个直流无刷电机&#xff0c;由一个内部电荷泵生成所需的栅极驱动电压电路和4个功率 NMOS组成H桥驱动&#xff0c;集成了电机正转/反…

polkit服务启动失败

使用systemctl 命令报错 Authorization not available. Check if polkit service is running or see debug message for more information. 查看polkit状态是失败的状态&#xff0c;报缺少libstdc.so.6 systemctl status polkit 需要安装libstdc.so.6库 先加载所有安装包 …

网络安全产品---堡垒机

what 在网上搜索 运维审计与风险控制系统就是是堡垒机 我认为的堡垒机就是提供高效运维、认证管理、访问控制、安全审计和报表分析功能的云服务设备 实现高效运维的同时最大程度控制运维风险。 how 能够对运维人员维护过程进行全面跟踪、控制、记录、回放 支持细粒度配置…

最新Java面试题3【2024中级】

互联网大厂面试题 1&#xff1a;阿里巴巴Java面试题 2&#xff1a;阿里云Java面试题-实习生岗 3&#xff1a;腾讯Java面试题-高级 4&#xff1a;字节跳动Java面试题 5&#xff1a;字节跳动Java面试题-大数据方向 6&#xff1a;百度Java面试题 7&#xff1a;蚂蚁金服Java…

SpringMVC02:注解模式

SpringMVC02&#xff1a;注解模式 文章目录 SpringMVC02&#xff1a;注解模式前言一、代码编写&#xff1a;1. 编写jsp页面2. 在web.xml中&#xff0c;注册DispatcherServlet&#xff08;须要绑定SpringMVC配置文件&#xff09;3. 编写SpringMVC 的 配置文件4. 编写Controller类…

医学临床预测模型发展新趋势-并联式

医学临床预测模型发展新姿势-并联式 现有的预测模型是对单个结局指标进行分类或者回归&#xff0c;得出最终的结论&#xff0c;而辅助医生进行临床决策。众所周知&#xff0c;临床决策过程中&#xff0c;医生通常会考虑多个结局指标来做出最终的决策&#xff1b;临床研究中也通…

【JavaScript编程实操14】DOM实操_回到顶部

前言 本次主要是针对Javascript阶段的DOM实操方面的练习&#xff0c;本次主要实现当页面内容过多时&#xff0c;可以点击按钮&#xff0c;快速回到页面顶部的效果。这次的实现逻辑比较简单&#xff0c;主要是应用函数实现页面的回到顶部功能&#xff0c;this.scrollTo(0, 0)可以…

万界星空科技机器人组装行业MES系统

一、为什么选择万界星空科技&#xff1f; 万界星空科技作为一家在云MES系统的研发、生产自动化方面拥有很多年行业经验的科技型企业&#xff0c;多年来专注于云MES系统的研发与技术支持服务&#xff0c;目前已成为国内知名的智能制造整体解决方案提供商。 公司凝聚了一支经验…

Redis系列之Cluster集群搭建

在上一篇博客&#xff0c;我们学习Redis哨兵Sentinel集群的搭建&#xff0c;redis的哨兵模式提供了比如监控、自动故障转移等高可用方案&#xff0c;但是这种方案&#xff0c;容量相对固定&#xff0c;要进行持续扩容或者数据分片就不适合&#xff0c;所以有另外一种更复杂的集…

线性代数基础3 行列式

行列式 行列式其实在机器学习中用的并不多&#xff0c;一个矩阵必须是方阵&#xff0c;才能计算它的行列式 行列式是把矩阵变成一个标量 import numpy as np A np.array([[1,3],[2,5]]) display(A) print(矩阵A的行列式是&#xff1a;\n,np.linalg.det(A))array([[1, 3],[2, …

视频质量评价 PSNR 算法详细介绍

PSNR PSNR(Peak Signal-to-Noise Ratio,峰值信噪比)是一种常用的评价图像质量的指标,尤其在图像压缩和图像处理领域。它基于最大可能的图像信号功率和图像的噪声功率之间的比率,通常用于衡量图像恢复或图像压缩算法的效果。 原理 PSNR是基于MSE(Mean Squared Error,均…

『 论文解读 』大语言模型(LLM)代理能够自主地利用1 day漏洞,利用成功率竟高达87%,单次利用成本仅8.8美元

1. 概览 该论文主要展示了大语言模型LLM代理能够自主利用现实世界的 1 day 漏洞。研究我发现&#xff0c; GPT-4 在提供了CVE描述的情况下&#xff0c;能够成功利用 87% 的漏洞。 这与其他测试模型&#xff08;如 GPT-3.5 和其他开源 LLM &#xff09;以及开源漏洞扫描器&…

Tomcat核心组件深度解析

Server组件 Service组件 连接器Connector组件 容器Container组件

【hackmyVM】whitedoor靶机

文章目录 信息收集1.IP地址2.端口探测nmapftp服务 3.访问主页 漏洞利用1.反弹shell2.尝试提权3.base64解密 提权1.切换用户2.john爆破3.切换Gonzalo用户4.vim提权 信息收集 1.IP地址 ┌─[✗]─[userparrot]─[~] └──╼ $fping -ag 192.168.9.0/24 2> /dev/null192.168…

【小程序】IOS wx小程序解压获取源文件

根据自己手机的系统&#xff0c;获取wx小程序的缓存目录 一、微信小程序文件存放路径 安卓&#xff1a; /data/data/com.tencent.mm/MicroMsg/{{user哈希值}}/appbrand/pkg/iOS越狱&#xff1a; /User/Containers/Data/Application/{{系统UUID}}/Library/WechatPrivate/{{user…

PCIe复位方式介绍

前言 PCIe总线中定义了四种复位名称&#xff1a;冷复位&#xff08;Cold Reset&#xff09;、暖复位&#xff08;Warm Reset&#xff09;、热复位&#xff08;Hot Reset&#xff09;和功能层复位&#xff08;Function-Level Reset&#xff0c;FLR&#xff09;。其中FLR是PCIe …

RocketMQ学习笔记

kafka适合于日志收集的场景&#xff08;不需要太多topic&#xff1b;topic下面的partition多了会造成写文件的速度变慢&#xff0c;因为要造很多索引&#xff09; RocketMQ更适合于电商场景&#xff08;适用于topic特别多的情况&#xff09; 快速安装RocketMQ RocketMQ的官网…

js 函数节流和函数防抖及区别详解

文章目录 1. 前言2. 函数节流3. 函数防抖4. 总结 1. 前言 浏览器中总是有一些操作非常耗费性能。所以就有了函数节流和函数防抖来提高浏览器性能。 函数节流&#xff1a;频繁触发一个事件时候&#xff0c;每隔一段时间&#xff0c;函数只会执行一次。 函数防抖&#xff1a;当触…