分解uber依赖注入库dig-使用篇
golang的依赖注入库非常的少,好用的更是少之又少,比较好用的目前有两个
本系列分几部分,先对dig进行分析,第一篇介绍dig
的使用,第二篇再从源码来剖析他是如何通过返射实现的的依赖注入的,后续会介绍fx 的使用和实现原理。
dig主要的思路是能过Provider
将不同的函数注册到容器内,一个函数可以通过参数来声明对其他函数返回值的依赖。在Invoke
的时候才会真正的去调用容器内相应的Provider
方法。
dig
还提供了可视化的方法Visualize
用于生成dot
有向图代码,更直观的观察依赖关系,关于dot
的基本语法,可以查看帖子dot 语法总结
使用的dig
版本为1.11.0-dev
,帖子所有的代码都在github
上,地址:fx_dig_adventure
简单使用
func TestSimple1(t *testing.T) {
type Config struct {
Prefix string
}
c := dig.New()
err := c.Provide(func() (*Config, error) {
return &Config{Prefix: "[foo] "}, nil
})
if err != nil {
panic(err)
}
err = c.Provide(func(cfg *Config) *log.Logger {
return log.New(os.Stdout, cfg.Prefix, 0)
})
if err != nil {
panic(err)
}
err = c.Invoke(func(l *log.Logger) {
l.Print("You've been invoked")
})
if err != nil {
panic(err)
}
}
输出
[foo] You've been invoked
可以生成dot
图,来更直观的查看依赖关系
b := &bytes.Buffer{}
if err := dig.Visualize(c, b); err != nil {
panic(err)
}
fmt.Println(b.String())
输出
digraph {
rankdir=RL;
graph [compound=true];
subgraph cluster_0 {
label = "main";
constructor_0 [shape=plaintext label="main.func1"];
"*main.Config" [label=<*main.Config>];
}
subgraph cluster_1 {
label = "main";
constructor_1 [shape=plaintext label="main.func2"];
"*log.Logger" [label=<*log.Logger>];
}
constructor_1 -> "*main.Config" [ltail=cluster_1];
}
可以看到 func2
返回的参数为Log
依赖 func1
返回参数 Config
。dot 语法总结
展示出来:
命名参数--多个返回相同类型的Provide
如果Provide
里提供的函数,有多个函数返回的数据类型是一样的怎么处理?比如,我们的数据库有主从两个连接库,怎么进行区分?
dig
可以将Provide
命名以进行区分
我们可以直接在Provide
函数里使用dig.Name
,为相同的返回类型设置不同的名字来进行区分。
func TestName1(t *testing.T) {
type DSN struct {
Addr string
}
c := dig.New()
p1 := func() (*DSN, error) {
return &DSN{Addr: "primary DSN"}, nil
}
if err := c.Provide(p1, dig.Name("primary")); err != nil {
t.Fatal(err)
}
p2 := func() (*DSN, error) {
return &DSN{Addr: "secondary DSN"}, nil
}
if err := c.Provide(p2, dig.Name("secondary")); err != nil {
t.Fatal(err)
}
type DBInfo struct {
dig.In
PrimaryDSN *DSN `name:"primary"`
SecondaryDSN *DSN `name:"secondary"`
}
if err := c.Invoke(func(db DBInfo) {
t.Log(db.PrimaryDSN)
t.Log(db.SecondaryDSN)
}); err != nil {
t.Fatal(err)
}
}
输出
&{primary DSN}
&{secondary DSN}
dot
图
这样做并不通用,一般我们是有一个结构体来实现,dig
也有相应的支持,用一个结构体嵌入dig.out
来实现,
相同类型的字段在tag
里设置不同的name
来实现
func TestName2(t *testing.T) {
type DSN struct {
Addr string
}
c := dig.New()
type DSNRev struct {
dig.Out
PrimaryDSN *DSN `name:"primary"`
SecondaryDSN *DSN `name:"secondary"`
}
p1 := func() (DSNRev, error) {
return DSNRev{PrimaryDSN: &DSN{Addr: "Primary DSN"},
SecondaryDSN: &DSN{Addr: "Secondary DSN"}}, nil
}
if err := c.Provide(p1); err != nil {
t.Fatal(err)
}
type DBInfo struct {
dig.In
PrimaryDSN *DSN `name:"primary"`
SecondaryDSN *DSN `name:"secondary"`
}
inv1 := func(db DBInfo) {
t.Log(db.PrimaryDSN)
t.Log(db.SecondaryDSN)
}
if err := c.Invoke(inv1); err != nil {
t.Fatal(err)
}
}
输出
&{primary DSN}
&{secondary DSN}
dot
图
和上面的不同之处就是一个function
返回了两个相同类型的字段。
组--把同类型的参数放在一个slice里
如果有很多相同类型的返回参数,可以把他们放在同一个slice
里,和命名方式一样,有两种使用方式
第一种在调用Provide
时直接使用dig.Group
func TestGroup1(t *testing.T) {
type Student struct {
Name string
Age int
}
NewUser := func(name string, age int) func() *Student {
return func() *Student {
return &Student{name, age}
}
}
container := dig.New()
if err := container.Provide(NewUser("tom", 3), dig.Group("stu")); err != nil {
t.Fatal(err)
}
if err := container.Provide(NewUser("jerry", 1), dig.Group("stu")); err != nil {
t.Fatal(err)
}
type inParams struct {
dig.In
StudentList []*Student `group:"stu"`
}
Info := func(params inParams) error {
for _, u := range params.StudentList {
t.Log(u.Name, u.Age)
}
return nil
}
if err := container.Invoke(Info); err != nil {
t.Fatal(err)
}
}
输出
jerry 1
tom 3
生成dot
图
或者使用结构体嵌入dig.Out
来实现,tag
里要加上了group
标签
type Rep struct {
dig.Out
StudentList []*Student `group:"stu,flatten"`
}
这个flatten
的意思是,底层把组表示成[]*Student
,如果不加flatten
会表示成[][]*Student
完整示例
func TestGroup2(t *testing.T) {
type Student struct {
Name string
Age int
}
type Rep struct {
dig.Out
StudentList []*Student `group:"stu,flatten"`
}
NewUser := func(name string, age int) func() Rep {
return func() Rep {
r := Rep{}
r.StudentList = append(r.StudentList, &Student{
Name: name,
Age: age,
})
return r
}
}
container := dig.New()
if err := container.Provide(NewUser("tom", 3)); err != nil {
t.Fatal(err)
}
if err := container.Provide(NewUser("jerry", 1)); err != nil {
t.Fatal(err)
}
type InParams struct {
dig.In
StudentList []*Student `group:"stu"`
}
Info := func(params InParams) error {
for _, u := range params.StudentList {
t.Log(u.Name, u.Age)
}
return nil
}
if err := container.Invoke(Info); err != nil {
t.Fatal(err)
}
}
输出
jerry 1
tom 3
生成dot
图
从dot
图可以看出有两个方法生成了Group: stu
需要注意的一点是,命名方式和组方式不能同时使用。
可选参数
如果注册的方法返回的参数是可以为nil的,可以使用option
来实现
func TestOption1(t *testing.T) {
type Student struct {
dig.Out
Name string
Age *int `option:"true"`
}
c := dig.New()
if err := c.Provide(func() Student {
return Student{
Name: "Tom",
}
}); err != nil {
t.Fatal(err)
}
if err := c.Invoke(func(n string, age *int) {
t.Logf("name: %s", n)
if age == nil {
t.Log("age is nil")
} else {
t.Logf("age: %d", age)
}
}); err != nil {
t.Fatal(err)
}
}
输出
name: Tom
age is nil
dry run
如果我们只是想看一下依赖注入的整个流程是不是通的,可以通过dry run
来跑一下,他不会调用具体的函数,而是直接返回函数的返回参数的zero
值
func TestDryRun1(t *testing.T) {
// Dry Run
c := dig.New(dig.DryRun(true))
type Config struct {
Prefix string
}
err := c.Provide(func() (*Config, error) {
return &Config{Prefix: "[foo] "}, nil
})
if err != nil {
panic(err)
}
err = c.Provide(func(cfg *Config) *log.Logger {
return log.New(os.Stdout, cfg.Prefix, 0)
})
if err != nil {
panic(err)
}
err = c.Invoke(func(l *log.Logger) {
l.Print("You've been invoked")
})
if err != nil {
panic(err)
}
}
运行代码不会有任何输出。
分解uber依赖注入库dig-使用篇的更多相关文章
- 分解uber依赖注入库dig-源码分析
上一篇帖子 分解uber依赖注入库dig-使用篇 把如何使用dig进行代码示例说明,这篇帖子分析dig的源码,看他是如何实现依赖注入的. dig实现的中心思想:所有传入Provide的函数必须要有除e ...
- Google 开源的依赖注入库,比 Spring 更小更快!
Google开源的一个依赖注入类库Guice,相比于Spring IoC来说更小更快.Elasticsearch大量使用了Guice,本文简单的介绍下Guice的基本概念和使用方式. 学习目标 概述: ...
- Json.NET提供依赖注
Json.NET提供依赖注 [.NET] 使用Json.NET提供依赖注入功能(Dependence Injection) 前言 在一些小型项目的开发情景里,系统不需要大型DI Framework所提 ...
- 任务21 :了解ASP.NET Core 依赖注入,看这篇就够了
DI在.NET Core里面被提到了一个非常重要的位置, 这篇文章主要再给大家普及一下关于依赖注入的概念,身边有工作六七年的同事还个东西搞不清楚.另外再介绍一下.NET Core的DI实现以及对实例 ...
- spring依赖注入时,什么时候会创建代理类
spring 依赖注入时,什么时候会创建代理类 有的会创建代理类来替代目标类的实现.比如有事务注解啊 有的直接使用目标类.啥拦截配置都没有.
- 了解ASP.NET Core 依赖注入,看这篇就够了 于2017年11月6日由jesseliu发布
DI在.NET Core里面被提到了一个非常重要的位置, 这篇文章主要再给大家普及一下关于依赖注入的概念,身边有工作六七年的同事还个东西搞不清楚.另外再介绍一下.NET Core的DI实现以及对实例 ...
- 了解ASP.NET Core 依赖注入,看这篇就够了
DI在.NET Core里面被提到了一个非常重要的位置, 这篇文章主要再给大家普及一下关于依赖注入的概念,身边有工作六七年的同事还个东西搞不清楚.另外再介绍一下.NET Core的DI实现以及对实例 ...
- 【开源项目7】Android视图注入库:butterknife
介绍 ButterKnife通过@InjectView和视图的ID注解的变量去找到并自动转换为你布局上相应的布局视图. class ExampleActivity extends Activity { ...
- spring 依赖注入时,什么时候会创建代理类
问题来源 以前一直有个疑惑,为什么我创建的controller中注入的service类有时候是代理类,有时候是普通javabean,当时能力不够,现在已经有了点经验就大胆跟了跟源码,看看到底咋回事. ...
随机推荐
- 剑指 Offer 41. 数据流中的中位数 + 堆 + 优先队列
剑指 Offer 41. 数据流中的中位数 Offer_41 题目详情 题解分析 本题使用大根堆和小根堆来解决这个寻找中位数和插入中位数的问题. 其实本题最直接的方法是先对数组进行排序,然后取中位数. ...
- 翻译:《实用的Python编程》04_02_Inheritance
目录 | 上一节 (4.1 类) | 下一节 (4.3 特殊方法) 4.2 继承 继承(inheritance)是编写可扩展程序程序的常用手段.本节对继承的思想(idea)进行探讨. 简介 继承用于特 ...
- 数据库索引知识到MySQL InnoDB
前言 本文聊聊数据库中的索引,涉及索引基础数据结构,分类.以及使用索引的缺点. 索引就像一本书的目录,商场里面各个楼层指示图,让我们不需要自己无目的的找,而是能够很快的找到自己想要的. 1. 索引的基 ...
- WPF 基础 - 事件
1. 前言 WPF 事件的路由环境是 UI 组件树,先来看看这棵树. 1.1 Logical Tree 和 Visual Tree WPF 中的树有两种,一颗是逻辑树,另一颗也是逻辑树. 开玩笑,WP ...
- go beego框架与python实现数据交互
目标:将go中一个二维数组传到pythone中处理并返回.难点在于数据格式的转换. go代码如下: package main import ( "os/exec" "sy ...
- hexo+github 博客绑定域名
关于博客的搭建分为以下几步: 申请域名可以在万维网上申请一个自己的独特域名,本博客的域名即为zhengwei.xyz. 域名解析域名申请成功后继续在万维网上进行操作,进入管理自己的域名界面,在要解析的 ...
- 二叉树的建立与遍历(c语言)入门
树其实在本质上就是一对多,链表就是一对一. 二叉树的建立: 这里的代码采用的是最粗暴的创建方法,无实际用处.但初次学习二叉树可以通过这个创建方法更好的理解二叉树. 二叉树的遍历: 遍历在大体上分为递归 ...
- IPFS是什么?IPFS与Filecoin有什么关系?
Filecoin 基于 IPFS 的去中心化存储网络,是 IPFS 上唯一的激励层,是一个基于区块链技术发行的通证.Filecoin 翻译过来就是文件币,简称为 FIL. 在 FIlecoin 网络中 ...
- 利用浏览器favicon的缓存机制(F-Cache)生成客户端浏览器唯一指纹
利用浏览器favicon的缓存机制(F-Cache)生成客户端浏览器唯一指纹 首先介绍下: 这个技术出自 UIC论文:https://www.cs.uic.edu/~polakis/papers/so ...
- Android Studio 如何在TextView中设置图标并按需调整图标大小
•任务 相信大家对这张图片都不陌生,没错,就是 QQ动态 向我们展示的界面. 如何实现呢? •添加文字并放入图标 新建一个 Activity,取名为 QQ,Android Studio 自动为我们生成 ...