Revision: 1546
Author: sberlin
Date: Mon May 2 11:03:35 2011
Log: fix @ProvidedBy circular dependencies.
http://code.google.com/p/google-guice/source/detail?r=1546
Added:
/trunk/core/src/com/google/inject/internal/DelayedInitialize.java
/trunk/core/src/com/google/inject/internal/ProvidedByInternalFactory.java
Modified:
/trunk/core/src/com/google/inject/internal/ConstructorBindingImpl.java
/trunk/core/src/com/google/inject/internal/InjectorImpl.java
/trunk/core/src/com/google/inject/internal/LinkedProviderBindingImpl.java
/trunk/core/test/com/google/inject/CircularDependencyTest.java
=======================================
--- /dev/null
+++ /trunk/core/src/com/google/inject/internal/DelayedInitialize.java Mon
May 2 11:03:35 2011
@@ -0,0 +1,17 @@
+// Copyright 2011 Google Inc. All Rights Reserved.
+
+package com.google.inject.internal;
+
+/**
+ * Something that needs some delayed initialization, typically
+ * a binding or internal factory that needs to be created & put
+ * into the bindings map & then initialized later.
+ *
+ * @author [email protected] (Sam Berlin)
+ */
+interface DelayedInitialize {
+
+ /** Initializes this binding, throwing any errors if necessary. */
+ void initialize(InjectorImpl injector, Errors errors) throws
ErrorsException;
+
+}
=======================================
--- /dev/null
+++
/trunk/core/src/com/google/inject/internal/ProvidedByInternalFactory.java
Mon May 2 11:03:35 2011
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.google.inject.internal;
+
+import static com.google.inject.internal.util.Preconditions.checkState;
+
+import com.google.inject.Key;
+import com.google.inject.ProvidedBy;
+import com.google.inject.Provider;
+import com.google.inject.internal.InjectorImpl.JitLimitation;
+import com.google.inject.spi.Dependency;
+
+/**
+ * An {@link InternalFactory} for {@literal @}{@link ProvidedBy} bindings.
+ *
+ * @author [email protected] (Sam Berlin)
+ */
+class ProvidedByInternalFactory<T> extends ProviderInternalFactory<T>
+ implements DelayedInitialize {
+
+ private final Class<?> rawType;
+ private final Class<? extends Provider<?>> providerType;
+ private final Key<? extends Provider<T>> providerKey;
+ private BindingImpl<? extends Provider<T>> providerBinding;
+
+ ProvidedByInternalFactory(
+ Class<?> rawType,
+ Class<? extends Provider<?>> providerType,
+ Key<? extends Provider<T>> providerKey,
+ boolean allowProxy) {
+ super(providerKey, allowProxy);
+ this.rawType = rawType;
+ this.providerType = providerType;
+ this.providerKey = providerKey;
+ }
+
+ public void initialize(InjectorImpl injector, Errors errors) throws
ErrorsException {
+ providerBinding =
+ injector.getBindingOrThrow(providerKey, errors,
JitLimitation.NEW_OR_EXISTING_JIT);
+ }
+
+ public T get(Errors errors, InternalContext context, Dependency
dependency, boolean linked)
+ throws ErrorsException {
+ checkState(providerBinding != null, "not initialized");
+
+ errors = errors.withSource(providerKey);
+ Provider provider = providerBinding.getInternalFactory().get(
+ errors, context, dependency, true);
+ try {
+ @SuppressWarnings("unchecked") // type is not checked within
circularGet
+ Object o = circularGet(provider, errors, context, dependency,
linked);
+ if (o != null && !rawType.isInstance(o)) {
+ throw errors.subtypeNotProvided(providerType,
rawType).toException();
+ }
+ @SuppressWarnings("unchecked") // protected by isInstance() check
above
+ T t = (T) o;
+ return t;
+ } catch (RuntimeException e) {
+ throw errors.errorInProvider(e).toException();
+ }
+ }
+}
=======================================
--- /trunk/core/src/com/google/inject/internal/ConstructorBindingImpl.java
Tue Dec 14 21:38:37 2010
+++ /trunk/core/src/com/google/inject/internal/ConstructorBindingImpl.java
Mon May 2 11:03:35 2011
@@ -38,7 +38,8 @@
import java.util.Map;
import java.util.Set;
-final class ConstructorBindingImpl<T> extends BindingImpl<T> implements
ConstructorBinding<T> {
+final class ConstructorBindingImpl<T> extends BindingImpl<T>
+ implements ConstructorBinding<T>, DelayedInitialize {
private final Factory<T> factory;
private final InjectionPoint constructorInjectionPoint;
=======================================
--- /trunk/core/src/com/google/inject/internal/InjectorImpl.java Mon May 2
06:30:37 2011
+++ /trunk/core/src/com/google/inject/internal/InjectorImpl.java Mon May 2
11:03:35 2011
@@ -503,22 +503,22 @@
}
<T> void initializeBinding(BindingImpl<T> binding, Errors errors) throws
ErrorsException {
- if (binding instanceof ConstructorBindingImpl<?>) {
- ((ConstructorBindingImpl) binding).initialize(this, errors);
+ if (binding instanceof DelayedInitialize) {
+ ((DelayedInitialize) binding).initialize(this, errors);
}
}
<T> void initializeJitBinding(BindingImpl<T> binding, Errors errors)
throws ErrorsException {
// 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 state.lock() during injector
creation.
- if (binding instanceof ConstructorBindingImpl<?>) {
+ // Note: We don't need to synchronize on state.lock() during injector
creation.
+ if (binding instanceof DelayedInitialize) {
Key<T> key = binding.getKey();
jitBindings.put(key, binding);
boolean successful = false;
- ConstructorBindingImpl cb = (ConstructorBindingImpl)binding;
+ DelayedInitialize delayed = (DelayedInitialize)binding;
try {
- cb.initialize(this, errors);
+ delayed.initialize(this, errors);
successful = true;
} finally {
if (!successful) {
@@ -662,8 +662,8 @@
/** Creates a binding for a type annotated with @ProvidedBy. */
<T> BindingImpl<T> createProvidedByBinding(Key<T> key, Scoping scoping,
ProvidedBy providedBy, Errors errors) throws ErrorsException {
- final Class<?> rawType = key.getTypeLiteral().getRawType();
- final Class<? extends Provider<?>> providerType = providedBy.value();
+ Class<?> rawType = key.getTypeLiteral().getRawType();
+ Class<? extends Provider<?>> providerType = providedBy.value();
// Make sure it's not the same type. TODO: Can we check for deeper
loops?
if (providerType == rawType) {
@@ -672,41 +672,20 @@
// Assume the provider provides an appropriate type. We double check
at runtime.
@SuppressWarnings("unchecked")
- final Key<? extends Provider<T>> providerKey
- = (Key<? extends Provider<T>>) Key.get(providerType);
- final BindingImpl<? extends Provider<?>> providerBinding
- = getBindingOrThrow(providerKey, errors,
JitLimitation.NEW_OR_EXISTING_JIT);
-
- InternalFactory<T> internalFactory =
- new
ProviderInternalFactory<T>(providerKey, !options.disableCircularProxies) {
- public T get(Errors errors, InternalContext context, Dependency
dependency, boolean linked)
- throws ErrorsException {
- errors = errors.withSource(providerKey);
- Provider provider = providerBinding.getInternalFactory().get(
- errors, context, dependency, true);
- try {
- @SuppressWarnings("unchecked") // type is not checked within
circularGet
- Object o = circularGet(provider, errors, context, dependency,
linked);
- if (o != null && !rawType.isInstance(o)) {
- throw errors.subtypeNotProvided(providerType,
rawType).toException();
- }
- @SuppressWarnings("unchecked") // protected by isInstance()
check above
- T t = (T) o;
- return t;
- } catch (RuntimeException e) {
- throw errors.errorInProvider(e).toException();
- }
- }
- };
+ Key<? extends Provider<T>> providerKey = (Key<? extends Provider<T>>)
Key.get(providerType);
+ ProvidedByInternalFactory<T> internalFactory =
+ new ProvidedByInternalFactory<T>(rawType, providerType,
+ providerKey, !options.disableCircularProxies);
Object source = rawType;
- return new LinkedProviderBindingImpl<T>(
+ return LinkedProviderBindingImpl.createWithInitializer(
this,
key,
source,
Scoping.<T>scope(key, this, internalFactory, source, scoping),
scoping,
- providerKey);
+ providerKey,
+ internalFactory);
}
/** Creates a binding for a type annotated with @ImplementedBy. */
=======================================
---
/trunk/core/src/com/google/inject/internal/LinkedProviderBindingImpl.java
Sat Jul 3 08:51:31 2010
+++
/trunk/core/src/com/google/inject/internal/LinkedProviderBindingImpl.java
Mon May 2 11:03:35 2011
@@ -28,21 +28,39 @@
import java.util.Set;
final class LinkedProviderBindingImpl<T>
- extends BindingImpl<T> implements ProviderKeyBinding<T>,
HasDependencies {
+ extends BindingImpl<T> implements ProviderKeyBinding<T>,
HasDependencies, DelayedInitialize {
final Key<? extends javax.inject.Provider<? extends T>> providerKey;
+ final DelayedInitialize delayedInitializer;
+
+ private LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key,
Object source,
+ InternalFactory<? extends T> internalFactory, Scoping scoping,
+ Key<? extends javax.inject.Provider<? extends T>> providerKey,
+ DelayedInitialize delayedInitializer) {
+ super(injector, key, source, internalFactory, scoping);
+ this.providerKey = providerKey;
+ this.delayedInitializer = delayedInitializer;
+ }
public LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key,
Object source,
InternalFactory<? extends T> internalFactory, Scoping scoping,
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
- super(injector, key, source, internalFactory, scoping);
- this.providerKey = providerKey;
+ this(injector, key, source, internalFactory, scoping, providerKey,
null);
}
LinkedProviderBindingImpl(Object source, Key<T> key, Scoping scoping,
Key<? extends javax.inject.Provider<? extends T>> providerKey) {
super(source, key, scoping);
this.providerKey = providerKey;
+ this.delayedInitializer = null;
+ }
+
+ static <T> LinkedProviderBindingImpl<T>
createWithInitializer(InjectorImpl injector, Key<T> key,
+ Object source, InternalFactory<? extends T> internalFactory, Scoping
scoping,
+ Key<? extends javax.inject.Provider<? extends T>> providerKey,
+ DelayedInitialize delayedInitializer) {
+ return new LinkedProviderBindingImpl<T>(injector, key, source,
internalFactory, scoping,
+ providerKey, delayedInitializer);
}
public <V> V acceptTargetVisitor(BindingTargetVisitor<? super T, V>
visitor) {
@@ -52,6 +70,12 @@
public Key<? extends javax.inject.Provider<? extends T>>
getProviderKey() {
return providerKey;
}
+
+ public void initialize(InjectorImpl injector, Errors errors) throws
ErrorsException {
+ if (delayedInitializer != null) {
+ delayedInitializer.initialize(injector, errors);
+ }
+ }
public Set<Dependency<?>> getDependencies() {
return ImmutableSet.<Dependency<?>>of(Dependency.get(providerKey));
=======================================
--- /trunk/core/test/com/google/inject/CircularDependencyTest.java Mon May
2 06:30:37 2011
+++ /trunk/core/test/com/google/inject/CircularDependencyTest.java Mon May
2 11:03:35 2011
@@ -88,21 +88,11 @@
assertCircularDependencies(injector);
}
- // TODO: This creates two As because circular dependencies between
@ProvidedBy
- // Providers aren't handled well right now. When we bind A, it looks
for its
- // Provider, which goes into InjectorImpl.createProvidedByBinding, which
- // goes into getBindingOrThrow for AutoAP. That creates a
ConstructorBinding
- // for AutoAP and looks up its dependency for Provider<B>, which ends up
- // in creativeProvidedByBinding for BP, which creates a
ConstructorBinding
- // for BP and looks up the dependency for Provider<A>. That ends up
creating
- // another providedByBinding for AutoAP, because the first one hasn't
been stored
- // anywhere yet. The solution is to initialize the dependency early,
similar
- // to what's done with ConstructorBindings.
-// public void testCircularlyDependentConstructorsWithProvidedBy()
-// throws CreationException {
-// Injector injector = Guice.createInjector();
-// assertCircularDependencies(injector);
-// }
+ public void testCircularlyDependentConstructorsWithProvidedBy()
+ throws CreationException {
+ Injector injector = Guice.createInjector();
+ assertCircularDependencies(injector);
+ }
private void assertCircularDependencies(Injector injector) {
A a = injector.getInstance(A.class);
@@ -238,9 +228,7 @@
fail();
} catch (ProvisionException expected) {
assertContains(expected.getMessage(),
- // TODO: this should fail at C2, but because of strangeness with
@ProvidedBy,
- // it fails in the wrong place right now.
- "Tried proxying " + D2.class.getName() + " to support a circular
dependency, ",
+ "Tried proxying " + C2.class.getName() + " to support a circular
dependency, ",
"but it is not an interface.");
}
}
@@ -336,9 +324,7 @@
fail();
} catch (ProvisionException expected) {
assertContains(expected.getMessage(),
- // TODO: this should fail at C2, but because of strangeness with
@ProvidedBy,
- // it fails in the wrong place right now.
- "Tried proxying " + D2.class.getName() + " to support a circular
dependency, ",
+ "Tried proxying " + C2.class.getName() + " to support a circular
dependency, ",
"but circular proxies are disabled.");
}
}
--
You received this message because you are subscribed to the Google Groups
"google-guice-dev" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/google-guice-dev?hl=en.