struct.go 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package di
  2. import (
  3. "fmt"
  4. "reflect"
  5. )
  6. // Scope is the struct injector's struct value scope/permant state.
  7. // See `Stateless` and `Singleton`.
  8. type Scope uint8
  9. const (
  10. // Stateless is the scope that the struct should be different on each binding,
  11. // think it like `Request Scoped`, per-request struct for mvc.
  12. Stateless Scope = iota
  13. // Singleton is the scope that the struct is the same
  14. // between calls, it has no dynamic dependencies or
  15. // any unexported fields that is not seted on creation,
  16. // so it doesn't need to be created on each call/request.
  17. Singleton
  18. )
  19. type (
  20. targetStructField struct {
  21. Object *BindObject
  22. FieldIndex []int
  23. }
  24. // StructInjector keeps the data that are needed in order to do the binding injection
  25. // as fast as possible and with the best possible and safest way.
  26. StructInjector struct {
  27. initRef reflect.Value
  28. initRefAsSlice []reflect.Value // useful when the struct is passed on a func as input args via reflection.
  29. elemType reflect.Type
  30. //
  31. fields []*targetStructField
  32. // is true when contains bindable fields and it's a valid target struct,
  33. // it maybe 0 but struct may contain unexported fields or exported but no bindable (Stateless)
  34. // see `setState`.
  35. Has bool
  36. CanInject bool // if any bindable fields when the state is NOT singleton.
  37. Scope Scope
  38. }
  39. )
  40. func (s *StructInjector) countBindType(typ BindType) (n int) {
  41. for _, f := range s.fields {
  42. if f.Object.BindType == typ {
  43. n++
  44. }
  45. }
  46. return
  47. }
  48. // MakeStructInjector returns a new struct injector, which will be the object
  49. // that the caller should use to bind exported fields or
  50. // embedded unexported fields that contain exported fields
  51. // of the "v" struct value or pointer.
  52. //
  53. // The hijack and the goodFunc are optional, the "values" is the dependencies collection.
  54. func MakeStructInjector(v reflect.Value, hijack Hijacker, goodFunc TypeChecker, values ...reflect.Value) *StructInjector {
  55. s := &StructInjector{
  56. initRef: v,
  57. initRefAsSlice: []reflect.Value{v},
  58. elemType: IndirectType(v.Type()),
  59. }
  60. fields := lookupFields(s.elemType, true, nil)
  61. for _, f := range fields {
  62. if hijack != nil {
  63. if b, ok := hijack(f.Type); ok && b != nil {
  64. s.fields = append(s.fields, &targetStructField{
  65. FieldIndex: f.Index,
  66. Object: b,
  67. })
  68. continue
  69. }
  70. }
  71. for _, val := range values {
  72. // the binded values to the struct's fields.
  73. b, err := MakeBindObject(val, goodFunc)
  74. if err != nil {
  75. return s // if error stop here.
  76. }
  77. if b.IsAssignable(f.Type) {
  78. // fmt.Printf("bind the object to the field: %s at index: %#v and type: %s\n", f.Name, f.Index, f.Type.String())
  79. s.fields = append(s.fields, &targetStructField{
  80. FieldIndex: f.Index,
  81. Object: &b,
  82. })
  83. break
  84. }
  85. }
  86. }
  87. s.Has = len(s.fields) > 0
  88. // set the overall state of this injector.
  89. s.fillStruct()
  90. s.setState()
  91. return s
  92. }
  93. // set the state, once.
  94. // Here the "initRef" have already the static bindings and the manually-filled fields.
  95. func (s *StructInjector) setState() {
  96. // note for zero length of struct's fields:
  97. // if struct doesn't contain any field
  98. // so both of the below variables will be 0,
  99. // so it's a singleton.
  100. // At the other hand the `s.HasFields` maybe false
  101. // but the struct may contain UNEXPORTED fields or non-bindable fields (request-scoped on both cases)
  102. // so a new controller/struct at the caller side should be initialized on each request,
  103. // we should not depend on the `HasFields` for singleton or no, this is the reason I
  104. // added the `.State` now.
  105. staticBindingsFieldsLength := s.countBindType(Static)
  106. allStructFieldsLength := NumFields(s.elemType, false)
  107. // check if unexported(and exported) fields are set-ed manually or via binding (at this time we have all fields set-ed inside the "initRef")
  108. // i.e &Controller{unexportedField: "my value"}
  109. // or dependencies values = "my value" and Controller struct {Field string}
  110. // if so then set the temp staticBindingsFieldsLength to that number, so for example:
  111. // if static binding length is 0
  112. // but an unexported field is set-ed then act that as singleton.
  113. if allStructFieldsLength > staticBindingsFieldsLength {
  114. structFieldsUnexportedNonZero := LookupNonZeroFieldsValues(s.initRef, false)
  115. staticBindingsFieldsLength = len(structFieldsUnexportedNonZero)
  116. }
  117. // println("staticBindingsFieldsLength: ", staticBindingsFieldsLength)
  118. // println("allStructFieldsLength: ", allStructFieldsLength)
  119. // if the number of static values binded is equal to the
  120. // total struct's fields(including unexported fields this time) then set as singleton.
  121. if staticBindingsFieldsLength == allStructFieldsLength {
  122. s.Scope = Singleton
  123. // the default is `Stateless`, which means that a new instance should be created
  124. // on each inject action by the caller.
  125. return
  126. }
  127. s.CanInject = s.Scope == Stateless && s.Has
  128. }
  129. // fill the static bindings values once.
  130. func (s *StructInjector) fillStruct() {
  131. if !s.Has {
  132. return
  133. }
  134. // if field is Static then set it to the value that passed by the caller,
  135. // so will have the static bindings already and we can just use that value instead
  136. // of creating new instance.
  137. destElem := IndirectValue(s.initRef)
  138. for _, f := range s.fields {
  139. // if field is Static then set it to the value that passed by the caller,
  140. // so will have the static bindings already and we can just use that value instead
  141. // of creating new instance.
  142. if f.Object.BindType == Static {
  143. destElem.FieldByIndex(f.FieldIndex).Set(f.Object.Value)
  144. }
  145. }
  146. }
  147. // String returns a debug trace message.
  148. func (s *StructInjector) String() (trace string) {
  149. for i, f := range s.fields {
  150. elemField := s.elemType.FieldByIndex(f.FieldIndex)
  151. trace += fmt.Sprintf("[%d] %s binding: '%s' for field '%s %s'\n",
  152. i+1, bindTypeString(f.Object.BindType), f.Object.Type.String(),
  153. elemField.Name, elemField.Type.String())
  154. }
  155. return
  156. }
  157. // Inject accepts a destination struct and any optional context value(s),
  158. // hero and mvc takes only one context value and this is the `context.Contex`.
  159. // It applies the bindings to the "dest" struct. It calls the InjectElem.
  160. func (s *StructInjector) Inject(dest interface{}, ctx ...reflect.Value) {
  161. if dest == nil {
  162. return
  163. }
  164. v := IndirectValue(ValueOf(dest))
  165. s.InjectElem(v, ctx...)
  166. }
  167. // InjectElem same as `Inject` but accepts a reflect.Value and bind the necessary fields directly.
  168. func (s *StructInjector) InjectElem(destElem reflect.Value, ctx ...reflect.Value) {
  169. for _, f := range s.fields {
  170. f.Object.Assign(ctx, func(v reflect.Value) {
  171. destElem.FieldByIndex(f.FieldIndex).Set(v)
  172. })
  173. }
  174. }
  175. // Acquire returns a new value of the struct or
  176. // the same struct that is used for resolving the dependencies.
  177. // If the scope is marked as singleton then it returns the first instance,
  178. // otherwise it creates new and returns it.
  179. //
  180. // See `Singleton` and `Stateless` for more.
  181. func (s *StructInjector) Acquire() reflect.Value {
  182. if s.Scope == Singleton {
  183. return s.initRef
  184. }
  185. return reflect.New(s.elemType)
  186. }
  187. // AcquireSlice same as `Acquire` but it returns a slice of
  188. // values structs, this can be used when a struct is passed as an input parameter
  189. // on a function, again if singleton then it returns a pre-created slice which contains
  190. // the first struct value given by the struct injector's user.
  191. func (s *StructInjector) AcquireSlice() []reflect.Value {
  192. if s.Scope == Singleton {
  193. return s.initRefAsSlice
  194. }
  195. return []reflect.Value{reflect.New(s.elemType)}
  196. }