0
# Dynamic Instance Access
1
2
Dynamic instance access provides runtime lookup capabilities for instances using the `wiredInModule` function. This creates a `Wired` instance that maps types to factory functions based on the public members of an object, enabling dynamic dependency injection patterns.
3
4
**Platform Availability:** This functionality is only fully implemented in Scala 2. The Scala 3 version exists but is not yet implemented (returns `???`).
5
6
## Core Functions
7
8
### wiredInModule
9
10
Creates a `Wired` instance that provides dynamic access to instances based on the public members of the given object.
11
12
```scala { .api }
13
def wiredInModule(in: AnyRef): Wired // Scala 2 only
14
```
15
16
**Parameters:**
17
- `in: AnyRef` - Object whose public members will be used to create instance mappings
18
19
**Returns:** `Wired` instance containing type-to-factory mappings
20
21
**Behavior:**
22
- Analyzes public nullary methods (methods with no parameters) of the input object
23
- Creates a mapping from each method's return type to a factory function that calls the method
24
- Excludes methods that don't return `AnyRef` subtypes
25
- Returns a `Wired` instance from the util module for dynamic instance lookup
26
27
## Wired Type
28
29
The `Wired` type provides dynamic instance access capabilities:
30
31
```scala { .api }
32
// From com.softwaremill.macwire.util module
33
class Wired(protected val instanceFactoryMap: InstanceFactoryMap) extends InstanceLookup with DynamicInstantiate
34
35
trait InstanceLookup {
36
def lookup[T](cls: Class[T]): List[T]
37
def lookupSingleOrThrow[T](cls: Class[T]): T
38
}
39
40
trait DynamicInstantiate {
41
def wireClassInstanceByName(className: String): Any
42
def wireClassInstance[T](cls: Class[T]): T
43
}
44
45
// Note: InstanceFactoryMap is private to macwire
46
private[macwire] type InstanceFactoryMap = Map[Class[_], () => AnyRef]
47
```
48
49
## Usage Examples
50
51
### Basic Module Wiring
52
53
```scala
54
import com.softwaremill.macwire._
55
56
class DatabaseAccess()
57
class SecurityFilter()
58
class UserFinder(databaseAccess: DatabaseAccess, securityFilter: SecurityFilter)
59
60
object UserModule {
61
lazy val databaseAccess = new DatabaseAccess()
62
lazy val securityFilter = new SecurityFilter()
63
lazy val userFinder = new UserFinder(databaseAccess, securityFilter)
64
}
65
66
// Create dynamic access to UserModule members
67
val wired = wiredInModule(UserModule)
68
69
// Dynamic lookup by type
70
val db: List[DatabaseAccess] = wired.lookup(classOf[DatabaseAccess])
71
val security: SecurityFilter = wired.lookupSingleOrThrow(classOf[SecurityFilter])
72
```
73
74
### Service Registry Pattern
75
76
```scala
77
trait EmailService
78
trait NotificationService
79
trait LoggingService
80
81
class SmtpEmailService extends EmailService
82
class PushNotificationService extends NotificationService
83
class FileLoggingService extends LoggingService
84
85
object ServiceRegistry {
86
def emailService: EmailService = new SmtpEmailService()
87
def notificationService: NotificationService = new PushNotificationService()
88
def loggingService: LoggingService = new FileLoggingService()
89
}
90
91
val serviceRegistry = wiredInModule(ServiceRegistry)
92
93
// Dynamic service lookup
94
val emailSvc = serviceRegistry.lookupSingleOrThrow(classOf[EmailService])
95
val notificationSvc = serviceRegistry.lookup(classOf[NotificationService])
96
```
97
98
### Configuration-Based Wiring
99
100
```scala
101
class AppConfig(environment: String) {
102
def isDevelopment: Boolean = environment == "dev"
103
104
def databaseUrl: String = if (isDevelopment) "jdbc:h2:mem:test" else "jdbc:postgresql://prod-db/app"
105
def cacheSize: Int = if (isDevelopment) 100 else 10000
106
def logger: Logger = if (isDevelopment) new ConsoleLogger() else new FileLogger("app.log")
107
}
108
109
val config = new AppConfig("dev")
110
val wiredConfig = wiredInModule(config)
111
112
// Access configuration values dynamically
113
val dbUrl: String = wiredConfig.lookupSingleOrThrow(classOf[String]) // Note: may be ambiguous
114
val logger: Logger = wiredConfig.lookupSingleOrThrow(classOf[Logger])
115
```
116
117
### Plugin System Implementation
118
119
```scala
120
trait Plugin {
121
def name: String
122
def version: String
123
}
124
125
class DatabasePlugin extends Plugin {
126
def name = "database"
127
def version = "1.0"
128
}
129
130
class CachePlugin extends Plugin {
131
def name = "cache"
132
def version = "2.0"
133
}
134
135
object PluginManager {
136
def databasePlugin: Plugin = new DatabasePlugin()
137
def cachePlugin: Plugin = new CachePlugin()
138
}
139
140
val pluginRegistry = wiredInModule(PluginManager)
141
142
// Dynamically access available plugins
143
val dbPlugin = pluginRegistry.lookup(classOf[Plugin]) // Returns list of Plugin instances
144
```
145
146
### Runtime Service Discovery
147
148
```scala
149
import scala.reflect.ClassTag
150
151
class ServiceDiscovery(modules: AnyRef*) {
152
private val allWired = modules.map(wiredInModule)
153
154
def findService[T](cls: Class[T]): Option[T] = {
155
allWired.view.flatMap(_.lookup(cls)).headOption
156
}
157
158
def getAllServices[T](cls: Class[T]): List[T] = {
159
allWired.flatMap(_.lookup(cls))
160
}
161
}
162
163
object DatabaseModule {
164
def databaseAccess: DatabaseAccess = new DatabaseAccess()
165
def userRepository: UserRepository = new UserRepository()
166
}
167
168
object ServiceModule {
169
def emailService: EmailService = new EmailService()
170
def notificationService: NotificationService = new NotificationService()
171
}
172
173
val discovery = new ServiceDiscovery(DatabaseModule, ServiceModule)
174
val dbAccess = discovery.findService(classOf[DatabaseAccess])
175
val allServices = discovery.getAllServices(classOf[Service]) // If Service is a common base type
176
```
177
178
### Dynamic Dependency Injection
179
180
```scala
181
class DynamicInjector(wired: Wired) {
182
def createInstance[T](factory: DependencyFactory[T])(implicit tag: ClassTag[T]): T = {
183
factory.create(wired)
184
}
185
}
186
187
trait DependencyFactory[T] {
188
def create(wired: Wired): T
189
}
190
191
object UserServiceFactory extends DependencyFactory[UserService] {
192
def create(wired: Wired): UserService = {
193
val db = wired.lookupSingleOrThrow(classOf[DatabaseAccess])
194
val security = wired.lookupSingleOrThrow(classOf[SecurityFilter])
195
new UserService(db, security)
196
}
197
}
198
199
val injector = new DynamicInjector(wiredInModule(AppModule))
200
val userService = injector.createInstance(UserServiceFactory)
201
```
202
203
## Advanced Patterns
204
205
### Type-Safe Registry
206
207
```scala
208
class TypedRegistry[T](wired: Wired, cls: Class[T]) {
209
def get: List[T] = wired.lookup(cls)
210
def getFirst: Option[T] = get.headOption
211
def getSingleOrThrow: T = wired.lookupSingleOrThrow(cls)
212
}
213
214
object RegistryFactory {
215
def createTypedRegistry[T](module: AnyRef, cls: Class[T]): TypedRegistry[T] = {
216
new TypedRegistry[T](wiredInModule(module), cls)
217
}
218
}
219
220
val dbRegistry = RegistryFactory.createTypedRegistry(DatabaseModule, classOf[DatabaseAccess])
221
val database = dbRegistry.getSingleOrThrow
222
```
223
224
### Module Composition
225
226
```scala
227
object CompositeModule {
228
private val database = wiredInModule(DatabaseModule)
229
private val services = wiredInModule(ServiceModule)
230
231
def lookup[T](cls: Class[T]): List[T] = {
232
database.lookup(cls) ++ services.lookup(cls)
233
}
234
}
235
236
// Unified lookup across multiple modules
237
val db = CompositeModule.lookup(classOf[DatabaseAccess])
238
val email = CompositeModule.lookup(classOf[EmailService])
239
```
240
241
## Behavior and Limitations
242
243
### Member Analysis
244
245
- Only public nullary methods (no parameters) are analyzed
246
- Methods must return subtypes of `AnyRef`
247
- Method names are not used for lookup - only return types matter
248
- Inherited methods are included in the analysis
249
250
### Type Mapping
251
252
- Each unique return type creates one factory mapping
253
- If multiple methods return the same type, behavior is undefined (one will be chosen)
254
- Generic types are supported but may have limitations with type erasure
255
- Primitive types and standard library types may not be mappable
256
257
### Runtime Behavior
258
259
- Factory functions are created that call the original methods
260
- Lazy values are evaluated when the factory is called
261
- Methods are called each time lookup is performed (no caching)
262
- Thread safety depends on the thread safety of the original methods
263
264
### Error Conditions
265
266
- **Type Not Found**: `lookup` returns empty list, `lookupSingleOrThrow` throws exception
267
- **Multiple Matches**: Undefined behavior when multiple methods return same type
268
- **Instantiation Errors**: Exceptions from the original methods are propagated
269
270
### Dependencies
271
272
- Requires the `util` module: `"com.softwaremill.macwire" %% "util" % "2.6.6"`
273
- The `Wired` type is defined in the util module, not the macros module
274
- Uses Scala reflection for runtime type information