Go语言实现依赖注入

news2024/9/21 18:52:48

文章目录

  • 前言
  • 依赖注入是什么
  • 依赖注入的好处是什么
  • 结构图
  • 应用程序上下文接口
  • 上下文管理器
  • 暴露的功能
  • 使用示例
  • 最后

前言

你好,我是醉墨居士,欢迎来到我的博客,今天带领大伙使用Go语言实现依赖自动注入,我们不会使用其它的第三方库,项目核心代码不到100行,是Go语言初学者难得的精简项目

依赖注入是什么

依赖注入(Dependency Injection,简称DI)是一种设计模式,用于实现控制反转(Inversion of Control,简称IoC)原则。它的核心思想是将对象的依赖关系从内部管理转移到外部管理,从而降低对象之间的耦合度,提高代码的灵活性和可测试性

依赖注入的好处是什么

降低耦合度:通过将依赖关系从对象内部转移到外部,可以降低对象之间的耦合度。这样,对象只需要知道它需要什么,而不需要知道如何获取这些依赖

提高可测试性:依赖注入使得单元测试变得更加容易。在测试时,可以轻松地替换掉真实的依赖对象,使用模拟对象(Mock Object)或存根(Stub)来进行测试,从而隔离被测试代码

增强可维护性:由于依赖关系被明确地定义和管理,代码的可读性和可维护性得到了提高。当需要修改依赖关系时,只需要在配置或注入点进行修改,而不需要修改对象内部的代码

促进代码重用:依赖注入使得组件可以更容易地在不同的上下文中重用。因为组件不直接创建和管理自己的依赖,所以它们可以在不同的环境中被配置和使用

支持面向接口编程:依赖注入鼓励使用接口来定义依赖关系,而不是具体的实现类。这使得代码更加灵活,因为可以在运行时替换不同的实现,而不需要修改调用代码

简化对象创建:依赖注入容器(如Spring的ApplicationContext)可以自动管理对象的创建和生命周期,开发者不需要手动创建和管理这些对象,从而简化了代码

结构图

现在介绍一下我们依赖注入这个小项目的结构图
在这里插入图片描述

应用程序上下文接口

type BeanProvider func() reflect.Value

type ApplicationContext interface {
	Inject(provider BeanProvider, name string) error
	Autowise(obj any, name string) error
}

上下文管理器

type context struct {
	namedConatiner map[string]BeanProvider
	typedContainer 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 inject
		ty := provider().Type()

		if _, ok := c.typedContainer[ty];ok {
			return fmt.Errorf("inject: %v is ambiguous", ty)
		}

		c.typedContainer[ty] = provider
	} else {
		// name inject
		if _, 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 BeanProvider
	if name == "" {
		// type autowise
		provider = c.typedContainer[rt]
	} else {
		// name autowise
		provider = 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 main

import (
	"fmt"
	"github.com/zm50/injector"
	"reflect"
)

type TwoString struct {
	s1 *string
	s2 *string
}

func main() {
	// 通过类型注入变量,注入一个string类型的变量
	var injectString string = "醉墨居士"
	err := injector.Inject(injectString, "")
	if err != nil {
		panic(err)
	}
	// 通过类型装配变量,通过string类型自动装配变量
	var autowiseString string
	err = 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 string
	err = 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.s2
		return 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)
}

示例结果
在这里插入图片描述

最后

至此,各位我们已经一起完成了这个依赖注入的小项目
我是醉墨居士,我们下篇博客见

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1990486.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

域控安全:多种方式提取ntds.dit

ntdsutils.exe提取ntds.dit vssadmin提取ntds.dit vssown提取ntds.dit IFM ntds.dit: ntds.dit为AD的数据库,内容有域用户、域组、用户hash等信息,域控上的ntds.dit只有可以登录到域控的用户(如域管用户、DC本地管理员用户)可以…

HexView 刷写文件脚本处理工具-基本功能介绍(一)-基本界面

HexView主要可以显示不同文件格式的内容,包括Intel-HEX、Motorola S-record二进制文件或其他特定汽车制造商的文件格式。此外,它还可以执行多种数据处理操作,如校验和计算、签名生成、数据加密/解密或压缩/解压缩,甚至重新排列文件…

ubuntu创建txt

点击模版 右键 输入下面代码 sudo gedit txt文档.txt 然后就可以右键新建文本文件了 解开权限 sudo chmod -R 777 /home/sjxy/CQ

探索下一代互联网协议:IPv6的前景与优势

探索下一代互联网协议:IPv6的前景与优势 文章目录 探索下一代互联网协议:IPv6的前景与优势**IPv6 的特点****IPv6的基本首部****IPv6的地址****总结** 互联网的核心协议:从IPv4到IPv6 互联网的核心协议IP(Internet Protocol&#…

【Nacos无压力源码领读】(一) Nacos 服务注册与订阅原理

本文将详细介绍 Nacos 客户端在启动时进行自动注册原理, 以及Nacos服务器是如何处理客户端的注册与订阅请求的; 本文会附带源码解读, 但不会死抠每一行代码, 主要是梳理整个流程, 过程中的关键步骤, 都会由思维导图的形式展现出来; 如果在阅读过程中对文中提到的 SpringBoot …

Comsol 弧形声学换能器声聚焦仿真

弧形声学换能器声聚焦是指将声波能量集中在弧形声学换能器的特定区域内,以实现更强的声场强度和分辨率。声聚焦在许多应用中非常有用,包括医学超声成像、声纳、声波聚焦破碎等领域。 弧形声学换能器的设计和优化可以通过以下几个因素来实现声聚焦&#…

【网站项目】SpringBoot743中珠商城仓库管理系统

🙊作者简介:拥有多年开发工作经验,分享技术代码帮助学生学习,独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。🌹赠送计算机毕业设计600个选题excel文件,帮助大学选题。赠送开题报告模板&#xff…

A股破新低后震荡盘整,后市如何演绎?

今天的A股,让人按耐不住了,你们知道是为什么吗?盘面上出现1个重要信号,一起来看看: 1、今天两市冲高回落,主力压盘、故意洗筹码的意图就更加明显了,一定要拿住!地板上卖出的&#xf…

《刚刚问世》系列初窥篇-Java+Playwright自动化测试-4-启动浏览器-基于Maven(详细教程)

1.简介 上一篇文章,宏哥已经在搭建的java项目环境中添加jar包实践了如何启动浏览器,今天就在基于maven项目的环境中给小伙伴们或者童鞋们演示一下如何启动浏览器。 2.eclipse中新建maven项目 1.依次点击eclipse的file - new - other ,如下…

初阶数据结构5 排序

排序 1. 排序概念及运用1.1 概念1.2运用1.3 常见排序算法 2. 实现常⻅排序算法2.1 插⼊排序2.1.1 直接插⼊排序2.1.2 希尔排序2.1.2.1 希尔排序的时间复杂度计算 2.2 选择排序2.2.1 直接选择排序2.2.2 堆排序 2.3 交换排序2.3.1冒泡排序2.3.2 快速排序2.3.2.1 hoare版本2.3.2.2…

学习c#-4语句 ,条件,循环

代码: string name "小赵"; //条件判断 if (name "小赵") { Console.WriteLine("我是小赵"); } else { Console.WriteLine("我不是小赵"); } // switch条件判断 switch (name) { case "小…

【letcode-c++】283.移动零

一、题目 二、分析 题目让我们不能复制,只能在数组中交换移动。那么数组中的移动,比较方便的是前后两个元素交换,并且非零元素相对位置不变,那就考虑0和它后面的非0元素进行交换,进一步思考,除了单个0移动…

双指针习题

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

1.3 C 语言入门实战:从 Hello World 到基础语法解析

目录 1 程序的作用 2 初识 Hello World 2.1 新建项目 2.2 程序解释 3 printf 基本使用 4 单行与多行注释 4.1 单行注释 4.2 多行注释 4.3 嵌套错误 5 项目在磁盘上的位置 1 程序的作用 如下图所示,我们编写了一个可以做加法的程序,我们给程序…

聚鼎科技:装饰画做起来真的难吗

在艺术的殿堂中,装饰画以其独特的魅力占据一席之地。不少人对制作装饰画心生向往,却犹豫于其实操难度。今天,让我们一同揭开装饰画制作的神秘面纱,探讨其背后的秘密。 装饰画的创作并非高不可攀,它更像是一场与色彩和形…

OpenAI API推出结构化输出功能

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗?订阅我们的简报,深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同,从行业内部的深度分析和实用指南中受益。不要错过这个机会,成为AI领…

win7系统利用定时启动+脚本实现MySQL文件自动备份

前言 最近接到项目,数据量不大但对运行数据的安全性要求极高,为避免因不可抗拒因素导致的数据丢失,选择机械硬盘作为数据存储盘,并使用脚本方式对文件进行备份 一、脚本 下面为自动备份文件的 脚本,可根据自身情况进…

(javaweb)Tomcat的入门和使用

开发好的web应用部署在Tomcat服务器上:打开浏览器直接访问到部署在Tomcat上的应用程序

CLEFT 基于高效大语言模型和快速微调的语言-图像对比学习

CLEFT: Language-Image Contrastive Learning with Efficient Large Language Model and Prompt Fine-Tuning github.com paper CLEFT是一种新型的对比语言图像预训练框架,专为医学图像而设计。它融合了医学LLM的预训练、高效微调和提示上下文学习,展…

什么是报表?分析报表在零售行业中的应用,并推荐“免费高质量”的报表工具

随着业务数据洪流般的激增,数据孤岛现象严重、处理流程冗长低效,以及报表管理体系的碎片化等问题,正悄然成为阻碍企业持续发展的绊脚石。 在此背景下,报表工具的角色变得愈发重要,它不仅是企业整合散落数据、加速工作…