【Spring之手写一个依赖注入容器】
Spring之手写一个依赖注入容器
- 1. 创建两个自定义注解
- 1.1 Component注解
- 1.2 DI注解
- 2. ApplicationContext接口与实现类
- 2.1 ApplicationContext 接口
- 2.2 实现类:DefaultListableApplicationContext
- 3. 定义DAO层和Service层及其实现
- 4. 定义异常信息类
- 4.1 InjectBeanException
- 4.2 NotExistsBean
- 4.3 NotSupportMoreSuperInterface
- 5. 测试自定义bean容器(带依赖注入)
- 5.1 新建测试类TestUser
- 5.2 输出结果
1. 创建两个自定义注解
1.1 Component注解
@Target(value = ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Component {String value() default "";
}
1.2 DI注解
@Target(value = ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DI {String value() default "";}
2. ApplicationContext接口与实现类
2.1 ApplicationContext 接口
public interface ApplicationContext {// 获取beanObject getBean(String name);// 获取bean<T> T getBean(String name, Class<T> clazz);// 判断bean是否存在boolean exists(String name);boolean exists(String name, Class<?> clazz);
}
2.2 实现类:DefaultListableApplicationContext
// 实现类
public class DefaultListableApplicationContext implements ApplicationContext {/*** 存放bean对象容器*/private Map<String, Object> objectsMap = new ConcurrentHashMap<>(128);private Map<Class, Object> objectsTypeMap = new ConcurrentHashMap<>(128);private Logger log = LoggerFactory.getLogger(DefaultListableApplicationContext.class);// 扫描的基础路径private String basePath;public DefaultListableApplicationContext(String basePackage) {//public static void pathDemo1(String basePackageName){String packagePath = basePackage.replaceAll("\\.", "\\\\");try {URL url = Thread.currentThread().getContextClassLoader().getResource(packagePath);if (StringUtils.hasText(url.getPath())) {String filePath = URLDecoder.decode(url.getFile(), "UTF8");// 扫描的基础路径basePath = filePath.substring(0, filePath.length() - packagePath.length());//包扫描loadBeans(new File(filePath));// 依赖注入loadDI();}} catch (IOException e) {throw new RuntimeException(e);}}@Override@Nullablepublic Object getBean(String name) {return objectsMap.get(name);}@Override@Nullablepublic <T> T getBean(String name, Class<T> clazz) {Object obj = objectsMap.get(name);if (obj == null) {Object bean;if ((bean = objectsTypeMap.get(clazz)) == null) {for (Class<?> interfaceClazz : clazz.getInterfaces()) {if ((bean = objectsTypeMap.get(interfaceClazz)) != null)return (T) bean;getBean(name, interfaceClazz);}return null;}return (T) bean;}return clazz.isInstance(obj) ? clazz.cast(obj) : null;}@Overridepublic boolean exists(String name) {return objectsMap.get(name) == null ? false : true;}@Overridepublic boolean exists(String name, Class<?> clazz) {if (objectsMap.get(name) == null) {if (objectsTypeMap.get(clazz) == null) {for (Class<?> interfaceClazz : clazz.getInterfaces()) {if (objectsTypeMap.get(interfaceClazz) != null)return true;exists(name, interfaceClazz);}return false;}return true;}return true;}/*** 包扫描** @param file*/private void loadBeans(File file) {// 1 判断当前文件是否文件夹if (file.isDirectory()) {File[] childrenFiles = file.listFiles();// 2. 获取文件夹里面的所有内容// 3. 判断文件夹为空,直接返回if (childrenFiles == null || childrenFiles.length == 0) {return;}// 4. 如果文件夹不为空,遍历文件夹内容for (File childFile : childrenFiles) {// 4.1 遍历得到每个file对象,继续判断。如果是文件夹,递归if (childFile.isDirectory()) {loadBeans(childFile);} else {// 4.2 遍历得到的file不是文件夹,是文件// 4.3 得到包路径 + 类名称部分// C:/desktop/IdeaProjects/spring-mytest/src/main/java/com/ypy/contextString pathWithClass = childFile.getAbsolutePath().substring(basePath.length() - 1);// 4.4 判断当前文件类型是否为.classif (pathWithClass.endsWith(".class")) {// 4.5 如果是.class类型,把路径\替换成., 把.class去掉// com.ypy.context.beans.UserServiceImplString allName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");// 4.6 使用反射实例化try {Class<?> clazz = Class.forName(allName);if (!clazz.isInterface()) {// 4.6.1 判断是否有@Component注解,有if (clazz.getAnnotation(Component.class) != null) {Object instance = clazz.getConstructor().newInstance();// 优先使用用户自定义的bean名称if (StringUtils.hasText(clazz.getAnnotation(Component.class).value())) {String beanName = clazz.getAnnotation(Component.class).value();objectsMap.put(beanName, instance);log.warn(">>> objectsMap store bean(name,obj) ===> (" + beanName + "," + instance + ")");if (clazz.getInterfaces().length == 1) {objectsTypeMap.put(clazz.getInterfaces()[0], instance);log.warn(">>> objectsTypeMap store bean(class,obj) ===> (" + clazz.getInterfaces()[0].getSimpleName() + "," + instance + ")");} else if (clazz.getInterfaces().length == 0) {objectsTypeMap.put(clazz, instance);log.warn(">>> objectsTypeMap store bean(class,obj) ===> (" + clazz.getInterfaces()[0].getSimpleName() + "," + instance + ")");} else {throw new NotSupportMoreSuperInterface("Not support the bean that has more than two super interfaces.");}}// 其次使用父接口的类名作为bean名称else if (clazz.getInterfaces().length > 0) {String interfaceName = clazz.getInterfaces()[0].getSimpleName();String beanName = lowercaseFirstLetter(interfaceName);// 放入容器中objectsMap.put(beanName, instance);log.warn(">>> objectsMap store bean(name,obj) ===> (" + beanName + "," + instance + ")");// 可能出现bugobjectsTypeMap.put(clazz.getInterfaces()[0], instance);log.warn(">>> objectsTypeMap store bean(class,obj) ===> (" + clazz.getInterfaces()[0].getSimpleName() + "," + instance + ")");}// 如果说没有父接口,就使用类名作为bean名称else {String beanName = lowercaseFirstLetter(clazz.getSimpleName());// 放入容器中objectsMap.put(beanName, instance);log.warn(">>> objectsMap store bean(name,obj) ===> (" + beanName + "," + instance + ")");objectsTypeMap.put(clazz, instance);log.warn(">>> objectsTypeMap store bean(class,obj) ===> (" + clazz.getInterfaces()[0].getSimpleName() + "," + instance + ")");}}}} catch (Exception e) {throw new RuntimeException(e);}}}}}}private void loadDI() {for (Map.Entry<String, Object> entry : objectsMap.entrySet()) {Object obj = entry.getValue();Class<?> clazz = obj.getClass();Field[] fields = clazz.getDeclaredFields();for (Field field : fields) {try {field.setAccessible(true);if (field.getAnnotation(DI.class) != null) {Class<?>[] interfaces = field.getType().getInterfaces();String needWiredBeanName;Object autoWiredBean;// 优先使用注解DI的value注入if (StringUtils.hasText(needWiredBeanName = field.getAnnotation(DI.class).value())) {autoWiredBean = objectsMap.get(needWiredBeanName);if (autoWiredBean != null) {field.set(obj, autoWiredBean);log.warn("<<< DI: Class " + clazz.getSimpleName() + " of field named " + field.getName() + " is injected with value of " + autoWiredBean + " from " + "objectsMap, by value of annotation @DI");continue;} /*else {throw new NotExistsBean("Not exists the bean named <" + needWiredBeanName + "> to inject as property");}*/}// 没有父接口needWiredBeanName = lowercaseFirstLetter(field.getType().getSimpleName());if ((autoWiredBean = objectsMap.get(needWiredBeanName)) != null || (autoWiredBean = objectsTypeMap.get(field.getType())) != null) {field.set(obj, autoWiredBean);log.warn("<<< DI: Class " + clazz.getSimpleName() + " of field named " + field.getName() + " is injected with value of " + autoWiredBean + " , by value or type of property itself ");continue;}// 使用父接口的名字从bean容器中查找,注入if (interfaces.length > 0) {for (Class<?> interfaceClazz : interfaces) {String interfaceClazzName = interfaceClazz.getSimpleName();if (interfaceClazz.isAssignableFrom(field.getType())) {needWiredBeanName = lowercaseFirstLetter(interfaceClazzName);if ((autoWiredBean = objectsMap.get(needWiredBeanName)) != null || (autoWiredBean = objectsTypeMap.get(interfaceClazz)) != null) {field.set(obj, autoWiredBean);log.warn("<<< DI: Class " + clazz.getSimpleName() + " of field named " + field.getName() + " is injected with value of " + autoWiredBean + ", by value or type of super interface");}}}continue;}throw new InjectBeanException("There occurs an Exception while injecting property filed [" + field.getName() + "] of Class <" + clazz.getSimpleName() + "> , because bean factory doesn't exist the bean named " + needWiredBeanName);}} catch (IllegalAccessException e) {throw new RuntimeException(e);}}}}private String lowercaseFirstLetter(String name) {return name.substring(0, 1).toLowerCase() + name.substring(1);}}
3. 定义DAO层和Service层及其实现
// 3.1 dao层
public interface UserDao {void addUser(User user);
}/**
* userDao实现
*/
@Component("userDao")
public class UserDaoImpl implements UserDao {@Overridepublic void addUser(User user) {System.out.println();System.out.println("------------------>>>执行UserDaoImpl中的addUser方法开始<<<<------------------");System.out.println("\t\t\t\t\t\t名字\t\t\t\t年龄");System.out.println("\t\t\t\t\t\t"+user.getName()+"\t\t\t\t"+user.getAge());System.out.println("------------------>>>执行UserDaoImpl中的addUser方法完毕<<<<------------------");}
}// 3.2 service层public interface UserService {void add();
}// service实现
@Component("userService")
public class UserServiceImpl implements UserService {@DI("userDao")private UserDaoImpl userDao;@Overridepublic void add() {System.out.println("》》》 user service impl execute add method...");User user = new User("张三", 25);userDao.addUser(user);}
}
4. 定义异常信息类
4.1 InjectBeanException
public class InjectBeanException extends RuntimeException{public InjectBeanException() {super();}public InjectBeanException(String message) {super(message);}
}
4.2 NotExistsBean
public class NotExistsBean extends RuntimeException{public NotExistsBean() {}public NotExistsBean(String message) {super(message);}
}
4.3 NotSupportMoreSuperInterface
public class NotSupportMoreSuperInterface extends RuntimeException{public NotSupportMoreSuperInterface() {super();}public NotSupportMoreSuperInterface(String message) {super(message);}
}
5. 测试自定义bean容器(带依赖注入)
5.1 新建测试类TestUser
public class TestUser {public static void main(String[] args) {DefaultListableApplicationContext context = new DefaultListableApplicationContext("com.ypy.context");UserService userService = context.getBean("aaa", UserServiceImpl.class);UserDao userDao = context.getBean("userDaoImpl", UserDao.class);boolean existFlag = context.exists("aaa"/*, UserService.class*/);System.out.println("UserService exists ? " + existFlag);System.out.println("从Bean容器中获取aaa对象地址: " + userService);System.out.println("从Bean容器中获取userDao对象地址: " + userDao);userService.add();}
}
5.2 输出结果
一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: >>> objectsMap store bean(name,obj) ===> (userDao,com.ypy.context.dao.impl.UserDaoImpl@2b193f2d)
一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: >>> objectsTypeMap store bean(class,obj) ===> (UserDao,com.ypy.context.dao.impl.UserDaoImpl@2b193f2d)
一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: >>> objectsMap store bean(name,obj) ===> (userService,com.ypy.context.service.impl.UserServiceImpl@7a81197d)
一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: >>> objectsTypeMap store bean(class,obj) ===> (UserService,com.ypy.context.service.impl.UserServiceImpl@7a81197d)
一月 18, 2024 4:01:20 上午 com.sun.org.slf4j.internal.Logger warn
警告: <<< DI: Class UserServiceImpl of field named userDao is injected with value of com.ypy.context.dao.impl.UserDaoImpl@2b193f2d from objectsMap, by value of annotation @DI
UserService exists ? false
从Bean容器中获取aaa对象地址: com.ypy.context.service.impl.UserServiceImpl@7a81197d
从Bean容器中获取userDao对象地址: com.ypy.context.dao.impl.UserDaoImpl@2b193f2d
》》》 user service impl execute add method...------------------>>>执行UserDaoImpl中的addUser方法开始<<<<------------------名字 年龄张三 25
------------------>>>执行UserDaoImpl中的addUser方法完毕<<<<------------------Process finished with exit code 0相关文章:
【Spring之手写一个依赖注入容器】
Spring之手写一个依赖注入容器 1. 创建两个自定义注解1.1 Component注解1.2 DI注解 2. ApplicationContext接口与实现类2.1 ApplicationContext 接口2.2 实现类:DefaultListableApplicationContext 3. 定义DAO层和Service层及其实现4. 定义异常信息类4.1 InjectBean…...
kafka之java客户端实战
1. kafka的客户端 Kafka提供了两套客户端API,HighLevel API和LowLevel API。 HighLevel API封装了kafka的运行细节,使用起来比较简单,是企业开发过程中最常用的客户端API。 而LowLevel API则需要客户端自己管理Kafka的运行细节,Pa…...
图解渠道网关:不只是对接渠道的接口(一)
这是《百图解码支付系统设计与实现》专栏系列文章中的第(20)篇。点击上方关注,深入了解支付系统的方方面面。 主要讲清楚什么是渠道,有哪些类型的渠道,什么是渠道网关,渠道网关在支付系统中定位、核心功能…...
【js版数据结构学习之队列】
队列 一、简要认识队列二、队列的封装三、队列的应用1.栈和队列的转换2.全排列3.任务调度4.缓存管理 一、简要认识队列 结构:一种特殊的线性表 入队:在队尾插入一个元素 出队:在队头删除一个元素 特点:先入先出 空队列࿱…...
iOS Xcode 升级Xcode15报错: SDK does not contain ‘libarclite‘
iOS Xcode 升级Xcode15报错: SDK does not contain libarclite 一、仔细查看报错代码: SDK does not contain libarclite at the path /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/ lib/arc/libarclite_iphonesimulator.a; try in…...
python高级练习题库实验2(B)部分
文章目录 题目1代码实验结果题目2代码实验结果题目总结题目1 注册课程小游戏程序 研究下面的例子,并编写一个与这些例子完全相同的程序。使用for loop和break来解决问题。提示用户输入课程数量,是否选择,并且课程代码,最后还需显示已经完成的课程注册数量或者未完成的注册…...
vue项目编译非常慢,经常卡在某个百分点
1、注册插件 2、在项目根目录下的 babel.config.js 文件中加入下方配置 3、将import导入方式改为require导入方式,返回promise 4、如果动态加载组件import引入组件找不到组件(Error: Cannot find module) 使用 webpack 的 require.ensure() …...
开源知识库zyplayer-doc部署指南
1.前置条件 docker已经安装 mysql已经安装且数据库zyplayer-doc存在 服务器ip:192.168.168.99/ 数据库账户:root,密码:123456 2.拉取镜像 docker pull zyplayer/zyplayer-doc:latest 3.启动 docker run -d \--restart unless-stopped \--name zyplayer-doc \-p 8083:8083 …...
第90讲:MySQL数据库主从复制集群原理概念以及搭建流程
文章目录 1.MySQL主从复制集群的核心概念1.1.什么是主从复制集群1.2.主从复制集群中的专业术语1.3.主从复制集群工作原理1.4.主从复制中的小细节1.5.搭建主从复制集群的前提条件1.6.MySQL主从复制集群的架构信息 2.搭建MySQL多实例环境2.1.在mysql-1中搭建身为主库的MySQL实例2…...
PHP反序列化漏洞-魔术方法绕过
一、__wakeup()魔法函数绕过: 在PHP中,__wakeup()是一个魔术方法,用于在反序列化对象时自动调用。当反序列化字符串中的对象属性个数大于实际属性个数时,可以利用这个漏洞进行绕过。 触发条件: PHP版本为5.6.25或早期版本,或者PHP7版本小于7.0.10。反序列化字符串中的对…...
抖店和商品橱窗的区别?这两个千万别再搞混了!
我是电商珠珠 很多人都会将抖店和商品橱窗搞混,想开抖店的人开了商品橱窗,想开橱窗的人开通了抖店。 我做抖店三年了,这种情况屡见不鲜。 那么抖店和商品橱窗究竟有什么区别呢? 1、属性不同 商品橱窗是抖音所展现商品的一个功…...
个人总结钉钉7.5新品发布会
钉钉发布了 7.5 版本,最主要推出了围绕AI能力的各项升级,通过AI“超级助理”提升组织内部的沟通协作效率、管理决策智能化程度,以及相关的音视频、在线文档、Teambition功能的升级,以满足不同企业的多元化需求。截至发布会&#x…...
连接超时的问题
连接超时的问题 通用第三方工具连接超时 connect timeout 方案一: /etc/ssh/sshd_config node1上操作,图是错的 方案二: windows上Hosts文件域名解析有问题 比如: 192.168.xx.100 node1 192.168.xx.161 node1 两个都解析成node…...
python贪吃蛇游戏
为了实现这个游戏,需要用到Python的pygame模块,它是一个专门用于开发游戏的模块,提供了很多方便的功能,比如窗口、图形、音效、事件处理等。 用pygame来创建一个窗口,设置游戏的背景色,画出蛇和食物&#…...
【Spring】Spring AOP
文章目录 前言1. 什么是 AOP2. 什么是 Spring AOP3. Spring AOP 的使用引入 AOP 依赖编写 AOP 程序 4. Spring AOP 详解4.1 Spring AOP 的概念4.1.1 切点4.1.2 连接点4.1.3 通知4.1.4 切面 4.2 通知类型4.3 切点4.4 切面优先级 Order注解4.5 切点表达式4.5.1 execution 切点表达…...
软件开发架构
【 一 】软件开发架构图 【 1】ATM和选课系统 三层的开发架构 前段展示台 后端逻辑层 数据处理层 【二】软件开发架构的步骤流程 需求分析:在软件开发架构设计之前,需要对应用系统进行需求分析,明确用户需求、功能模块、业务流程等内容。…...
计图大模型推理库部署指南,CPU跑大模型,具有高性能、配置要求低、中文支持好、可移植等特点
Excerpt 计图大模型推理库,具有高性能、配置要求低、中文支持好、可移植等特点 计图大模型推理库,具有高性能、配置要求低、中文支持好、可移植等特点 计图大模型推理库 - 笔记本没有显卡也能跑大模型 本大模型推理库JittorLLMs有以下几个特点: 成本低:相比同类框架,本库…...
CSS||Emmet语法
1、简介 Emmet语法的前身是Zen coding,它使用缩写,来提高html/css的编写速度, Vscode内部已经集成该语法。 快速生成HTML结构语法 快速生成CSS样式语法 2、快速生成HTML结构语法 生成标签 直接输入标签名 按tab键即可 比如 div 然后tab 键, 就可以生成 <…...
Android中的anr定位指导与建议
1.背景 8月份安卓出现了一次直播间卡死(ANR)问题,且由于排查难度较大,持续了较长时间。本文针对如何快速定位安卓端出现ANR问题进行总结和探讨. 这里大致补充一下当时的情况,当时看到情景的是从某一个特定的场景下进入直播间后整个直播间界面立刻就卡住…...
YOLOV7剪枝流程
YOLOV7剪枝流程 1、训练 1)划分数据集进行训练前的准备,按正常的划分流程即可 2)修改train.py文件 第一次处在参数列表里添加剪枝的参数,正常训练时设置为False,剪枝后微调时设置为True parser.add_argument(--pr…...
基于算法竞赛的c++编程(28)结构体的进阶应用
结构体的嵌套与复杂数据组织 在C中,结构体可以嵌套使用,形成更复杂的数据结构。例如,可以通过嵌套结构体描述多层级数据关系: struct Address {string city;string street;int zipCode; };struct Employee {string name;int id;…...
【网络】每天掌握一个Linux命令 - iftop
在Linux系统中,iftop是网络管理的得力助手,能实时监控网络流量、连接情况等,帮助排查网络异常。接下来从多方面详细介绍它。 目录 【网络】每天掌握一个Linux命令 - iftop工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景…...
Prompt Tuning、P-Tuning、Prefix Tuning的区别
一、Prompt Tuning、P-Tuning、Prefix Tuning的区别 1. Prompt Tuning(提示调优) 核心思想:固定预训练模型参数,仅学习额外的连续提示向量(通常是嵌入层的一部分)。实现方式:在输入文本前添加可训练的连续向量(软提示),模型只更新这些提示参数。优势:参数量少(仅提…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
[ACTF2020 新生赛]Include 1(php://filter伪协议)
题目 做法 启动靶机,点进去 点进去 查看URL,有 ?fileflag.php说明存在文件包含,原理是php://filter 协议 当它与包含函数结合时,php://filter流会被当作php文件执行。 用php://filter加编码,能让PHP把文件内容…...
C语言中提供的第三方库之哈希表实现
一. 简介 前面一篇文章简单学习了C语言中第三方库(uthash库)提供对哈希表的操作,文章如下: C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...
ZYNQ学习记录FPGA(一)ZYNQ简介
一、知识准备 1.一些术语,缩写和概念: 1)ZYNQ全称:ZYNQ7000 All Pgrammable SoC 2)SoC:system on chips(片上系统),对比集成电路的SoB(system on board) 3)ARM:处理器…...
深度解析:etcd 在 Milvus 向量数据库中的关键作用
目录 🚀 深度解析:etcd 在 Milvus 向量数据库中的关键作用 💡 什么是 etcd? 🧠 Milvus 架构简介 📦 etcd 在 Milvus 中的核心作用 🔧 实际工作流程示意 ⚠️ 如果 etcd 出现问题会怎样&am…...
