手写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 中的一个全局对象(类),用于创建字符串对象࿰…...

jna调用c++动态库linux测试
1、 编译代码和运行指令 javac -cp .:jna-5.7.0.jar:jna-platform-5.7.0.jar JnaTest.java VideoAiLibrary.java java -cp .:jna-5.7.0.jar:jna-platform-5.7.0.jar JnaTest javac -cp .:jna-5.7.0.jar:jna-platform-5.7.0.jar JnaTest.java VideoAiLibrary.java -cp 指定c…...

智诊小助手TF卡记录文件导出
若想将TF卡中记录的数据文件导出可按以下的流程进行配置: 点击主界面中的导出选项即可进入到下图中TF卡应用界面点击TF卡应用界面中“查看记录文件”的选项,进入导出文件界面。点击“选择”进入勾选文件的界面 点击“导出”后,点击“确定”即…...

Jetpack-ViewModel+LiveData+DataBinding
1.ViewModel 解决问题: 瞬态数据丢失异步调用内存泄漏类膨胀提高维护难度和测试难度 作用: 介于View视图和Model数据模型之间桥梁使视图和数据能够分离,也能保持通信 public class MainActivity extends AppCompatActivity {private Tex…...

Servlet[springmvc]的Servlet.init()引发异常
报错: 原因之一: web.xml配置文件中监听器导入依赖项错误...

总结:SQL查询变慢,常见原因分析!
文章目录 引言SQL查询慢原因索引失效特殊情况-执行计划中,key有值,还是很慢怎么办? 多表JOIN为什么互联网公司都不建议使用多表join? 索引基数太小不合理查询字段太多表中数据量太大数据库连接数不够为什么乐观锁还会导致大量的锁…...

基于webrtc实现音视频通信
与传统通信方式不同,p2p通信的实现过程不依赖于中间服务器的信息收发,直接通过信令等完成通信过程的建立; 通过websocket实现信令服务器的建立,而通过信令来确定通信双方; webrtc通过 sdp协议来完善通信双方间协议的…...

【多版本并发控制(MVCC)】
并发事务问题: MySQL隔离级别-未提交读,提交读,可重复读,序列化 隔离级别对于并发事务的解决情况 隔离级别脏读不可重复读幻读未提交读不可不可不可读已提交可不可不可可重复读 (默认)可可不可串行化&…...

常见漏洞及webshell工具的流量特征
常见攻击的流量特征 信息泄露 请求/路径中,包含 特殊文件 或 路径;响应包中,包含敏感信息(如,数据结构,用户信息,网络结构等) 弱口令爆破 非常规流量:短时间内大量数据…...

python学习-怎么在Pycharm写代码
打开Pycharm,点击文件-新建项目 2.选择pure python-点击箭头 展开 3.选择 Existing interpreter 如果 Existing interpreter 下没有相关环境 (1)点击**…** (2)选择python的安装路径 4.可修改文件名称-点击创建 …...

牛客周赛63(C++实现)
🌈个人主页:Yui_ 🌈Linux专栏:Linux 🌈C语言笔记专栏:C语言笔记 🌈数据结构专栏:数据结构 🌈C专栏:C 文章目录 1.小红的好数1.1 题目描述1.2 思路1.3 代码 2.…...