introduces InjectorBuilder.disableCircularProxies, to forcibly prevent guice from creating a proxy to resolve circular dependencies.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@1151 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/InjectorBuilder.java b/src/com/google/inject/InjectorBuilder.java
index 8a1beef..57586a8 100644
--- a/src/com/google/inject/InjectorBuilder.java
+++ b/src/com/google/inject/InjectorBuilder.java
@@ -1,5 +1,6 @@
package com.google.inject;
+import java.lang.reflect.Proxy;
import java.util.Arrays;
import com.google.inject.internal.InternalInjectorCreator;
@@ -43,7 +44,8 @@
private final InternalInjectorCreator creator = new InternalInjectorCreator();
private Stage stage = Stage.DEVELOPMENT;
- private boolean jitDisabled;
+ private boolean jitDisabled = false;
+ private boolean allowCircularProxy = true;
/**
* Sets the stage for the injector. If the stage is {@link Stage#PRODUCTION},
@@ -71,6 +73,15 @@
this.jitDisabled = true;
return this;
}
+
+ /**
+ * Prevents Guice from constructing a {@link Proxy} when a circular dependency
+ * is found.
+ */
+ public InjectorBuilder disableCircularProxies() {
+ this.allowCircularProxy = false;
+ return this;
+ }
/** Adds more modules that will be used when the Injector is created. */
public InjectorBuilder addModules(Iterable<? extends Module> modules) {
@@ -86,7 +97,7 @@
/** Builds the injector. */
public Injector build() {
- creator.injectorOptions(new InternalInjectorCreator.InjectorOptions(stage, jitDisabled));
+ creator.injectorOptions(new InternalInjectorCreator.InjectorOptions(stage, jitDisabled, allowCircularProxy));
return creator.build();
}
diff --git a/src/com/google/inject/internal/ConstructorBindingImpl.java b/src/com/google/inject/internal/ConstructorBindingImpl.java
index 94c0847..b363b50 100644
--- a/src/com/google/inject/internal/ConstructorBindingImpl.java
+++ b/src/com/google/inject/internal/ConstructorBindingImpl.java
@@ -117,6 +117,7 @@
@SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type
public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
+ factory.allowCircularProxy = injector.options.allowCircularProxy;
factory.constructorInjector
= (ConstructorInjector<T>) injector.constructors.get(constructorInjectionPoint, errors);
}
@@ -177,6 +178,7 @@
private static class Factory<T> implements InternalFactory<T> {
private final boolean failIfNotLinked;
private final Key<?> key;
+ private boolean allowCircularProxy;
private ConstructorInjector<T> constructorInjector;
Factory(boolean failIfNotLinked, Key<?> key) {
@@ -196,7 +198,7 @@
// This may not actually be safe because it could return a super type of T (if that's all the
// client needs), but it should be OK in practice thanks to the wonders of erasure.
return (T) constructorInjector.construct(errors, context,
- dependency.getKey().getTypeLiteral().getRawType());
+ dependency.getKey().getTypeLiteral().getRawType(), allowCircularProxy);
}
}
}
diff --git a/src/com/google/inject/internal/ConstructorInjector.java b/src/com/google/inject/internal/ConstructorInjector.java
index 9e9e377..1f8d65d 100644
--- a/src/com/google/inject/internal/ConstructorInjector.java
+++ b/src/com/google/inject/internal/ConstructorInjector.java
@@ -55,14 +55,18 @@
* Construct an instance. Returns {@code Object} instead of {@code T} because
* it may return a proxy.
*/
- Object construct(Errors errors, InternalContext context, Class<?> expectedType)
+ Object construct(Errors errors, InternalContext context, Class<?> expectedType, boolean allowProxy)
throws ErrorsException {
ConstructionContext<T> constructionContext = context.getConstructionContext(this);
// We have a circular reference between constructors. Return a proxy.
if (constructionContext.isConstructing()) {
- // TODO (crazybob): if we can't proxy this object, can we proxy the other object?
- return constructionContext.createProxy(errors, expectedType);
+ if(!allowProxy) {
+ throw errors.circularProxiesDisabled(expectedType).toException();
+ } else {
+ // TODO (crazybob): if we can't proxy this object, can we proxy the other object?
+ return constructionContext.createProxy(errors, expectedType);
+ }
}
// If we're re-entering this factory while injecting fields or methods,
diff --git a/src/com/google/inject/internal/Errors.java b/src/com/google/inject/internal/Errors.java
index ca09096..99c7732 100644
--- a/src/com/google/inject/internal/Errors.java
+++ b/src/com/google/inject/internal/Errors.java
@@ -370,6 +370,12 @@
"Tried proxying %s to support a circular dependency, but it is not an interface.",
expectedType);
}
+
+ public Errors circularProxiesDisabled(Class<?> expectedType) {
+ return addMessage(
+ "Tried proxying %s to support a circular dependency, but circular proxies are disabled.",
+ expectedType);
+ }
public void throwCreationExceptionIfErrorsExist() {
if (!hasErrors()) {
diff --git a/src/com/google/inject/internal/InternalInjectorCreator.java b/src/com/google/inject/internal/InternalInjectorCreator.java
index d018d21..3e491c0 100644
--- a/src/com/google/inject/internal/InternalInjectorCreator.java
+++ b/src/com/google/inject/internal/InternalInjectorCreator.java
@@ -67,10 +67,12 @@
public static class InjectorOptions {
final Stage stage;
final boolean jitDisabled;
+ final boolean allowCircularProxy;
- public InjectorOptions(Stage stage, boolean jitDisabled) {
+ public InjectorOptions(Stage stage, boolean jitDisabled, boolean allowCircularProxy) {
this.stage = stage;
this.jitDisabled = jitDisabled;
+ this.allowCircularProxy = allowCircularProxy;
}
}
diff --git a/test/com/google/inject/CircularDependencyTest.java b/test/com/google/inject/CircularDependencyTest.java
index aed3984..1cc4e74 100644
--- a/test/com/google/inject/CircularDependencyTest.java
+++ b/test/com/google/inject/CircularDependencyTest.java
@@ -104,6 +104,26 @@
});
injector.getInstance(A.class);
}
+
+ public void testDisablingCircularProxies() {
+ Injector injector = new InjectorBuilder()
+ .disableCircularProxies()
+ .addModules(new AbstractModule() {
+ protected void configure() {
+ binder.bind(A.class).to(E.class);
+ binder.bind(B.class).to(E.class);
+ }
+ }).build();
+
+ try {
+ injector.getInstance(A.class);
+ fail("expected exception");
+ } catch(ProvisionException expected) {
+ assertContains(expected.getMessage(),
+ "Tried proxying " + A.class.getName() + " to support a circular dependency, but circular proxies are disabled",
+ "Tried proxying " + B.class.getName() + " to support a circular dependency, but circular proxies are disabled");
+ }
+ }
@Singleton
static class E implements A, B {