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平台开发的水下根石监测系统。该系统利用高精度姿态传感器与位移传感器,实现了水下根石状态的实时自动监测,提高了水利工程安全管理的现代化和精细化水平,具有高精度、高稳定性和良好的操作性。 项目背景: …...
打造沉浸式智能AI问答助手:Vue + UniApp 全端实战(支持 Markdown/公式/多模态交互)畔
OCP原则 ocp指开闭原则,对扩展开放,对修改关闭。是七大原则中最基本的一个原则。 依赖倒置原则(DIP) 什么是依赖倒置原则 核心是面向接口编程、面向抽象编程, 不是面向具体编程。 依赖倒置原则的目的 降低耦合度&#…...
Z-Image-Turbo-辉夜巫女高性能部署:Xinference量化加载+Gradio并发优化实测
Z-Image-Turbo-辉夜巫女高性能部署:Xinference量化加载Gradio并发优化实测 1. 项目简介 Z-Image-Turbo-辉夜巫女是基于Z-Image-Turbo模型的Lora版本,专门用于生成高质量的辉夜巫女风格图片。这个镜像通过Xinference框架实现了高效的模型部署࿰…...
AI:词向量模型详解(Word Embedding)
词向量模型详解(Word Embedding) 词向量(Word Embedding)是自然语言处理(NLP)中最基础且影响深远的表示学习方法之一。它将离散的词汇映射为低维、稠密的实数向量,使计算机能够“理解”词语之间…...
攻克表情显示难题:Noto Emoji企业级解决方案
攻克表情显示难题:Noto Emoji企业级解决方案 【免费下载链接】noto-emoji Noto Emoji fonts 项目地址: https://gitcode.com/gh_mirrors/no/noto-emoji 当你精心设计的聊天应用在用户手机上显示为"□□"乱码,当跨国团队的沟通因表情差异…...
保姆级避坑指南:在Ubuntu 22.04上搞定Hi3516CV610 SDK环境(附完整依赖包清单)
保姆级避坑指南:在Ubuntu 22.04上搞定Hi3516CV610 SDK环境(附完整依赖包清单) 第一次接触海思Hi3516CV610开发板的开发者,往往会在搭建SDK环境时踩不少坑。Ubuntu 22.04作为较新的LTS版本,与海思官方推荐的开发环境存在…...
UE5第三人称相机避障实战:SpringArmComponent参数调优与常见Bug修复
UE5第三人称相机避障实战:SpringArmComponent参数调优与常见Bug修复 在虚幻引擎5(UE5)开发第三人称游戏时,相机系统的表现直接影响玩家的游戏体验。一个优秀的第三人称相机应该既能跟随角色流畅移动,又能智能避开场景障…...
**基于Python的基因序列分析工具链:从原始数据到功能注释全流程实战**
基于Python的基因序列分析工具链:从原始数据到功能注释全流程实战 在生物信息学领域,基因分析已成为理解生命本质的核心手段之一。无论是疾病机制探索、药物靶点筛选还是群体遗传研究,高效的基因序列处理能力都至关重要。本文将带你构建一套完…...
新手也能搞定的应急响应实战:用知攻善防靶场复现近源渗透与挖矿事件
新手也能搞定的应急响应实战:用知攻善防靶场复现近源渗透与挖矿事件 网络安全应急响应是每个安全从业者的必修课,但对于刚入门的新手来说,面对真实的攻击事件往往无从下手。本文将带你通过知攻善防靶场,手把手复现"近源渗透O…...
Nextjs从入门到实战保姆级教程:环境配置与项目初始化
本系列文章将围绕Next.js技术栈,旨在为AI Agent开发者提供一套完整的客户端侧工程实践指南。 本章将引导你完成 Next.js 开发环境的搭建,创建第一个项目并理解其基本结构。我们将详细说明每个步骤的原理,确保你不仅知道"怎么做"&am…...
Python核心控制结构全解析,Docker经典安装命令失效排查:Ubuntu/CentOS多系统测试与解决方案。
Python学习历程:核心控制结构解析 for循环结构 Python的for循环基于迭代器协议,可直接遍历序列或可迭代对象。典型语法为: for item in iterable:# 循环体print(item)支持else子句,当循环正常结束时执行: for i in ran…...
