掌握Golang,无需依赖注入:轻松实现类似JAVA Spring注解的DI和AOP实践指南

更新:11-02 神话故事 我要投稿 纠错 投诉

大家好,掌握Golang,无需依赖注入:轻松实现类似JAVA Spring注解的DI和AOP实践指南相信很多的网友都不是很明白,包括也是一样,不过没有关系,接下来就来为大家分享关于掌握Golang,无需依赖注入:轻松实现类似JAVA Spring注解的DI和AOP实践指南和的一些知识点,大家可以关注收藏,免得下次来找不到哦,下面我们开始吧!

尽管社区已经有很多基于运行时的依赖注入,但Go 官方更推荐的玩法其实还是基于代码生成和静态分析。例如,wire 是Google 提供的依赖注入实现。

不过我认为wire的可用性还存在一个用户体验问题,就是需要额外维护wire.Set相关语句,比如:

要使用以下材料组装类似以下目标的结构,

类型StructA 结构{}

类型StructB 结构体{

接口C

}

类型StructC 结构体{

结构A

}

func (StructC) Foo() {}

类型InterfaceC接口{

富()

}

类型目标结构体{

结构A

结构B

接口C

}您必须提供附加声明:

变量(

_Set=线.NewSet(

线.Struct(new(StructA), "*"),

线.Struct(new(StructB), "*"),

线.Bind(new(InterfaceC), new(*StructC)),

线.Struct(new(StructC), "*"),

线.Struct(新(目标), "*"),

)此声明需要开发者额外维护。我想这也是电线无法在企业广泛普及的一个重要原因。

由于用户的对象声明和关系声明将在空间上分离,因此核心交互体验受到损害。甚至同一个对象的逻辑也需要维护在不同的代码文件中。

即使使用各种额外的中间wire.NewSet来组合,也没有办法完全优化这种体验。

可以参考JAVA Spring的交互设计。用户只需要在对象上添加注解即可完成声明依赖注入关系的工作。

在笔者之前的工作中,我在团队内部维护和推广了一个工具,可以使用类似于Spring的注解自动生成依赖注入语句。该工具使Wire 非常易于使用。

因此,团队已成功将依赖注入模型实现到几乎所有Golang项目中,极大提升了团队的代码质量和架构设计能力。

经过多年的积累和其他功能的整合,这个工具的开源版本是Gozz。 Gozz提供的wire插件将有效提高用户使用wire的体验和上手难度:

基本原理是:通过对注解和注解对象上下文进行额外的语法分析,可以直接推断出被注入对象的注入方法和注入参数,然后由依赖注入框架直接生成注入语句。

比如上面我们刚刚提到的例子,使用Gozz之后,可以直接删除各种手动维护的wire.Set。

相反,只需在代码中添加注释即可:

//+zz:wire

类型StructA 结构{}

//+zz:wire

类型StructB 结构体{

接口C

}

//+zz:wire:bind=接口C

类型StructC 结构体{

结构A

}

func (StructC) Foo() {}

typeInterfaceC 接口{

富()

}

//+zz:wire:inject=./

类型目标结构体{

结构A

结构B

接口C

}上面出现的两个选项的意思是:

bind表示绑定接口

ject表示生成目标函数Injector以及该对象生成的文件地址

执行gozz run -p "wire" ${filename} 后

你会发现使用wire时需要添加的所有额外的东西都已经生成了,并且wire会自动为你执行。

整个过程只需要几个注解和一条命令,就可以得到如下完整的依赖注入功能:

func Initialize_Target() (*目标, func(), 错误) {

结构A :=结构A{}

结构C :=结构C{

结构A:结构A,

}

结构B :=结构B{

接口C:结构C,

}

目标:=目标{

结构A:结构A,

结构B: 结构B,

接口C:结构C,

}

返回目标,func() {

}, 无

}除了自动依赖注入之外,Gozz 还可以在依赖注入中进行AOP,自动生成接口的动态代理。

例如,在下面的示例中,Interface 绑定到两种类型,其中一种类型具有aop 选项。

最终的Target 需要三个Interface 来构造,尽管它们实际上都是同一类型的别名。

类型实现结构{}

//+zz:wire:bind=InterfaceX

//+zz:wire:bind=InterfaceX2:aop

类型接口接口{

Foo(ctx context.Context, param int) (结果int, err 错误)

Bar(ctx context.Context, param int) (结果int, err 错误)

}

类型InterfaceXInterface

typeInterfaceX2 接口

//+zz:wire:注入=/

类型目标结构体{

界面

接口X

接口X2

}

func (实现) Foo(ctx context.Context, param int) (result int, err error) {

返回

}

func (实现) Bar(ctx context.Context, param int) (result int, err error) {

返回

}通过执行gozz run -p "wire" ./${filename}

将生成以下注入。你会发现InterfaceX2的注入会被wire02_impl_aop_InterfaceX2代替

自动生成的结构

//由Wire 生成的代码。请勿编辑。

//go:生成go 运行github.com/google/wire/cmd/wire

//+构建!wireinject

包线02

//来自wire_zinject.go:的注入器

//github.com/go-zing/gozz-doc-examples/wire02.Target

func Initialize_Target() (*目标, func(), 错误) {

实施:=实施{}

wire02_impl_aop_InterfaceX2 :=_impl_aop_InterfaceX2{

_aop_InterfaceX2: 实现,

}

目标:=目标{

Interface:实现,

InterfaceX:实现,

InterfaceX2:wire02_impl_aop_InterfaceX2,

}

返回目标,func() {

}, 无

它的定义可以在wire_zzaop.go中看到,另一个生成的问题:

类型_aop_interceptor 接口{

拦截(v接口{},名称字符串,参数,结果[]接口{})(func(),bool)

}

//接口X2

类型(

_aop_InterfaceX2 接口X2

_impl_aop_InterfaceX2 结构体{ _aop_InterfaceX2 }

func (i _impl_aop_InterfaceX2) Foo(p0 context.Context, p1 int) (r0 int, r1 error) {

如果t, x :=i._aop_InterfaceX2.(_aop_interceptor); x {

如果向上,则ok :=t.Intercept(i._aop_InterfaceX2, "Foo",

[]接口{}{p0, p1},

[]接口{}{r0, r1},

);向上!=零{

推迟()

} 否则如果!ok {

返回

}

}

返回i._aop_InterfaceX2.Foo(p0, p1)

}

func (i _impl_aop_InterfaceX2) Bar(p0 context.Context, p1 int) (r0 int, r1 error) {

如果t, x :=i._aop_InterfaceX2.(_aop_interceptor); x {

如果向上,则ok :=t.Intercept(i._aop_InterfaceX2, "Bar",

[]接口{}{p0, p1},

[]接口{}{r0, r1},

);向上!=零{

推迟()

} 否则如果!ok {

返回

}

}

返回i._aop_InterfaceX2.Bar(p0, p1)

简而言之,它通过实现所有原来的Interface方法,为原来的绑定调用实现了一层代理封装,并且可以通过代理提供所有参数和返回值的指针,以及调用的原始对象和方法名封装。

只需一些指针断言和接口操作,我们实际上可以:

自定义函数调用中的前后逻辑,获取实际调用者和调用方法名,替换函数参数和返回值,无需经过实际调用者,直接终止调用。通过这些功能,我们可以实现:

检查返回值错误,自动打印错误堆栈和调用信息,自动注入日志,链接跟踪,隐藏点报告等。检查授权状态和访问权限。自动缓存调用参数和返回值。检查或替换context.Context,添加超时或检查中断。这个功能也是目前社区中大多数依赖注入框架无法实现的。使用Gozz只需要添加一个选项aop即可。

事实上,gozz在运行时工具库gozz-kit中也提供了工具来帮助你生成这种关系依赖图:

例如,上面例子的运行时依赖实际上是:

test.png的最后一个例子将展示gozz-wire强大的兼容性和推理能力:

注入值对象使用值对象将接口引用类型绑定为结构体使用指定函数提供注入类型使用结构体字段值进行注入使用集合对注入进行分组使用额外的原生wire.NewSet //go:generate gozz run - p"电线"./

//提供值和接口值

//+zz:wire:bind=io.Writer:aop

//+zz:wire

var Buffer=bytes.Buffer{}

//提供引用类型

//+zz:wire

类型空字符串空字符串

类型nullString sql.NullString

//使用provider函数提供引用的类型别名

//+zz:wire

类型字符串=字符串

func ProvideString() 字符串{

返回""

}

//从隐式类型提供值

//+zz:wire

var 布尔=false

//+zz:wire:注入=/

类型目标结构体{

缓冲区*bytes.Buffer

作家.作家

空字符串空字符串

整型

}

//原点线组

//+zz:wire

var Set=wire.NewSet(wire.Value(Int))

变量整数=0

//模拟集注入器

//+zz:wire:inject=/:set=mock

类型mockString sql.NullString

//模拟设置字符串

//从函数提供类型

//+zz:wire:set=模拟

func MockString() 字符串{

返回“模拟”

}

//模拟集结构类型提供字段

//+zz:wire:set=mock:field=*

类型MockConfig 结构{ Bool bool }

//模拟设置值

//+zz:wire:set=模拟

var mock=MockConfig{Bool: true} 其实这样复杂的注入场景是可以完美处理的:

//github.com/go-zing/gozz-doc-examples/wire03.Target

func Initialize_Target() (*目标, func(), 错误) {

缓冲区:=_wireBufferValue

wire03_aop_io_Writer :=_wireBytesBufferValue

wire03_impl_aop_io_Writer :=_impl_aop_io_Writer{

_aop_io_Writer:wire03_aop_io_Writer,

}

字符串2 :=提供字符串()

布尔2 :=_wireBoolValue

线03空字符串:=空字符串{

字符串:字符串2,

有效:布尔2,

}

int2 :=_wireIntValue

目标:=目标{

缓冲区:缓冲区,

Writer:wire03_impl_aop_io_Writer,

NullString: 线03NullString,

int: int2,

}

返回目标,func() {

}, 无

}

变量(

_wireBufferValue=缓冲区

_wireBytesBufferValue=缓冲区

_wireBoolValue=布尔值

_wireIntValue=整数

//github.com/go-zing/gozz-doc-examples/wire03.mockString

func Initialize_mock_mockString() (mockString, func(), 错误) {

字符串2 :=模拟字符串()

模拟配置:=_wireMockConfigValue

bool2 :=模拟配置.Bool

wire03MockString :=模拟字符串{

字符串:字符串2,

有效:布尔2,

}

返回wire03MockString,func(){

}, 无

}

变量(

_wireMockConfigValue=模拟

)当然,这些强大的能力部分归功于线材本身的卓越性能。 Gozz只是站在巨人的肩膀上。

以上实际上是Gozz提供的示例,可以在文档页面找到。

OK,关于掌握Golang,无需依赖注入:轻松实现类似JAVA Spring注解的DI和AOP实践指南和的内容到此结束了,希望对大家有所帮助。

用户评论

别悲哀

感觉这篇文章挺有意思,我一直好奇Golang有没有类似Java Spring那种依赖注入的机制。

    有18位网友表示赞同!

一别经年

最近在学习Golang,对这种方法挺感兴趣的。

    有9位网友表示赞同!

顶个蘑菇闯天下i

说手把手带我实现啊,那肯定要好好看看了!

    有13位网友表示赞同!

在哪跌倒こ就在哪躺下

Golang开发效率高,再搭配DI和AOP就更牛了吧?

    有16位网友表示赞同!

秒淘你心窝

不知道这种注解式的DI在性能上跟传统的实现方式比怎么样。

    有6位网友表示赞同!

各自安好ぃ

Java Spring依赖注入用到很广啊,这篇文章让我对Golang的潜力有了更深的了解。

    有8位网友表示赞同!

焚心劫

我以前没接触过Golang的框架,期待学习新东西!

    有16位网友表示赞同!

ˉ夨落旳尐孩。

AOP也是一个很好的提高代码可维护性的手段,在Golang里实现肯定很方便。

    有10位网友表示赞同!

你tm的滚

Go语言本身简洁明了,能用注解的方式实现DI和AOP真是太棒了。

    有13位网友表示赞同!

淡抹烟熏妆丶

这篇文章能不能详细讲解一下 GoLang AOP的具体原理?

    有7位网友表示赞同!

限量版女汉子

学习新技能总是充满乐趣,期待看看这个方法!

    有7位网友表示赞同!

如梦初醒

Golang越来越强大! 这种依赖注入的方式是不是可以应用到很多场景里?

    有15位网友表示赞同!

凉月流沐@

Spring已经很成熟了,不知道Golang里的 DI 和 AOP 能实现到什么程度?

    有8位网友表示赞同!

闷骚闷出味道了

Go语言现在越来越流行,这篇文章应该对很多开发者很有帮助

    有16位网友表示赞同!

滴在键盘上的泪

分享一下你对Go语言AOP应用的看法吗?

    有6位网友表示赞同!

寂莫

我也在学习Golang,这种手把手教程太棒了!

    有14位网友表示赞同!

话少情在

期待看到文章,希望可以学到一些实用的知识。

    有16位网友表示赞同!

一样剩余

aprender Go 应该会有很多乐趣,这篇文章就很有帮助了。

    有13位网友表示赞同!

执妄

现在用golang来做软件开发感觉越来越不错了!

    有18位网友表示赞同!

空谷幽兰

Go语言的 DI 和 AOP 实现看起来非常清晰简单!

    有19位网友表示赞同!

【掌握Golang,无需依赖注入:轻松实现类似JAVA Spring注解的DI和AOP实践指南】相关文章:

1.蛤蟆讨媳妇【哈尼族民间故事】

2.米颠拜石

3.王羲之临池学书

4.清代敢于创新的“浓墨宰相”——刘墉

5.“巧取豪夺”的由来--米芾逸事

6.荒唐洁癖 惜砚如身(米芾逸事)

7.拜石为兄--米芾逸事

8.郑板桥轶事十则

9.王献之被公主抢亲后的悲惨人生

10.史上真实张三丰:在棺材中竟神奇复活

上一篇:揭秘用户行为:深度解析个性化体验的关键要素 下一篇:如何获取京东大额优惠券?揭秘优惠券抢购攻略