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平台开发的水下根石监测系统。该系统利用高精度姿态传感器与位移传感器,实现了水下根石状态的实时自动监测,提高了水利工程安全管理的现代化和精细化水平,具有高精度、高稳定性和良好的操作性。 项目背景: …...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...

超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...

【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...

以下是对华为 HarmonyOS NETX 5属性动画(ArkTS)文档的结构化整理,通过层级标题、表格和代码块提升可读性:
一、属性动画概述NETX 作用:实现组件通用属性的渐变过渡效果,提升用户体验。支持属性:width、height、backgroundColor、opacity、scale、rotate、translate等。注意事项: 布局类属性(如宽高)变化时&#…...

【HarmonyOS 5.0】DevEco Testing:鸿蒙应用质量保障的终极武器
——全方位测试解决方案与代码实战 一、工具定位与核心能力 DevEco Testing是HarmonyOS官方推出的一体化测试平台,覆盖应用全生命周期测试需求,主要提供五大核心能力: 测试类型检测目标关键指标功能体验基…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
三体问题详解
从物理学角度,三体问题之所以不稳定,是因为三个天体在万有引力作用下相互作用,形成一个非线性耦合系统。我们可以从牛顿经典力学出发,列出具体的运动方程,并说明为何这个系统本质上是混沌的,无法得到一般解…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...

tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...