Fixing the binding order problem. My strategy was to break binding creation into two steps:
- creating the binding itself
- "initializing" the binding, ie. validating the bindings dependencies
This CL is a start of something that we could go further on. In particular, we could change the @ProvidedBy etc. bindings to use a BindingImpl.initialize() method uniformly to separate creating the binding from building its dependencies.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@473 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/BindCommandProcessor.java b/src/com/google/inject/BindCommandProcessor.java
index 1ab63c6..59db4b9 100644
--- a/src/com/google/inject/BindCommandProcessor.java
+++ b/src/com/google/inject/BindCommandProcessor.java
@@ -158,18 +158,25 @@
return null;
}
+ // This cast is safe after the preceeding check.
+ @SuppressWarnings("unchecked")
+ Class<T> clazz = (Class<T>) type;
+ final BindingImpl<T> binding;
+ try {
+ binding = injector.createUnitializedBinding(clazz, scope, source);
+ createBinding(source, shouldPreload, binding);
+ } catch (ResolveFailedException e) {
+ injector.errorHandler.handle(source, e.getMessage());
+ createBinding(source, shouldPreload, invalidBinding(injector, key, source));
+ return null;
+ }
+
untargettedBindings.add(new Runnable() {
public void run() {
- // This cast is safe after the preceeding check.
- @SuppressWarnings("unchecked")
- Class<T> clazz = (Class<T>) type;
-
try {
- BindingImpl<T> binding = injector.createBindingFromType(clazz, scope, source);
- createBinding(source, shouldPreload, binding);
+ injector.initializeBinding(binding);
} catch (ResolveFailedException e) {
injector.errorHandler.handle(source, e.getMessage());
- createBinding(source, shouldPreload, invalidBinding(injector, key, source));
}
}
});
diff --git a/src/com/google/inject/BindingImpl.java b/src/com/google/inject/BindingImpl.java
index acf4ae3..771d13d 100644
--- a/src/com/google/inject/BindingImpl.java
+++ b/src/com/google/inject/BindingImpl.java
@@ -16,6 +16,7 @@
package com.google.inject;
+import com.google.inject.internal.ResolveFailedException;
import com.google.inject.internal.ToStringBuilder;
import com.google.inject.spi.ProviderBinding;
@@ -79,6 +80,12 @@
return internalFactory instanceof ConstantFactory<?>;
}
+ /**
+ * Perform any post-creation initialization, that could require construction
+ * of other bindings.
+ */
+ void initialize(InjectorImpl injector) throws ResolveFailedException {}
+
public String toString() {
return new ToStringBuilder(Binding.class)
.add("key", key)
diff --git a/src/com/google/inject/ClassBindingImpl.java b/src/com/google/inject/ClassBindingImpl.java
index 9eb2756..65cdf30 100644
--- a/src/com/google/inject/ClassBindingImpl.java
+++ b/src/com/google/inject/ClassBindingImpl.java
@@ -17,6 +17,7 @@
package com.google.inject;
import com.google.inject.InjectorImpl.SingleParameterInjector;
+import com.google.inject.internal.ResolveFailedException;
import com.google.inject.internal.ToStringBuilder;
import com.google.inject.spi.BindingVisitor;
import com.google.inject.spi.ClassBinding;
@@ -40,6 +41,10 @@
this.lateBoundConstructor = lateBoundConstructor;
}
+ @Override void initialize(InjectorImpl injector) throws ResolveFailedException {
+ lateBoundConstructor.bind(injector, getBoundClass());
+ }
+
public void accept(BindingVisitor<? super T> visitor) {
visitor.visit(this);
}
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index c2b2822..1014a07 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -492,6 +492,38 @@
<T> BindingImpl<T> createBindingFromType(Class<T> type, Scope scope,
Object source) throws ResolveFailedException {
+ BindingImpl<T> binding = createUnitializedBinding(type, scope, source);
+ initializeBinding(binding);
+ return binding;
+ }
+
+ <T> void initializeBinding(BindingImpl<T> binding) throws ResolveFailedException {
+ // Put the partially constructed binding in the map a little early. This
+ // enables us to handle circular dependencies.
+ // Example: FooImpl -> BarImpl -> FooImpl.
+ // Note: We don't need to synchronize on jitBindings during injector
+ // creation.
+ if (binding instanceof ClassBindingImpl<?>) {
+ Key<T> key = binding.getKey();
+ jitBindings.put(key, binding);
+ boolean successful = false;
+ try {
+ binding.initialize(this);
+ successful = true;
+ } finally {
+ if (!successful) {
+ jitBindings.remove(key);
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates a binding for an injectable type with the given scope. Looks for
+ * a scope on the type if none is specified.
+ */
+ <T> BindingImpl<T> createUnitializedBinding(Class<T> type,
+ Scope scope, Object source) throws ResolveFailedException {
// Don't try to inject primitives, arrays, or enums.
if (type.isArray() || type.isEnum() || type.isPrimitive()) {
throw new ResolveFailedException(ErrorMessages.MISSING_BINDING, type);
@@ -511,16 +543,6 @@
return createProvidedByBinding(type, providedBy);
}
- return createBindingForInjectableType(type, scope, source);
- }
-
- /**
- * Creates a binding for an injectable type with the given scope. Looks for
- * a scope on the type if none is specified.
- */
- <T> BindingImpl<T> createBindingForInjectableType(Class<T> type,
- Scope scope, Object source) throws ResolveFailedException {
-
// We can't inject abstract classes.
// TODO: Method interceptors could actually enable us to implement
// abstract types. Should we remove this restriction?
@@ -544,26 +566,7 @@
InternalFactory<? extends T> scopedFactory
= Scopes.scope(key, this, lateBoundConstructor, scope);
- BindingImpl<T> binding
- = new ClassBindingImpl<T>(this, key, source, scopedFactory, scope, lateBoundConstructor);
-
- // Put the partially constructed binding in the map a little early. This
- // enables us to handle circular dependencies.
- // Example: FooImpl -> BarImpl -> FooImpl.
- // Note: We don't need to synchronize on jitBindings during injector
- // creation.
- jitBindings.put(key, binding);
- boolean successful = false;
- try {
- lateBoundConstructor.bind(this, type);
- successful = true;
- } finally {
- if (!successful) {
- jitBindings.remove(key);
- }
- }
-
- return binding;
+ return new ClassBindingImpl<T>(this, key, source, scopedFactory, scope, lateBoundConstructor);
}
static class LateBoundConstructor<T> implements InternalFactory<T> {