当前位置: 首页 > news >正文

手写Spring IOC-简易版

目录

    • 项目结构
      • entity
      • dao
        • IUserDao
        • UserDaoImpl
      • service
        • IUserService
        • UserServiceImpl
      • ApplicationContext
    • 配置文件初始化 IOC 容器
      • RunApplication
    • 注解初始化 IOC 容器
      • @Bean
      • @Autowired
    • Reference

项目结构

在这里插入图片描述

entity

  1. 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();

如何解决这个问题****❓

我们可以将 IUserDaoImplClass 作为 key,IUserDaoImpl 实例作为 value 存储在一个HashMap<Class, Object>中。

  1. 在 Web 应用程序运行之前,将 ApplicationConext 类中的对象加载并初始化到 IOC 容器中。
  2. 当调用 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 中的反射。

  1. 创建一个application.properties文件,在其中可以添加我们需要的对象信息。
IOC.service.IUserService = IOC.service.Impl.UserServiceImpl
IOC.dao.IUserDao = IOC.dao.Impl.UserDaoImpl
  1. 利用反射机制加载对象,并存储到 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

  1. 新增 annotation 包,创建 Bean 注解类。
@Target(ElementType.TYPE)    // 只能修饰类型元素
@Retention(RetentionPolicy.RUNTIME)
public @interface Bean {}
  1. UserDaoImplUserServiceImpl 类上加上 **@Bean **注解。
@Bean
public class UserServiceImpl implements IUserService {}@Bean
public class UserDaoImpl implements IUserDao {}
  1. 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);}}}}}}
}
  1. 修改 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

  1. 在 annotation 包中添加 Aurowired 注解类。
@Target(ElementType.FIELD)    // 只能修饰成员变量
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {}
  1. UserServiceImpl 成员变量添加 @Autowired 注解。
@Bean
public class UserServiceImpl implements IUserService {@Autowiredprivate IUserDao userDao;
}
  1. ApplicationContext 类中添加使用注解初始化 bean 对象后,初始化各个 bean 对象的成员变量。
    1. 这里默认只实现了根据类型初始化成员变量(在 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);}}}
}
  1. 答复
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

  1. 一堂课深挖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 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图…...

【VUE】Vue中的data属性为什么是一个函数而不是一个对象

在 Vue.js 中&#xff0c;组件的 data 属性可以是一个对象或者一个函数但通常建议将其设置为函数。这是因为组件可能会被多次使用&#xff0c;如果 data 是一个普通对象&#xff0c;那么该对象会被所有实例共享&#xff0c;导致数据混乱。将 data 设置为一个函数可以保证每个组…...

ddos攻击介绍和排查方法

一、DDoS攻击介绍 DDoS攻击&#xff0c;全称为分布式拒绝服务攻击&#xff08;Distributed Denial of Service Attack&#xff09;&#xff0c;是一种常见的网络攻击手段。它通过利用多个计算机系统向目标服务器、服务或网络发送大量请求&#xff0c;导致目标无法处理正常流量…...

git clone --single-branch 提升效率

git clone --single-branch 是一个Git命令&#xff0c;用于从远程仓库中仅克隆单个分支到本地仓库。这个命令在软件开发中非常有用&#xff0c;尤其是在需要特定分支的代码而无需整个仓库的情况下。 基本用法 git clone --single-branch 命令的基本语法如下&#xff1a; git…...

代码随想录算法训练营第十天|1. 两数之和,第454题.四数相加II

文档讲解&#xff1a;代码随想录 难度&#xff1a;一般嗷~~ 1. 两数之和 力扣题目链接(opens new window) 给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那 两个 整数&#xff0c;并返回他们的数组下标。 你可以假设每种输入只会对…...

龙迅LT8911EX LVDS转EDP 点屏,大批量出货产品

龙迅LT8911EX描述&#xff1a; Lontium LT8911EX是LVDS到eDP转换器&#xff0c;具有单端口或双端口可配置的LVDS接收器&#xff0c;有1个时钟通道和最多8个数据通道&#xff0c;每个数据通道最大运行1.2Gbps&#xff0c;最大输入带宽为9.6Gbps。转换器将输入LVDS数据去序列化&…...

浅谈Oracle之游标

一、基本介绍 在Oracle数据库中&#xff0c;游标&#xff08;Cursor&#xff09;是一种强大的工具&#xff0c;用于逐行处理查询结果集。然而&#xff0c;游标的使用需要谨慎&#xff0c;因为不当的使用可能会导致性能问题。 二、最佳实践和优化技巧 尽量避免使用游标&#xf…...

基于在线教育系统源码的企业培训平台开发解决方案详解

本篇文章&#xff0c;笔者将详细解析基于在线教育系统源码开发企业培训平台的解决方案&#xff0c;探讨其开发步骤、关键功能模块及技术实现方案。 一、在线教育系统源码的优势 在构建企业培训平台时&#xff0c;选择基于在线教育系统源码的开发方式具有以下几个显著优势&…...

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

先上效果图 项目源码&#xff1a;https://download.csdn.net/download/qq_43055855/89891285 源码地址 手机扫描二维码跳转到指定网页 概述 这个项目是一个基于 Java 的二维码生成与解析工具&#xff0c;主要由 QRCodeUtil 和 QRCodeController 两个类组成。它利用了 Google…...

vue跨标签页通信(或跨窗口)详细教程

在 Vue 应用中,跨标签页(或跨窗口)的通信通常涉及到两个或多个浏览器标签页之间的信息共享。由于每个标签页或窗口都是独立的 JavaScript 执行环境,它们不能直接通过 Vue 或其他 JavaScript 库来直接相互通信。但是,有一些方法可以实现这种跨标签页的通信,主要依靠浏览器…...

【VUE】Vue3通过数组下标更改数组视图为什么会更新?

在 Vue 3 中&#xff0c;使用 Proxy 来实现了对数组的响应式监听&#xff0c;相比于 Vue 2 使用的 Object.defineProperty()&#xff0c;Proxy 更加高效和灵活。 因此&#xff0c;在 Vue 3 中&#xff0c;通过数组下标直接更改数组中某一项的值&#xff0c;也能够被 Vue 正确监…...

前端转换double数据,保留两位小数

Number Number(1.00) 1 Number(1.10) 1.1 Number(1.101) 1.101 要想前端展示页面按 1.00展示1&#xff0c;1.10 展示1.1 需要套一个number() 1.1 保留两位小数&#xff0c;并三位一个分隔符 indexView.value[key] formatNumber(indexView.value[key].toFixed(2))//格式…...

【实战案例】JSR303统一校验与SpringBoot项目的整合

前后端分离项目中&#xff0c;当前前端请求后端接口的时候通常需要传输参数&#xff0c;对于参数的校验应该在哪一步进行校验&#xff1f;Controller中还是Service中&#xff1f;答案是都需要校验&#xff0c;只不过负责的板块不一样&#xff0c;Controller中通常校验请求参数的…...

忘记了系统root密码,如何重置root密码?

重置root密码&#xff08;CentOS7&#xff09; 文章目录 重置root密码&#xff08;CentOS7&#xff09;[toc] 1.开启系统时&#xff0c;在引导界面按下字母e。 2.进入到内核界面&#xff0c;找到Linux开头字样一行&#xff0c;然后在最末尾输入参数rd.break&#xff0c;然后按住…...

7-基于国产化FT-M6678+JFM7K325T的6U CPCI信号处理卡

一、板卡概述 本板卡系我公司自主研发&#xff0c;基于6U CPCI的通用高性能信号处理平台。板卡采用一片国产8核DSP FT-C6678和一片国产FPGA JFM7K325T-2FFG900作为主处理器。为您提供了丰富的运算资源。如下图所示&#xff1a; 二、设计参考标准 ● PCIMG 2.0 R3.0 CompactP…...

计算机毕业设计 | SSM超市进销存管理系统(附源码)

1&#xff0c;绪论 1.1 开发背景 世界上第一个购物中心诞生于美国纽约&#xff0c;外国人迈克尔库伦开设了第一家合作商店&#xff0c;为了更好地吸引大量客流量&#xff0c;迈克尔库伦精心设计了低价策略&#xff0c;通过大量进货把商品价格压低&#xff0c;通过商店一次性集…...

手撕数据结构 —— 堆(C语言讲解)

目录 1.堆的认识 什么是堆 堆的性质 2.堆的存储 3.堆的实现 Heap.h中接口总览 具体实现 堆结构的定义 初始化堆 销毁堆 堆的插入 堆的向上调整算法 堆的插入的实现 堆的删除 堆的向下调整算法 堆的删除的实现 使用数组初始化堆 获取堆顶元素 获取堆中的数据…...

TS和JS中,string与String的区别

1. string string 是 TypeScript 的基本类型&#xff0c;用于表示简单的字符串值&#xff0c;同时它是一个原始类型&#xff0c;可直接表示文本数据。 2. String String 是 JavaScript 中的一个全局对象&#xff08;类&#xff09;&#xff0c;用于创建字符串对象&#xff0…...

srs linux

下载编译运行 git clone https:///ossrs/srs.git ./configure --h265on make 编译完成后即可启动SRS # 启动 ./objs/srs -c conf/srs.conf # 查看日志 tail -n 30 -f ./objs/srs.log 开放端口 默认RTMP接收推流端口是1935&#xff0c;SRS管理页面端口是8080&#xff0c;可…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

Unit 1 深度强化学习简介

Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库&#xff0c;例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体&#xff0c;比如 SnowballFight、Huggy the Do…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容

目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法&#xff0c;当前调用一个医疗行业的AI识别算法后返回…...

AGain DB和倍数增益的关系

我在设置一款索尼CMOS芯片时&#xff0c;Again增益0db变化为6DB&#xff0c;画面的变化只有2倍DN的增益&#xff0c;比如10变为20。 这与dB和线性增益的关系以及传感器处理流程有关。以下是具体原因分析&#xff1a; 1. dB与线性增益的换算关系 6dB对应的理论线性增益应为&…...

JS手写代码篇----使用Promise封装AJAX请求

15、使用Promise封装AJAX请求 promise就有reject和resolve了&#xff0c;就不必写成功和失败的回调函数了 const BASEURL ./手写ajax/test.jsonfunction promiseAjax() {return new Promise((resolve, reject) > {const xhr new XMLHttpRequest();xhr.open("get&quo…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

NPOI操作EXCEL文件 ——CAD C# 二次开发

缺点:dll.版本容易加载错误。CAD加载插件时&#xff0c;没有加载所有类库。插件运行过程中用到某个类库&#xff0c;会从CAD的安装目录找&#xff0c;找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库&#xff0c;就用插件程序加载进…...

在鸿蒙HarmonyOS 5中使用DevEco Studio实现企业微信功能

1. 开发环境准备 ​​安装DevEco Studio 3.1​​&#xff1a; 从华为开发者官网下载最新版DevEco Studio安装HarmonyOS 5.0 SDK ​​项目配置​​&#xff1a; // module.json5 {"module": {"requestPermissions": [{"name": "ohos.permis…...

ABAP设计模式之---“Tell, Don’t Ask原则”

“Tell, Don’t Ask”是一种重要的面向对象编程设计原则&#xff0c;它强调的是对象之间如何有效地交流和协作。 1. 什么是 Tell, Don’t Ask 原则&#xff1f; 这个原则的核心思想是&#xff1a; “告诉一个对象该做什么&#xff0c;而不是询问一个对象的状态再对它作出决策。…...