Zero-cost, compile-time, type-safe dependency injection library providing macros for automatic instance creation in Scala applications
—
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.
Platform Availability: This functionality is only fully implemented in Scala 2. The Scala 3 version exists but is not yet implemented (returns ???).
Creates a Wired instance that provides dynamic access to instances based on the public members of the given object.
def wiredInModule(in: AnyRef): Wired // Scala 2 onlyParameters:
in: AnyRef - Object whose public members will be used to create instance mappingsReturns: Wired instance containing type-to-factory mappings
Behavior:
AnyRef subtypesWired instance from the util module for dynamic instance lookupThe Wired type provides dynamic instance access capabilities:
// From com.softwaremill.macwire.util module
class Wired(protected val instanceFactoryMap: InstanceFactoryMap) extends InstanceLookup with DynamicInstantiate
trait InstanceLookup {
def lookup[T](cls: Class[T]): List[T]
def lookupSingleOrThrow[T](cls: Class[T]): T
}
trait DynamicInstantiate {
def wireClassInstanceByName(className: String): Any
def wireClassInstance[T](cls: Class[T]): T
}
// Note: InstanceFactoryMap is private to macwire
private[macwire] type InstanceFactoryMap = Map[Class[_], () => AnyRef]import com.softwaremill.macwire._
class DatabaseAccess()
class SecurityFilter()
class UserFinder(databaseAccess: DatabaseAccess, securityFilter: SecurityFilter)
object UserModule {
lazy val databaseAccess = new DatabaseAccess()
lazy val securityFilter = new SecurityFilter()
lazy val userFinder = new UserFinder(databaseAccess, securityFilter)
}
// Create dynamic access to UserModule members
val wired = wiredInModule(UserModule)
// Dynamic lookup by type
val db: List[DatabaseAccess] = wired.lookup(classOf[DatabaseAccess])
val security: SecurityFilter = wired.lookupSingleOrThrow(classOf[SecurityFilter])trait EmailService
trait NotificationService
trait LoggingService
class SmtpEmailService extends EmailService
class PushNotificationService extends NotificationService
class FileLoggingService extends LoggingService
object ServiceRegistry {
def emailService: EmailService = new SmtpEmailService()
def notificationService: NotificationService = new PushNotificationService()
def loggingService: LoggingService = new FileLoggingService()
}
val serviceRegistry = wiredInModule(ServiceRegistry)
// Dynamic service lookup
val emailSvc = serviceRegistry.lookupSingleOrThrow(classOf[EmailService])
val notificationSvc = serviceRegistry.lookup(classOf[NotificationService])class AppConfig(environment: String) {
def isDevelopment: Boolean = environment == "dev"
def databaseUrl: String = if (isDevelopment) "jdbc:h2:mem:test" else "jdbc:postgresql://prod-db/app"
def cacheSize: Int = if (isDevelopment) 100 else 10000
def logger: Logger = if (isDevelopment) new ConsoleLogger() else new FileLogger("app.log")
}
val config = new AppConfig("dev")
val wiredConfig = wiredInModule(config)
// Access configuration values dynamically
val dbUrl: String = wiredConfig.lookupSingleOrThrow(classOf[String]) // Note: may be ambiguous
val logger: Logger = wiredConfig.lookupSingleOrThrow(classOf[Logger])trait Plugin {
def name: String
def version: String
}
class DatabasePlugin extends Plugin {
def name = "database"
def version = "1.0"
}
class CachePlugin extends Plugin {
def name = "cache"
def version = "2.0"
}
object PluginManager {
def databasePlugin: Plugin = new DatabasePlugin()
def cachePlugin: Plugin = new CachePlugin()
}
val pluginRegistry = wiredInModule(PluginManager)
// Dynamically access available plugins
val dbPlugin = pluginRegistry.lookup(classOf[Plugin]) // Returns list of Plugin instancesimport scala.reflect.ClassTag
class ServiceDiscovery(modules: AnyRef*) {
private val allWired = modules.map(wiredInModule)
def findService[T](cls: Class[T]): Option[T] = {
allWired.view.flatMap(_.lookup(cls)).headOption
}
def getAllServices[T](cls: Class[T]): List[T] = {
allWired.flatMap(_.lookup(cls))
}
}
object DatabaseModule {
def databaseAccess: DatabaseAccess = new DatabaseAccess()
def userRepository: UserRepository = new UserRepository()
}
object ServiceModule {
def emailService: EmailService = new EmailService()
def notificationService: NotificationService = new NotificationService()
}
val discovery = new ServiceDiscovery(DatabaseModule, ServiceModule)
val dbAccess = discovery.findService(classOf[DatabaseAccess])
val allServices = discovery.getAllServices(classOf[Service]) // If Service is a common base typeclass DynamicInjector(wired: Wired) {
def createInstance[T](factory: DependencyFactory[T])(implicit tag: ClassTag[T]): T = {
factory.create(wired)
}
}
trait DependencyFactory[T] {
def create(wired: Wired): T
}
object UserServiceFactory extends DependencyFactory[UserService] {
def create(wired: Wired): UserService = {
val db = wired.lookupSingleOrThrow(classOf[DatabaseAccess])
val security = wired.lookupSingleOrThrow(classOf[SecurityFilter])
new UserService(db, security)
}
}
val injector = new DynamicInjector(wiredInModule(AppModule))
val userService = injector.createInstance(UserServiceFactory)class TypedRegistry[T](wired: Wired, cls: Class[T]) {
def get: List[T] = wired.lookup(cls)
def getFirst: Option[T] = get.headOption
def getSingleOrThrow: T = wired.lookupSingleOrThrow(cls)
}
object RegistryFactory {
def createTypedRegistry[T](module: AnyRef, cls: Class[T]): TypedRegistry[T] = {
new TypedRegistry[T](wiredInModule(module), cls)
}
}
val dbRegistry = RegistryFactory.createTypedRegistry(DatabaseModule, classOf[DatabaseAccess])
val database = dbRegistry.getSingleOrThrowobject CompositeModule {
private val database = wiredInModule(DatabaseModule)
private val services = wiredInModule(ServiceModule)
def lookup[T](cls: Class[T]): List[T] = {
database.lookup(cls) ++ services.lookup(cls)
}
}
// Unified lookup across multiple modules
val db = CompositeModule.lookup(classOf[DatabaseAccess])
val email = CompositeModule.lookup(classOf[EmailService])AnyReflookup returns empty list, lookupSingleOrThrow throws exceptionutil module: "com.softwaremill.macwire" %% "util" % "2.6.6"Wired type is defined in the util module, not the macros moduleInstall with Tessl CLI
npx tessl i tessl/maven-com-softwaremill-macwire--macros