Sergey Volkov
Sergey Volkov

Reputation: 35

Play Framework 2.4 Global object migration

I'm currently migrating my Play Framework project from 2.3 to 2.4. Right now I need to migrate my Global object. Here's a record from their migration guide:

GlobalSettings.beforeStart and GlobalSettings.onStart: Anything that needs to happen on start up should now be happening in the constructor of a dependency injected class. A class will perform its initialization when the dependency injection framework loads it. If you need eager initialization (because you need to execute some code before the application is actually started), define an eager binding.

First, I need to migrate my Global.onStart code. Among other things, it subscribes to various events using akka. Here's how it usually looks like:

import play.api.Play.current

class ApplicationGlobal @Inject()(@Named("event-handler") eventHandlerActor: ActorRef) {

  system.eventStream.subscribe(eventHandlerActor, classOf[SomeEvent])

}

Please, pay attention to the import statement. It is required by the subscribe method. And because of this simple fact I can't define an eager binding for this ApplicationGlobal class, as suggested in migration doc, since the application doesn't exist yet. Therefore, I have a couple of questions:

  1. As far as I understand, defining an eager binding actually means defining an analog to the beforeStart method, since the code executes before the application starts. Is that correct?

  2. If it's correct, then what's the analog for the onStart method? According to the doc, this code should now be happening in the constructor of a dependency injected class. But where exactly should I inject this ApplicationGlobal class? Into Controller class? But I have like 10 of them for different pieces of functionality. Should I inject it into all of them?

Upvotes: 0

Views: 140

Answers (1)

rethab
rethab

Reputation: 8423

I think you have to think about it this way:

Is my initialization code something that needs to happend globally before (pretty much) everything? Or, is it required by a specific component?

Things that shall happen once at startup should go into an eagerly bound singleton. Things that are required by a certain component shall go into the constructor of the respective class.

Each of them has a respective consequence:

  • Global Initialization: Regular bindings will only create and instance of the implementation at the time the instance is required and will create a new instance every time they are to be injected somewhere. Eager singletons, however, are instantiated at the time (or a little later) the binding is created regardless of whether anything depends on them. Also, there will be only one instance. This is what you want to for global initialization code: run in any case AND run only once. So to answer your second question: No, you don't need to inject it anywhere.
  • Anything else that is only required for a ceratin component may be called in the constructor of the respective class. You need to be aware of the scope of that class though. If you have code that should be run only once (eg. addLifecycleHook), that class needs to be a singleton or the hook will be registered every time a new instance of that class is created.

So, essentially guice will create a dependency graph of your entire application and then pull on the first thread. Everything else follows from that. And if some of your code needs an instance of Application, you can inject that as well.

Upvotes: 0

Related Questions