@@ -9,52 +9,84 @@ import (
9
9
"unsafe"
10
10
)
11
11
12
- // binding holds a resolver and a concrete (if singleton ).
12
+ // binding holds a resolver and a concrete (if already resolved ).
13
13
// It is the break for the Container wall!
14
14
type binding struct {
15
- resolver interface {} // resolver is the function that is responsible for making the concrete.
16
- concrete interface {} // concrete is the stored instance for singleton bindings.
15
+ resolver interface {} // resolver is the function that is responsible for making the concrete.
16
+ concrete interface {} // concrete is the stored instance for singleton bindings.
17
+ isSingleton bool // isSingleton is true if the binding is a singleton.
17
18
}
18
19
19
20
// make resolves the binding if needed and returns the resolved concrete.
20
- func (b binding ) make (c Container ) (interface {}, error ) {
21
+ func (b * binding ) make (c Container ) (interface {}, error ) {
21
22
if b .concrete != nil {
22
23
return b .concrete , nil
23
24
}
24
- return c .invoke (b .resolver )
25
+
26
+ retVal , err := c .invoke (b .resolver )
27
+ if b .isSingleton {
28
+ b .concrete = retVal
29
+ }
30
+
31
+ return retVal , err
25
32
}
26
33
27
34
// Container holds the bindings and provides methods to interact with them.
28
35
// It is the entry point in the package.
29
- type Container map [reflect.Type ]map [string ]binding
36
+ type Container map [reflect.Type ]map [string ]* binding
30
37
31
38
// New creates a new concrete of the Container.
32
39
func New () Container {
33
40
return make (Container )
34
41
}
35
42
36
43
// bind maps an abstraction to concrete and instantiates if it is a singleton binding.
37
- func (c Container ) bind (resolver interface {}, name string , isSingleton bool ) error {
44
+ func (c Container ) bind (resolver interface {}, name string , isSingleton bool , isLazy bool ) error {
38
45
reflectedResolver := reflect .TypeOf (resolver )
39
46
if reflectedResolver .Kind () != reflect .Func {
40
47
return errors .New ("container: the resolver must be a function" )
41
48
}
42
49
43
50
if reflectedResolver .NumOut () > 0 {
44
51
if _ , exist := c [reflectedResolver .Out (0 )]; ! exist {
45
- c [reflectedResolver .Out (0 )] = make (map [string ]binding )
52
+ c [reflectedResolver .Out (0 )] = make (map [string ]* binding )
46
53
}
47
54
}
48
55
49
- concrete , err := c .invoke (resolver )
50
- if err != nil {
56
+ if err := c .validateResolverFunction (reflectedResolver ); err != nil {
51
57
return err
52
58
}
53
59
60
+ var concrete interface {}
61
+ if ! isLazy {
62
+ var err error
63
+ concrete , err = c .invoke (resolver )
64
+ if err != nil {
65
+ return err
66
+ }
67
+ }
68
+
54
69
if isSingleton {
55
- c [reflectedResolver .Out (0 )][name ] = binding {resolver : resolver , concrete : concrete }
70
+ c [reflectedResolver .Out (0 )][name ] = & binding {resolver : resolver , concrete : concrete , isSingleton : isSingleton }
56
71
} else {
57
- c [reflectedResolver .Out (0 )][name ] = binding {resolver : resolver }
72
+ c [reflectedResolver .Out (0 )][name ] = & binding {resolver : resolver , isSingleton : isSingleton }
73
+ }
74
+
75
+ return nil
76
+ }
77
+
78
+ func (c Container ) validateResolverFunction (funcType reflect.Type ) error {
79
+ retCount := funcType .NumOut ()
80
+
81
+ if retCount == 0 || retCount > 2 {
82
+ return errors .New ("container: resolver function signature is invalid - it must return abstract, or abstract and error" )
83
+ }
84
+
85
+ resolveType := funcType .Out (0 )
86
+ for i := 0 ; i < funcType .NumIn (); i ++ {
87
+ if funcType .In (i ) == resolveType {
88
+ return fmt .Errorf ("container: resolver function signature is invalid - depends on abstract it returns" )
89
+ }
58
90
}
59
91
60
92
return nil
@@ -69,17 +101,12 @@ func (c Container) invoke(function interface{}) (interface{}, error) {
69
101
}
70
102
71
103
values := reflect .ValueOf (function ).Call (arguments )
72
-
73
- if len (values ) == 1 || len (values ) == 2 {
74
- if len (values ) == 2 && values [1 ].CanInterface () {
75
- if err , ok := values [1 ].Interface ().(error ); ok {
76
- return values [0 ].Interface (), err
77
- }
104
+ if len (values ) == 2 && values [1 ].CanInterface () {
105
+ if err , ok := values [1 ].Interface ().(error ); ok {
106
+ return values [0 ].Interface (), err
78
107
}
79
- return values [0 ].Interface (), nil
80
108
}
81
-
82
- return nil , errors .New ("container: resolver function signature is invalid" )
109
+ return values [0 ].Interface (), nil
83
110
}
84
111
85
112
// arguments returns the list of resolved arguments for a function.
@@ -91,10 +118,13 @@ func (c Container) arguments(function interface{}) ([]reflect.Value, error) {
91
118
for i := 0 ; i < argumentsCount ; i ++ {
92
119
abstraction := reflectedFunction .In (i )
93
120
if concrete , exist := c [abstraction ]["" ]; exist {
94
- instance , _ := concrete .make (c )
121
+ instance , err := concrete .make (c )
122
+ if err != nil {
123
+ return nil , err
124
+ }
95
125
arguments [i ] = reflect .ValueOf (instance )
96
126
} else {
97
- return nil , errors .New ("container: no concrete found for " + abstraction .String ())
127
+ return nil , errors .New ("container: no concrete found for: " + abstraction .String ())
98
128
}
99
129
}
100
130
@@ -112,24 +142,52 @@ func (c Container) Reset() {
112
142
// It takes a resolver function that returns the concrete, and its return type matches the abstraction (interface).
113
143
// The resolver function can have arguments of abstraction that have been declared in the Container already.
114
144
func (c Container ) Singleton (resolver interface {}) error {
115
- return c .bind (resolver , "" , true )
145
+ return c .bind (resolver , "" , true , false )
146
+ }
147
+
148
+ // SingletonLazy binds an abstraction to concrete lazily in singleton mode.
149
+ // The concrete is resolved only when the abstraction is resolved for the first time.
150
+ // It takes a resolver function that returns the concrete, and its return type matches the abstraction (interface).
151
+ // The resolver function can have arguments of abstraction that have been declared in the Container already.
152
+ func (c Container ) SingletonLazy (resolver interface {}) error {
153
+ return c .bind (resolver , "" , true , true )
116
154
}
117
155
118
156
// NamedSingleton binds a named abstraction to concrete in singleton mode.
119
157
func (c Container ) NamedSingleton (name string , resolver interface {}) error {
120
- return c .bind (resolver , name , true )
158
+ return c .bind (resolver , name , true , false )
159
+ }
160
+
161
+ // NamedSingleton binds a named abstraction to concrete lazily in singleton mode.
162
+ // The concrete is resolved only when the abstraction is resolved for the first time.
163
+ func (c Container ) NamedSingletonLazy (name string , resolver interface {}) error {
164
+ return c .bind (resolver , name , true , true )
121
165
}
122
166
123
167
// Transient binds an abstraction to concrete in transient mode.
124
168
// It takes a resolver function that returns the concrete, and its return type matches the abstraction (interface).
125
169
// The resolver function can have arguments of abstraction that have been declared in the Container already.
126
170
func (c Container ) Transient (resolver interface {}) error {
127
- return c .bind (resolver , "" , false )
171
+ return c .bind (resolver , "" , false , false )
128
172
}
129
173
130
- // NamedTransient binds a named abstraction to concrete in transient mode.
174
+ // TransientLazy binds an abstraction to concrete lazily in transient mode.
175
+ // Normally the resolver will be called during registration, but that is skipped in lazy mode.
176
+ // It takes a resolver function that returns the concrete, and its return type matches the abstraction (interface).
177
+ // The resolver function can have arguments of abstraction that have been declared in the Container already.
178
+ func (c Container ) TransientLazy (resolver interface {}) error {
179
+ return c .bind (resolver , "" , false , true )
180
+ }
181
+
182
+ // NamedTransient binds a named abstraction to concrete lazily in transient mode.
131
183
func (c Container ) NamedTransient (name string , resolver interface {}) error {
132
- return c .bind (resolver , name , false )
184
+ return c .bind (resolver , name , false , false )
185
+ }
186
+
187
+ // NamedTransient binds a named abstraction to concrete in transient mode.
188
+ // Normally the resolver will be called during registration, but that is skipped in lazy mode.
189
+ func (c Container ) NamedTransientLazy (name string , resolver interface {}) error {
190
+ return c .bind (resolver , name , false , true )
133
191
}
134
192
135
193
// Call takes a receiver function with one or more arguments of the abstractions (interfaces).
@@ -214,21 +272,22 @@ func (c Container) Fill(structure interface{}) error {
214
272
} else if t == "name" {
215
273
name = s .Type ().Field (i ).Name
216
274
} else {
217
- return errors .New (
218
- fmt .Sprintf ("container: %v has an invalid struct tag" , s .Type ().Field (i ).Name ),
219
- )
275
+ return fmt .Errorf ("container: %v has an invalid struct tag" , s .Type ().Field (i ).Name )
220
276
}
221
277
222
278
if concrete , exist := c [f .Type ()][name ]; exist {
223
- instance , _ := concrete .make (c )
279
+ instance , err := concrete .make (c )
280
+ if err != nil {
281
+ return err
282
+ }
224
283
225
284
ptr := reflect .NewAt (f .Type (), unsafe .Pointer (f .UnsafeAddr ())).Elem ()
226
285
ptr .Set (reflect .ValueOf (instance ))
227
286
228
287
continue
229
288
}
230
289
231
- return errors . New ( fmt .Sprintf ("container: cannot make %v field" , s .Type ().Field (i ).Name ) )
290
+ return fmt .Errorf ("container: cannot make %v field" , s .Type ().Field (i ).Name )
232
291
}
233
292
}
234
293
0 commit comments