Go语言实现依赖注入
文章目录
- 前言
- 依赖注入是什么
- 依赖注入的好处是什么
- 结构图
- 应用程序上下文接口
- 上下文管理器
- 暴露的功能
- 使用示例
- 最后
前言
你好,我是醉墨居士,欢迎来到我的博客,今天带领大伙使用Go语言实现依赖自动注入,我们不会使用其它的第三方库,项目核心代码不到100行,是Go语言初学者难得的精简项目
依赖注入是什么
依赖注入(Dependency Injection,简称DI)是一种设计模式,用于实现控制反转(Inversion of Control,简称IoC)原则。它的核心思想是将对象的依赖关系从内部管理转移到外部管理,从而降低对象之间的耦合度,提高代码的灵活性和可测试性
依赖注入的好处是什么
降低耦合度:通过将依赖关系从对象内部转移到外部,可以降低对象之间的耦合度。这样,对象只需要知道它需要什么,而不需要知道如何获取这些依赖
提高可测试性:依赖注入使得单元测试变得更加容易。在测试时,可以轻松地替换掉真实的依赖对象,使用模拟对象(Mock Object)或存根(Stub)来进行测试,从而隔离被测试代码
增强可维护性:由于依赖关系被明确地定义和管理,代码的可读性和可维护性得到了提高。当需要修改依赖关系时,只需要在配置或注入点进行修改,而不需要修改对象内部的代码
促进代码重用:依赖注入使得组件可以更容易地在不同的上下文中重用。因为组件不直接创建和管理自己的依赖,所以它们可以在不同的环境中被配置和使用
支持面向接口编程:依赖注入鼓励使用接口来定义依赖关系,而不是具体的实现类。这使得代码更加灵活,因为可以在运行时替换不同的实现,而不需要修改调用代码
简化对象创建:依赖注入容器(如Spring的ApplicationContext)可以自动管理对象的创建和生命周期,开发者不需要手动创建和管理这些对象,从而简化了代码
结构图
现在介绍一下我们依赖注入这个小项目的结构图

应用程序上下文接口
type BeanProvider func() reflect.Valuetype ApplicationContext interface {Inject(provider BeanProvider, name string) errorAutowise(obj any, name string) error
}
上下文管理器
type context struct {namedConatiner map[string]BeanProvidertypedContainer map[reflect.Type]BeanProvider
}// 实现依赖注入
func (c *context) Inject(provider BeanProvider, name string) error {if provider == nil {return fmt.Errorf("inject: provider can not be nil")}if name == "" {// type injectty := provider().Type()if _, ok := c.typedContainer[ty];ok {return fmt.Errorf("inject: %v is ambiguous", ty)}c.typedContainer[ty] = provider} else {// name injectif _, ok := c.namedConatiner[name];ok {return fmt.Errorf("inject: %v is ambiguous", name)}c.namedConatiner[name] = provider}return nil
}// 实现自动装配
func (c *context) Autowise(val any, name string) error {if val == nil {return fmt.Errorf("inject: nil value")}rv := reflect.ValueOf(val)if rv.Kind() != reflect.Ptr {return fmt.Errorf("inject: %v is not a pointer", rv)}ri := reflect.Indirect(rv)rt := ri.Type()var provider BeanProviderif name == "" {// type autowiseprovider = c.typedContainer[rt]} else {// name autowiseprovider = c.namedConatiner[name]}if provider == nil {return fmt.Errorf("inject: %v is not found", name)}obj := provider()if obj.CanConvert(rt) {ri.Set(obj.Convert(rt))return nil}return fmt.Errorf("inject: value can not convert to %s", ri.Type())
}
暴露的功能
func defaultBeanProvider(v any) BeanProvider {return func() reflect.Value {return reflect.ValueOf(v)}
}// 对外暴露依赖注入的能力,name为空字符串时表示默认使用类型注入
func Inject(obj any, name string) error {return instance.Inject(defaultBeanProvider(obj), name)
}// 对外暴露依赖注入的能力,name为空字符串时表示默认使用类型注入
func DeepInject(provider BeanProvider, name string) error {return instance.Inject(provider, name)
}// 对外暴露自动装配的能力,name为空字符串时表示默认使用类型自动装配
func Autowise[T any](obj *T, name string) error {return instance.Autowise(obj, name)
}
使用示例
示例代码
package mainimport ("fmt""github.com/zm50/injector""reflect"
)type TwoString struct {s1 *strings2 *string
}func main() {// 通过类型注入变量,注入一个string类型的变量var injectString string = "醉墨居士"err := injector.Inject(injectString, "")if err != nil {panic(err)}// 通过类型装配变量,通过string类型自动装配变量var autowiseString stringerr = injector.Autowise(&autowiseString, "")if err != nil {panic(err)}fmt.Println("类型注入和装配的演示结果")fmt.Println("注入的变量:", injectString, "装配的变量:", autowiseString)// 通过名称注入变量var injectName string = "醉墨"var injectString2 string = "居士"err = injector.Inject(injectString2, injectName)if err != nil {panic(err)}// 通过名称装配变量var autowiseString2 stringerr = injector.Autowise(&autowiseString2, "醉墨")if err != nil {panic(err)}fmt.Println("名称注入和装配的演示结果")fmt.Println("注入的变量:", injectString2, "装配的变量:", autowiseString2)// 通过类型注入结构体指针injectStruct := &TwoString{}injectStruct.s1 = new(string)injectStruct.s2 = new(string)*injectStruct.s1 = "醉墨"*injectStruct.s2 = "居士"err = injector.Inject(injectStruct, "")if err != nil {panic(err)}// 通过类型装配结构体指针autowiseStruct := &TwoString{}err = injector.Autowise(&autowiseStruct, "")if err != nil {panic(err)}fmt.Println("结构体指针注入和装配的演示结果")fmt.Println("注入的变量:", injectStruct, *(injectStruct.s1), *(injectStruct.s2))fmt.Println("装配的变量:", autowiseStruct, *(autowiseStruct.s1), *(autowiseStruct.s2))fmt.Println("是否相等:", injectStruct == autowiseStruct, injectStruct.s1 == autowiseStruct.s1, injectStruct.s2 == autowiseStruct.s2)// 自定义依赖注入和装配的能力,演示自定义依赖注入和装配的能力实现深拷贝,大家可以也根据自己的需求自定义依赖注入和装配的能力injectStruct2 := &TwoString{s1: new(string), s2: new(string),}*(injectStruct2.s1) = "醉墨"*(injectStruct2.s2) = "居士"provider := func() reflect.Value {twoString := TwoString{}twoString.s1 = new(string)twoString.s2 = new(string)*twoString.s1 = *injectStruct.s1*twoString.s2 = *injectStruct.s2return reflect.ValueOf(twoString)}err = injector.DeepInject(provider, "")if err != nil {panic(err)}autowiseStruct2 := &TwoString{}err = injector.Autowise(autowiseStruct2, "")if err != nil {panic(err)}fmt.Println("自定义规则实现结构体深拷贝注入和装配的演示结果")fmt.Println("注入的变量:", injectStruct2, *(injectStruct2.s1), *(injectStruct2.s2))fmt.Println("装配的变量:", autowiseStruct2, *(autowiseStruct2.s1), *(autowiseStruct2.s2))fmt.Println("是否相等:", injectStruct2 == autowiseStruct2, injectStruct2.s1 == autowiseStruct2.s1, injectStruct2.s2 == autowiseStruct2.s2)
}
示例结果

最后
至此,各位我们已经一起完成了这个依赖注入的小项目
我是醉墨居士,我们下篇博客见
相关文章:
Go语言实现依赖注入
文章目录 前言依赖注入是什么依赖注入的好处是什么结构图应用程序上下文接口上下文管理器暴露的功能使用示例最后 前言 你好,我是醉墨居士,欢迎来到我的博客,今天带领大伙使用Go语言实现依赖自动注入,我们不会使用其它的第三方库…...
不仅能防沉迷游戏的防沉迷软件(Python)
介绍 一个有那么一点功能但是又不太保险的防沉迷工具 我脑子进水了才会写这玩意儿 为了变强,我不择手段(笑出zhu jiao 代码 好像没什么用的设定界面 # -*- coding: utf-8 -*- # Environment PyCharm # File_name login |User Pfolg # 2024/…...
数学建模--智能算法之鱼群算法
目录 核心原理 应用与实现 实现步骤 性能分析与改进 鱼群算法在解决哪些具体优化问题方面表现最佳? 如何根据不同的应用场景调整鱼群算法的参数设置以提高其性能? 鱼群算法与其他群体智能优化算法(如遗传算法、粒子群优化)…...
html+css+js前端作业qq音乐官网5个页面 带js
htmlcssjs前端作业qq音乐官网5个页面 带js 有轮播图,tab切换等多种效果 网页作品代码简单,可使用任意HTML编辑软件(如:Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编…...
【mars3d】加载超图s3m模型说明
建议替换Cesium库,换成 超图版本Cesium mars3d mars3d-supermap ,需要引入的资源为: "mars3d": ["Cesium-supermap/Widgets/widgets.css", //超图版本Cesium "Cesium-supermap/Cesium.js","mars3d/plu…...
LeetCode Hot100 二叉搜索树中第K小的元素
给定一个二叉搜索树的根节点 root ,和一个整数 k ,请你设计一个算法查找其中第 k 小的元素(从 1 开始计数)。 示例 1: 输入:root [3,1,4,null,2], k 1 输出:1示例 2: 输入…...
CBK-D5-安全测试与开发osg15、20、21
CBK-D5-安全测试与开发osg15、20、21 安全评估与测试 构建安全评估和测试方案 安全测试 旨在验证某项控制措施是否正常运行。 包括自动化扫描、工具辅助的渗透测试、破坏安全性的手动测试。 渗透测试可以每年开展一次,以最大限度地降低费用并减小业务中断的影响。 仅简…...
期权杠杆与期货杠杆的区别是什么?
期权与股指期货在杠杆性上展现出截然不同的特性,这些特性对投资者的策略选择具有深远影响。首先,股指期货采用保证金制度,其杠杆比例是恒定的,无论市场如何波动,投资者在月初设定的十倍杠杆到月尾仍保持不变。相比之下…...
数字人解决方案——音频驱动机器人
音频集成 机器人 标志着 人工智能(AI)。 想象一下,机器人可以通过视觉和听觉导航并与周围环境互动。音频驱动的机器人使这成为可能,提高了它们更高效、更直观地执行任务的能力。这一发展可能会影响到各个领域,包括家庭…...
Linux Tcp 连接 状态 检测 处理
查看不同状态的链接数 netstat -na | awk /^tcp/ {S[$NF]} END {for(a in S) print a, S[a]} 输出如下: TIME_WAIT 2 CLOSE_WAIT 2149 LISTEN 18 ESTABLISHED 214...
String respIson = objectMapper.writeValueAsString(response);
**一、代码解释** 这段代码的作用是使用ObjectMapper(假设是 Jackson 的ObjectMapper)将一个 Java 对象response转换为 JSON 格式的字符串。 1. ObjectMapper: 它是 Jackson 库中用于在 Java 对象和 JSON 之间进行序列化和反序列化的核心类。…...
git squash、merge 、 rebase
Git Merge、Rebase 和 Squash 之间的区别_git squash-CSDN博客...
案例开发-日程管理2第一期(超详细教程、配备图文和源代码注释,没学过也能看懂)
文章目录 一、 项目前期准备1.数据库准备2.导入依赖3.pojo包处理4.dao包处理5.service包处理6.controller包处理7.加密工具类的使用8.页面文件的导入 总结 一、 项目前期准备 1.数据库准备 创建schedule_system数据库并执行如下语句 SET NAMES utf8mb4; SET FOREIGN_KEY_CHE…...
c# 逻辑运算符和条件运算符
前言 在 C# 中,&&、|| 用于处理布尔值(true 和 false),而&、|、^ 位运算符可以用于按位操作整数。 后者总是计算其两个操作数 而前者可能不会计算第二个操作数,这取决于第一个操作数的值。 非短路逻辑运…...
Linux驱动开发—设备树传递给内核,匹配驱动过程分析
文章目录 总体流程图传递DTB过程编译设备树源文件将 .dtb 文件与内核或引导加载程序集成 内核初始化阶段解析DTB内核启动阶段解析 DTB注册设备树节点驱动程序绑定 内核解析设备树二进制文件(DTB)的过程主要分为几个步骤,从设备树的传递到最终…...
深入理解 Go 语言信号量 Semaphore
1. 什么是信号量 信号量的概念是荷兰计算机科学家 Edsger Wybe Dijkstra 在 1963 年左右提出来的,被广泛应用在不同的操作系统中。在操作系统中,会给每一个进程分配一个信号量,代表每个进程目前的状态。未得到控制权的进程,会在特定的地方被迫停下来,等待可以继续进行的信…...
git——删除远程仓库中的文件或文件夹步骤图解(只是从远程仓库中删除,本地文件不受影响、不会被删除)
目录 一、删除远程仓库中的文件或文件夹1.1、 以删除远程仓库jetcache-demo项目中的logs文件夹为例1.2、 删除远程仓库jetcache-demo项目中的logs文件夹步骤图解 一、删除远程仓库中的文件或文件夹 1.1、 以删除远程仓库jetcache-demo项目中的logs文件夹为例 删除远程仓库jet…...
详解贪心算法
贪心算法(Greedy Algorithm) 概述: 贪心算法是一种在求解最优化问题时采取的一种常用算法策略。贪心算法的基本思想是,每次选择当前情况下的局部最优解,并相信这个局部最优解能够导致全局最优解。贪心算法通过迭代的方式一步步地…...
LabVIEW工件表面瑕疵识别系统
开发了一种利用LabVIEW和IMAQ Vision视觉工具进行工件表面瑕疵识别的系统。该系统通过图像处理技术识别并分类工件表面的裂纹、划痕等缺陷,从而提升生产线的分拣效率和产品质量。 项目背景 工业生产中,工件表面的缺陷直接影响产品质量和生产效率。传统人…...
LabVIEW水下根石监测系统
开发了一种基于LabVIEW平台开发的水下根石监测系统。该系统利用高精度姿态传感器与位移传感器,实现了水下根石状态的实时自动监测,提高了水利工程安全管理的现代化和精细化水平,具有高精度、高稳定性和良好的操作性。 项目背景: …...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
【kafka】Golang实现分布式Masscan任务调度系统
要求: 输出两个程序,一个命令行程序(命令行参数用flag)和一个服务端程序。 命令行程序支持通过命令行参数配置下发IP或IP段、端口、扫描带宽,然后将消息推送到kafka里面。 服务端程序: 从kafka消费者接收…...
Linux相关概念和易错知识点(42)(TCP的连接管理、可靠性、面临复杂网络的处理)
目录 1.TCP的连接管理机制(1)三次握手①握手过程②对握手过程的理解 (2)四次挥手(3)握手和挥手的触发(4)状态切换①挥手过程中状态的切换②握手过程中状态的切换 2.TCP的可靠性&…...
在 Nginx Stream 层“改写”MQTT ngx_stream_mqtt_filter_module
1、为什么要修改 CONNECT 报文? 多租户隔离:自动为接入设备追加租户前缀,后端按 ClientID 拆分队列。零代码鉴权:将入站用户名替换为 OAuth Access-Token,后端 Broker 统一校验。灰度发布:根据 IP/地理位写…...
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...
Cinnamon修改面板小工具图标
Cinnamon开始菜单-CSDN博客 设置模块都是做好的,比GNOME简单得多! 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...
IP如何挑?2025年海外专线IP如何购买?
你花了时间和预算买了IP,结果IP质量不佳,项目效率低下不说,还可能带来莫名的网络问题,是不是太闹心了?尤其是在面对海外专线IP时,到底怎么才能买到适合自己的呢?所以,挑IP绝对是个技…...
破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...
