Актеры Guice и Akka в Scala

Я хотел бы уменьшить шаблон, необходимый для связывания этих актеров akka.

В настоящее время мой код выглядит так:

bind(classOf[ActorRef]).
  annotatedWith(Names.named("mines")).
  toProvider(new TypeLiteral[ActorProvider[MyActor]]() {}).
  asEagerSingleton()

Я бы тоже хотел, чтобы это выглядело так:

bindActor[MyActor].withName("mines")

Я пытался создать подкласс AbstractModule, чтобы втиснуть эту концепцию, но безрезультатно.

Соответствующий код:

class ActorProvider[T <: Actor] @Inject() ( val key:Key[T], val injector:Injector, val system: ActorSystem ) extends Provider[ActorRef] {
  def get = {
    system.actorOf(Props(injector.getInstance(key)))
  }
}

person Scoobie    schedule 18.12.2012    source источник


Ответы (2)


Посетите https://github.com/codingwell/scala-guice/. Вот образец на его основе. Это позволяет

bindActor[MyActor].withName("mines")

Имейте в виду, что вы привязываете неуниверсальный объект ActorRef, а не сам Актер. Это создаст привязку для @Named("foo") ActorRef. Вы никогда не должны работать с Актером напрямую.

Вы не можете получить ключ в провайдере. Провайдер не принимает никаких контекстных инъекций, как вы пробовали с ключом или, например. Точка инъекции. Что вы можете сделать, так это создать другой экземпляр провайдера для каждой привязки актера и впоследствии внедрить его с помощью ActorSystem. В качестве альтернативы вы можете изменить API, включив в него экземпляр системы акторов.

trait AkkaModule extends AbstractModule {
  // should be:
  // this: AbstractModule =>
  // see http://lampsvn.epfl.ch/trac/scala/ticket/3564
  import ScalaModule._

  private def binderAccess = super.binder // shouldn't need super

  def bindActor[T <: Actor](implicit m: Manifest[T]) = new ActorBindingBuilder {
    //Hack, no easy way to exclude the bind method that gets added to classes inheriting ScalaModule
    //So we experamentally figured out how many calls up is the source, so we use that
    //Commit 52c2e92f8f6131e4a9ea473f58be3e32cd172ce6 has better class exclusion
    val mybinder = binderAccess.withSource((new Throwable).getStackTrace()(3))
    val self = (mybinder bind classOf[ActorRef]).asInstanceOf[AnnotatedBindingBuilder[ActorRef]]
  }

}

object AkkaModule {

  class ActorProvider(val name: String) extends Provider[ActorRef] {
    @Inject var system: ActorSystem = _
    def get = {
      system.actorFor(system.name + "/user/" + name)
    }
  }

  trait ActorBindingBuilder {
    val mybinder: Binder
    val self: AnnotatedBindingBuilder[ActorRef]

    def withName(name: String) = {
      val provider = new ActorProvider(name)
      self.annotatedWith(Names.named(name)).toProvider(provider)
      mybinder.requestInjection(provider)
    }
  }
}
person Alen Vrečko    schedule 19.12.2012

Что-то вроде этого, использующее манифест типа scala, может работать http://www.scala-lang.org/api/current/scala/reflect/Manifest.html, где Foo аналогичен ActorRef, а Bla аналогичен MyActor:


scala> import com.google.inject
import com.google.inject

scala> val binder:inject.Binder = null
binder: com.google.inject.Binder = null

scala> class Foo {}
defined class Foo

scala> class Bla extends Foo {}
defined class Bla

scala> def bind[T <: Foo:Manifest] = binder.bind( classOf[Foo] ).toProvider( new
 inject.TypeLiteral[inject.Provider[T]](){} ).asEagerSingleton
bind: [T <: Foo](implicit evidence$1: Manifest[T])Unit

Возможно, совместите это с неявным преобразованием для преобразования Binder в MyBinder: http://daily-scala.blogspot.com/2009/08/implicit-methods.html


class MyBinder {
    def bindActor[T <: ActorRef:Manifest]( nameToBind:String ):Unit = ...
}


object MyBinder {
    implicit def binderToMyBinder( ...
}

Удачи!

person Reuben    schedule 18.12.2012