-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
740 additions
and
0 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
# Golang 依赖注入 | ||
|
||
为什么我们需要依赖注入: | ||
|
||
``` | ||
Most important, for me, is making it easy to follow the Single Responsibility Principle. | ||
DI/IoC makes it simple for me to manage dependencies between objects. | ||
In turn, that makes it easier for me to break coherent functionality off into it's | ||
own contract (interface). | ||
As a result, my code has been far more modularized since I learned of DI/IoC. | ||
Another result of this is that I can much more easily see my way through to a | ||
design that supports the Open-Closed Principle. This is one of the most confidence | ||
inspiring techniques (second only to automated testing). I doubt I could espouse the | ||
virtues of Open-Closed Principle enough. | ||
DI/IoC is one of the few things in my programming career that has been a "game changer." | ||
There is a huge gap in quality between code I wrote before & after learning DI/IoC. | ||
Let me emphasize that some more. HUGE improvement in code quality. | ||
``` | ||
|
||
# 安装 | ||
|
||
`` | ||
|
||
# 用法 | ||
|
||
构造一个问候服务需要知道 | ||
|
||
1. 向谁问候 | ||
2. 问候什么 | ||
|
||
```go | ||
package simple | ||
|
||
import ( | ||
"fmt" | ||
di "github.com/douyacun/go-ioc" | ||
"testing" | ||
) | ||
|
||
type MessagePrinter interface { | ||
Print() | ||
} | ||
|
||
type UserProvider interface { | ||
GetUserName() string | ||
} | ||
|
||
type UserProviderImpl struct { | ||
UserName string `di:"userName"` // will match message | ||
} | ||
|
||
func (impl *UserProviderImpl) GetUserName() string { | ||
return impl.UserName | ||
} | ||
|
||
type MessagePrinterImpl struct { | ||
message string `di:"message"` // will match message | ||
} | ||
|
||
func (impl *MessagePrinterImpl) Print() { | ||
fmt.Println(impl.message) | ||
} | ||
|
||
func (impl *MessagePrinterImpl) SetMessage(m string) { | ||
impl.message = m | ||
} | ||
|
||
type GreeterService struct { | ||
UserProvider UserProvider `di:"*"` // will match type | ||
Printer MessagePrinter `di:"*"` // will match type | ||
} | ||
|
||
func (s *GreeterService) Print() { | ||
fmt.Printf("%s:", s.UserProvider.GetUserName()) | ||
s.Printer.Print() | ||
} | ||
``` | ||
|
||
通常来说的初始化过程 | ||
|
||
``` | ||
message := "say hello" | ||
printer := &MessagePrinterImpl{} | ||
printer.SetMessage(message) | ||
userName := "word" | ||
userProvider := &UserProviderImpl{UserName: userName} | ||
service := &MessagePrinterService{Printer: printer, UserProvider:userProvider} | ||
service.Print() | ||
``` | ||
|
||
使用依赖注入后 | ||
|
||
``` | ||
di.Register("message", "hello world") | ||
di.Register("userName", "douyacun") | ||
di.Register("printer", &MessagePrinterImpl{}) | ||
di.Register("userProvider", &UserProviderImpl{}) | ||
service := &GreeterService{} | ||
di.MustBind(service) | ||
service.Print() | ||
``` | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package di | ||
|
||
import ( | ||
"fmt" | ||
"reflect" | ||
"strings" | ||
|
||
"github.com/pkg/errors" | ||
|
||
"github.com/sirupsen/logrus" | ||
) | ||
|
||
var ( | ||
ErrNotSupport = fmt.Errorf("dp bind should be used on struct") | ||
) | ||
|
||
type bind struct { | ||
target interface{} | ||
registry *Ioc | ||
} | ||
|
||
func NewBinder(target interface{}, registry *Ioc) *bind { | ||
return &bind{ | ||
target: target, | ||
registry: registry, | ||
} | ||
} | ||
|
||
func (b *bind) Bind() error { | ||
if b.target == nil { | ||
return fmt.Errorf("can not bind nil") | ||
} | ||
|
||
return b.bind(reflect.ValueOf(b.target), b.registry) | ||
} | ||
|
||
func (b *bind) bind(target reflect.Value, registry *Ioc) error { | ||
v := target | ||
|
||
for v.Kind() == reflect.Ptr || v.Kind() == reflect.Interface { | ||
if v.IsNil() { | ||
return fmt.Errorf("DipRegistry bind target nil") | ||
} | ||
v = v.Elem() | ||
} | ||
t := v.Type() | ||
|
||
logrus.Infof("dp bind type:%s, canset:%v", t.Name(), v.CanSet()) | ||
|
||
if v.Kind() != reflect.Struct { | ||
return ErrNotSupport | ||
} | ||
|
||
if err := b.bindMember(target, v, registry); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (b *bind) bindMember(inputV reflect.Value, v reflect.Value, registry *Ioc) error { | ||
for i := 0; i != v.NumField(); i++ { | ||
fieldType := v.Type().Field(i) | ||
fieldValue := v.Field(i) | ||
tag := &Tag{} | ||
tag.ParseTag(fieldType, fieldType.Tag.Get("di")) | ||
|
||
if tag.IsSkip() { | ||
continue | ||
} | ||
|
||
if fieldType.Anonymous { | ||
if err := b.bind(fieldValue, b.registry); err != nil { | ||
if errors.Cause(err) == ErrNotSupport { | ||
continue | ||
} else { | ||
return errors.Wrapf(err, "embed field bind err, in type:%s, field:%s", v.Type(), fieldType.Name) | ||
} | ||
} | ||
continue | ||
} | ||
|
||
if tag.GetName() != "*" { | ||
value, err := registry.FetchByName(tag.GetName()) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if value == nil { | ||
if tag.AllowEmpty() { | ||
continue | ||
} else { | ||
return fmt.Errorf("field value not found set in type:%s, field:%s", v.Type(), fieldType.Name) | ||
} | ||
} | ||
|
||
if err := b.SetField(inputV, fieldType, fieldValue, value); err != nil { | ||
return err | ||
} | ||
} else { | ||
value, err := registry.FetchByType(fieldType.Type) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
if value == nil { | ||
if tag.AllowEmpty() { | ||
continue | ||
} else { | ||
return fmt.Errorf("field value not found set in type:%s, field:%s", v.Type(), fieldType.Name) | ||
} | ||
} | ||
|
||
if err := b.SetField(inputV, fieldType, fieldValue, value); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (b *bind) SetField(v reflect.Value, fieldType reflect.StructField, fieldValue reflect.Value, input interface{}) error { | ||
if fieldValue.CanSet() { | ||
if reflect.TypeOf(input).AssignableTo(fieldType.Type) { | ||
fieldValue.Set(reflect.ValueOf(input)) | ||
return nil | ||
} else { | ||
return fmt.Errorf("field value not assignable in type:%s, field:%s, input:%s", v.Type(), fieldType.Name, reflect.TypeOf(input)) | ||
} | ||
} else { | ||
setMethodName := fmt.Sprintf("Set%s", strings.Title(fieldType.Name)) | ||
setMethod := v.MethodByName(setMethodName) | ||
if !setMethod.IsValid() { | ||
return fmt.Errorf("field value not assignable by method %s in type:%s, field:%s, input:%s", setMethodName, v.Type(), fieldType.Name, reflect.TypeOf(input)) | ||
} | ||
|
||
setMethod.Call([]reflect.Value{reflect.ValueOf(input)}) | ||
return nil | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package di | ||
|
||
import ( | ||
"sync" | ||
) | ||
|
||
var ( | ||
once sync.Once | ||
gInstance *diInstance | ||
) | ||
|
||
type diInstance struct { | ||
registry *Ioc | ||
} | ||
|
||
func newInstance() *diInstance { | ||
return &diInstance{ | ||
registry: NewRegistry(), | ||
} | ||
} | ||
|
||
func init() { | ||
once.Do(func() { | ||
gInstance = newInstance() | ||
}) | ||
} | ||
|
||
func Register(name string, value interface{}, opts ...RegistryOption) { | ||
gInstance.registry.Register(name, value) | ||
} | ||
|
||
func Bind(target interface{}) error { | ||
return gInstance.registry.Bind(target) | ||
} | ||
|
||
func MustBind(target interface{}) { | ||
if err := Bind(target); err != nil { | ||
panic(err) | ||
} | ||
} |
Oops, something went wrong.