使用guice注入某个特征/类的所有实现

时间:2018-03-21 14:55:10

标签: scala generics dependency-injection guice

我有

trait Builder[T, K] {
    def build(brick: T) : K

对于那个特性,我有多个实现......

class StringBuilder extends Builder[Foo, String] { ... }
class HouseBuilder  extends Builder[Baa, House]  { ... }
class BaaBuilder    extends Builder[Baz, Int]    { ... }

根据给定的类型,我想从一个实现中选择。像这样的东西(伪代码):

class BuildingComponent @Inject()(builder: Set[Builder]){
    def doIt(item: Any) = {
       item match {
          case _: Foo => builder.filter(Foo).build(item)
          case _: Baa => builder.filter(Baa).build(item)
          case _: Baz => builder.filter(Baz).build(item)
    }
}

所以2点:

  1. 我怎么能注入特征“Builder”的所有实现? 我发现一堆问题朝着同一个方向发展(使用multibinder,TypeLiteral等,但没有一个问题涉及注入所有实现的问题。它只是关于“如何注入特定的实现”)我知道如何绑定多个实例使用multibinder;但不是如果它是一般类......

  2. 最后我想使用一种立面图案。有一个“构建器”要注入,它会获取所有实现注入并知道需要什么构建器(参见上面的match-case-fracment)。但是我没有使用匹配案例,而是关注了MapBinder。类似于将Builder实现绑定到Map的东西,它将类用作键。

  3. e.g。 (伪码)

    class BuildingComponent @Inject()(builder: Map[Class,Builder]){
      def doIt(item: Any) = {
         builder.get(item.class).build(item)
      }
    }
    

1 个答案:

答案 0 :(得分:1)

Guice只初始化它所知道的类。因此,您可以将所有实现注入到外观中,并根据需要对其进行排序。每次添加新实现时,您都需要更改此外观。所以不是那么好..

<强>替代

要动态告知guice你的实现,你需要反思。如果您可以将Builder作为sealed特征(获取所有子类的示例here)或使用第三方库(例如reflections),则可以使用标准scala

我将解释最后一个案例

您需要在build.sbt中输入:

libraryDependencies ++= Seq(
  "com.google.inject.extensions" % "guice-multibindings" % "<your guice version>",
  "org.reflections" % "reflections" % "0.9.11")

您需要创建模块并通知guice,例如:如果是play框架,你需要输入application.conf

play.modules.enabled += "com.example.MyModule"

现在,我假设您可以将所有实现放入相同的包中(您可以检查文档如何在其他情况下获取所有实现)。我们说它是com.example.builders。同样在我的示例中,我假设您能够将类型参数TK移动到类型别名中(使用泛型,绑定和注入会有更多技巧 - 您可以尝试找到这种方式你自己)。你的建设者将是:

trait Builder {
  type T
  type K
  def build(brick: T) : K
}

现在到您的模块MyModule

package com.example

import com.google.inject.AbstractModule
import com.google.inject.multibindings.Multibinder
import org.reflections.Reflections


class MyModule extends AbstractModule {
  override def configure(): Unit = {
    import scala.collection.JavaConverters._
    val r = new Reflections("com.example.builders")
    val subtypes = r.getSubTypesOf(classOf[Builder])

    val executorBinder = Multibinder.newSetBinder(binder(), classOf[Builder])
    subtypes.asScala.foreach {clazz =>
      executorBinder.addBinding().to(clazz)
    }
  }
}

最后,您可以在需要的地方注入builders: java.util.Set[Builder]

更新(添加了如何处理类型化实施的示例)

例如:您需要额外的类来保存您的砖类型信息。我abstract class使用Builder时,import scala.reflect.runtime.universe._ trait Builder[T, K] { def build(brick: T): K } abstract class TypeChecker[T: TypeTag] { this: Builder[T, _] => def isDefinedAt[C: TypeTag](t: C) = { typeOf[C] =:= typeOf[T] } } class Foo class Baa class House // first builder implementation class StringBuilder extends TypeChecker[Foo] with Builder[Foo, String] { override def build(brick: Foo) = { println("StringBuilder") "" } } // second builder implementation class HouseBuilder extends TypeChecker[Baa] with Builder[Baa, House] { override def build(brick: Baa) = { println("HouseBuilder") new House } } // our set of builders val s: Set[Builder[_, _] with TypeChecker[_]] = Set( new StringBuilder, new HouseBuilder ) // here we check and apply arrived brick on our set of builders def check[T: TypeTag](t: T) = s.filter(_.isDefinedAt(t)). foreach {b => b.asInstanceOf[Builder[T, _]].build(t)} check(new Foo) check(new Baa) 就是好的

int arrsize;
BYTE copyrow[18];
arrsize = sizeof(copyrow);