Spring之丐版IOC实现

news/2024/4/25 7:30:52/文章来源:https://blog.csdn.net/zly03/article/details/129228472

文章目录

    • IOC控制反转
      • 依赖注入
        • Bean的自动装配方式
      • 丐版IOC实现
        • BeanDefinition.java
        • ResourceLoader.java
        • BeanRegister.java
        • Bean和DI的注解
        • BeanFactory.java
        • ApplicationContext
        • 测试,实现
    • 在这里插入图片描述

大家好,我是Leo。Spring核心中依赖注入和IOC容器是非常常见的,用起来也是非常的顺手,只能说是真香,那如何实现一个丐版的SpringIOC呢?那今天我们就来讲如何实现一个乞丐版的IOC和通过注解进行依赖注入。

IOC控制反转

在开始之前,首先得先理解什么是IOC的控制反转,Spring 通过 IoC 容器来管理所有 Java 对象的实例化和初始化,控制对象与对象之间的依赖关系 。我们将由 IoC 容器管理的 Java 对象称为 Spring Bean,它与使用关键字 new 创建的 Java 对象没有任何区别。

说白了就是,IOC负责我们的饮食,我们要饭和菜,只需要从IOC容器获取就行。

那这样的作用也就非常明显了,解耦、方便

依赖注入

依赖注入肯定是IOC的精髓所在,我们平常使用@Autowired注解等自动注入方式,用起来确实舒服。

那依赖注入常用方式有哪些呢?

  • 构造函数的参数实现注入

  • 基于set注入(静态工厂和动态工厂)

  • 基于属性的注入

Bean的自动装配方式

可以在文件中设置自动装配(autowire)方式,支持的装配方式有

方式描述
no手动装配
byName通过id的名字自动注入对象
byType通过类型自动注入对象
constructor通过构造方法自动注入对象
autodectect完全交给Spring管理,按先Constructor后byType的顺序进行匹配

丐版IOC实现

BeanDefinition.java

Bean的定义类,主要是包括配置Bean定义的对应的实体,对应的构造方法和getset方法就省略了

public class BeanDefinition {private String beanName;private Class beanClass;

ResourceLoader.java

资源加载器,用来完成包的扫描和Bean的加载

public class ResourceLoader {private static String rootPath;private Map<String, BeanDefinition> beanDefinitionMap = new HashMap(16);public Map<String, BeanDefinition> getResource(String basePackage) {// com.zlytry {// 1. 把.换成/String packagePath = basePackage.replaceAll("\\.", "\\\\");// 2.获取包的绝对路径Enumeration<URL> urls =Thread.currentThread().getContextClassLoader().getResources(packagePath);while (urls.hasMoreElements()) {URL url = urls.nextElement();String filePath = URLDecoder.decode(url.getFile(), "utf-8");rootPath = filePath.substring(0, filePath.length() - packagePath.length());// 包扫描try {loadBean(new File(filePath));} catch (Exception e) {e.printStackTrace();}}} catch (IOException e) {e.printStackTrace();}return this.beanDefinitionMap;}private void loadBean(File file) throws Exception {// 1. 判断是否是文件夹if (file.isDirectory()) {// 2. 获取文件夹的所有内容File[] childFiles = file.listFiles();// 3. 判断文件夹内为空,直接返回if (childFiles == null || childFiles.length == 0) {return;}// 4. 如果文件夹里面不为空,遍历文件夹所有的内容for (File childFile : childFiles) {// 4.1 遍历得到每个File对象,继续是文件夹,递归if (childFile.isDirectory()) {loadBean(childFile);} else {// 4.2 得到不是文件夹,是文件// 4.3 得到包路径 和类名称String pathWithClass = childFile.getAbsolutePath().substring(rootPath.length() - 1);// 4.4 判断当前文件类型是否为classif (pathWithClass.contains(".class")) {// 4.5 是class类型,把\替换成. 把.class去掉String allName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");// 4.6 判断类上面是否有注解@Bean,放到map集合beanFactory// 4.6.1 获取类的Class对象Class<?> clazz = Class.forName(allName);// 4.6.2 判断不是接口if (!clazz.isInterface()) {// 4.6.3 判断上面是否有@Bean注解Bean annotation = clazz.getAnnotation(Bean.class);if (annotation != null) {// 4.6.4 实例化String beanName = annotation.value();if ("".equals(beanName)) {beanName = allName;}if (clazz.getInterfaces().length > 0) {System.out.println("正在加载【"+ clazz.getInterfaces()[0] +"】");BeanDefinition beanDefinition = new BeanDefinition(beanName, clazz);beanDefinitionMap.put(beanName, beanDefinition);} else {System.out.println("正在加载【"+ clazz.getInterfaces()[0]);BeanDefinition beanDefinition = new BeanDefinition(beanName, clazz);beanDefinitionMap.put(beanName, beanDefinition);}}}}}}}}}

BeanRegister.java

用于向容器中注册一个Bean,在扫描后,我们的Bean主要是放在了beanDefinitionMap中,还没有进行加载和初始化,在获取中再进行加载到这个缓存Map中

public class BeanRegister {private Map<String, Object> singletonMap = new HashMap<>(32);public Object getSingletonBean(String beanName) {return singletonMap.get(beanName);}public void registerSingletonBean(String beanName, Object bean) {if (singletonMap.containsKey(beanName)) {return;}singletonMap.put(beanName, bean);}}

Bean和DI的注解

这里的两个注解主要是用来标志这是一个Bean和进行依赖注入的

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {String value() default "";
}
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Di {
}

BeanFactory.java

这里的对象工厂主要是我们最重要的一个类,在创建时,需要加载注册器和资源加载,在获取Bean中,需要进行依赖注入,并创建一个Bean

public class BeanFactory {/*** 创建一个map集合,放bean对象*/private Map<String , BeanDefinition> beanDefinitionMap = new HashMap<>();private BeanRegister beanRegister;public BeanFactory(String basePackage) {beanRegister = new BeanRegister();this.beanDefinitionMap = new ResourceLoader().getResource(basePackage);}public Object getBean(String beanName) {// 从缓存中获取Object bean = beanRegister.getSingletonBean(beanName);if (bean != null) {return bean;}// 创建beanreturn createBean(beanDefinitionMap.get(beanName));}public Object getBean(Class<?> clazz) {BeanDefinition beanDefinition = getBeanNameByType(clazz);if (Objects.isNull(beanDefinition)) {log.error("can not find {}", clazz);return null;} else {return getBean(beanDefinition.getBeanName());}}private Object createBean(BeanDefinition beanDefinition) {try {Object bean = beanDefinition.getBeanClass().getConstructor().newInstance();beanRegister.registerSingletonBean(beanDefinition.getBeanName(), bean);loadDi(beanDefinition.getBeanName());return bean;} catch (Exception e) {e.printStackTrace();}return null;}/*** 属性注入*/private void loadDi(String beanName) {// 1. 实例化对象都在beanFactory的map集合中,遍历Object bean = this.beanRegister.getSingletonBean(beanName);// 2. 获取map集合每个对象和属性Class<?> objectClass = bean.getClass();// 3. 遍历得到每个对象属性数组,得到每个属性Field[] declaredFields = objectClass.getDeclaredFields();for (Field declaredField : declaredFields) {// 4. 判断属性上面是否有@Di注解Di annotation = declaredField.getAnnotation(Di.class);if (annotation != null) {declaredField.setAccessible(true);// 如果私有属性,可以设置值// 5. 如果有@Di注解,把对象进行注入try {BeanDefinition beanDefinition = getBeanNameByType(declaredField.getType());if (Objects.isNull(beanDefinition)) {declaredField.set(bean, null);} else {declaredField.set(bean, getBean(beanDefinition.getBeanName()));}} catch (IllegalAccessException e) {throw new RuntimeException(e);}}}}private BeanDefinition getBeanNameByType(Class clazz) {Set<Map.Entry<String, BeanDefinition>> entries = this.beanDefinitionMap.entrySet();for (Map.Entry<String, BeanDefinition> entry : entries) {if (entry.getValue().getBeanClass().equals(clazz)) {return entry.getValue();}if (entry.getValue().getBeanClass().getInterfaces()[0].equals(clazz)) {return entry.getValue();}}return null;}}

ApplicationContext

我也模仿了Spring一样定义了一个ApplicationContext容器,获取Bean从这个容器获取,context容器再从BeanFactory中获取

public class AnnotationApplicationContext implements ApplicationContext {/*** 创建一个map集合,放bean对象*/private BeanFactory beanFactory;@Overridepublic Object getBean(Class clazz) {return beanFactory.getBean(clazz);}@Overridepublic Object getBean(String beanName) {return beanFactory.getBean(beanName);}/*** 设置包扫描规则* 当前包及其子包,哪个类有@Bean注解,把这个类通过反射化*/public AnnotationApplicationContext(String basePackage) throws Exception {this.beanFactory = getBeanFactory(basePackage);}private BeanFactory getBeanFactory(String baskPackage) {return new BeanFactory(baskPackage);}}

测试,实现

@Bean
public class UserServiceImpl implements UserService {@Diprivate UserDao userDao;@Overridepublic void add() {System.out.println("service.........");userDao.add();}
}
public interface UserService {void add();
}
@Bean
public class UserDaoImpl implements UserDao {@Overridepublic void add() {System.out.println("dao add...");}
}
public interface UserDao {void add();
}
public class UserTest {public static void main(String[] args){try {ApplicationContext context = new AnnotationApplicationContext("com.zly");UserService userService = (UserService)context.getBean(UserService.class);userService.add();} catch (Exception e) {e.printStackTrace();}}
}

在这里插入图片描述

相信你看到这里,就可以很容易的理解了这个丐版的Spring就算完成了,希望可以对大家有帮助啦👍👍👍

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

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

相关文章

Springboot整合 Thymeleaf增删改查一篇就够了

很早之前写过Thymeleaf的文章&#xff0c;所以重新温习一下&#xff0c;非前后端分离&#xff0c;仅仅只是学习 官网&#xff1a; https://www.thymeleaf.org/ SpringBoot可以快速生成Spring应用&#xff0c;简化配置&#xff0c;自动装配&#xff0c;开箱即用。 JavaConfigur…

Python每日一练(20230226)

目录 1. 合并列表中字典字段 ★ 2. 乘积最大子数组 ★★ 3. 加油站 ★★ 附录 贪心算法 一般步骤 使用条件 存在问题 应用实例 1. 合并列表中字典字段 如下两个列表&#xff0c;需要将oldList转化为newList&#xff0c;去掉相同字段的字典&#xff0c;并且去掉的参…

【RockerMQ】002-RockerMQ 基本概念、系统架构

【RockerMQ】002-RockerMQ 基本概念、系统架构 文章目录【RockerMQ】002-RockerMQ 基本概念、系统架构一、基本概念1、消息&#xff08;Message&#xff09;2、主题&#xff08;Topic&#xff09;3、标签&#xff08;Tag&#xff09;4、队列&#xff08;Queue&#xff09;5、消…

MySql触发器学习

文章目录1 触发器1.1介绍1.2 创建触发器1.2 删除触发器1.3查看触发器1 触发器 1.1介绍 触发器是与表有关的数据库对象&#xff0c;指在 insert/update/delete 之前或之后&#xff0c;触发并执行触发器中定义的SQL语句集合。触发器的这种特性可以协助应用在数据库端确保数据的…

解决前端组件下拉框选择功能失效问题

问题&#xff1a; 页面下拉框选择功能失效 现象&#xff1a; 在下拉框有默认值的情况下&#xff0c;点击下拉框的其他值&#xff0c;发现并没有切换到其他值 但是在下拉框没默认值的情况下&#xff0c;功能就正常 原因 select 已经绑定选项&#xff08;有默认值&#xff09; 在…

Java异常架构与异常关键字

Java异常简介 Java异常是Java提供的一种识别及响应错误的一致性机制。 Java异常机制可以使程序中异常处理代码和正常业务代码分离&#xff0c;保证程序代码更加优雅&#xff0c;并提高程序健壮性。在有效使用异常的情况下&#xff0c;异常能清晰的回答what, where, why这3个问…

keepalive + nginx 来实现 对于nginx的高可用, 以及如何搭建主备模式

keepalive nginx 来实现 对于nginx的高可用, 以及如何搭建主备模式。 keeplived简介 Keepalived是用纯ANSI/ISO C编写的。该软件围绕一个中央I/O多路复用器进行连接&#xff0c;以提供实时网络设计。 1.1 Keepalived进程被分为3个不同进程 A.一个极简的父进程&#xff0c…

【JavaSE】复习(进阶)

文章目录1.final关键字2.常量3.抽象类3.1概括3.2 抽象方法4. 接口4.1 接口在开发中的作用4.2类型和类型之间的关系4.3抽象类和接口的区别5.包机制和import5.1 包机制5.2 import6.访问控制权限7.Object7.1 toString()7.2 equals()7.3 String类重写了toString和equals8.内部类8.1…

【深度学习】什么是线性回归逻辑回归单层神经元的缺陷

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录逻辑回归&线性回归单层神经元的缺陷单层神经元的缺陷逻辑回归&线性回归 线性回归预测的是一个连续值&#xff0c; 逻辑回归给出的”是”和“否”的回答. 等…

4、算法MATLAB---认识矩阵

认识矩阵1、矩阵定义和基本运算1.1 赋值运算符&#xff1a;1.2 等号运算符&#xff1a;1.3 空矩阵1.4 一行一列矩阵1.5 行矩阵&#xff08;元素用空格或逗号分隔&#xff09;1.6 列矩阵&#xff08;分号表示换行&#xff09;1.7 m行n列的矩阵&#xff1a;行值用逗号间隔&#x…

如何在Linux中实现进程间通信

致前行路上的人&#xff1a; 要努力&#xff0c;但不要着急&#xff0c;繁花锦簇&#xff0c;硕果累累都需要过程&#xff01; 目录 1.进程间通信介绍 1.1进程间通信的目的 1.2进程间通信发展 1.3进程间通信分类 1.4进程间通信的本质 2.管道 2.1什么是管道 2.2管道与进程的关系…

轻量级网络模型ShuffleNet V2

在学习ShuffleNet V2内容前需要简单了解卷积神经网络和MobileNet,以及Shuffnet V1的相关内容&#xff0c;大家可以出门左转&#xff0c;去看我之前的几篇博客MobileNet发展脉络&#xff08;V1-V2-V3&#xff09;&#xff0c;轻量级网络模型ShuffleNet V1&#x1f197;&#xff…

Android 高工分享一波性能优化的总结~

随着 Android 开发越来越规范&#xff0c;国内工程师的素质&#xff0c;以及用户对产品的要求也越来越高。这也间接导致我们对研发项目的质量要求到了近乎苛刻的地步&#xff0c;**内存优化、UI 卡顿优化、App 崩溃监控等性能调优也逐渐成了人手必备的技能。**工作之余&#xf…

【数据挖掘】1、综述:背景、数据的特征、数据挖掘的六大应用方向、有趣的案例

目录一、背景1.1 学习资料1.2 数据的特征1.3 数据挖掘的应用案例1.4 获取数据集1.5 数据挖掘的定义二、分类三、聚类四、关联分析五、回归六、可视化七、数据预处理八、有趣的案例8.1 隐私保护8.2 云计算的弹性资源8.3 并行计算九、总结一、背景 1.1 学习资料 推荐书籍如下&a…

【Spark分布式内存计算框架——Spark Streaming】3.入门案例(上)官方案例运行

2.1 官方案例运行 运行官方提供案例&#xff0c;使用【$SPARK_HOME/bin/run-example】命令运行&#xff0c;效果如下&#xff1a; 具体步骤如下&#xff1a; 第一步、准备数据源启动端口&#xff0c;准备数据 nc -lk 9999 spark spark hive hadoop spark hive 第二步、运行…

面试官: 你知道 JWT、JWE、JWS 、JWK嘛?

想起了 之前做过的 很多 登录授权 的项目 它相比原先的session、cookie来说&#xff0c;更快更安全&#xff0c;跨域也不再是问题&#xff0c;更关键的是更加优雅 &#xff0c;所以今天总结了一篇文章来介绍他 JWT 指JSON Web Token&#xff0c;如果在项目中通过 jjwt 来支持 J…

hook与mixin

看完vue3就开始看vue3的源码&#xff0c;表示很懵~ 刚把rollup打包搞完&#xff0c;这不响应式就接着来了&#xff01;&#xff0c;还是写篇直接使用vue3的博客清清脑吧&#xff01; 什么是hook、mixin&#xff1f; mixin: Vue2中多个组件内存在重复JS业务逻辑&#xff0c;使…

k8s学习之路 | Day15 k8s 中的 yaml 语法

文章目录yaml 基础什么是 yaml&#xff1f;yaml 特性适用场景基本语法规则数据类型yaml 对象yaml 数组yaml 纯量yaml 引用k8s 中的 yaml 语法\<string>\<Object>\<map[string]string>\<[]Object>\<boolean>示例 yaml 说明我在学习过程中&#xf…

Mr. Cappuccino的第44杯咖啡——Kubernetes之Service

Kubernetes之ServiceService的概念Service的类型Service演示案例环境准备ClusterIP&#xff08;集群内部访问&#xff09;IptablesIPVSEndpointNodePort&#xff08;对外暴露应用&#xff09;LoadBalancer&#xff08;对外暴露应用&#xff0c;适用于公有云&#xff09;Ingress…

echo命令

这是一条内置命令。 输出指定的字符串 一、语法 echo [选项] [参数] 二、选项 -e&#xff1a;激活转义字符。 使用-e选项时&#xff0c;若字符串中出现以下字符&#xff0c;则特别加以处理&#xff0c;而不会将它当成一般文字输出&#xff1a; \a 发出警告声&#xff1b; \b 删…