Skip to content

Commit f8cc171

Browse files
higheggronshapiro
authored andcommitted
Fix a subtle bug in Guice eager singleton evaluation. Add a test.
A linked binding should only be treated as an eager singleton if the target binding is eager singleton *and* the source binding is unscoped. If the source binding is scoped, it may be a scope with context constraints (such as @RequestScoped) that shouldn't be evaluated during injector creation. Bug repro: bind(Key1).toInstance(<something>) bind(Key2).to(Key1).in(RequestScoped.class); ------------- Created by MOE: https://github.com/google/moe MOE_MIGRATED_REVID=249417146
1 parent 6a2b571 commit f8cc171

File tree

2 files changed

+39
-2
lines changed

2 files changed

+39
-2
lines changed

core/src/com/google/inject/internal/InternalInjectorCreator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,10 +230,11 @@ private boolean isEagerSingleton(InjectorImpl injector, BindingImpl<?> binding,
230230

231231
// handle a corner case where a child injector links to a binding in a parent injector, and
232232
// that binding is singleton. We won't catch this otherwise because we only iterate the child's
233-
// bindings.
233+
// bindings. This only applies if the linked binding is not itself scoped.
234234
if (binding instanceof LinkedBindingImpl) {
235235
Key<?> linkedBinding = ((LinkedBindingImpl<?>) binding).getLinkedKey();
236-
return isEagerSingleton(injector, injector.getBinding(linkedBinding), stage);
236+
return binding.getScoping().isNoScope()
237+
&& isEagerSingleton(injector, injector.getBinding(linkedBinding), stage);
237238
}
238239

239240
return false;

core/test/com/google/inject/ScopesTest.java

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1340,4 +1340,40 @@ public Boolean visitNoScoping() {
13401340
}
13411341
}));
13421342
}
1343+
1344+
public void testScopedLinkedBindingDoesNotPropagateEagerSingleton() {
1345+
final Key<String> a = Key.get(String.class, named("A"));
1346+
final Key<String> b = Key.get(String.class, named("B"));
1347+
1348+
final Scope notInScopeScope =
1349+
new Scope() {
1350+
@Override
1351+
public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
1352+
return new Provider<T>() {
1353+
@Override
1354+
public T get() {
1355+
throw new IllegalStateException("Not in scope");
1356+
}
1357+
};
1358+
}
1359+
};
1360+
1361+
Module module =
1362+
new AbstractModule() {
1363+
@Override
1364+
protected void configure() {
1365+
bind(a).toInstance("a");
1366+
bind(b).to(a).in(CustomScoped.class);
1367+
bindScope(CustomScoped.class, notInScopeScope);
1368+
}
1369+
};
1370+
1371+
Injector injector = Guice.createInjector(module);
1372+
Provider<String> bProvider = injector.getProvider(b);
1373+
try {
1374+
bProvider.get();
1375+
fail("expected failure");
1376+
} catch (ProvisionException expected) {
1377+
}
1378+
}
13431379
}

0 commit comments

Comments
 (0)