手写Spring IOC-简易版
目录
- 项目结构
- entity
- dao
- IUserDao
- UserDaoImpl
- service
- IUserService
- UserServiceImpl
- ApplicationContext
- 配置文件初始化 IOC 容器
- RunApplication
- 注解初始化 IOC 容器
- @Bean
- @Autowired
- Reference
项目结构

entity
- User
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
public class User implements Serializable {private String username;private String password;private int age;
}
dao
IUserDao
public interface IUserDao {/*** Add user*/int addUser(User user);/*** Get user by id.*/User getUserById(int id);/*** Get all users.*/List<User> getAllUsers();/*** Get users by name.*/List<User> getUsersByName(String name);
}
UserDaoImpl
public class UserDaoImpl implements IUserDao {@Overridepublic int addUser(User user) {System.out.println("Dao: addUser()");return 0;}@Overridepublic User getUserById(int id) {System.out.println("Dao: getUserById()");return null;}@Overridepublic List<User> getAllUsers() {System.out.println("Dao: getAllUsers()");return Collections.emptyList();}@Overridepublic List<User> getUsersByName(String name) {System.out.println("Dao: getUsersByName()");return Collections.emptyList();}
}
service
IUserService
public interface IUserService {void login();void register();
}
UserServiceImpl
public class UserServiceImpl implements IUserService {/*** Each time this interface is called, aenw UserDaoImpl object is created,* resulting in resource waste or causing OutOfMemoryError in serious cases.*/IUserDao userDao = new UserDaoImpl();@Overridepublic void login() {userDao.getAllUsers();System.out.println("This is implementation o login method.");}@Overridepublic void register() {userDao.getAllUsers();System.out.println("This is implementation o register method.");}
}
假设 Web 应用程序现在运行正常。
每次用户调用 IUserService 接口时,都会创建一个 IUserDaoImpl 对象,这会导致资源浪费,在严重情况下可能会引发 OutOfMemoryError。
IUserDao userDao = new UserDaoImpl();
如何解决这个问题****❓
我们可以将 IUserDaoImpl 的 Class 作为 key,IUserDaoImpl 实例作为 value 存储在一个HashMap<Class, Object>中。

- 在 Web 应用程序运行之前,将 ApplicationConext 类中的对象加载并初始化到 IOC 容器中。
- 当调用
IUserService接口时,Java 会获取IUserDaoImpl对象,而不再需要创建新的IUserDaoImpl对象。
ApplicationContext
public class ApplicationContext {// Store bean objects in a Hashmap, equivalent to ioc container.private HashMap<Class<?>, Object> beanFactory = new HashMap<>();/*** init ApplicationContext, and put bean objects into IOC container.*/public void initContext() throws IOException, ClassNotFoundException {// Load specified objects to IOC container.beanFactory.put(IUserService.class,new UserServiceImpl());beanFactory.put(IUserDao.class,new UserDaoImpl());}/*** get bean object by class from IOC container.*/public Object getBean(Class<?> clazz) {return beanFactory.get(clazz);}/*** return all bean objects in IOC container.*/public HashMap<Class<?>, Object> getAllBeans() {return beanFactory;}
}
配置文件初始化 IOC 容器
❓****问题:如上述代码所示,我们可以将指定的对象加载到IOC容器中,但如果我们需要添加一些新对象时,就必须修改源代码,这非常麻烦。
public void initContext() throws IOException, ClassNotFoundException {// Load specified objects to IOC container.beanFactory.put(IUserService.class,new UserServiceImpl());beanFactory.put(IUserDao.class,new UserDaoImpl());// Add new objects into beanFactory.// beanFactory.put(xxxx.class,new xxxxx());// ...........
}
为了解决这个问题,我们可以使用 Java 中的反射。
- 创建一个
application.properties文件,在其中可以添加我们需要的对象信息。
IOC.service.IUserService = IOC.service.Impl.UserServiceImpl
IOC.dao.IUserDao = IOC.dao.Impl.UserDaoImpl
- 利用反射机制加载对象,并存储到 IOC 容器。
public void initContext() throws IOException, ClassNotFoundException {// Load application.properties file and Get information of bean object.Properties properties = new Properties();properties.load(new FileInputStream("src/main/resources/application.properties"));Set<Object> keys = properties.keySet();for (Object key : keys) {Class<?> keyClass = Class.forName(key.toString());String value = properties.getProperty(key.toString());Class<?> valueClass = Class.forName(value);Object instance = valueClass.newInstance();// put bean object into IOC container.beanFactory.put(keyClass, instance);}
}
RunApplication
public class RunApplication {public static void main(String[] args) throws IOException, ClassNotFoundException {ApplicationContext applicationContext = new ApplicationContext();applicationContext.initContext();// Get all beans.HashMap<Class<?>, Object> allBeans = applicationContext.getAllBeans();Set<Class<?>> classes = allBeans.keySet();for (Class<?> clazz : classes) {System.out.println(clazz.getName() + " -> " + allBeans.get(clazz));}}
}

注解初始化 IOC 容器
@Bean
- 新增
annotation包,创建 Bean 注解类。
@Target(ElementType.TYPE) // 只能修饰类型元素
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {}
- 在
UserDaoImpl和UserServiceImpl类上加上 **@Bean **注解。
@Bean
public class UserServiceImpl implements IUserService {}@Bean
public class UserDaoImpl implements IUserDao {}
- 在
ApplicationContext类中添加使用注解初始化 bean 对象。
// root path of project.
private String filePath;/**
* Load all bean objects with annotation @Bean from the project path.
*/
public void initContextByAnnotation() throws ClassNotFoundException, InstantiationException, IllegalAccessException {// Get the project path.filePath = Objects.requireNonNull(ApplicationContext.class.getClassLoader().getResource("")).getFile();// Load all bean objects into IOC container.loadOne(new File(filePath));
}/**
* Load all class file with @Bean.
*/
public void loadOne(File fileParent) throws ClassNotFoundException, InstantiationException, IllegalAccessException {if(!fileParent.isDirectory()){return;}File[] childrenFiles = fileParent.listFiles();if (childrenFiles == null) {return;}for (File child : childrenFiles) {if (child.isDirectory()) {loadOne(child);} else {String pathWithClass = child.getAbsolutePath().substring(filePath.length() - 1);// Get file name like UserServiceImpl.classif (pathWithClass.contains(".class")) {String fullName = pathWithClass.replaceAll("\\\\", ".").replace(".class", "");Class<?> clazz = Class.forName(fullName);if (!clazz.isInterface()) {Bean annotation = clazz.getAnnotation(Bean.class);if (annotation != null) {Object instance = clazz.newInstance();Class<?>[] interfacesList = clazz.getInterfaces();// interface as key, if object has no interface.if (interfacesList.length > 0) {Class<?> interfaceClass = interfacesList[0];beanFactory.put(interfaceClass, instance);}// clazz as key, if object has interface.else {beanFactory.put(clazz, instance);}}}}}}
}
- 修改
RunApplication启动类中使用注解初始化 IOC 容器。
public class RunApplication {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {ApplicationContext applicationContext = new ApplicationContext();//applicationContext.initContext();// Load beans by @Bean.applicationContext.initContextByAnnotation();// Get all beans.HashMap<Class<?>, Object> allBeans = applicationContext.getAllBeans();Set<Class<?>> classes = allBeans.keySet();for (Class<?> clazz : classes) {System.out.println(clazz.getName() + " -> " + allBeans.get(clazz));}}
}
输出:

@Autowired
- 在 annotation 包中添加 Aurowired 注解类。
@Target(ElementType.FIELD) // 只能修饰成员变量
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {}
- 在
UserServiceImpl成员变量添加@Autowired注解。
@Bean
public class UserServiceImpl implements IUserService {@Autowiredprivate IUserDao userDao;
}
- 在
ApplicationContext类中添加使用注解初始化 bean 对象后,初始化各个 bean 对象的成员变量。- 这里默认只实现了根据类型初始化成员变量(在 springboot 中支持类型和名称)。
/**
* Load all bean objects with annotation @Bean from the project path.
*/
public void initContextByAnnotation() throws ClassNotFoundException, InstantiationException, IllegalAccessException {// Get the project path.filePath = Objects.requireNonNull(ApplicationContext.class.getClassLoader().getResource("")).getFile();// Load all bean objects into IOC container.loadOne(new File(filePath));// initiate fields of object.assembleObject();
}/**
* @Autowired annotation to initiate fields of bean objects.
*/
public void assembleObject() throws IllegalAccessException {Set<Class<?>> keys = beanFactory.keySet();for (Class<?> key : keys) {Object value = beanFactory.get(key);Class<?> clazz = value.getClass();// Set value of field by @Autowired.Field[] declaredFields = clazz.getDeclaredFields();for (Field filed : declaredFields) {Autowired annotation = filed.getAnnotation(Autowired.class);if (annotation != null) {filed.setAccessible(true);// Get bean object by type from IOC container.Object object = beanFactory.get(filed.getType());filed.set(value, object);}}}
}
- 答复
public class RunApplication {public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {ApplicationContext applicationContext = new ApplicationContext();//applicationContext.initContext();applicationContext.initContextByAnnotation();// Get all beans.HashMap<Class<?>, Object> allBeans = applicationContext.getAllBeans();Set<Class<?>> classes = allBeans.keySet();for (Class<?> clazz : classes) {System.out.println(clazz.getName() + " -> " + allBeans.get(clazz));}// Get bean object by class type.IUserService bean = (IUserService) applicationContext.getBean(IUserService.class);// call login method.bean.login();}
}
输出:

Dao 层调用了 getAllUsers() 方法,说明依赖注入成功。
源码:https://github.com/RainbowJier/HandWrittenSpring
Reference
- 一堂课深挖java反射机制,手撸一个ioc,实现自动装配,顺便spring也了解了_哔哩哔哩_bilibili
相关文章:
手写Spring IOC-简易版
目录 项目结构entitydaoIUserDaoUserDaoImpl serviceIUserServiceUserServiceImpl ApplicationContext 配置文件初始化 IOC 容器RunApplication 注解初始化 IOC 容器BeanAutowired Reference 项目结构 entity User Data NoArgsConstructor AllArgsConstructor Accessors(chai…...
【算法题】62. 不同路径(LeetCode)
【算法题】62. 不同路径(LeetCode) 1.题目 下方是力扣官方题目的地址 62. 不同路径 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图…...
【VUE】Vue中的data属性为什么是一个函数而不是一个对象
在 Vue.js 中,组件的 data 属性可以是一个对象或者一个函数但通常建议将其设置为函数。这是因为组件可能会被多次使用,如果 data 是一个普通对象,那么该对象会被所有实例共享,导致数据混乱。将 data 设置为一个函数可以保证每个组…...
ddos攻击介绍和排查方法
一、DDoS攻击介绍 DDoS攻击,全称为分布式拒绝服务攻击(Distributed Denial of Service Attack),是一种常见的网络攻击手段。它通过利用多个计算机系统向目标服务器、服务或网络发送大量请求,导致目标无法处理正常流量…...
git clone --single-branch 提升效率
git clone --single-branch 是一个Git命令,用于从远程仓库中仅克隆单个分支到本地仓库。这个命令在软件开发中非常有用,尤其是在需要特定分支的代码而无需整个仓库的情况下。 基本用法 git clone --single-branch 命令的基本语法如下: git…...
代码随想录算法训练营第十天|1. 两数之和,第454题.四数相加II
文档讲解:代码随想录 难度:一般嗷~~ 1. 两数之和 力扣题目链接(opens new window) 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。 你可以假设每种输入只会对…...
龙迅LT8911EX LVDS转EDP 点屏,大批量出货产品
龙迅LT8911EX描述: Lontium LT8911EX是LVDS到eDP转换器,具有单端口或双端口可配置的LVDS接收器,有1个时钟通道和最多8个数据通道,每个数据通道最大运行1.2Gbps,最大输入带宽为9.6Gbps。转换器将输入LVDS数据去序列化&…...
浅谈Oracle之游标
一、基本介绍 在Oracle数据库中,游标(Cursor)是一种强大的工具,用于逐行处理查询结果集。然而,游标的使用需要谨慎,因为不当的使用可能会导致性能问题。 二、最佳实践和优化技巧 尽量避免使用游标…...
基于在线教育系统源码的企业培训平台开发解决方案详解
本篇文章,笔者将详细解析基于在线教育系统源码开发企业培训平台的解决方案,探讨其开发步骤、关键功能模块及技术实现方案。 一、在线教育系统源码的优势 在构建企业培训平台时,选择基于在线教育系统源码的开发方式具有以下几个显著优势&…...
Whisper 音视频转写
Whisper 音视频转写 API 接口文档 api.py import os import shutil import socket import torch import whisper from moviepy.editor import VideoFileClip import opencc from fastapi import FastAPI, File, UploadFile, Form, HTTPException, Request from fastapi.respons…...
【详尽-实战篇】使用Springboot生成自带logo或者图片的二维码-扫描二维码可以跳转到指定的页面-Zing-core
先上效果图 项目源码:https://download.csdn.net/download/qq_43055855/89891285 源码地址 手机扫描二维码跳转到指定网页 概述 这个项目是一个基于 Java 的二维码生成与解析工具,主要由 QRCodeUtil 和 QRCodeController 两个类组成。它利用了 Google…...
vue跨标签页通信(或跨窗口)详细教程
在 Vue 应用中,跨标签页(或跨窗口)的通信通常涉及到两个或多个浏览器标签页之间的信息共享。由于每个标签页或窗口都是独立的 JavaScript 执行环境,它们不能直接通过 Vue 或其他 JavaScript 库来直接相互通信。但是,有一些方法可以实现这种跨标签页的通信,主要依靠浏览器…...
【VUE】Vue3通过数组下标更改数组视图为什么会更新?
在 Vue 3 中,使用 Proxy 来实现了对数组的响应式监听,相比于 Vue 2 使用的 Object.defineProperty(),Proxy 更加高效和灵活。 因此,在 Vue 3 中,通过数组下标直接更改数组中某一项的值,也能够被 Vue 正确监…...
前端转换double数据,保留两位小数
Number Number(1.00) 1 Number(1.10) 1.1 Number(1.101) 1.101 要想前端展示页面按 1.00展示1,1.10 展示1.1 需要套一个number() 1.1 保留两位小数,并三位一个分隔符 indexView.value[key] formatNumber(indexView.value[key].toFixed(2))//格式…...
【实战案例】JSR303统一校验与SpringBoot项目的整合
前后端分离项目中,当前前端请求后端接口的时候通常需要传输参数,对于参数的校验应该在哪一步进行校验?Controller中还是Service中?答案是都需要校验,只不过负责的板块不一样,Controller中通常校验请求参数的…...
忘记了系统root密码,如何重置root密码?
重置root密码(CentOS7) 文章目录 重置root密码(CentOS7)[toc] 1.开启系统时,在引导界面按下字母e。 2.进入到内核界面,找到Linux开头字样一行,然后在最末尾输入参数rd.break,然后按住…...
7-基于国产化FT-M6678+JFM7K325T的6U CPCI信号处理卡
一、板卡概述 本板卡系我公司自主研发,基于6U CPCI的通用高性能信号处理平台。板卡采用一片国产8核DSP FT-C6678和一片国产FPGA JFM7K325T-2FFG900作为主处理器。为您提供了丰富的运算资源。如下图所示: 二、设计参考标准 ● PCIMG 2.0 R3.0 CompactP…...
计算机毕业设计 | SSM超市进销存管理系统(附源码)
1,绪论 1.1 开发背景 世界上第一个购物中心诞生于美国纽约,外国人迈克尔库伦开设了第一家合作商店,为了更好地吸引大量客流量,迈克尔库伦精心设计了低价策略,通过大量进货把商品价格压低,通过商店一次性集…...
手撕数据结构 —— 堆(C语言讲解)
目录 1.堆的认识 什么是堆 堆的性质 2.堆的存储 3.堆的实现 Heap.h中接口总览 具体实现 堆结构的定义 初始化堆 销毁堆 堆的插入 堆的向上调整算法 堆的插入的实现 堆的删除 堆的向下调整算法 堆的删除的实现 使用数组初始化堆 获取堆顶元素 获取堆中的数据…...
TS和JS中,string与String的区别
1. string string 是 TypeScript 的基本类型,用于表示简单的字符串值,同时它是一个原始类型,可直接表示文本数据。 2. String String 是 JavaScript 中的一个全局对象(类),用于创建字符串对象࿰…...
UE5 学习系列(二)用户操作界面及介绍
这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…...
IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...
19c补丁后oracle属主变化,导致不能识别磁盘组
补丁后服务器重启,数据库再次无法启动 ORA01017: invalid username/password; logon denied Oracle 19c 在打上 19.23 或以上补丁版本后,存在与用户组权限相关的问题。具体表现为,Oracle 实例的运行用户(oracle)和集…...
vscode(仍待补充)
写于2025 6.9 主包将加入vscode这个更权威的圈子 vscode的基本使用 侧边栏 vscode还能连接ssh? debug时使用的launch文件 1.task.json {"tasks": [{"type": "cppbuild","label": "C/C: gcc.exe 生成活动文件"…...
理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
【大模型RAG】Docker 一键部署 Milvus 完整攻略
本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装;只需暴露 19530(gRPC)与 9091(HTTP/WebUI)两个端口,即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...
Leetcode 3577. Count the Number of Computer Unlocking Permutations
Leetcode 3577. Count the Number of Computer Unlocking Permutations 1. 解题思路2. 代码实现 题目链接:3577. Count the Number of Computer Unlocking Permutations 1. 解题思路 这一题其实就是一个脑筋急转弯,要想要能够将所有的电脑解锁&#x…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...
【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力
引言: 在人工智能快速发展的浪潮中,快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型(LLM)。该模型代表着该领域的重大突破,通过独特方式融合思考与非思考…...
