I have some tests that I would like to have fail if certain Guice scopes are used incorrectly. For example, a @Singleton
should not have any @RequestScoped
or @TestScoped
dependencies (Provider<>
s are okay, of course).
In production, this is partially solved because eagerly-bound singletons will be constructed before the scope is entered, resulting in OutOfScopeException
s. But in development, the singleton will be created lazily while inside the scope, and no problems are evident.
Judging by these two open issues, it seems like there is no easy, built-in way to do this. Can I achieve this using the SPI? I tried using a TypeListener
but it's not clear how to get the dependencies of a given type.
Here's how I've accomplished this with the 4.0 beta of Guice, using ProvisionListener
. I tried TypeListener
but it seems that TypeListener
s get called before Guice necessarily has bindings for that type's dependencies. This caused exceptions, and even a deadlock in one case.
private static class ScopeValidator implements ProvisionListener {
private @Inject Injector injector;
private @Inject WhateverScope scope;
@Override
public <T> void onProvision(ProvisionInvocation<T> provision) {
if (injector == null) {
// The injector isn't created yet, just return. This isn't a
// problem because any scope violations will be caught by
// WhateverScope itself here (throwing an OutOfScopeException)
return;
}
Binding<?> binding = provision.getBinding();
Key<?> key = binding.getKey();
if (Scopes.isSingleton(binding) && binding instanceof HasDependencies) {
Set<Dependency<?>> dependencies = ((HasDependencies) binding).getDependencies();
for (Dependency<?> dependency : dependencies) {
Key<?> dependencyKey = dependency.getKey();
Binding<?> dependencyBinding = injector.getExistingBinding(dependencyKey);
if (dependencyBinding != null && Scopes.isScoped(dependencyBinding, whateverScope, WhateverScoped.class)) {
throw new ProvisionException(String.format(
"Singleton %s depends on @WhateverScoped %s",
key, dependencyKey));
}
}
}
}
}