Skip to content

Commit

Permalink
ioc
Browse files Browse the repository at this point in the history
  • Loading branch information
douyacun committed Jun 20, 2022
1 parent 580a694 commit 8922829
Show file tree
Hide file tree
Showing 13 changed files with 740 additions and 0 deletions.
8 changes: 8 additions & 0 deletions .idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 9 additions & 0 deletions .idea/go-ioc.iml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions .idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions .idea/vcs.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

110 changes: 110 additions & 0 deletions README.md
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()
```


141 changes: 141 additions & 0 deletions binder.go
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
}
}
40 changes: 40 additions & 0 deletions di.go
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)
}
}
Loading

0 comments on commit 8922829

Please sign in to comment.