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

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: 输入&#xf…...

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平台开发的水下根石监测系统。该系统利用高精度姿态传感器与位移传感器,实现了水下根石状态的实时自动监测,提高了水利工程安全管理的现代化和精细化水平,具有高精度、高稳定性和良好的操作性。 项目背景: …...

国防科技大学计算机基础课程笔记02信息编码

1.机内码和国标码 国标码就是我们非常熟悉的这个GB2312,但是因为都是16进制,因此这个了16进制的数据既可以翻译成为这个机器码,也可以翻译成为这个国标码,所以这个时候很容易会出现这个歧义的情况; 因此,我们的这个国…...

Debian系统简介

目录 Debian系统介绍 Debian版本介绍 Debian软件源介绍 软件包管理工具dpkg dpkg核心指令详解 安装软件包 卸载软件包 查询软件包状态 验证软件包完整性 手动处理依赖关系 dpkg vs apt Debian系统介绍 Debian 和 Ubuntu 都是基于 Debian内核 的 Linux 发行版&#xff…...

基础测试工具使用经验

背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...

【论文笔记】若干矿井粉尘检测算法概述

总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN,根据VPN原理,打通两个内网必然需要借助一个公共中继节点,ktconnect工具巧妙的利用k8s原生的portforward能力,简化了建立连接的过程,apiserver间接起到了中继节…...

作为测试我们应该关注redis哪些方面

1、功能测试 数据结构操作:验证字符串、列表、哈希、集合和有序的基本操作是否正确 持久化:测试aof和aof持久化机制,确保数据在开启后正确恢复。 事务:检查事务的原子性和回滚机制。 发布订阅:确保消息正确传递。 2、性…...

苹果AI眼镜:从“工具”到“社交姿态”的范式革命——重新定义AI交互入口的未来机会

在2025年的AI硬件浪潮中,苹果AI眼镜(Apple Glasses)正在引发一场关于“人机交互形态”的深度思考。它并非简单地替代AirPods或Apple Watch,而是开辟了一个全新的、日常可接受的AI入口。其核心价值不在于功能的堆叠,而在于如何通过形态设计打破社交壁垒,成为用户“全天佩戴…...

【记录坑点问题】IDEA运行:maven-resources-production:XX: OOM: Java heap space

问题:IDEA出现maven-resources-production:operation-service: java.lang.OutOfMemoryError: Java heap space 解决方案:将编译的堆内存增加一点 位置:设置setting-》构建菜单build-》编译器Complier...

Element-Plus:popconfirm与tooltip一起使用不生效?

你们好&#xff0c;我是金金金。 场景 我正在使用Element-plus组件库当中的el-popconfirm和el-tooltip&#xff0c;产品要求是两个需要结合一起使用&#xff0c;也就是鼠标悬浮上去有提示文字&#xff0c;并且点击之后需要出现气泡确认框 代码 <el-popconfirm title"是…...

vxe-table vue 表格复选框多选数据,实现快捷键 Shift 批量选择功能

vxe-table vue 表格复选框多选数据&#xff0c;实现快捷键 Shift 批量选择功能 查看官网&#xff1a;https://vxetable.cn 效果 代码 通过 checkbox-config.isShift 启用批量选中,启用后按住快捷键和鼠标批量选取 <template><div><vxe-grid v-bind"gri…...