The official Play DI manual does not provide sufficient explanation. This post is the first in the sequence of posts, explaining in the different way and in more details, how DI with Guice should be done.
Injection of Play API-s with Guice
Starting from Play 2.5 several API-s, which were static in the earlier versions, should be created with DI. These are, for example, Configuration, JPAApi, CacheApi, etc.
Injecting method of these API-s depends, in what class this should be done: a class, which is automatically injected by Play or a custom class.
Injection from an automatically injected class is just as simple as putting appropriate @Inject annotation on either field or constructor. For example, to inject Configuration in a controller with property injection:
@Inject
private Configuration configuration;
or with constructor injection: private Configuration configuration;
@Inject
public MyController(Configuration configuration) {
this.configuration = configuration;
}
Injection from a custom class, which has injection binding, should be done just like it is done for automatically injected class - with @Inject annotation.Injection from a custom class, which is not injected by itself, should be done by explicit call to the injector with Play.current().injector(). For example, to inject Configuration in a custom class define a configuration data member like this:
private Configuration configuration = Play.current().injector().instanceOf(Configuration.class);
Injection of WSClient and Application
Both WSClient and Application may be injected with @Inject annotation like in the example:
public class MyApplication extends Controller {
private Configuration configuration;
private Application applicationProvider;
private WSClient wsClientProvider;
@Inject
public MyApplication(Configuration configuration, Application applicationProvider, WSClient wsClientProvider) {
this.configuration = configuration;
this.applicationProvider = applicationProvider;
this.wsClientProvider = wsClientProvider;
}
...
}
The official documentation example for WSClient uses this very injecting method.Such injection method is not good enough for the following reasons:
1. The whole dependency tree is injected upon injecting of the MyApplication instance.
2. Due to injecting of the whole tree, a unit test for the MyApplication will not work: it will fail with the ProvisioningException.
The preferred way of injecting the WSClient and Application is with injection provider:
public class MyApplication extends Controller {
private Configuration configuration;
private Provider<Application> applicationProvider;
private Provider<WSClient> wsClientProvider;
@Inject
public MyApplication (Configuration configuration,
Provider<Application> applicationProvider,
Provider<WSClient> wsClientProvider) {
this.configuration = configuration;
this.applicationProvider = applicationProvider;
this.wsClientProvider = wsClientProvider;
}
...
}
When injection of the Provider<WSClient> the instance of the WSClient is not injected. It will be injected only on call to the Provider get() method: WSRequest request = wsClientProvider.get().url("Some Url");
More reading
See the dedicated post Custom injection binding in Guice with Play.
See the dedicated post Using Guice for Play tests
See working examples on Git.
No comments :
Post a Comment