Reputation: 2470
I'm new to the Scala world and I'm using PLAY to make an API. It's going well but I am having some trouble understanding some of the notation and there is not a lot of documentation on it. Specifically, I am confused by the following controller method in one of the examples on the PLAY site:
class HomeController @Inject()(@Named("userParentActor") userParentActor: ActorRef,
cc: ControllerComponents)
(implicit ec: ExecutionContext) {
}
My question is what is going on in this constructor? Which part is the constructor and which part is the injected parameters? Is the ExecutionContext
injected as well? And why is the ec
in a separate parenthesis?
Thank you for the clarification.
Upvotes: 1
Views: 1207
Reputation: 1789
Ok, so lets go back a little and talk about why Play evolved to be like this. Normally when you want to write a class/method/function in an imperative kind of manner we write something like:
class XProviderFromCloud {
def getX (xId: String) : X = ??? // ???: To be implemented
}
Assuming the above code is somewhere in your models
, this is ok, you can import models and use the method here. However a good engineering
approach here is to create the interface and test things: something like test driven development (TDD). Well in this case, the code will be:
trait XProvider{
def getX(xId: String): x
}
class XProviderFromCloud extends Xprovider{
override def getX (xId: String) : X = ??? // ???: To be implemented
}
Here you go through the interface, so you can inject the interface to a controller:
class MyController @inject()(xProvider: XProvider)
So you can see here, that your controller class, and have number of injectable components to use. One of the main reason I do this,
is because I can mock the interface and retrun a result; and test on that. So it means, I do not need to have code within
override def getX
to test the controller implementing this. After I'm sure the controller can use the result from getX, I write the
test for getX
, and then write the code for the body of it.
Now lets go to the next point, using the @Named
annotation. Sometimes an interface has multiple implementation (extended by the number of
classes), we use the @Named annotation, to explicitly express which implementation we want. For example I can extend the above interface
with two classes on get the X from Amazon cloud (e.g., S3), the other get it from Google cloud. As simple as that, you can
also look at the docs: https://www.playframework.com/documentation/2.6.x/ScalaDependencyInjection#Programmatic-bindings
What about the ec: ExecutionContext
part, you may ask. Well, this is later on when you want to deal with concurrency and Futures. The above
code, is not good in a sense that it is not concurrent, if we want to call a cloud service or a database; we need to write a non-blocking
concurrent code, using Future. Futures run on the cpu theads, and we can either use the default execution context (as you shown in your code),
or create our own execution context, as demonstrated in Play's doc: https://www.playframework.com/documentation/2.6.x/ScalaAsync#Creating-non-blocking-actions.
Upvotes: 1
Reputation: 44918
It's just a constructor with two parameter lists, and the parameter in the second list is implicit.
class HomeController @Inject()( // list-1 start
@Named("userParentActor") userParentActor: ActorRef, // list-1, arg-1
cc: ControllerComponents // list-1, arg-2
)( // 1 end, 2 start
implicit ec: ExecutionContext // list-2, arg-1
) { // list-2 end
// body
}
The @Inject
annotation applies to both argument lists, so ec
is also injected by guice (using Play's default thread pool).
The @Named
annotation affects only the first argument.
The ec
argument is in a separate list, because implicit arguments must be declared in a separate list.
It is declared in a separate list probably because the author anticipated the use case where the controller is instantiated manually instead by the dependency injection container: it is simpler then, because you don't have to specify the default thread pool everywhere.
Upvotes: 2