matthewrk
matthewrk

Reputation: 687

Binding a trait to an object with Scala Guice

This was going to be a question about how to get the binding to work, but in cleaning up my example ready to post I've actually managed to get it working - the problem is I have no idea why this works:

import org.specs2.mutable._
import com.google.inject.{ Inject, Module, Binder, Guice }
import net.codingwell.scalaguice.ScalaModule

object InjectorSpec extends Specification {
  val injector = Guice.createInjector(new ScalaModule() {
    def configure() {
      bind[Message].toInstance(MessageImpl)
      bind[MessageService.type].toInstance(MessageService) // This line makes it work?
    }
  })

  trait Message {
    val body: String
  }

  object MessageImpl extends Message {
    val body: String = "Hello!"
  }

  object MessageService {
    @Inject
    val message: Message = null

    def get = message.body
  }

  "MessageService" should {
    "Inject Message Implementation" in {
      MessageService.get mustEqual "Hello!"
    }
  }
}

Initially I was just binding the type to be injected (inject MessageImpl for Message). Somewhere along the way I picked up the second bind which is for the service, that isn't injected (so I don't understand the bind being required). Can anyone explain what's happening here and if this is the correct way to proceed?

Upvotes: 5

Views: 5543

Answers (1)

Daniel Martin
Daniel Martin

Reputation: 23548

So the thing is that guice won't search through your program and find all the @Inject points that are in any class that you load anywhere. Instead, it needs to be given some path to find objects that you want it to inject stuff into. Generally, this is taken care of by the way you use Guice, since the standard pattern is:

val injector = Guice.createInjector(/* stuff */)
val main = injector.getInstance(classOf[Main])
main.mainMethod()

Guice performs injection on the instance of Main that you ask it to make/get, and recursively performs injection on all the things @Injected into main.

In your case, one of these statements would also have worked; I say "one of", because though I could tell you exactly how to inject into java I'm not entirely clear on how the scala compiler compiles scala companion objects into java classes. (that is, I don't know if message ends up being at the java level a static variable on the class called MessageService$ or an instance variable, with MessageService$ being instantiated only once through some threadsafe singleton pattern)

requestInjection(MessageService)
requestStaticInjection(MessageService.type)

The reason the toInstance statement worked is that Guice automatically performs injection on all onjects passed to toInstance as the injector is created. (something bound .asEagerSingleton will also have injection performed on it as the injector is coming up, as will all things bound as singletons if you create a "production mode" injector)

Upvotes: 3

Related Questions