后面更新采用肝一篇go官方源码,肝一篇框架源码形式,伤肝->护肝,如果你喜欢就点个赞吧。官方源码比较伤肝(* ̄︶ ̄)。
1依赖注入初识依赖注入来自开源项目Grafana 的源码,该项目框架采用依赖注入方式对各结构体字段进行赋值。DI 依赖注入包为https://github.com/facebookarchive/inject,后面我会专门介绍这个包依赖注入的原理。不过今天的主角是它:https://github.com/uber-go/fx。
该包统一采用构造函数Newxx()形式进行依赖注入,对比与inject ,我认为比较好的点:
采用Newxx()形式显示声明,更利于构造单元测试采用Newxx()能更直观,表明我这个对象需要什么,inject 后面是tag,与结构体字段混在一起。有时我们需要另一个对象,但不希望它出现在结构体字段里面来看看我们自己给结构体赋值怎么做
假设我们一个对象需要b对象赋值,b 对象需要c 对象赋值,那么我们该这么写
package main
type A struct {
B* B
}
func NewA( b *B)* A {
return &A{B: b}
}
type B struct {
C *C
}
func NewB(c * C)*B {
return &B{c}
}
type C struct {
}
func NewC()*C {
return &C{}
}
func main() {
//我们需要一个a
b:=NewB(NewC())
a:=NewA(b)
_=a
PrintA(a)
}
func PrintA(a* A) {
fmt.Println(*a)
}
如果选择依赖注入呢,实际情况可能更加复杂,如果有更好的方式,那么一定是DI 依赖注入了
package main
import (
"fmt"
"go.uber.org/fx"
)
type A struct {
B* B
}
func NewA( b *B)* A {
return &A{B: b}
}
type B struct {
C *C
}
func NewB(c * C)*B {
return &B{c}
}
type C struct {
}
func NewC()*C {
return &C{}
}
func main() {
fx.New(
fx.Provide(NewB),
fx.Provide(NewA),
fx.Provide(NewC),
fx.Invoke(PrintA),
)
}
func PrintA(a* A) {
fmt.Println(*a)
}
文章末尾有完整http项目实践例子,附上github地址:https://github.com/yangtaolirong/fx-demo,大家可以根据自己需求进行优化。
2使用 New()该函数时创建一个依赖注入实例
option 的结构
// An Option configures an App using the functional options paradigm
// popularized by Rob Pike. If you're unfamiliar with this style, see
// https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html.
type Option interface {
fmt.Stringer
apply(*App)
}
option 必须使用下面的几个方法进行生成,来看个demo
package main
import (
"context"
"go.uber.org/fx"
)
type Girl struct {
Name string
Age int
}
func NewGirl()*Girl {
return &Girl{
Name: "苍井",
Age: 18,
}
}
type Gay struct {
Girl * Girl
}
func NewGay (girl * Girl)*Gay {
return &Gay{girl}
}
func main() {
app:=fx.New(
fx.Provide(NewGay),
fx.Provide(NewGirl),
)
err:=app.Start(context.Background())
if err!=nil{
panic(err)
}
}
Provide()
该函数将被依赖的对象的构造函数传进去,传进去的函数必须是个待返回值的函数指针
fx.Provide(NewGay)
fx.Provide(NewGirl)
Invoke()该函数将函数依赖的对象作为参数传进函数然后调用函数
func main() {
invoke:= func(gay* Gay) {
fmt.Println(gay.Girl) //&{苍井 18}
}
app:=fx.New(
fx.Provide(NewGay),
fx.Provide(NewGirl),
fx.Invoke(invoke),
)
err:=app.Start(context.Background())
if err!=nil{
panic(err)
}
}
Supply()
该函数直接提供被依赖的对象。不过这个supply 不能提供一个接口
func main() {
invoke:= func(gay* Gay) {
fmt.Println(gay.Girl)
}
girl:=NewGirl() //直接提供对象
app:=fx.New(
fx.Provide(NewGay),
fx.Supply(girl),
fx.Invoke(invoke),
)
err:=app.Start(context.Background())
if err!=nil{
panic(err)
}
}
不能提供接口类型,比如我们使用Provide可以提供一个SayInterface类型的接口,该代码运行不会报错,但我们换成supply 以后就会有问题
type Girl struct {
Name string
Age int
}
func NewGirl()SayInterface {
return &Girl{
Name: "苍井",
Age: 18,
}
}
type Gay struct {
Girl * Girl
}
func (g* Girl)SayHello() {
fmt.Println("girl sayhello")
}
func NewGay (say SayInterface)*Gay {//此处能够正常获取到
return &Gay{}
}
type SayInterface interface {
SayHello()
}
func main() {
invoke:= func(gay *Gay) {
}
app:=fx.New(
fx.Provide(NewGirl),
fx.Provide(NewGay),
fx.Invoke(invoke),
)
err:=app.Start(context.Background())
if err!=nil{
panic(err)
}
}
通过supply 提供就会报错
func main() {
invoke:= func(gay *Gay) {
}
app:=fx.New(
fx.Supply(NewGirl()),
fx.Provide(NewGay),
fx.Invoke(invoke),
)
err:=app.Start(context.Background())
if err!=nil{
panic(err)
}
}
或者这种形式
func main() {
invoke:= func(gay *Gay) {
}
var girl SayInterface=&Girl{}
app:=fx.New(
fx.Supply(girl),
fx.Provide(NewGay),
fx.Invoke(invoke),
)
err:=app.Start(context.Background())
if err!=nil{
panic(err)
}
}
错误:
Failed: could not build arguments for function "main".main.func1 (D:/code/leetcode/fx.go:39): failed to build *
main.Gay: missing dependencies for function "main".NewGay (D:/code/leetcode/fx.go:29): missing type: main.SayIn
terface (did you mean *main.Girl?)
原因我会在后面分析,反正是识别成了结构体真正的类型而不是接口类型,平时在使用中,也是一个坑
Populate()该函数将通过容器内值外面的变量进行赋值
func main() {
invoke:= func(gay *Gay) {
}
var gay *Gay //定义一个对象,值为nil
app:=fx.New(
fx.Provide(NewGirl),
fx.Provide(NewGay),
fx.Invoke(invoke),
fx.Populate(&gay),//调用Populate,这里必须是指针,因为是通过*target 来给元素赋值的
)
fmt.Println(gay) //&{0xc00008c680},将NewGay返回的对象放进var定义的变量里面了
err:=app.Start(context.Background())
if err!=nil{
panic(err)
}
}
原理
将传进来的参数,换成函数,参数为target,函数结果为类似下面这种类型,最后转换成invoke类型进行调用
// Build a function that looks like:
//
// func(t1 T1, t2 T2, ...) {
// *targets[0] = t1
// *targets[1] = t2
// [...]
// }
//
下面是函数实现
// Populate sets targets with values from the dependency injection container
// during application initialization. All targets must be pointers to the
// values that must be populated. Pointers to structs that embed In are
// supported, which can be used to populate multiple values in a struct.
//
// This is most helpful in unit tests: it lets tests leverage Fx's automatic
// constructor wiring to build a few structs, but then extract those structs
// for further testing.
func Populate(targets ...interface{}) Option {
// Validate all targets are non-nil pointers.
targetTypes := make([]reflect.Type, len(targets))
for i, t := range targets {
if t == nil {
return invokeErr(fmt.Errorf("failed to Populate: target %v is nil", i+1))
}
rt := reflect.TypeOf(t)
if rt.Kind() != reflect.Ptr {
return invokeErr(fmt.Errorf("failed to Populate: target %v is not a pointer type, got %T", i+1, t))
}
targetTypes[i] = reflect.TypeOf(t).Elem()
}
// Build a function that looks like:
//
// func(t1 T1, t2 T2, ...) {
// *targets[0] = t1
// *targets[1] = t2
// [...]
// }
//
fnType := reflect.FuncOf(targetTypes, nil, false /* variadic */) //制造函数的类型
fn := reflect.MakeFunc(fnType, func(args []reflect.Value) []reflect.Value {//制造函数
for i, arg := range args {
reflect.ValueOf(targets[i]).Elem().Set(arg)
}
return nil
})
return Invoke(fn.Interface()) //invoke选项
}
reflect.FuncOf该函数作用是通过指定的参数类型和返回类型创造一个函数,共有3个参数,variadic代表是不是可选参数FuncOf(in, out []Type, variadic bool) Typereflect.MakeFunc代表按照什么函数类型制造函数,其中第二个参数是个回调函数,代表函数的传参值和返回值,意思是将函数传进来的参数值赋值给Populate传进来的值 Annotated http://fx.in
annotated提供高级功能,让相同的对象按照tag能够赋值到一个结构体上面,结构体必须内嵌http://fx.in
type Gay struct {
fx.In
Girl1 * Girl `name:"波多"`
Girl2 * Girl `name:"海翼"`
Girls []*Girl `group:"actor"`
}
func main() {
invoke:= func(gay Gay) {
fmt.Println(gay.Girl1.Name)//波多
fmt.Println(gay.Girl2.Name)//海翼
fmt.Println(len(gay.Girls),gay.Girls[0].Name)//1 杏梨
}
app:=fx.New(
fx.Invoke(invoke),
fx.Provide(
fx.Annotated{
Target: func() *Girl { return &Girl{Name: "波多"} },
Name: "波多",
},
fx.Annotated{
Target: func() *Girl { return &Girl{Name: "海翼"} },
Name: "海翼",
},
fx.Annotated{
Target: func() *Girl { return &Girl{Name: "杏梨"} },
Group: "actor",
},
),
)
err:=app.Start(context.Background())
if err!=nil{
panic(err)
}
}
不带tag的annotated,下面这种写法是可以的
type Gay struct {
fx.In
Girl1 * Girl
}
func main() {
invoke:= func(gay Gay) {
fmt.Println(gay.Girl1.Name)
}
app:=fx.New(
fx.Invoke(invoke),
fx.Provide(
fx.Annotated{
Target: func() *Girl { return &Girl{Name: "波多"} },
},
//下面不能再添加fx.Annotated,不能识别了,因为是匿名的
),
)
err:=app.Start(context.Background())
if err!=nil{
panic(err)
}
}
group写多个,用","分开
type Gay struct {
fx.In
Girl1[]* Girl `group:"actor"`
}
func main() {
invoke:= func(gay Gay) {
fmt.Println(len(gay.Girl1))
}
app:=fx.New(
fx.Invoke(invoke),
fx.Provide(
fx.Annotated{
Target: func() *Girl { return &Girl{Name: "波多"} },
Group: "actor,beauty",
},
),
)
err:=app.Start(context.Background())
if err!=nil{
panic(err)
}
}
错误的写法,Group和Name 是不能同时存在的
fx.Annotated{
Target: func() *Girl { return &Girl{Name: "波多"} },
Group: "actor,beauty",
Name:"波多"
},
当返回切片时,需要在group 后面加上flatten
func NewGirl()[]*Girl {
return []*Girl{{
Name: "苍井",
Age: 18,
}}
}
type Gay struct {
fx.In
Girl1 []* Girl `group:"actor"`
}
func main() {
invoke:= func(gay Gay) {
fmt.Println(gay)
}
app:=fx.New(
fx.Invoke(invoke),
fx.Provide(
fx.Annotated{
Target: NewGirl,
Group: "actor,flatten",
},
),)
err:=app.Start(context.Background())
if err!=nil{
panic(err)
}
}
fx.out
fx.out会将当前结构体的字段按名字输出,相当于
fx.Annotated{
Target: func() *Girl { return &Girl{Name: "海翼"} },
Name: "海翼",
},
//或者
fx.Annotated{
Target: func() *Girl { return &Girl{Name: "杏梨"} },
Group: "actor",
},
所以在另一个结构体写上http://fx.in 就能按名字接收到了
type Gay struct {
fx.Out
Girl1 * Girl `name:"波多"`
}
type Gay1 struct {
fx.Out
Girl1 * Girl `name:"仓井"`
}
type Man struct {
fx.In
Girl1 * Girl `name:"波多"`
Girl2 * Girl `name:"仓井"`
}
func NewGay()Gay {
return Gay{
Girl1:&Girl{Name: "波多"},
}
}
func NewGay1()Gay1 {
return Gay1{
Girl1:&Girl{Name: "仓井"},
}
}
func main() {
invoke:= func(man Man) {
fmt.Println(man.Girl1.Name)//波多
fmt.Println(man.Girl2.Name) //仓井
}
app:=fx.New(
fx.Invoke(invoke),
fx.Provide(
NewGay,NewGay1,
),)
err:=app.Start(context.Background())
if err!=nil{
panic(err)
}
}
源码解析
核心方法New
// New creates and initializes an App, immediately executing any functions
//创建和初始化app 实例,并且是立即执行注册和调用的
// registered via Invoke options. See the documentation of the App struct for
// details on the application's initialization, startup, and shutdown logic.
func New(opts ...Option) *App {
logger := fxlog.DefaultLogger(os.Stderr) //获取日志实例
app := &App{//创建app 实例
// We start with a logger that writes to stderr. One of the
// following three things can change this:
//
// - fx.Logger was provided to change the output stream
// - fx.WithLogger was provided to change the logger
// implementation
// - Both, fx.Logger and fx.WithLogger were provided
//
// The first two cases are straightforward: we use what the
// user gave us. For the last case, however, we need to fall
// back to what was provided to fx.Logger if fx.WithLogger
// fails.
log: logger,
startTimeout: DefaultTimeout, //启动超时时间
stopTimeout: DefaultTimeout, //停止超时时间
}
for _, opt := range opts {
opt.apply(app)//用opt 初始化app
}
// There are a few levels of wrapping on the lifecycle here. To quickly
// cover them:
//
// - lifecycleWrapper ensures that we don't unintentionally expose the
// Start and Stop methods of the internal lifecycle.Lifecycle type
// - lifecycleWrapper also adapts the internal lifecycle.Hook type into
// the public fx.Hook type.
// - appLogger ensures that the lifecycle always logs events to the
// "current" logger associated with the fx.App.
app.lifecycle = &lifecycleWrapper{ //初始生命周期函数
lifecycle.New(appLogger{app}),
}
var (
bufferLogger *logBuffer // nil if WithLogger was not used
// Logger we fall back to if the custom logger fails to build.
// This will be a DefaultLogger that writes to stderr if the
// user didn't use fx.Logger, and a DefaultLogger that writes
// to their output stream if they did.
fallbackLogger fxevent.Logger
)
if app.logConstructor != nil {
// Since user supplied a custom logger, use a buffered logger
// to hold all messages until user supplied logger is
// instantiated. Then we flush those messages after fully
// constructing the custom logger.
bufferLogger = new(logBuffer)
fallbackLogger, app.log = app.log, bufferLogger
}
app.container = dig.New( //创建container
dig.DeferAcyclicVerification(),
dig.DryRun(app.validate),
)
for _, p := range app.provides { //app.provides 通过opt 已经初始化了,所以这就是调用fx.Provide()里面的构造函数
app.provide(p)
}
frames := fxreflect.CallerStack(0, 0) // include New in the stack for default Provides
app.provide(provide{
Target: func() Lifecycle { return app.lifecycle }, //将app.lifecycle这个对象提供出去
Stack: frames,
})
//提供shutdowner,和dotGraph这两个实例
app.provide(provide{Target: app.shutdowner, Stack: frames})
app.provide(provide{Target: app.dotGraph, Stack: frames})
// If you are thinking about returning here after provides: do not (just yet)!
// If a custom logger was being used, we're still buffering messages.
// We'll want to flush them to the logger.
// If WithLogger and Printer are both provided, WithLogger takes
// precedence.
if app.logConstructor != nil {
// If we failed to build the provided logger, flush the buffer
// to the fallback logger instead.
if err := app.constructCustomLogger(bufferLogger); err != nil {
app.err = multierr.Append(app.err, err)
app.log = fallbackLogger
bufferLogger.Connect(fallbackLogger)
return app
}
}
// This error might have come from the provide loop above. We've
// already flushed to the custom logger, so we can return.
if app.err != nil {
return app
}
if err := app.executeInvokes(); err != nil { //执行调用
app.err = err
if dig.CanVisualizeError(err) {//如果错误可以可视化,就走下面逻辑打印
var b bytes.Buffer
dig.Visualize(app.container, &b, dig.VisualizeError(err))
err = errorWithGraph{
graph: b.String(),
err: err,
}
}
errorHandlerList(app.errorHooks).HandleError(err)
}
return app
}
下面将依次剖析这些方法
Optionnew 函数传进来的Option结构,必须要实现对app 初始化的方法apply(*App),要实现打印接口fmt.Stringer方法,现在做框架传配置几乎都采用这种套路了, 优雅的传配置。
// An Option configures an App using the functional options paradigm
// popularized by Rob Pike. If you're unfamiliar with this style, see
// https://commandcenter.blogspot.com/2014/01/self-referential-functions-and-design.html.
type Option interface {
fmt.Stringer
apply(*App)
}
app.provide
func (app *App) provide(p provide) {
if app.err != nil {
return
}
constructor := p.Target
if _, ok := constructor.(Option); ok {
app.err = fmt.Errorf("fx.Option should be passed to fx.New directly, "+
"not to fx.Provide: fx.Provide received %v from:\n%+v",
constructor, p.Stack)
return
}
var info dig.ProvideInfo
opts := []dig.ProvideOption{
dig.FillProvideInfo(&info),
}
defer func() {
var ev fxevent.Event
switch {
case p.IsSupply:
ev = &fxevent.Supplied{
TypeName: p.SupplyType.String(),
Err: app.err,
}
default:
outputNames := make([]string, len(info.Outputs))
for i, o := range info.Outputs {
outputNames[i] = o.String()
}
ev = &fxevent.Provided{
ConstructorName: fxreflect.FuncName(constructor),
OutputTypeNames: outputNames,
Err: app.err,
}
}
app.log.LogEvent(ev)
}()
//处理anotated类型,生成相应的选项opts
if ann, ok := constructor.(Annotated); ok {
switch {
case len(ann.Group) > 0 && len(ann.Name) > 0:
app.err = fmt.Errorf(
"fx.Annotated may specify only one of Name or Group: received %v from:\n%+v",
ann, p.Stack)
return
case len(ann.Name) > 0:
opts = append(opts, dig.Name(ann.Name))
case len(ann.Group) > 0:
opts = append(opts, dig.Group(ann.Group))
}
if err := app.container.Provide(ann.Target, opts...); err != nil {
app.err = fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %v", ann, p.Stack, err)
}
return
}
if reflect.TypeOf(constructor).Kind() == reflect.Func {
ft := reflect.ValueOf(constructor).Type()
for i := 0; i < ft.NumOut(); i++ {
t := ft.Out(i)
if t == reflect.TypeOf(Annotated{}) {
app.err = fmt.Errorf(
"fx.Annotated should be passed to fx.Provide directly, "+
"it should not be returned by the constructor: "+
"fx.Provide received %v from:\n%+v",
fxreflect.FuncName(constructor), p.Stack)
return
}
}
}
if err := app.container.Provide(constructor, opts...); err != nil {
app.err = fmt.Errorf("fx.Provide(%v) from:\n%+vFailed: %v", fxreflect.FuncName(constructor), p.Stack, err)
}
}
app.executeInvokes
该函数将会执行函数调用,fx.Inovke()添加invoke 函数调用
// Execute invokes in order supplied to New, returning the first error
// encountered.
func (app *App) executeInvokes() error {
// TODO: consider taking a context to limit the time spent running invocations.
for _, i := range app.invokes { //循环遍历invokes函数
if err := app.executeInvoke(i); err != nil {
return err
}
}
return nil
}
app.executeInvoke
//执行调用
func (app *App) executeInvoke(i invoke) (err error) {
fn := i.Target
fnName := fxreflect.FuncName(fn) //获取调用的函数名
//日志相关
app.log.LogEvent(&fxevent.Invoking{FunctionName: fnName})
defer func() {
app.log.LogEvent(&fxevent.Invoked{
FunctionName: fnName,
Err: err,
Trace: fmt.Sprintf("%+v", i.Stack), // format stack trace as multi-line
})
}()
//对fn 进行校验,如果还是Option类型,说明是错误了,报错
if _, ok := fn.(Option); ok {
return fmt.Errorf("fx.Option should be passed to fx.New directly, "+
"not to fx.Invoke: fx.Invoke received %v from:\n%+v",
fn, i.Stack)
}
return app.container.Invoke(fn) //执行容器的调用方法Invoke
}
dig.Container
container.Provide
该函数作用是将构造函数赋值给容器,在这之前还要做一系列检查
// Provide teaches the container how to build values of one or more types and
// expresses their dependencies.
//
// The first argument of Provide is a function that accepts zero or more
// parameters and returns one or more results. The function may optionally
// return an error to indicate that it failed to build the value. This
// function will be treated as the constructor for all the types it returns.
// This function will be called AT MOST ONCE when a type produced by it, or a
// type that consumes this function's output, is requested via Invoke. If the
// same types are requested multiple times, the previously produced value will
// be reused.
//
// In addition to accepting constructors that accept dependencies as separate
// arguments and produce results as separate return values, Provide also
// accepts constructors that specify dependencies as dig.In structs and/or
// specify results as dig.Out structs.
func (c *Container) Provide(constructor interface{}, opts ...ProvideOption) error {
ctype := reflect.TypeOf(constructor)
if ctype == nil { //构造函数不能为nil
return errors.New("can't provide an untyped nil")
}
if ctype.Kind() != reflect.Func { //构造函数必须是函数
return errf("must provide constructor function, got %v (type %v)", constructor, ctype)
}
var options provideOptions
for _, o := range opts {
o.applyProvideOption(&options) //如果有选项就应用选项
}
if err := options.Validate(); err != nil {
return err
}
//调用provide
if err := c.provide(constructor, options); err != nil {
return errProvide{
Func: digreflect.InspectFunc(constructor),
Reason: err,
}
}
return nil
}
provide
func (c *Container) provide(ctor interface{}, opts provideOptions) error {
n, err := newNode(
ctor,
nodeOptions{
ResultName: opts.Name,
ResultGroup: opts.Group,
},
) //创建1个node节点
if err != nil {
return err
}
//验证结果
keys, err := c.findAndValidateResults(n)
if err != nil {
return err
}
ctype := reflect.TypeOf(ctor) //获取构造函数的反射类型
if len(keys) == 0 {
return errf("%v must provide at least one non-error type", ctype)
}
for k := range keys {
c.isVerifiedAcyclic = false
oldProviders := c.providers[k]
c.providers[k] = append(c.providers[k], n) //给c.providers[k] 赋值,代表该key 哪些节点能够提供
if c.deferAcyclicVerification {
continue
}
//验证是否循环依赖
if err := verifyAcyclic(c, n, k); err != nil {
c.providers[k] = oldProviders
return err
}
c.isVerifiedAcyclic = true
}
c.nodes = append(c.nodes, n)
// Record introspection info for caller if Info option is specified
if info := opts.Info; info != nil { //一些打印信息
params := n.ParamList().DotParam()
results := n.ResultList().DotResult()
info.ID = (ID)(n.id)
info.Inputs = make([]*Input, len(params))
info.Outputs = make([]*Output, len(results))
for i, param := range params {
info.Inputs[i] = &Input{
t: param.Type,
optional: param.Optional,
name: param.Name,
group: param.Group,
}
}
for i, res := range results {
info.Outputs[i] = &Output{
t: res.Type,
name: res.Name,
group: res.Group,
}
}
}
return nil
}
该步主要是生成node节点
newNode
func newNode(ctor interface{}, opts nodeOptions) (*node, error) {
cval := reflect.ValueOf(ctor) //获取构造函数的反射值
ctype := cval.Type()//获取构造函数的反射类型,获取构造函数的指针
cptr := cval.Pointer()
params, err := newParamList(ctype)//获取参数列表
if err != nil {
return nil, err
}
results, err := newResultList(//获取返回列表
ctype,
resultOptions{
Name: opts.ResultName,
Group: opts.ResultGroup,
},
)
if err != nil {
return nil, err
}
return &node{
ctor: ctor,//构造函数
ctype: ctype, //构造函数类型
location: digreflect.InspectFunc(ctor),
id: dot.CtorID(cptr), //用指针地址作为节点的id
paramList: params,//构造函数的参数
resultList: results,//构造函数的结果
}, err
}
newParamList
// newParamList builds a paramList from the provided constructor type.
//
// Variadic arguments of a constructor are ignored and not included as
// dependencies.
func newParamList(ctype reflect.Type) (paramList, error) {
numArgs := ctype.NumIn() //获取invoke 函数的参数
if ctype.IsVariadic() { //如果函数是可选参数,我们跳过最后一个参数,从这里可以知道,invoke 函数后面写可选参数,是可以的
// NOTE: If the function is variadic, we skip the last argument
// because we're not filling variadic arguments yet. See #120.
numArgs--
}
pl := paramList{
ctype: ctype,
Params: make([]param, 0, numArgs),
}
for i := 0; i < numArgs; i++ {
p, err := newParam(ctype.In(i)) //获取函数的参数列表
if err != nil {
return pl, errf("bad argument %d", i+1, err)
}
pl.Params = append(pl.Params, p) //添加封装后的参数
}
return pl, nil
}
newParam
// newParam builds a param from the given type. If the provided type is a
// dig.In struct, an paramObject will be returned.
func newParam(t reflect.Type) (param, error) {
switch {
//参数如果是out 类型则报错
case IsOut(t) || (t.Kind() == reflect.Ptr && IsOut(t.Elem())) || embedsType(t, _outPtrType):
return nil, errf("cannot depend on result objects", "%v embeds a dig.Out", t)
case IsIn(t)://如果是fx.In 类型,创建newParamObject类型
return newParamObject(t)
case embedsType(t, _inPtrType):
return nil, errf(
"cannot build a parameter object by embedding *dig.In, embed dig.In instead",
"%v embeds *dig.In", t)
case t.Kind() == reflect.Ptr && IsIn(t.Elem()):
return nil, errf(
"cannot depend on a pointer to a parameter object, use a value instead",
"%v is a pointer to a struct that embeds dig.In", t)
default:
//创建paramSingle类型
return paramSingle{Type: t}, nil
}
}
newResultList
func newResultList(ctype reflect.Type, opts resultOptions) (resultList, error) {
rl := resultList{
ctype: ctype,
Results: make([]result, 0, ctype.NumOut()),
resultIndexes: make([]int, ctype.NumOut()),
}
resultIdx := 0
for i := 0; i < ctype.NumOut(); i++ { //循环遍历构造函数的输出参数
t := ctype.Out(i)//获取参数
if isError(t) {//如果是错误类型,将这行结果索引赋值为-1
rl.resultIndexes[i] = -1
continue
}
r, err := newResult(t, opts)
if err != nil {
return rl, errf("bad result %d", i+1, err)
}
rl.Results = append(rl.Results, r)
rl.resultIndexes[i] = resultIdx //添加结果类型,注意这里没有用i,说明是有效的返回类型才会添加
resultIdx++
}
return rl, nil
}
newResult
// newResult builds a result from the given type.
func newResult(t reflect.Type, opts resultOptions) (result, error) {
switch {
//如果该类型内嵌fx.IN,那么就报错
case IsIn(t) || (t.Kind() == reflect.Ptr && IsIn(t.Elem())) || embedsType(t, _inPtrType):
return nil, errf("cannot provide parameter objects", "%v embeds a dig.In", t)
//是错误也返回,不能返回错误类型在构造函数里面
case isError(t):
return nil, errf("cannot return an error here, return it from the constructor instead")
//结构体如果内嵌fx.Out,返回ResultObject类型
case IsOut(t):
return newResultObject(t, opts)
//结果类型内嵌必须是dig.Out而不是*dig.Out
case embedsType(t, _outPtrType):
return nil, errf(
"cannot build a result object by embedding *dig.Out, embed dig.Out instead",
"%v embeds *dig.Out", t)
//结果对象不能是指针
case t.Kind() == reflect.Ptr && IsOut(t.Elem()):
return nil, errf(
"cannot return a pointer to a result object, use a value instead",
"%v is a pointer to a struct that embeds dig.Out", t)
case len(opts.Group) > 0: //如果构造函数是group类型,则创建resultGrouped类型
g, err := parseGroupString(opts.Group)
if err != nil {
return nil, errf(
"cannot parse group %q", opts.Group, err)
}
rg := resultGrouped{Type: t, Group: g.Name, Flatten: g.Flatten}
if g.Flatten { //如果group 后面有g.Flatten,那么这个构造函数返回值必须是切片类型
if t.Kind() != reflect.Slice {
return nil, errf(
"flatten can be applied to slices only",
"%v is not a slice", t)
}
rg.Type = rg.Type.Elem()
}
return rg, nil
default:
//返回单个参数类型
return resultSingle{Type: t, Name: opts.Name}, nil
}
}
根据构造函数返回的每个参数类型和选项创建一个result对象可见内嵌fx.Out 返回必须是个对象
findAndValidateResults
// Builds a collection of all result types produced by this node.
func (c *Container) findAndValidateResults(n *node) (map[key]struct{}, error) {
var err error
keyPaths := make(map[key]string)
walkResult(n.ResultList(), connectionVisitor{
c: c,
n: n,
err: &err,
keyPaths: keyPaths,
})
if err != nil {
return nil, err
}
keys := make(map[key]struct{}, len(keyPaths))
for k := range keyPaths {
keys[k] = struct{}{}
}
return keys, nil
}
walkResult
// walkResult walks the result tree for the given result with the provided
// visitor.
//
// resultVisitor.Visit will be called on the provided result and if a non-nil
// resultVisitor is received, it will be used to walk its descendants. If a
// resultObject or resultList was visited, AnnotateWithField and
// AnnotateWithPosition respectively will be called before visiting the
// descendants of that resultObject/resultList.
//
// This is very similar to how go/ast.Walk works.
func walkResult(r result, v resultVisitor) {
v = v.Visit(r)
if v == nil {
return
}
switch res := r.(type) {
case resultSingle, resultGrouped:
// No sub-results
case resultObject:
w := v
for _, f := range res.Fields {
if v := w.AnnotateWithField(f); v != nil {
walkResult(f.Result, v)//递归调用walkResult,传入参数为返回结构体的字段
}
}
case resultList:
w := v
for i, r := range res.Results {
if v := w.AnnotateWithPosition(i); v != nil {
walkResult(r, v)//递归调用walkResult,传入参数为切片的每个值
}
}
default:
panic(fmt.Sprintf(
"It looks like you have found a bug in dig. "+
"Please file an issue at https://github.com/uber-go/dig/issues/ "+
"and provide the following message: "+
"received unknown result type %T", res))
}
}
connectionVisitor.Visit
func (cv connectionVisitor) Visit(res result) resultVisitor {
// Already failed. Stop looking.
if *cv.err != nil {
return nil
}
path := strings.Join(cv.currentResultPath, ".")
switch r := res.(type) {
case resultSingle:
k := key{name: r.Name, t: r.Type}
//如果k 存在,并且返回值类型是resultSingle类型,说明该提供依赖以及存在了,根name和group 稍微有区别
if conflict, ok := cv.keyPaths[k]; ok {
*cv.err = errf(
"cannot provide %v from %v", k, path,
"already provided by %v", conflict,
)
return nil
}
if ps := cv.c.providers[k]; len(ps) > 0 {
cons := make([]string, len(ps))
for i, p := range ps {
cons[i] = fmt.Sprint(p.Location())
}
*cv.err = errf(
"cannot provide %v from %v", k, path,
"already provided by %v", strings.Join(cons, "; "),
)
return nil
}
cv.keyPaths[k] = path
case resultGrouped:
// we don't really care about the path for this since conflicts are
// okay for group results. We'll track it for the sake of having a
// value there.
//group 类型直接赋值就行了,代表该类型提供了值
k := key{group: r.Group, t: r.Type}
cv.keyPaths[k] = path
}
return cv
}
container.Invoke
Invoke会在初始化依赖后调用,这个函数的任何参数都会被认为是它的依赖,这个依赖被初始化没有顺序,invoke 调用可能会有错误,这个错误将会被返回
// Invoke runs the given function after instantiating its dependencies.
//
// Any arguments that the function has are treated as its dependencies. The
// dependencies are instantiated in an unspecified order along with any
// dependencies that they might have.
//
// The function may return an error to indicate failure. The error will be
// returned to the caller as-is.
func (c *Container) Invoke(function interface{}, opts ...InvokeOption) error {
ftype := reflect.TypeOf(function) //获取函数类型
if ftype == nil { //判断是不是nil
return errors.New("can't invoke an untyped nil")
}
if ftype.Kind() != reflect.Func { //判断是不是函数
return errf("can't invoke non-function %v (type %v)", function, ftype)
}
pl, err := newParamList(ftype)//获取函数参数列表
if err != nil {
return err
}
if err := shallowCheckDependencies(c, pl); err != nil { //检查依赖
return errMissingDependencies{
Func: digreflect.InspectFunc(function),
Reason: err,
}
}
if !c.isVerifiedAcyclic {//没有验证循环,验证循环
if err := c.verifyAcyclic(); err != nil {
return err
}
}
args, err := pl.BuildList(c)//将参数赋值,返回赋值后的参数
if err != nil {
return errArgumentsFailed{
Func: digreflect.InspectFunc(function),
Reason: err,
}
}
returned := c.invokerFn(reflect.ValueOf(function), args)//调用函数结果
if len(returned) == 0 {
return nil
}
if last := returned[len(returned)-1]; isError(last.Type()) {//如果最后一个结果是错误,会将此错误进行返回
if err, _ := last.Interface().(error); err != nil {
return err
}
}
return nil
}
shallowCheckDependencies
检查依赖是否缺少,比如func( a A),如果A 这种类型的对象在container 里面找不到,也就是说构造函数没有提供,那么在这里将会报错
// Checks that all direct dependencies of the provided param are present in
// the container. Returns an error if not.
func shallowCheckDependencies(c containerStore, p param) error {
var err errMissingTypes
var addMissingNodes []*dot.Param
walkParam(p, paramVisitorFunc(func(p param) bool {
ps, ok := p.(paramSingle)
if !ok {
return true
}
if ns := c.getValueProviders(ps.Name, ps.Type); len(ns) == 0 && !ps.Optional {
err = append(err, newErrMissingTypes(c, key{name: ps.Name, t: ps.Type})...)
addMissingNodes = append(addMissingNodes, ps.DotParam()...)
}
return true
}))
if len(err) > 0 {
return err
}
return nil
}
verifyAcyclic
if !c.isVerifiedAcyclic {
if err := c.verifyAcyclic(); err != nil {
return err
}
}
校验循环,如果没有校验过循环,就校验循环
func (c *Container) verifyAcyclic() error {
visited := make(map[key]struct{})
for _, n := range c.nodes {
if err := detectCycles(n, c, nil /* path */, visited); err != nil {
return errf("cycle detected in dependency graph", err)
}
}
c.isVerifiedAcyclic = true
return nil
}
检验循环的原理是递归遍历该参数的提供者,如果该提供者出现过说明出现了循环,例如a ->b->c ->d->a ,d 的提供者是a ,但a 已经出现过了,所以出现了循环
pl.BuildList
该函数通过容器,查找到invoke 函数需要的参数值,然后通过下面的invokerFn进行调用。该函数返回有序的结果列表
// BuildList returns an ordered list of values which may be passed directly
// to the underlying constructor.
func (pl paramList) BuildList(c containerStore) ([]reflect.Value, error) {
args := make([]reflect.Value, len(pl.Params))
for i, p := range pl.Params {
var err error
args[i], err = p.Build(c)
if err != nil {
return nil, err
}
}
return args, nil
}
对象不用的参数p,Build 表现不也一样,这里以paramSingle为例
func (ps paramSingle) Build(c containerStore) (reflect.Value, error) {
if v, ok := c.getValue(ps.Name, ps.Type); ok { //从容器里面查找该名字和类型的参数,如果查到了就返回
return v, nil
}
//如果上面一步没有直接获取到,那么就查找能提供这个key 的节点,ps.Name, ps.Type组合成的结构体为key
providers := c.getValueProviders(ps.Name, ps.Type)
if len(providers) == 0 { //如果提供的节点找不到,如果参数是可选的,那么直接返回零值
if ps.Optional {
return reflect.Zero(ps.Type), nil
}
//如果没找到说明没有这个类型直接返回
return _noValue, newErrMissingTypes(c, key{name: ps.Name, t: ps.Type})
}
for _, n := range providers {
err := n.Call(c)
if err == nil {
continue
}
// If we're missing dependencies but the parameter itself is optional,
// we can just move on.
if _, ok := err.(errMissingDependencies); ok && ps.Optional {
return reflect.Zero(ps.Type), nil
}
return _noValue, errParamSingleFailed{
CtorID: n.ID(),
Key: key{t: ps.Type, name: ps.Name},
Reason: err,
}
}
// If we get here, it's impossible for the value to be absent from the
// container.
v, _ := c.getValue(ps.Name, ps.Type) //再尝试找,如果查找到这里,那么一定是有值得
return v, nil
}
Call
call 调用节点的构造函数,获得结果,然后存储在该节点里面,这个过程可能会出现递归,比如a 依赖b,b 的参数依赖c,就会调用BuildList,一层一层往上找,找不到返回错误,找到了,最后调用a 的构造函数创建结果
// Call calls this node's constructor if it hasn't already been called and
// injects any values produced by it into the provided container.
func (n *node) Call(c containerStore) error {
if n.called { //这里用来标识该节点是否被call 过
return nil
}
if err := shallowCheckDependencies(c, n.paramList); err != nil {
return errMissingDependencies{
Func: n.location,
Reason: err,
}
}
args, err := n.paramList.BuildList(c) //一个递归过程,将该节点的参数进行BuildList,获取该节点的参数
if err != nil {
return errArgumentsFailed{
Func: n.location,
Reason: err,
}
}
receiver := newStagingContainerWriter()
//然后调用该节点的构造函数,将刚刚获取的参数传进去进行调用,获取调用后的results
results := c.invoker()(reflect.ValueOf(n.ctor), args)
if err := n.resultList.ExtractList(receiver, results); err != nil {
return errConstructorFailed{Func: n.location, Reason: err}
}
receiver.Commit(c)//将结果放入container
n.called = true
return nil
}
ExtractList
ExtractList 将构造函数获取的结果赋值到节点的Results
func (rl resultList) ExtractList(cw containerWriter, values []reflect.Value) error {
for i, v := range values { //循环遍历结果值
if resultIdx := rl.resultIndexes[i]; resultIdx >= 0 {
rl.Results[resultIdx].Extract(cw, v) //将结果值赋值到containerWriter里面去
continue
}
//调用结构包含err,直接返回
if err, _ := v.Interface().(error); err != nil {
return err
}
}
return nil
}
Extract就是给containerWriter赋值,然后最后调用 receiver.Commit(c)将值复制到容器里面去
func (rs resultSingle) Extract(cw containerWriter, v reflect.Value) {
cw.setValue(rs.Name, rs.Type, v)
}
invokerFn
默认的调用,就是通过反射获取invoke 函数的
// invokerFn specifies how the container calls user-supplied functions.
type invokerFn func(fn reflect.Value, args []reflect.Value) (results []reflect.Value)
func defaultInvoker(fn reflect.Value, args []reflect.Value) []reflect.Value {
return fn.Call(args)
}
项目实践
下面我们将通过搭建一个http 服务器在项目中实践来练习fx 的使用,项目目录结构
server/main.go
package main
import (
"server/pkg/config"
"server/pkg/log"
"server/server"
)
func main() {
srv:=server.NewServer() //创建一个服务器
srv.Provide(
log.GetLogger, //依赖注入Logger
config.NewConfig,//依赖注入配置文件
)
srv.Run()//运行服务
}
server/pkg/router/router.go
package router
import "gopkg.in/macaron.v1"
func Register(router * macaron.Router) {
router.Get("/hello", func(ctx *macaron.Context) {
ctx.Write([]byte("hello"))
})
}
server/http_server.go
package server
import (
"fmt"
"go.uber.org/zap"
"gopkg.in/macaron.v1"
"net/http"
"server/pkg/config"
)
type HttpServer struct {
cfg * config.Config
logger *zap.Logger
mar * macaron.Macaron
}
func NewHttpServer(cfg * config.Config,logger *zap.Logger)*HttpServer {
return &HttpServer{
cfg: cfg,
logger: logger.Named("http_server"),
mar:macaron.Classic() ,
}
}
func (srv* HttpServer)Run()error {
router.Register(srv.mar.Router)
addr:=fmt.Sprintf("0.0.0.0:%v",srv.cfg.HttpConfig.Port)
srv.logger.Info("http run ",zap.String("addr",addr))
return http.ListenAndServe(addr, srv.mar)
}
server/server.go
package server
import (
"go.uber.org/fx"
"golang.org/x/sync/errgroup"
)
type Server struct {
group errgroup.Group //errgroup,参考我的文章,专门讲这个原理
app *fx.App //fx 实例
provides []interface{}
invokes []interface{}
supplys []interface{}
httSrv *HttpServer //该http server 可以换成fibber gin 之类的
}
func NewServer(
)*Server {
return &Server{
}
}
func(srv*Server) Run() {
srv.app=fx.New(
fx.Provide(srv.provides...),
fx.Invoke(srv.invokes...),
fx.Supply(srv.supplys...),
fx.Provide(NewHttpServer),//注入http server
fx.Supply(srv),
fx.Populate(&srv.httSrv), //给srv 实例赋值
fx.NopLogger,//禁用fx 默认logger
)
srv.group.Go(srv.httSrv.Run) //启动http 服务器
err:=srv.group.Wait() //等待子协程退出
if err!=nil{
panic(err)
}
}
func(srv*Server)Provide(ctr ...interface{}){
srv.provides= append(srv.provides, ctr...)
}
func(srv*Server)Invoke(invokes ...interface{}){
srv.invokes=append(srv.invokes,invokes...)
}
func(srv*Server)Supply(objs ...interface{}){
srv.supplys=append(srv.supplys,objs...)
}
server/pkg/config/config.go
package config
import (
"gopkg.in/yaml.v2"
"io/ioutil"
)
type Config struct {
HttpConfig struct{
Port int `yaml:"port"`
} `yaml:"http"`
LogConfig struct{
Output string`yaml:"output"`
} `yaml:"log"`
}
func NewConfig()*Config {
data,err:=ioutil.ReadFile("./config.yaml")
if err!=nil{
panic(err)
}
c:=&Config{}
err=yaml.Unmarshal(data,c)
if err!=nil{
panic(err)
}
return c
}
server/pkg/log/log.go
package log
import (
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"os"
"server/pkg/config"
)
func GetLogger(cfg *config.Config)*zap.Logger {
writeSyncer := getLogWriter()
encoder := getEncoder()
switch cfg.LogConfig.Output {
case "all":
writeSyncer=zapcore.NewMultiWriteSyncer(os.Stdout,writeSyncer) //暂时不启用文件
case "file":
default:
writeSyncer=zapcore.NewMultiWriteSyncer(os.Stdout) //暂时不启用文件
}
core := zapcore.NewCore(encoder, writeSyncer, zapcore.DebugLevel)
logger := zap.New(core, zap.AddCaller())
return logger
}
func getEncoder() zapcore.Encoder {
encoderConfig := zap.NewProductionEncoderConfig()
encoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
encoderConfig.EncodeLevel = zapcore.CapitalLevelEncoder
return zapcore.NewConsoleEncoder(encoderConfig)
}
func getLogWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "./data/server.log",
MaxSize: 1,
MaxBackups: 5,
MaxAge: 30,
Compress: false,
}
return zapcore.AddSync(lumberJackLogger)
}
server/config.yaml
http:
port: 9999
log:
#console:终端,file:文件,all:所有
output: "console"
启动服务器:
go run main.go
01T19:51:55.169+0800 INFO http_server server/http_server.go:28 http run {"addr": "0.0.0.0:9999"}
浏览器输入http://127.0.0.1:9999/hello,可以看见打印hello
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)