手写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 中的一个全局对象(类),用于创建字符串对象࿰…...
linux之kylin系统nginx的安装
一、nginx的作用 1.可做高性能的web服务器 直接处理静态资源(HTML/CSS/图片等),响应速度远超传统服务器类似apache支持高并发连接 2.反向代理服务器 隐藏后端服务器IP地址,提高安全性 3.负载均衡服务器 支持多种策略分发流量…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
现代密码学 | 椭圆曲线密码学—附py代码
Elliptic Curve Cryptography 椭圆曲线密码学(ECC)是一种基于有限域上椭圆曲线数学特性的公钥加密技术。其核心原理涉及椭圆曲线的代数性质、离散对数问题以及有限域上的运算。 椭圆曲线密码学是多种数字签名算法的基础,例如椭圆曲线数字签…...
MySQL 8.0 OCP 英文题库解析(十三)
Oracle 为庆祝 MySQL 30 周年,截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始,将英文题库免费公布出来,并进行解析,帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
Springboot社区养老保险系统小程序
一、前言 随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱,社区养老保险系统小程序被用户普遍使用,为方…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)
漏洞概览 漏洞名称:Apache Flink REST API 任意文件读取漏洞CVE编号:CVE-2020-17519CVSS评分:7.5影响版本:Apache Flink 1.11.0、1.11.1、1.11.2修复版本:≥ 1.11.3 或 ≥ 1.12.0漏洞类型:路径遍历&#x…...
