diff --git a/annotation/annotation.go b/annotation/annotation.go index ddae130..7d6622b 100644 --- a/annotation/annotation.go +++ b/annotation/annotation.go @@ -8,6 +8,7 @@ import ( const ( AnnotationTag = "annotation" + AnnotationMetaTag = "annotation-meta" AnnotationChar = "@" AnnotationStartChar = "(" AnnotationEndChar = ")" @@ -16,18 +17,7 @@ const ( AnnotationKeyValueSpliter = "=" ) -var annotationRegistry map[string]reflect.Type - -func init() { - annotationRegistry = make(map[string]reflect.Type, 0) -} - -func registerAnnotation(name string, t reflect.Type) { - annotationRegistry[name] = t -} - type Annotation interface { - parseAttribute(attributes map[string]string) error } // @Inject(name? string) @@ -49,14 +39,14 @@ func ParseAnnotation(tag reflect.StructTag) (map[string]Annotation, error) { if "" == a { continue } - aName, attributes, err := ParseAnnotationItem(a) + aName, attributes, err := parseAnnotationItem(a) if nil != err { return nil, err } if "" == aName { continue } - annotation, err := NewAnnotation(aName, attributes) + annotation, err := newAnnotation(aName, attributes) if nil != err { return nil, err } @@ -66,22 +56,22 @@ func ParseAnnotation(tag reflect.StructTag) (map[string]Annotation, error) { return rKVs, nil } -func NewAnnotation(name string, attributes map[string]string) (Annotation, error) { - t, ok := annotationRegistry[name] +func newAnnotation(name string, attributes map[string]string) (Annotation, error) { + def, ok := annotationRegistry[name] if !ok { return nil, fmt.Errorf("There is no annotation[%s]", name) } - v := reflect.New(t.Elem()) + v := reflect.New(def.rt) i := v.Interface().(Annotation) if nil != attributes { - i.parseAttribute(attributes) + setMetaAttributes(def, v.Elem(), attributes) } return i, nil } -func ParseAnnotationItem(a string) (name string, attributes map[string]string, err error) { +func parseAnnotationItem(a string) (name string, attributes map[string]string, err error) { s := strings.Trim(a, " ") if "" == s { return @@ -120,7 +110,7 @@ func ParseAnnotationItem(a string) (name string, attributes map[string]string, e return } - attrs, pErr := ParseAttribute(s[aStart+1 : aEnd]) + attrs, pErr := parseAttributeString(s[aStart+1 : aEnd]) if nil != pErr { err = pErr return @@ -131,7 +121,7 @@ func ParseAnnotationItem(a string) (name string, attributes map[string]string, e return } -func ParseAttribute(s string) (map[string]string, error) { +func parseAttributeString(s string) (map[string]string, error) { attr := strings.Trim(s, " ") if "" == attr { return nil, nil diff --git a/annotation/component.go b/annotation/component.go deleted file mode 100644 index 26f95a9..0000000 --- a/annotation/component.go +++ /dev/null @@ -1,13 +0,0 @@ -package annotation - -import ( - "git.loafle.net/commons_go/di" -) - -type ComponentAnnotation struct { - Annotation - Name string - InitMethod string // func (receiver interface{}, cr ComponentRegistry) error - DestroyMethod string // func (receiver interface{}, cr ComponentRegistry) error - Scope di.ScopeType -} diff --git a/annotation/definition.go b/annotation/definition.go new file mode 100644 index 0000000..c53a0a6 --- /dev/null +++ b/annotation/definition.go @@ -0,0 +1,33 @@ +package annotation + +import ( + "reflect" + + cdur "git.loafle.net/commons_go/di/util/reflect" +) + +var annotationRegistry map[string]*AnnotationDefinition + +func init() { + annotationRegistry = make(map[string]*AnnotationDefinition, 0) +} + +func RegisterAnnotation(name string, t reflect.Type) { + meta := getMetaFields(t) + fRT, _, _ := cdur.GetTypeInfo(t) + + def := &AnnotationDefinition{ + t: t, + rt: fRT, + fields: meta, + } + + annotationRegistry[name] = def +} + +type AnnotationDefinition struct { + t reflect.Type + rt reflect.Type + + fields map[string]*AnnotationFieldMeta +} diff --git a/annotation/inject.go b/annotation/inject.go deleted file mode 100644 index 87973a6..0000000 --- a/annotation/inject.go +++ /dev/null @@ -1,33 +0,0 @@ -package annotation - -// @Inject(name? string) - -import ( - "fmt" - "reflect" -) - -const ( - InjectTag = "@Inject" -) - -func init() { - registerAnnotation(InjectTag, reflect.TypeOf((*Inject)(nil))) -} - -type Inject struct { - Annotation - Name string -} - -func (a *Inject) parseAttribute(attributes map[string]string) error { - for k, v := range attributes { - switch k { - case "name": - a.Name = v - default: - return fmt.Errorf("Syntax error: not supported attribute[%s]", k) - } - } - return nil -} diff --git a/annotation/meta.go b/annotation/meta.go new file mode 100644 index 0000000..b249315 --- /dev/null +++ b/annotation/meta.go @@ -0,0 +1,87 @@ +package annotation + +import ( + "reflect" + "strings" + + cdur "git.loafle.net/commons_go/di/util/reflect" +) + +type AnnotationFieldMeta struct { + fieldName string + options anotationMetaOptions +} + +type anotationMetaOptions string + +func (o anotationMetaOptions) Contains(optionName string) bool { + if len(o) == 0 { + return false + } + s := string(o) + for s != "" { + var next string + i := strings.Index(s, AnnotationAttributeSpliter) + if i >= 0 { + s, next = s[:i], s[i+1:] + } + if s == optionName { + return true + } + s = next + } + return false +} + +func getMetaFields(t reflect.Type) map[string]*AnnotationFieldMeta { + fields := make(map[string]*AnnotationFieldMeta, 0) + rt, _, _ := cdur.GetTypeInfo(t) + if reflect.Struct != rt.Kind() { + return fields + } + + for i := 0; i < rt.NumField(); i++ { + f := rt.Field(i) + + if f.Anonymous { + pFields := getMetaFields(f.Type) + for k, v := range pFields { + fields[k] = v + } + } else { + name, metaOptions := parseAnnotationMeta(f.Tag) + if "" == name { + continue + } + fields[name] = &AnnotationFieldMeta{ + fieldName: f.Name, + options: metaOptions, + } + } + } + + return fields +} + +func parseAnnotationMeta(tag reflect.StructTag) (string, anotationMetaOptions) { + s := strings.Trim(tag.Get(AnnotationMetaTag), " ") + if "" == s { + return "", "" + } + + if idx := strings.Index(s, AnnotationAttributeSpliter); idx != -1 { + return s[:idx], anotationMetaOptions(s[idx+1:]) + } + + return s, anotationMetaOptions("") +} + +func setMetaAttributes(def *AnnotationDefinition, rv reflect.Value, attributes map[string]string) { + for k, v := range attributes { + meta := def.fields[k] + + f := rv.FieldByName(meta.fieldName) + + f.Set(reflect.ValueOf(v)) + } +} diff --git a/annotation/resource.go b/annotation/resource.go deleted file mode 100644 index 9d560fd..0000000 --- a/annotation/resource.go +++ /dev/null @@ -1,31 +0,0 @@ -package annotation - -import ( - "fmt" - "reflect" -) - -const ( - ResourceTag = "@Resource" -) - -func init() { - registerAnnotation(ResourceTag, reflect.TypeOf((*Resource)(nil))) -} - -type Resource struct { - Annotation - Name string -} - -func (a *Resource) parseAttribute(attributes map[string]string) error { - for k, v := range attributes { - switch k { - case "name": - a.Name = v - default: - return fmt.Errorf("Syntax error: not supported attribute[%s]", k) - } - } - return nil -} diff --git a/annotation/type.go b/annotation/type.go new file mode 100644 index 0000000..28b3416 --- /dev/null +++ b/annotation/type.go @@ -0,0 +1,5 @@ +package annotation + +type TypeAnnotation interface { + Annotation +} diff --git a/constants.go b/constants.go index dec394a..5d2ce45 100644 --- a/constants.go +++ b/constants.go @@ -16,10 +16,10 @@ package di // destroyMethod string // scope enum singleton, transiant -type ScopeType int +type ScopeType string const ( - ScopeTypeDefault ScopeType = iota // == ScopeTypeSingleton - ScopeTypeSingleton - ScopeTypeTransiant + ScopeTypeDefault ScopeType = "Default" // == ScopeTypeSingleton + ScopeTypeSingleton ScopeType = "Singleton" + ScopeTypeTransiant ScopeType = "Transiant" ) diff --git a/injection/annotation/component.go b/injection/annotation/component.go new file mode 100644 index 0000000..2a26152 --- /dev/null +++ b/injection/annotation/component.go @@ -0,0 +1,26 @@ +package annotation + +// @Inject(name? string) + +import ( + "reflect" + + "git.loafle.net/commons_go/di" + cda "git.loafle.net/commons_go/di/annotation" +) + +const ( + ComponentTag = "@Component" +) + +func init() { + cda.RegisterAnnotation(ComponentTag, reflect.TypeOf((*Component)(nil))) +} + +type Component struct { + cda.Annotation + Name string `annotation-meta:"name"` + InitMethod string `annotation-meta:"initMethod"` // func (receiver interface{}, cr ComponentRegistry) error + DestroyMethod string `annotation-meta:"destroyMethod"` // func (receiver interface{}, cr ComponentRegistry) error + Scope di.ScopeType `annotation-meta:"scope"` +} diff --git a/injection/annotation/inject.go b/injection/annotation/inject.go new file mode 100644 index 0000000..c440d16 --- /dev/null +++ b/injection/annotation/inject.go @@ -0,0 +1,22 @@ +package annotation + +// @Inject(name? string) + +import ( + "reflect" + + cda "git.loafle.net/commons_go/di/annotation" +) + +const ( + InjectTag = "@Inject" +) + +func init() { + cda.RegisterAnnotation(InjectTag, reflect.TypeOf((*Inject)(nil))) +} + +type Inject struct { + cda.Annotation + Name string `annotation-meta:"name"` +} diff --git a/annotation/inject_test.go b/injection/annotation/inject_test.go similarity index 100% rename from annotation/inject_test.go rename to injection/annotation/inject_test.go diff --git a/injection/annotation/resource.go b/injection/annotation/resource.go new file mode 100644 index 0000000..7f702d3 --- /dev/null +++ b/injection/annotation/resource.go @@ -0,0 +1,20 @@ +package annotation + +import ( + "reflect" + + cda "git.loafle.net/commons_go/di/annotation" +) + +const ( + ResourceTag = "@Resource" +) + +func init() { + cda.RegisterAnnotation(ResourceTag, reflect.TypeOf((*Resource)(nil))) +} + +type Resource struct { + cda.Annotation + Name string `annotation-meta:"name"` +} diff --git a/annotation/resource_test.go b/injection/annotation/resource_test.go similarity index 100% rename from annotation/resource_test.go rename to injection/annotation/resource_test.go diff --git a/registry/definition.go b/registry/definition.go index 88db906..0719b10 100644 --- a/registry/definition.go +++ b/registry/definition.go @@ -14,7 +14,15 @@ type TypeDefinition struct { Type reflect.Type RealType reflect.Type - Fields []*FieldDefinition + TypeAnnotations map[string]cda.Annotation + Fields []*FieldDefinition +} + +func (td *TypeDefinition) GetAnnotation(name string) cda.Annotation { + if nil == td.TypeAnnotations { + return nil + } + return td.TypeAnnotations[name] } type FieldDefinition struct { diff --git a/registry/registry.go b/registry/registry.go index a6a5c6e..29e5664 100644 --- a/registry/registry.go +++ b/registry/registry.go @@ -8,6 +8,7 @@ import ( "strings" cda "git.loafle.net/commons_go/di/annotation" + cdia "git.loafle.net/commons_go/di/injection/annotation" cdur "git.loafle.net/commons_go/di/util/reflect" "git.loafle.net/commons_go/logging" ) @@ -19,7 +20,7 @@ func init() { var registry ComponentRegistry type ComponentRegistry interface { - RegisterType(t reflect.Type, ca *cda.ComponentAnnotation) + RegisterType(t reflect.Type) RegisterResource(name string, resource interface{}) error GetInstance(t reflect.Type) interface{} @@ -43,16 +44,16 @@ type defaultComponentRegistry struct { resourceByName map[string]interface{} } -func RegisterType(t reflect.Type, ca *cda.ComponentAnnotation) { +func RegisterType(t reflect.Type) { pc, _, _, ok := runtime.Caller(1) details := runtime.FuncForPC(pc) if ok && details != nil { log.Printf("called from %s\n", details.Name()) } - registry.RegisterType(t, ca) + registry.RegisterType(t) } -func (cr *defaultComponentRegistry) RegisterType(t reflect.Type, ca *cda.ComponentAnnotation) { +func (cr *defaultComponentRegistry) RegisterType(t reflect.Type) { if nil == t { logging.Logger().Panic("DI: t[reflect.Type] is nil") } @@ -70,6 +71,9 @@ func (cr *defaultComponentRegistry) RegisterType(t reflect.Type, ca *cda.Compone } cr.definitionByType[td.RealType] = td + // _ca := ca.(*cdia.ComponentAnnotation) + ca := td.GetAnnotation(cdia.ComponentTag).(*cdia.Component) + name := "" if nil != ca && "" != strings.Trim(ca.Name, " ") { name = ca.Name @@ -145,13 +149,13 @@ func (cr *defaultComponentRegistry) GetInstance(t reflect.Type) interface{} { logging.Logger().Panic(fmt.Sprintf("DI: Field[%s] can not set", fd.FieldName)) } - annotation = fd.GetAnnotation(cda.InjectTag) + annotation = fd.GetAnnotation(cdia.InjectTag) if nil != annotation { fV = cr.GetInstance(fd.Type) } - annotation = fd.GetAnnotation(cda.ResourceTag) + annotation = fd.GetAnnotation(cdia.ResourceTag) if nil != annotation { - n := annotation.(*cda.Resource).Name + n := annotation.(*cdia.Resource).Name if "" == n { n = fd.FieldName } @@ -193,32 +197,32 @@ func (cr *defaultComponentRegistry) buildDefinition(t reflect.Type) (*TypeDefini td.TypeName = tName td.Type = t td.RealType = rt + td.Fields = make([]*FieldDefinition, 0) - fields := getFields(rt) - - if 0 < len(fields) { - td.Fields = fields - } + parseFields(rt, td) return td, nil } -func getFields(t reflect.Type) []*FieldDefinition { - fields := make([]*FieldDefinition, 0) +func parseFields(t reflect.Type, td *TypeDefinition) { + // fields := make([]*FieldDefinition, 0) rt, _, _ := cdur.GetTypeInfo(t) if reflect.Struct != rt.Kind() { - return fields + return } for i := 0; i < rt.NumField(); i++ { f := rt.Field(i) if f.Anonymous { - fields = append(fields, getFields(f.Type)...) + if parseAnonymousField(f, td) { + continue + } + parseFields(f.Type, td) } else { as, err := cda.ParseAnnotation(f.Tag) if nil != err { - return fields + return } if nil != as && 0 < len(as) { fRT, fPkgName, fTName := cdur.GetTypeInfo(f.Type) @@ -231,10 +235,31 @@ func getFields(t reflect.Type) []*FieldDefinition { RealType: fRT, Annotations: as, } - fields = append(fields, fd) + td.Fields = append(td.Fields, fd) } } } - - return fields +} + +func parseAnonymousField(f reflect.StructField, td *TypeDefinition) bool { + logging.Logger().Debug(fmt.Sprintf("Anonymous Pkg:%s, Type: %s", f.Type.PkgPath(), f.Type.Name())) + + ct := reflect.TypeOf((*cda.TypeAnnotation)(nil)).Elem() + logging.Logger().Debug(fmt.Sprintf("TypeAnnon Pkg:%s, Type: %s", ct.PkgPath(), ct.Name())) + + if f.Type != ct { + return false + } + + as, err := cda.ParseAnnotation(f.Tag) + if nil != err { + return false + } + + if nil != as && 0 < len(as) { + td.TypeAnnotations = as + return true + } + + return false } diff --git a/registry/registry_test.go b/registry/registry_test.go index 7206950..1549ef9 100644 --- a/registry/registry_test.go +++ b/registry/registry_test.go @@ -4,10 +4,13 @@ import ( "log" "reflect" "testing" + + "git.loafle.net/commons_go/di/annotation" ) func TestRegisterType(t *testing.T) { - // RegisterType(reflect.TypeOf((*TestStruct1)(nil)), &cda.ComponentAnnotation{}) + RegisterType(reflect.TypeOf((*AService)(nil))) + RegisterType(reflect.TypeOf((*BService)(nil))) // RegisterType(reflect.TypeOf((*TestStruct2)(nil)), &cda.ComponentAnnotation{ // Name: "test1", // }) @@ -20,21 +23,21 @@ func TestRegisterType(t *testing.T) { RegisterResource("List", []string{"dfdkf", "skgkfg"}) - i, err := GetInstance(reflect.TypeOf((*CService)(nil))) - if nil != err { - log.Printf("%v", err) - } else { - cs := i.(*CService) - log.Printf("%v", cs) - } + cs := GetInstance(reflect.TypeOf((*CService)(nil))).(*CService) + + log.Printf("%v", cs) } type AService struct { + annotation.TypeAnnotation `annotation:"@Component(name=dkdkdf)"` + NameA string } type BService struct { + annotation.TypeAnnotation `annotation:"@Component()"` + NameB string }