參考:
https://studygolang.com/articles/13783?fr=sidebar
https://studygolang.com/articles/7716
先寫(xiě)著,我現(xiàn)在吃不透原理
一、IOC
Ioc的思想就是解耦,只依賴容器而不依賴具體的類(lèi),當(dāng)你的類(lèi)有修改時(shí),最多需要改動(dòng)一下容器相關(guān)代碼,業(yè)務(wù)代碼并不受影響。
主要原理通俗來(lái)講就是根據(jù)給出的類(lèi)名(字符串方式)來(lái)動(dòng)態(tài)地生成對(duì)象,這種編程方式可以讓對(duì)象在生成時(shí)才決定到底是哪一種對(duì)象。
我們可以把IOC容器的工作模式看做是工廠模式的升華,可以把IOC容器看作是一個(gè)工廠,這個(gè)工廠里要生產(chǎn)的對(duì)象都在配置文件中給出定義,然后利用編程語(yǔ)言的的反射編程,根據(jù)配置文件中給出的類(lèi)名生成相應(yīng)的對(duì)象。從實(shí)現(xiàn)來(lái)看,IOC是把以前在工廠方法里寫(xiě)死的對(duì)象生成代碼,改變?yōu)橛膳渲梦募?lái)定義,也就是把工廠和對(duì)象生成這兩者獨(dú)立分隔開(kāi)來(lái),目的就是提高靈活性和可維護(hù)性。
golang的依賴注入原理
步驟如下:(golang不支持動(dòng)態(tài)創(chuàng)建對(duì)象,所以需要先手動(dòng)創(chuàng)建對(duì)象然后注入,java可以直接動(dòng)態(tài)創(chuàng)建對(duì)象)
1.通過(guò)反射讀取對(duì)象的依賴(golang是通過(guò)tag實(shí)現(xiàn))
2.在容器中查找有無(wú)該對(duì)象實(shí)例
3.如果有該對(duì)象實(shí)例或者創(chuàng)建對(duì)象的工廠方法,則注入對(duì)象或使用工廠創(chuàng)建對(duì)象并注入
4.如果無(wú)該對(duì)象實(shí)例,則報(bào)錯(cuò)
代碼實(shí)現(xiàn)
package di
import (
"sync"
"reflect"
"fmt"
"strings"
"errors"
)
var (
ErrFactoryNotFound = errors.New("factory not found")
)
type factory = func() (interface{}, error)
// 容器
type Container struct {
sync.Mutex
singletons map[string]interface{}
factories map[string]factory
}
// 容器實(shí)例化
func NewContainer() *Container {
return &Container{
singletons: make(map[string]interface{}),
factories: make(map[string]factory),
}
}
// 注冊(cè)單例對(duì)象
func (p *Container) SetSingleton(name string, singleton interface{}) {
p.Lock()
p.singletons[name] = singleton
p.Unlock()
}
// 獲取單例對(duì)象
func (p *Container) GetSingleton(name string) interface{} {
return p.singletons[name]
}
// 獲取實(shí)例對(duì)象
func (p *Container) GetPrototype(name string) (interface{}, error) {
factory, ok := p.factories[name]
if !ok {
return nil, ErrFactoryNotFound
}
return factory()
}
// 設(shè)置實(shí)例對(duì)象工廠
func (p *Container) SetPrototype(name string, factory factory) {
p.Lock()
p.factories[name] = factory
p.Unlock()
}
// 注入依賴
func (p *Container) Ensure(instance interface{}) error {
elemType := reflect.TypeOf(instance).Elem()
ele := reflect.ValueOf(instance).Elem()
for i := 0; i < elemType.NumField(); i++ { // 遍歷字段
fieldType := elemType.Field(i)
tag := fieldType.Tag.Get("di") // 獲取tag
diName := p.injectName(tag)
if diName == "" {
continue
}
var (
diInstance interface{}
err error
)
if p.isSingleton(tag) {
diInstance = p.GetSingleton(diName)
}
if p.isPrototype(tag) {
diInstance, err = p.GetPrototype(diName)
}
if err != nil {
return err
}
if diInstance == nil {
return errors.New(diName + " dependency not found")
}
ele.Field(i).Set(reflect.ValueOf(diInstance))
}
return nil
}
// 獲取需要注入的依賴名稱
func (p *Container) injectName(tag string) string {
tags := strings.Split(tag, ",")
if len(tags) == 0 {
return ""
}
return tags[0]
}
// 檢測(cè)是否單例依賴
func (p *Container) isSingleton(tag string) bool {
tags := strings.Split(tag, ",")
for _, name := range tags {
if name == "prototype" {
return false
}
}
return true
}
// 檢測(cè)是否實(shí)例依賴
func (p *Container) isPrototype(tag string) bool {
tags := strings.Split(tag, ",")
for _, name := range tags {
if name == "prototype" {
return true
}
}
return false
}
// 打印容器內(nèi)部實(shí)例
func (p *Container) String() string {
lines := make([]string, 0, len(p.singletons)+len(p.factories)+2)
lines = append(lines, "singletons:")
for name, item := range p.singletons {
line := fmt.Sprintf(" %s: %x %s", name, &item, reflect.TypeOf(item).String())
lines = append(lines, line)
}
lines = append(lines, "factories:")
for name, item := range p.factories {
line := fmt.Sprintf(" %s: %x %s", name, &item, reflect.TypeOf(item).String())
lines = append(lines, line)
}
return strings.Join(lines, "\n")
}