1
1
package autowire
2
2
3
3
import (
4
+ "io"
4
5
"log"
5
6
"os"
6
7
"os/signal"
7
8
"reflect"
8
9
"regexp"
9
10
"strings"
11
+ "unicode"
10
12
)
11
13
12
14
var dependencies map [string ]interface {}
@@ -19,16 +21,50 @@ func init() {
19
21
log .SetFlags (log .LstdFlags | log .Lshortfile )
20
22
log .Println ("Init Autowire Context" )
21
23
signal .Notify (ch , os .Interrupt , os .Kill )
22
-
23
24
dependencies = make (map [string ]interface {})
24
25
}
25
26
26
- func RunOmitTest (f func ()) {
27
- if currentProfile != _Testing {
28
- f ()
27
+ // InitProd executes function only in production so it is preventing execution in go tests.
28
+ // This flexibility could help if we want to skip autowiring struct in our tests.
29
+ func InitProd (initFunc func ()) {
30
+ if currentProfile == _Production {
31
+ initFunc ()
29
32
}
30
33
}
31
34
35
+ // Autowire function injects all dependencies for the given structure v.
36
+ // In order dependencies to be injected the desired struct fields should be marked with
37
+ // autowire tag.
38
+ //
39
+ // Injection of concrete type
40
+ //
41
+ // Currently both type of fields exported and unexported are supported.
42
+ // Following snippet shows injecting dependencies inside private structure fields using `autowire:""` tag:
43
+ // type Application struct {
44
+ // config *configuration.ApplicationConfig `autowire:""`
45
+ // }
46
+ // func (a *Application) SetConfig(config *configuration.ApplicationConfig) {
47
+ // a.config = config
48
+ // }
49
+ // If we need dependency to be injected into unexported field Set<FieldName> function is required, as show above.
50
+ // type Application struct {
51
+ // Config *configuration.ApplicationConfig `autowire:""`
52
+ // }
53
+ // Injection of dependency into exported is supported also and there is no need to provide additional Setter.
54
+ //
55
+ // Injection of interface
56
+ //
57
+ // Often it`s better to rely on interface instead of concrete type, in order to accomplish this decoupling we specify
58
+ // interfaces as a type of struct fields. The following snippet demonstrate that
59
+ // type UserService struct {
60
+ // userRoleRepository UserRoleRepository `autowire:"service/InMemoryUserRoleRepository"`
61
+ // }
62
+ // func (u *UserService) SetUserRoleRepository(userRoleRepository UserRoleRepository) {
63
+ // u.userRoleRepository = userRoleRepository
64
+ // }
65
+ // UserRoleRepository is simply an interface and InMemoryUserRoleRepository is a struct, which implements this interface.
66
+ // As it`s done dependency injection on unexported field, providing Setter is required. Just to highlight unexported fields
67
+ // needs Setter while exported not. For more information take a look at example package.
32
68
func Autowire (v interface {}) {
33
69
value := reflect .ValueOf (v )
34
70
switch value .Kind () {
@@ -38,17 +74,23 @@ func Autowire(v interface{}) {
38
74
structType := getStructPtrFullPath (value )
39
75
_ , ok := dependencies [structType ]
40
76
if ok {
41
- log .Printf ("%s already autowired. Going go overwrite it`s value! " , structType )
77
+ log .Printf ("%s already autowired... ignored " , structType )
42
78
} else {
79
+ log .Printf ("Autowiring %s" , structType )
43
80
autowireDependencies (value )
44
81
dependencies [structType ] = v
45
82
}
46
83
default : // reflect.Array, reflect.Struct, reflect.Interface
47
84
log .Println (value .Type ().String () + " value" )
48
85
}
49
- log .Println (dependencies )
86
+ // log.Println(dependencies)
50
87
}
51
88
89
+ // Autowired function returns fully initialized with all dependencies instance, which is ready to be used.
90
+ //As the result is empty interface, type assertions is required before using the instance.
91
+ //Take a look at https://golang.org/ref/spec#Type_assertions for more information.
92
+ // The following snippet demonstrate how could be done :
93
+ // app := autowire.Autowired(app.Application{}).(*app.Application)
52
94
func Autowired (v interface {}) interface {} {
53
95
value := reflect .ValueOf (v )
54
96
var path string
@@ -62,7 +104,6 @@ func Autowired(v interface{}) interface{} {
62
104
default :
63
105
log .Panicln ("Unknown Autowired Typed!" )
64
106
}
65
- log .Println (path )
66
107
dependency , ok := dependencies [path ]
67
108
if ok {
68
109
return dependency
@@ -86,39 +127,62 @@ func autowireDependencies(value reflect.Value) {
86
127
tag , ok := field .Tag .Lookup (Tag )
87
128
if ok {
88
129
var t reflect.Value
89
- log .Println ("......." + field .Type .String ())
90
130
if tag != "" {
91
- log .Printf ("field type: %v\n " , getFullPath (field .Type .PkgPath (), field .Type .String ()))
92
131
currentDep := findDependency (tag )
93
132
if len (currentDep ) == 0 {
94
133
msg := "Unknown dependency " + tag + " found none"
95
134
if currentProfile != _Testing {
96
135
log .Panicln (msg )
97
136
} else {
98
- log .Println (msg )
137
+ log .Println (msg + ", ready for spy" )
99
138
}
100
139
} else {
101
140
v := reflect .ValueOf (currentDep [0 ])
102
141
if v .Type ().Implements (field .Type ) {
103
142
t = reflect .New (v .Type ())
104
- log . Println ( "found dependency by tag " + tag + " will be used " + t . Type (). String ())
105
- elem . Field ( i ). Set ( reflect . ValueOf ( currentDep [ 0 ]) )
143
+ dependency := currentDep [ 0 ]
144
+ setValue ( value , elem , i , dependency )
106
145
} else {
107
146
log .Panicln (v .Type ().String () + " doesnt Implements: " + field .Type .String ())
108
147
}
109
148
}
110
149
} else {
111
150
t = reflect .New (elem .Type ().Field (i ).Type .Elem ())
112
- log .Printf ("field type: %v" , getStructPtrFullPath (t ))
113
151
dependency , found := dependencies [getStructPtrFullPath (t )]
114
152
if found {
115
- elem .Field (i ).Set (reflect .ValueOf (dependency ))
153
+ runeName := []rune (elem .Type ().Field (i ).Name )
154
+ exported := unicode .IsUpper (runeName [0 ])
155
+ if exported {
156
+ elem .Field (i ).Set (reflect .ValueOf (dependency ))
157
+ } else {
158
+ runeName [0 ] = unicode .ToUpper (runeName [0 ])
159
+ methodName := "Set" + string (runeName )
160
+ method := value .MethodByName (methodName )
161
+ if method .IsValid () {
162
+ method .Call ([]reflect.Value {reflect .ValueOf (dependency )})
163
+ }
164
+ }
116
165
}
117
166
}
118
167
}
119
168
}
120
169
}
121
170
171
+ func setValue (value reflect.Value , elem reflect.Value , i int , dependency interface {}) {
172
+ runeName := []rune (elem .Type ().Field (i ).Name )
173
+ exported := unicode .IsUpper (runeName [0 ])
174
+ if exported {
175
+ elem .Field (i ).Set (reflect .ValueOf (dependency ))
176
+ } else {
177
+ runeName [0 ] = unicode .ToUpper (runeName [0 ])
178
+ methodName := "Set" + string (runeName )
179
+ method := value .MethodByName (methodName )
180
+ if method .IsValid () {
181
+ method .Call ([]reflect.Value {reflect .ValueOf (dependency )})
182
+ }
183
+ }
184
+ }
185
+
122
186
func findDependency (tagDependencyType string ) []interface {} {
123
187
var result []interface {}
124
188
for tmp , dep := range dependencies {
@@ -129,16 +193,24 @@ func findDependency(tagDependencyType string) []interface{} {
129
193
return result
130
194
}
131
195
132
- type AutoClosable interface {
133
- Close ()
134
- }
135
-
136
- func Run (mainFunction func ()) {
196
+ // Run executes function function after the function execution completes for each autowired struct
197
+ // which implements io.Closer interface will be invoked Call() function, so currently active
198
+ //occupied resources(connections, channels, etc.) could be released.
199
+ func Run (function func ()) {
137
200
defer close ()
138
- mainFunction ()
201
+ function ()
139
202
}
140
203
141
204
func close () {
142
- println ("HEREE" )
143
-
205
+ log .Println ("Closing..." )
206
+ for _ , dependency := range dependencies {
207
+ valueDepend := reflect .ValueOf (dependency )
208
+ closerType := reflect .TypeOf ((* io .Closer )(nil )).Elem ()
209
+ if valueDepend .Type ().Implements (closerType ) {
210
+ err := dependency .(io.Closer ).Close ()
211
+ if err != nil {
212
+ log .Println (err .Error ())
213
+ }
214
+ }
215
+ }
144
216
}
0 commit comments