Fixing a critical bug! The following code was broken (although we did have a TODO dating back to sometime last year)
@ImplementedBy(RealFoo.class)
interface Foo {}
Module fooModule = new AbstractModule() {
public void configure() {
bind(Foo.class).in(Scopes.SINGLETON);
}
}
The problem was we had a TODO to support scopes on @ProvidedBy and @ImplementedBy that wasn't actually done. This is now fixed. I'm surprised we didn't catch this earlier.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@594 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/BindingProcessor.java b/src/com/google/inject/BindingProcessor.java
index 92b071d..9ed2f37 100644
--- a/src/com/google/inject/BindingProcessor.java
+++ b/src/com/google/inject/BindingProcessor.java
@@ -18,10 +18,8 @@
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
-import com.google.inject.internal.Classes;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
-import com.google.inject.internal.StackTraceElements;
import com.google.inject.spi.BindingScopingVisitor;
import com.google.inject.spi.BindingTargetVisitor;
import java.lang.annotation.Annotation;
@@ -229,14 +227,7 @@
}
private <T> void validateKey(Object source, Key<T> key) {
- Class<? super T> rawType = key.getRawType();
- if (!Classes.isConcrete(rawType)) {
- Class<? extends Annotation> scopeAnnotation = Scopes.findScopeAnnotation(errors, rawType);
- if (scopeAnnotation != null) {
- errors.withSource(StackTraceElements.forType(rawType))
- .scopeAnnotationOnAbstractType(scopeAnnotation, rawType, source);
- }
- }
+ Scopes.checkForMisplacedScopeAnnotations(key.getRawType(), source, errors);
}
<T> InvalidBindingImpl<T> invalidBinding(InjectorImpl injector, Key<T> key, Object source) {
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index 918c6d2..5db555d 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -442,15 +442,15 @@
// Handle @ImplementedBy
ImplementedBy implementedBy = type.getAnnotation(ImplementedBy.class);
if (implementedBy != null) {
- // TODO: Scope internal factory.
- return createImplementedByBinding(type, implementedBy, loadStrategy, errors);
+ Scopes.checkForMisplacedScopeAnnotations(type, source, errors);
+ return createImplementedByBinding(type, scope, implementedBy, loadStrategy, errors);
}
// Handle @ProvidedBy.
ProvidedBy providedBy = type.getAnnotation(ProvidedBy.class);
if (providedBy != null) {
- // TODO: Scope internal factory.
- return createProvidedByBinding(type, providedBy, loadStrategy, errors);
+ Scopes.checkForMisplacedScopeAnnotations(type, source, errors);
+ return createProvidedByBinding(type, scope, providedBy, loadStrategy, errors);
}
// We can't inject abstract classes.
@@ -509,8 +509,8 @@
}
/** Creates a binding for a type annotated with @ProvidedBy. */
- <T> BindingImpl<T> createProvidedByBinding(final Class<T> type, ProvidedBy providedBy,
- LoadStrategy loadStrategy, Errors errors) throws ErrorsException {
+ <T> BindingImpl<T> createProvidedByBinding(final Class<T> type, Scope scope,
+ ProvidedBy providedBy, LoadStrategy loadStrategy, Errors errors) throws ErrorsException {
final Class<? extends Provider<?>> providerType = providedBy.value();
// Make sure it's not the same type. TODO: Can we check for deeper loops?
@@ -542,19 +542,20 @@
}
};
+ Key<T> key = Key.get(type);
return new LinkedProviderBindingImpl<T>(
this,
- Key.get(type),
+ key,
StackTraceElements.forType(type),
- internalFactory,
- Scopes.NO_SCOPE,
+ Scopes.<T>scope(key, this, internalFactory, scope),
+ scope,
providerKey,
loadStrategy);
}
/** Creates a binding for a type annotated with @ImplementedBy. */
- <T> BindingImpl<T> createImplementedByBinding(
- Class<T> type, ImplementedBy implementedBy, LoadStrategy loadStrategy, Errors errors)
+ <T> BindingImpl<T> createImplementedByBinding(Class<T> type, Scope scope,
+ ImplementedBy implementedBy, LoadStrategy loadStrategy, Errors errors)
throws ErrorsException {
// TODO: Use scope annotation on type if present. Right now, we always use NO_SCOPE.
Class<?> implementationType = implementedBy.value();
@@ -583,12 +584,13 @@
}
};
+ Key<T> key = Key.get(type);
return new LinkedBindingImpl<T>(
this,
- Key.get(type),
+ key,
StackTraceElements.forType(type),
- internalFactory,
- Scopes.NO_SCOPE,
+ Scopes.<T>scope(key, this, internalFactory, scope),
+ scope,
Key.get(subclass),
loadStrategy);
}
diff --git a/src/com/google/inject/Scopes.java b/src/com/google/inject/Scopes.java
index 52e36ec..faf99bc 100644
--- a/src/com/google/inject/Scopes.java
+++ b/src/com/google/inject/Scopes.java
@@ -16,7 +16,9 @@
package com.google.inject;
+import com.google.inject.internal.Classes;
import com.google.inject.internal.Errors;
+import com.google.inject.internal.StackTraceElements;
import java.lang.annotation.Annotation;
/**
@@ -124,6 +126,22 @@
}
/**
+ * Adds an error if there is a misplaced annotations on {@code type}. Scoping
+ * annotations are not allowed on abstract classes or interfaces.
+ */
+ static void checkForMisplacedScopeAnnotations(Class<?> type, Object source, Errors errors) {
+ if (Classes.isConcrete(type)) {
+ return;
+ }
+
+ Class<? extends Annotation> scopeAnnotation = findScopeAnnotation(errors, type);
+ if (scopeAnnotation != null) {
+ errors.withSource(StackTraceElements.forType(type))
+ .scopeAnnotationOnAbstractType(scopeAnnotation, type, source);
+ }
+ }
+
+ /**
* Scopes an internal factory.
*/
static <T> InternalFactory<? extends T> scope(Key<T> key,
diff --git a/test/com/google/inject/ScopesTest.java b/test/com/google/inject/ScopesTest.java
index 1a9b7a1..1de9daf 100644
--- a/test/com/google/inject/ScopesTest.java
+++ b/test/com/google/inject/ScopesTest.java
@@ -41,6 +41,8 @@
bind(LinkedSingleton.class).to(RealLinkedSingleton.class);
bind(DependsOnJustInTimeSingleton.class);
bind(NotASingleton.class);
+ bind(ImplementedBySingleton.class).in(Scopes.SINGLETON);
+ bind(ProvidedBySingleton.class).in(Scopes.SINGLETON);
}
};
@@ -51,6 +53,8 @@
RealLinkedSingleton.nextInstanceId = 0;
JustInTimeSingleton.nextInstanceId = 0;
NotASingleton.nextInstanceId = 0;
+ Implementation.nextInstanceId = 0;
+ ProvidedBySingleton.nextInstanceId = 0;
}
public void testSingletons() {
@@ -79,6 +83,14 @@
assertNotSame(
injector.getInstance(NotASingleton.class),
injector.getInstance(NotASingleton.class));
+
+ assertSame(
+ injector.getInstance(ImplementedBySingleton.class),
+ injector.getInstance(ImplementedBySingleton.class));
+
+ assertSame(
+ injector.getInstance(ProvidedBySingleton.class),
+ injector.getInstance(ProvidedBySingleton.class));
}
public void testJustInTimeAnnotatedSingleton() {
@@ -350,4 +362,24 @@
@Singleton @CustomScoped
static class SingletonAndCustomScoped {}
+
+ @ImplementedBy(Implementation.class)
+ static interface ImplementedBySingleton {}
+
+ @ProvidedBy(ImplementationProvider.class)
+ static class ProvidedBySingleton {
+ static int nextInstanceId;
+ final int instanceId = nextInstanceId++;
+ }
+
+ static class Implementation implements ImplementedBySingleton {
+ static int nextInstanceId;
+ final int instanceId = nextInstanceId++;
+ }
+
+ static class ImplementationProvider implements Provider<ProvidedBySingleton> {
+ public ProvidedBySingleton get() {
+ return new ProvidedBySingleton();
+ }
+ }
}