diff --git a/Gopkg.lock b/Gopkg.lock index 92a57af..e526505 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -5,13 +5,19 @@ branch = "master" name = "git.loafle.net/overflow/annotation-go" packages = ["."] - revision = "9d80d778c61e1a08a46c19835e36f18cf08c1f91" + revision = "178b12d303bdd83566a35d71cce68b0c6cc1f4d3" [[projects]] branch = "master" name = "git.loafle.net/overflow/util-go" packages = ["reflect"] - revision = "01cc315e25b38331c0a366d8025600ed4e297e8e" + revision = "fae2846a85aad314ee44957d428478fbe800045f" + +[[projects]] + name = "gopkg.in/yaml.v2" + packages = ["."] + revision = "5420a8b6744d3b0345ab293f6fcba19c978f1183" + version = "v2.2.1" [solve-meta] analyzer-name = "dep" diff --git a/annotation/inject.go b/annotation/inject.go index 85b7f89..b161990 100644 --- a/annotation/inject.go +++ b/annotation/inject.go @@ -17,4 +17,5 @@ var InjectAnnotationType = reflect.TypeOf((*InjectAnnotation)(nil)) type InjectAnnotation struct { oa.Annotation `@name:"@Inject"` Name string `json:"name" @default:""` + Required bool `json:"required" @default:"true"` } diff --git a/annotation/injectable.go b/annotation/injectable.go index 063bcac..c1a9d6d 100644 --- a/annotation/injectable.go +++ b/annotation/injectable.go @@ -16,16 +16,5 @@ var InjectableAnnotationType = reflect.TypeOf((*InjectableAnnotation)(nil)) type InjectableAnnotation struct { oa.Annotation `@name:"@Injectable"` - Name string `json:"name" @default:""` - InitMethod string `json:"initMethod"` - DestroyMethod string `json:"destroyMethod"` - Scope ScopeType `json:"scope"` + Name string `json:"name" @default:""` } - -type ScopeType string - -const ( - ScopeTypeDefault ScopeType = "Default" // == ScopeTypeSingleton - ScopeTypeSingleton ScopeType = "Singleton" - ScopeTypeTransiant ScopeType = "Transiant" -) diff --git a/annotation/resource.go b/annotation/resource.go deleted file mode 100644 index 01749b7..0000000 --- a/annotation/resource.go +++ /dev/null @@ -1,20 +0,0 @@ -package annotation - -// @Resource(name? string) - -import ( - "reflect" - - oa "git.loafle.net/overflow/annotation-go" -) - -func init() { - oa.Register(ResourceAnnotationType) -} - -var ResourceAnnotationType = reflect.TypeOf((*ResourceAnnotation)(nil)) - -type ResourceAnnotation struct { - oa.Annotation `@name:"@Resource"` - Name string `json:"name" @default:""` -} diff --git a/annotation/scope.go b/annotation/scope.go new file mode 100644 index 0000000..65cf7a3 --- /dev/null +++ b/annotation/scope.go @@ -0,0 +1,71 @@ +package annotation + +// @Scope(name? string) + +import ( + "encoding/json" + "fmt" + "reflect" + + oa "git.loafle.net/overflow/annotation-go" +) + +func init() { + oa.Register(ScopeAnnotationType) +} + +var ScopeAnnotationType = reflect.TypeOf((*ScopeAnnotation)(nil)) + +type ScopeAnnotation struct { + oa.Annotation `@name:"@Scope"` + Value ScopeType `json:"value" @default:"singleton"` +} + +type ScopeType int8 + +const ( + ScopeTypeDefault ScopeType = iota + 1 + ScopeTypeSingleton + ScopeTypeTransiant +) + +var ( + scopeTypeID = map[ScopeType]string{ + ScopeTypeDefault: "default", + ScopeTypeSingleton: "singleton", + ScopeTypeTransiant: "transiant", + } + + scopeTypeKey = map[string]ScopeType{ + "default": ScopeTypeDefault, + "singleton": ScopeTypeSingleton, + "transiant": ScopeTypeTransiant, + } +) + +func (st ScopeType) String() string { + return scopeTypeID[st] +} + +func (st ScopeType) MarshalJSON() ([]byte, error) { + value, ok := scopeTypeID[st] + if !ok { + return nil, fmt.Errorf("Invalid EnumType[%s] value", st) + } + return json.Marshal(value) +} + +func (st ScopeType) UnmarshalJSON(b []byte) error { + var s string + err := json.Unmarshal(b, &s) + if err != nil { + return err + } + + value, ok := scopeTypeKey[s] + if !ok { + return fmt.Errorf("Invalid EnumType[%s] value", s) + } + st = value + return nil +} diff --git a/registry.go b/registry.go index b11312c..0f990b0 100644 --- a/registry.go +++ b/registry.go @@ -13,7 +13,8 @@ import ( type Registry interface { RegisterType(t reflect.Type) - RegisterResource(name string, resource interface{}) error + RegisterSingleton(singleton interface{}) error + RegisterSingletonByName(name string, singleton interface{}) error GetInstance(t reflect.Type) (interface{}, error) GetInstances(ts []reflect.Type) ([]interface{}, error) @@ -59,7 +60,7 @@ func (r *InstanceRegistry) RegisterType(t reflect.Type) { log.Panicf("t[reflect.Type] is nil") } if !our.IsTypeKind(t, reflect.Struct, true) { - log.Panicf("t[reflect.Type] must be specified but is %v", t) + log.Panicf("t[reflect.Type] must be Struct but is %v", t) } td, err := r.buildDefinition(t) @@ -91,14 +92,42 @@ func (r *InstanceRegistry) RegisterType(t reflect.Type) { r.definitionByName[name] = td } -func RegisterResource(name string, resource interface{}) error { - return AppRegistry.RegisterResource(name, resource) +func RegisterSingleton(singleton interface{}) error { + return AppRegistry.RegisterSingleton(singleton) } -func (r *InstanceRegistry) RegisterResource(name string, resource interface{}) error { - if _, ok := r.instanceByName[name]; ok { - return fmt.Errorf("Resource[%s] is already exist", name) +func (r *InstanceRegistry) RegisterSingleton(singleton interface{}) error { + t := reflect.TypeOf(singleton) + + if nil == t { + log.Panicf("t[reflect.Type] is nil") } - r.instanceByName[name] = resource + + rt, _, tName := our.GetTypeInfo(t) + if !our.IsTypeKind(rt, reflect.Struct, true) { + log.Panicf("t[reflect.Type] must be Struct but is %v", t) + } + + r.instanceByType[rt] = singleton + + r.RegisterSingletonByName(tName, singleton) + + return nil +} + +func RegisterSingletonByName(name string, singleton interface{}) error { + return AppRegistry.RegisterSingletonByName(name, singleton) +} +func (r *InstanceRegistry) RegisterSingletonByName(name string, singleton interface{}) error { + t := reflect.TypeOf(singleton) + + if nil == t { + log.Panicf("t[reflect.Type] is nil") + } + + if _, ok := r.instanceByName[name]; ok { + return fmt.Errorf("name[%s] of Singleton is already exist", name) + } + r.instanceByName[name] = singleton return nil } @@ -138,9 +167,9 @@ func (r *InstanceRegistry) GetInstance(t reflect.Type) (instance interface{}, er } var ( - td *TypeDefinition - comV interface{} - ok bool + td *TypeDefinition + injectableV interface{} + ok bool ) rt, _, _ := our.GetTypeInfo(t) @@ -150,8 +179,8 @@ func (r *InstanceRegistry) GetInstance(t reflect.Type) (instance interface{}, er } } - if comV, ok = r.instanceByType[td.RealType]; ok { - return comV, nil + if injectableV, ok = r.instanceByType[td.RealType]; ok { + return injectableV, nil } v := reflect.New(rt) @@ -159,6 +188,14 @@ func (r *InstanceRegistry) GetInstance(t reflect.Type) (instance interface{}, er instance = v.Interface() r.instanceByType[td.RealType] = instance + + if a, err := oa.GetTypeAnnotation(td.Type, annotation.InjectableAnnotationType); nil == err && nil != a { + _a := a.(*annotation.InjectableAnnotation) + if "" != _a.Name { + r.instanceByName[_a.Name] = instance + } + } + err = nil defer func() { if nil != err { @@ -177,6 +214,7 @@ func (r *InstanceRegistry) GetInstance(t reflect.Type) (instance interface{}, er return nil, fmt.Errorf("%v", err) } if nil != ass { + LOOP: for n, as := range ass { f := rv.FieldByName(n) @@ -191,26 +229,47 @@ func (r *InstanceRegistry) GetInstance(t reflect.Type) (instance interface{}, er a, ok = as[annotation.InjectAnnotationType] if ok { - if fV, err = r.GetInstance(f.Type()); nil != err { - return - } - } + _a := a.(*annotation.InjectAnnotation) + if "" == _a.Name { + if fV, err = r.GetInstance(f.Type()); nil == err { + log.Printf("%s of %s injected by type[%s]", n, td.RealType.Name(), reflect.TypeOf(fV)) + f.Set(reflect.ValueOf(fV)) + continue LOOP + } else { + err = fmt.Errorf("cannot find instance for %s[Type:%s]", n, f.Type()) + } - a, ok = as[annotation.ResourceAnnotationType] - if ok { - name := a.(*annotation.ResourceAnnotation).Name - if "" == name { - name = n - } - if fV, err = r.GetInstanceByName(name); nil != err { - return - } - } + if fV, err = r.GetInstanceByName(n); nil == err { + log.Printf("%s of %s injected by name[%s]", n, td.RealType.Name(), n) + f.Set(reflect.ValueOf(fV)) + continue LOOP + } else { + err = fmt.Errorf("cannot find instance for %s[Name:%s]", n, n) + } + } else { + if fV, err = r.GetInstanceByName(_a.Name); nil == err { + log.Printf("%s of %s injected by name[%s]", n, td.RealType.Name(), _a.Name) + f.Set(reflect.ValueOf(fV)) + continue LOOP + } else { + err = fmt.Errorf("cannot find instance for %s[Name:%s]", n, _a.Name) + } - if nil != err { - return + if fV, err = r.GetInstance(f.Type()); nil == err { + log.Printf("%s of %s injected by type[%s]", n, td.RealType.Name(), reflect.TypeOf(fV)) + f.Set(reflect.ValueOf(fV)) + continue LOOP + } else { + err = fmt.Errorf("cannot find instance for %s[Type:%s]", n, f.Type()) + } + } + + if nil != err { + if _a.Required { + return + } + } } - f.Set(reflect.ValueOf(fV)) } } @@ -221,11 +280,18 @@ func GetInstanceByName(name string) (interface{}, error) { return AppRegistry.GetInstanceByName(name) } func (r *InstanceRegistry) GetInstanceByName(name string) (interface{}, error) { + if td, ok := r.definitionByName[name]; ok { + v, err := r.GetInstance(td.Type) + if nil == err { + return v, nil + } + } + v, ok := r.instanceByName[name] if ok { return v, nil } - return nil, fmt.Errorf("Resource[%s] is not exist", name) + return nil, fmt.Errorf("Instance[%s] is not exist", name) } // GetInstances returns instance of annotated diff --git a/registry_test.go b/registry_test.go index 7e8391e..0b4e21a 100644 --- a/registry_test.go +++ b/registry_test.go @@ -52,7 +52,6 @@ func TestRegisterType(t *testing.T) { name string args args }{ - // TODO: Add test cases. { name: "InjectableService", args: args{ @@ -99,7 +98,7 @@ func TestInstanceRegistry_RegisterType(t *testing.T) { } } -func TestRegisterResource(t *testing.T) { +func TestRegisterSingletonByName(t *testing.T) { type args struct { name string resource interface{} @@ -113,14 +112,14 @@ func TestRegisterResource(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if err := RegisterResource(tt.args.name, tt.args.resource); (err != nil) != tt.wantErr { - t.Errorf("RegisterResource() error = %v, wantErr %v", err, tt.wantErr) + if err := RegisterSingletonByName(tt.args.name, tt.args.resource); (err != nil) != tt.wantErr { + t.Errorf("RegisterSingletonByName() error = %v, wantErr %v", err, tt.wantErr) } }) } } -func TestInstanceRegistry_RegisterResource(t *testing.T) { +func TestInstanceRegistry_RegisterSingletonByName(t *testing.T) { type fields struct { parent Registry definitionByType map[reflect.Type]*TypeDefinition @@ -149,8 +148,8 @@ func TestInstanceRegistry_RegisterResource(t *testing.T) { instanceByType: tt.fields.instanceByType, instanceByName: tt.fields.instanceByName, } - if err := r.RegisterResource(tt.args.name, tt.args.resource); (err != nil) != tt.wantErr { - t.Errorf("InstanceRegistry.RegisterResource() error = %v, wantErr %v", err, tt.wantErr) + if err := r.RegisterSingletonByName(tt.args.name, tt.args.resource); (err != nil) != tt.wantErr { + t.Errorf("InstanceRegistry.RegisterSingletonByName() error = %v, wantErr %v", err, tt.wantErr) } }) } @@ -208,7 +207,6 @@ func TestGetInstance(t *testing.T) { args args wantErr bool }{ - // TODO: Add test cases. { name: "InjectService", args: args{