Fukuoka.go #11 by Yuichi Watanabe @nulab
Golang の DI ライブラリ vvatanabe/shot を書きました
object App {
def main(args: Array[String]) {
val injector = Guice.createInjector(new Module() {
override def configure(binder: Binder): Unit = {
binder.bind(classOf[UserRepository]).to(classOf[UserRepositoryOnDB])
binder.bind(classOf[GroupRepository]).to(classOf[GroupRepositoryOnDB])
}
})
val userRepository = injector.getInstance(classOf[UserRepository])
val groupRepository = injector.getInstance(classOf[GroupRepository])
}
}
func main() {
injector, err := CreateInjector(func(binder Binder) {
binder.Bind(new(UserRepository)).To(new(UserRepositoryOnDB))
binder.Bind(new(GroupRepository)).To(new(GroupRepositoryOnDB))
})
userRepository := injector.Get(new(UserRepository))
groupRepository := injector.Get(new(GroupRepository))
}
Scala:
Golang:
package main
import (
"github.com/vvatanabe/shot/shot"
)
func main() {
injector, err := shot.CreateInjector(func(binder shot.Binder) {
binder.Bind(new(UserRepository)).To(new(UserRepositoryOnDB))
binder.Bind(new(GroupRepository)).To(new(GroupRepositoryOnDB))
})
}
Configure 関数にオブジェクトの生成ルールを記述すると、依存関係を解決してオブジェクトを生成する。
Constructor 関数で依存するオブジェクトをバインドする
injector, err := shot.CreateInjector(func(binder shot.Binder) {
binder.Bind(new(GroupRepository)).ToConstructor(NewGroupRepositoryOnDB)
})
Struct の Field へ直接依存するオブジェクトをバインドする
type ProjectService struct {
UserRepository UserRepository `inject:""`
GroupRepository GroupRepository `inject:""`
}
injector, err := shot.CreateInjector(func(binder shot.Binder) {
binder.Bind(new(ProjectService)).To(new(ProjectService))
})
生成済のオブジェクトを直接バインドする
injector, err := shot.CreateInjector(func(binder shot.Binder) {
binder.Bind(new(Config)). ToInstance(&Config{})
})
シングルトンとしてオブジェクトを生成する
injector, err := shot.CreateInjector(func(binder shot.Binder) {
binder.Bind(new(UserRepository)).
To(new(UserRepositoryOnDB)).In(shot.SingletonInstance)
binder.Bind(new(GroupRepository)).
To(new(GroupRepositoryOnDB)).AsEagerSingleton()
})
コンテナからオブジェクトを取得する
injector, err := shot.CreateInjector(func(binder shot.Binder) {
binder.Bind(new(UserRepository)).To(new(UserRepositoryOnDB))
binder.Bind(new(GroupRepository)).To(new(GroupRepositoryOnDB))
})
userRepository := injector.Get(new(UserRepository)).(UserRepository)
groupRepository := injector.Get(new(GroupRepository)).(GroupRepository)
userRepository, err := injector.SafeGet(new(UserRepository))
groupRepository, err := injector.SafeGet(new(GroupRepository))
コンテナからオブジェクトを安全に取得する
constructor 関数の reflect.Value と reflect.Type を取得する。
func Resolve(injector Injector, constructorFunc interface{}) interface{} {
constructor := reflect.ValueOf(constructorFunc)
constructorType := reflect.TypeOf(constructorFunc)
...
}
constructor 関数自身の reflect.Type から、各引数の reflect.Type を取得する。
func Resolve(injector Injector, constructorFunc interface{}) interface{} {
constructor := reflect.ValueOf(constructorFunc)
constructorType := reflect.TypeOf(constructorFunc)
var constructorArgs []reflect.Value
for i := 0; i < constructorType.NumIn(); i++ {
argType := constructorType.In(i)
...
}
各引数の reflect.Type を元に、解決済のオブジェクトリストから引数に突っ込む値を取得する。
func Resolve(injector Injector, constructorFunc interface{}) interface{} {
constructor := reflect.ValueOf(constructorFunc)
constructorType := reflect.TypeOf(constructorFunc)
var constructorArgs []reflect.Value
for i := 0; i < constructorType.NumIn(); i++ {
argType := constructorType.In(i)
value := injector.Get(argType)
...
}
引数に突っ込む値を、reflect.Value に変換してコンストラクタ関数に渡す引数の配列に追加する。
func Resolve(injector Injector, constructorFunc interface{}) interface{} {
constructor := reflect.ValueOf(constructorFunc)
constructorType := reflect.TypeOf(constructorFunc)
var constructorArgs []reflect.Value
for i := 0; i < constructorType.NumIn(); i++ {
argType := constructorType.In(i)
value := injector.Get(argType)
reflectValue := reflect.ValueOf(value)
constructorArgs = append(constructorArgs, reflectValue)
}
...
}
コンストラクタ関数の reflect.Value が持つ Call 関数に、先程取得した引数の []reflect.Value を与えて実行する。
func Resolve(injector Injector, constructorFunc interface{}) interface{} {
constructor := reflect.ValueOf(constructorFunc)
constructorType := reflect.TypeOf(constructorFunc)
var constructorArgs []reflect.Value
for i := 0; i < constructorType.NumIn(); i++ {
argType := constructorType.In(i)
value := injector.Get(argType)
reflectValue := reflect.ValueOf(value)
constructorArgs = append(constructorArgs, reflectValue)
}
results := constructor.Call(constructorArgs)
...
}
コンストラクタ関数は1つだけ値を返すとゆう制約のもと、結果の配列の頭にDI済のオブジェクトが格納されている。
func Resolve(injector Injector, constructorFunc interface{}) interface{} {
constructor := reflect.ValueOf(constructorFunc)
constructorType := reflect.TypeOf(constructorFunc)
var constructorArgs []reflect.Value
for i := 0; i < constructorType.NumIn(); i++ {
argType := constructorType.In(i)
value := injector.Get(argType)
reflectValue := reflect.ValueOf(value)
constructorArgs = append(constructorArgs, reflectValue)
}
results := constructor.Call(constructorArgs)
return results[0].Interface()
}
コンストラクタ関数は1つだけ値を返すとゆう制約のもと、結果の配列の頭にDI済のオブジェクトが格納されている。
func Resolve(injector Injector, structure interface{}) interface{} {
structureType := reflect.ValueOf(structure)
structureValue := reflect.TypeOf(structure)
...
}
struct の reflect.Type 型 から NumField 関数 を 実行して フィールドの数を取得する。 Field 関数に indexを 渡して、フィールドの型情報 (reflect.StructField) と フィールドの値情報 (reflect.Value) を取得する。
func Resolve(injector Injector, structure interface{}) interface{} {
structureType := reflect.ValueOf(structure)
structureValue := reflect.TypeOf(structure)
for i := 0; i < structureType.NumField(); i++ {
structField := structureType.Field(i)
structFieldValue := structureValue.Field(i)
...
}
それを元に解決済のオブジェクトリストからフィールドの値として実態を取得する。
func Resolve(injector Injector, structure interface{}) interface{} {
structureType := reflect.ValueOf(structure)
structureValue := reflect.TypeOf(structure)
for i := 0; i < structureType.NumField(); i++ {
structField := structureType.Field(i)
structFieldValue := structureValue.Field(i)
value := injector.Get(structField.Type)
...
}
それを reflect.Value に変換して、Set 関数でフィールドにセットする。
func Resolve(injector Injector, structure interface{}) interface{} {
structureType := reflect.ValueOf(structure)
structureValue := reflect.TypeOf(structure)
for i := 0; i < structureType.NumField(); i++ {
structField := structureType.Field(i)
structFieldValue := structureValue.Field(i)
value := injector.Get(structField.Type)
reflectValue := reflect.ValueOf(value)
structFieldValue.Set(reflectValue)
...
}
対象のオブジェクトは依存しているオブジェクトが解決された状態となる。
func Resolve(injector Injector, structure interface{}) interface{} {
structureType := reflect.ValueOf(structure)
structureValue := reflect.TypeOf(structure)
for i := 0; i < structureType.NumField(); i++ {
structField := structureType.Field(i)
structFieldValue := structureValue.Field(i)
value := injector.Get(structField.Type)
reflectValue := reflect.ValueOf(value)
structFieldValue.Set(reflectValue)
}
return structureValue.Interface()
}
package module
// @injector-gen-go
func GenerateInjector(binder Binder) {
binder.Bind(new(UserRepository)).To(new(UserRepositoryOnDB))
binder.Bind(new(GroupRepository)).To(new(GroupRepositoryOnDB))
binder.Bind(new(ProjectService)).AsSingleton()
})
こんな感じでコードベースの設定をパースして
// Code generated by injector-gen-go. DO NOT EDIT.
// source: module/module.go
package module
type Injector interface {
func GetUserRepository() UserRepository
func GroupRepository() GroupRepository
func ProjectService() *ProjectService
}
func NewInjector() Injector {
injector := &injector{}
return injector
}
type injector struct {
projectService *ProjectService
projectServiceOnce sync.Once
}
〜〜
こんなのを自動生成する
〜〜
func (i *injector) GetUserRepository() UserRepository {
return &UserRepositoryOnDB{}
}
func(i *injector) GroupRepository() GroupRepositoryOnDB {
return &GroupRepositoryOnDB{}
}
func(i *injector) GetProjectService() *ProjectService {
i.projectServiceOnce.Do(func() {
i.projectService = &ProjectService{
UserRepository: GetUserRepository(),
GroupRepository: GroupRepository(),
}
})
return i.projectService
}
次の Fukuoka.go までにやってみたい