Search code examples
scaladependency-injectionplayframeworkguice

Difference between Dev & Prod modes in dependency injection modules initialization order


I have a Play application and I'm using the Play libraries for DI, with Guice out of the box.

Some of the play modules I'm defining, depend on each other, and in order for them to be able to inject one's bindings in another, I'm trying to keep a reference to the Play injector in a Global place, i.e.

class MyApplicationLoader extends GuiceApplicationLoader() {
  override def builder(context: ApplicationLoader.Context): 
    GuiceApplicationBuilder = {
    val builder = super.builder(context)
    Global.Injector = builder.injector()
    builder
  }
}

This works well while Play runs in Dev mode, but in Production mode, the call Global.Injector = builder.injector() causes circular dependency because it invokes modules and bindings that needs Global.Injector to be initialized, but it wasn't yet initialized at this point.

I understand it may be related to the fact that in Dev mode, eager singletons are initialized first whereas in Production mode, eager singletons & regular singletons are initialized together.

  1. Do I understand it right?
  2. Is my approach valid? How do I solve the issue in Production mode?

Solution

  • I'm trying to keep a reference to the Play injector in a Global place

    This should be unnecessary in most cases, and you should try to avoid it.

    For solving your issue, it looks like you only need to use the Provides annotation, a simple example is like this:

    class MyModule extends AbstractModule {
      @Provides
      def complexConstructor(a: ObjA, b: ObjB): ComplexClass = ???
    }
    

    This approach should not have any issues.