问题
I have a class PluginManager
which accepts a Set<Plugin>
using the Guice multi-bindings feature. However, the PluginManager
has some runtime information that needs to be passed to the Plugin
constructor.
This seems to be a perfect use-case for Guice assisted injection i.e. my PluginManager
would have Set<PluginFactory>
injected, where the runtime information is provided to each factory, resulting in the required Plugin
instances.
I don't know the syntax to use in the Module
however. The multibinder addBinding
does not seem to have any facility to connect the result of FactoryModuleBuilder
.
I can create my own custom Factory implementations and multibind that obviously, but is there a way to combine multibinder with assisted inject?
回答1:
I think this gives you an example to do exactly what you want. Please note that scala's multibinder has a pending pull request that allows you to create the set binder in multiple places.
object Test {
trait Plugin {
def name(): String
}
object Plugin {
trait Factory[+T <: Plugin] {
def newPlugin(name: String): T
}
}
case class MyPlugin @Inject() (@Assisted name: String) extends Plugin
case class OtherPlugin @Inject() (@Assisted name: String) extends Plugin
class PluginManager @Inject() (pluginFactories: Set[Plugin.Factory[Plugin]]) {
for (factory <- pluginFactories) {
println(factory.newPlugin("assisted injection"))
}
}
def main(args: Array[String]): Unit = {
val injector = Guice.createInjector(new ScalaModule {
override def configure(): Unit = {
val plugins = ScalaMultibinder.newSetBinder[Plugin.Factory[Plugin]](binder)
plugins.addBinding().to[Plugin.Factory[MyPlugin]]
plugins.addBinding().to[Plugin.Factory[OtherPlugin]]
bindFactory[Plugin, MyPlugin, Plugin.Factory[MyPlugin]]()
bindFactory[Plugin, OtherPlugin, Plugin.Factory[OtherPlugin]]()
bind[PluginManager].asEagerSingleton()
}
def bindFactory[I: Manifest, C <: I : Manifest, F: Manifest](): Unit = {
import net.codingwell.scalaguice._
install(new FactoryModuleBuilder()
.implement(typeLiteral[I], typeLiteral[C])
.build(typeLiteral[F]))
}
})
}
}
You can do a bunch of things depending on the style you want. For example you could make a generic addPlugin method (when a newer version of scala-guice is released) like this:
val injector = Guice.createInjector(new ScalaModule {
override def configure(): Unit = {
bindPlugin[MyPlugin]()
bindPlugin[OtherPlugin]()
bind[PluginManager].asEagerSingleton()
}
def bindPlugin[T <: Plugin : Manifest](): Unit = {
val plugins = ScalaMultibinder.newSetBinder[Plugin.Factory[T]](binder)
plugins.addBinding().to[Plugin.Factory[T]]
bindFactory[Plugin, T, Plugin.Factory[T]]()
}
def bindFactory[I: Manifest, C <: I : Manifest, F: Manifest](): Unit = {
import net.codingwell.scalaguice._
install(new FactoryModuleBuilder()
.implement(typeLiteral[I], typeLiteral[C])
.build(typeLiteral[F]))
}
})
来源:https://stackoverflow.com/questions/26514009/use-guice-multibindings-with-assisted-inject-for-the-set-members