Skip to content

Instantly share code, notes, and snippets.

@gissuebot
Created July 7, 2014 18:12
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gissuebot/2af8db53bba2d4506915 to your computer and use it in GitHub Desktop.
Save gissuebot/2af8db53bba2d4506915 to your computer and use it in GitHub Desktop.
Migrated attachment for Guice issue 342, comment 5
Index: test/com/google/inject/JitBindingsTest.java
===================================================================
--- test/com/google/inject/JitBindingsTest.java (revision 0)
+++ test/com/google/inject/JitBindingsTest.java (revision 0)
@@ -0,0 +1,165 @@
+package com.google.inject;
+
+import junit.framework.TestCase;
+
+public class JitBindingsTest extends TestCase {
+
+ private String jitFailed(Class<?> clazz) {
+ return jitFailed(TypeLiteral.get(clazz));
+ }
+
+ private String jitFailed(TypeLiteral<?> clazz) {
+ return "Just in time binding is disabled, and " + clazz + " is not explicitly bound.";
+ }
+
+ public void testLinkedBindingWorks() {
+ Injector injector = Guice.createInjectorBuilder().disableJit(true).addModules(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Foo.class).to(FooImpl.class);
+ }
+ }).build();
+ injector.getInstance(Foo.class);
+
+ try {
+ injector.getInstance(FooImpl.class);
+ fail("should have failed");
+ } catch(ConfigurationException expected) {
+ Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(FooImpl.class));
+ assertTrue(expected.getMessage(), !expected.getMessage().contains("2)"));
+ }
+ try {
+ injector.getProvider(FooImpl.class);
+ fail("should have failed");
+ } catch(ConfigurationException expected) {
+ Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(FooImpl.class));
+ assertTrue(expected.getMessage(), !expected.getMessage().contains("2)"));
+ }
+ }
+
+ public void testMoreBasicsWork() {
+ Injector injector = Guice.createInjectorBuilder().disableJit(true).addModules(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Foo.class).to(FooImpl.class);
+ bind(Bar.class);
+ bind(FooBar.class);
+ }
+ }).build();
+ injector.getInstance(FooBar.class);
+ injector.getInstance(Bar.class);
+ injector.getInstance(Foo.class);
+ try {
+ injector.getInstance(FooImpl.class);
+ fail("should have failed");
+ } catch(ConfigurationException expected) {
+ Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(FooImpl.class));
+ assertTrue(expected.getMessage(), !expected.getMessage().contains("2)"));
+ }
+ try {
+ injector.getProvider(FooImpl.class);
+ fail("should have failed");
+ } catch(ConfigurationException expected) {
+ Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(FooImpl.class));
+ assertTrue(expected.getMessage(), !expected.getMessage().contains("2)"));
+ }
+ }
+
+ public void testLinkedProviderBindingWorks() {
+ Injector injector = Guice.createInjectorBuilder().disableJit(true).addModules(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Foo.class).toProvider(FooImplProvider.class);
+ }
+ }).build();
+ injector.getInstance(Foo.class);
+
+ try {
+ injector.getInstance(FooImpl.class);
+ fail("should have failed");
+ } catch(ConfigurationException expected) {
+ Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(FooImpl.class));
+ assertTrue(expected.getMessage(), !expected.getMessage().contains("2)"));
+ }
+ try {
+ injector.getProvider(FooImpl.class);
+ fail("should have failed");
+ } catch(ConfigurationException expected) {
+ Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(FooImpl.class));
+ assertTrue(expected.getMessage(), !expected.getMessage().contains("2)"));
+ }
+ }
+
+ public void testJitGetFails() {
+ try {
+ Guice.createInjectorBuilder().disableJit(true).build().getInstance(Bar.class);
+ fail("should have failed");
+ } catch(ConfigurationException expected) {
+ Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
+ assertTrue(expected.getMessage(), !expected.getMessage().contains("2)"));
+ }
+ }
+
+ public void testJitInjectionFails() {
+ try {
+ Guice.createInjectorBuilder().disableJit(true).addModules(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Foo.class).to(FooImpl.class);
+ bind(FooBar.class);
+ }
+ }).build();
+ fail("should have failed");
+ } catch (CreationException expected) {
+ Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
+ assertTrue(expected.getMessage(), !expected.getMessage().contains("2)"));
+ }
+ }
+
+ public void testJitProviderGetFails() {
+ try {
+ Guice.createInjectorBuilder().disableJit(true).build().getProvider(Bar.class);
+ fail("should have failed");
+ } catch (ConfigurationException expected) {
+ Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
+ assertTrue(expected.getMessage(), !expected.getMessage().contains("2)"));
+ }
+ }
+
+ public void testJitProviderInjectionFails() {
+ try {
+ Guice.createInjectorBuilder().disableJit(true).addModules(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Foo.class).to(FooImpl.class);
+ bind(ProviderFooBar.class);
+ }
+ }).build();
+ fail("should have failed");
+ } catch (CreationException expected) {
+ Asserts.assertContains(expected.getMessage(), "1) " + jitFailed(Bar.class));
+ assertTrue(expected.getMessage(), !expected.getMessage().contains("2)"));
+ }
+ }
+
+
+ private static interface Foo {}
+ private static class FooImpl implements Foo {}
+ private static class Bar {}
+ private static class FooBar {
+ @Inject Foo foo;
+ @Inject Bar bar;
+ }
+ private static class ProviderFooBar {
+ @Inject Provider<Foo> foo;
+ @Inject Provider<Bar> bar;
+ }
+ private static class FooImplProvider implements Provider<Foo> {
+ @Override
+ public Foo get() {
+ return new FooImpl();
+ }
+ }
+
+
+}
Index: test/com/google/inject/ReflectionTest.java
===================================================================
--- test/com/google/inject/ReflectionTest.java (revision 1113)
+++ test/com/google/inject/ReflectionTest.java (working copy)
@@ -16,11 +16,14 @@
package com.google.inject;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
import java.lang.annotation.Retention;
-import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
import junit.framework.TestCase;
-import com.google.inject.internal.InjectorBuilder;
+import com.google.inject.internal.InjectorBuilderImpl;
+
/**
* @author crazybob@google.com (Bob Lee)
*/
@@ -30,7 +33,7 @@
@BindingAnnotation @interface I {}
public void testNormalBinding() throws CreationException {
- InjectorBuilder builder = new InjectorBuilder();
+ InjectorBuilder builder = new InjectorBuilderImpl();
final Foo foo = new Foo();
Injector injector = Guice.createInjector(new AbstractModule() {
@@ -59,7 +62,7 @@
}
public void testLinkedBinding() throws CreationException {
- InjectorBuilder builder = new InjectorBuilder();
+ InjectorBuilder builder = new InjectorBuilderImpl();
final Bar bar = new Bar();
Injector injector = Guice.createInjector(new AbstractModule() {
Index: src/com/google/inject/InjectorBuilder.java
===================================================================
--- src/com/google/inject/InjectorBuilder.java (revision 0)
+++ src/com/google/inject/InjectorBuilder.java (revision 0)
@@ -0,0 +1,35 @@
+package com.google.inject;
+
+/**
+ * Allows Injectors to be built with many different parameters.
+ */
+public interface InjectorBuilder {
+
+ /**
+ * Sets the stage for the created injector. If the stage is {@link Stage#PRODUCTION},
+ * singletons will be eagerly loaded in when the Injector is built.
+ */
+ InjectorBuilder stage(Stage stage);
+
+ /**
+ * If just-in-time binding is disabled, then classes cannot inject classes
+ * that are not explicitly bound in a module.
+ *
+ * Tools can still retrieve bindings for implicit bindings (bindings created
+ * through a linked binding) if jit binding is disabled, however they will be
+ * unable to retrieve an instance of the binding.
+ *
+ * By default, JIT binding is enabled.
+ */
+ InjectorBuilder disableJit(boolean jitDisabled);
+
+ /** Adds more modules that will be used when the Injector is created. */
+ InjectorBuilder addModules(Iterable<? extends Module> modules);
+
+ /** Adds more modules that will be used when the Injector is created. */
+ InjectorBuilder addModules(Module... modules);
+
+ /** Builds the injector. */
+ Injector build();
+
+}
\ No newline at end of file
Index: src/com/google/inject/internal/InternalFactory.java
===================================================================
--- src/com/google/inject/internal/InternalFactory.java (revision 1113)
+++ src/com/google/inject/internal/InternalFactory.java (working copy)
@@ -27,11 +27,12 @@
/**
* Creates an object to be injected.
+ * @param context of this injection
+ * @param linked true if getting as a result of a linked binding
*
- * @param context of this injection
* @throws com.google.inject.internal.ErrorsException if a value cannot be provided
* @return instance to be injected
*/
- T get(Errors errors, InternalContext context, Dependency<?> dependency)
+ T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException;
}
Index: src/com/google/inject/internal/BindingProcessor.java
===================================================================
--- src/com/google/inject/internal/BindingProcessor.java (revision 1113)
+++ src/com/google/inject/internal/BindingProcessor.java (working copy)
@@ -88,7 +88,7 @@
public Void visit(ConstructorBinding<? extends T> binding) {
try {
ConstructorBindingImpl<T> onInjector = ConstructorBindingImpl.create(injector, key,
- binding.getConstructor(), source, scoping, errors);
+ binding.getConstructor(), source, scoping, errors, false);
scheduleInitialization(onInjector);
putBinding(onInjector);
} catch (ErrorsException e) {
@@ -165,7 +165,7 @@
// This cast is safe after the preceeding check.
try {
BindingImpl<T> binding = injector.createUninitializedBinding(
- key, scoping, source, errors);
+ key, scoping, source, errors, false);
scheduleInitialization(binding);
putBinding(binding);
} catch (ErrorsException e) {
Index: src/com/google/inject/internal/Errors.java
===================================================================
--- src/com/google/inject/internal/Errors.java (revision 1113)
+++ src/com/google/inject/internal/Errors.java (working copy)
@@ -124,6 +124,10 @@
public Errors missingImplementation(Key key) {
return addMessage("No implementation for %s was bound.", key);
}
+
+ public Errors jitDisabled(Key key) {
+ return addMessage("Just in time binding is disabled, and %s is not explicitly bound.", key);
+ }
public Errors converterReturnedNull(String stringValue, Object source,
TypeLiteral<?> type, MatcherAndConverter matchingConverter) {
Index: src/com/google/inject/internal/ProviderToInternalFactoryAdapter.java
===================================================================
--- src/com/google/inject/internal/ProviderToInternalFactoryAdapter.java (revision 1113)
+++ src/com/google/inject/internal/ProviderToInternalFactoryAdapter.java (working copy)
@@ -40,7 +40,7 @@
T t = injector.callInContext(new ContextualCallable<T>() {
public T call(InternalContext context) throws ErrorsException {
Dependency dependency = context.getDependency();
- return internalFactory.get(errors, context, dependency);
+ return internalFactory.get(errors, context, dependency, false);
}
});
errors.throwIfNewErrors(0);
Index: src/com/google/inject/internal/InjectorImpl.java
===================================================================
--- src/com/google/inject/internal/InjectorImpl.java (revision 1113)
+++ src/com/google/inject/internal/InjectorImpl.java (working copy)
@@ -21,6 +21,7 @@
import com.google.inject.ConfigurationException;
import com.google.inject.ImplementedBy;
import com.google.inject.Injector;
+import com.google.inject.InjectorBuilder;
import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Module;
@@ -60,16 +61,18 @@
final InjectorImpl parent;
final BindingsMultimap bindingsMultimap = new BindingsMultimap();
final Initializer initializer;
+ final boolean jitDisabled;
/** Just-in-time binding cache. Guarded by state.lock() */
final Map<Key<?>, BindingImpl<?>> jitBindings = Maps.newHashMap();
Lookups lookups = new DeferredLookups(this);
- InjectorImpl(@Nullable InjectorImpl parent, State state, Initializer initializer) {
+ InjectorImpl(@Nullable InjectorImpl parent, State state, Initializer initializer, boolean jitDisabled) {
this.parent = parent;
this.state = state;
this.initializer = initializer;
+ this.jitDisabled = jitDisabled;
if (parent != null) {
localContext = parent.localContext;
@@ -101,7 +104,7 @@
public <T> BindingImpl<T> getBinding(Key<T> key) {
Errors errors = new Errors(key);
try {
- BindingImpl<T> result = getBindingOrThrow(key, errors);
+ BindingImpl<T> result = getBindingOrThrow(key, errors, true);
errors.throwConfigurationExceptionIfErrorsExist();
return result;
} catch (ErrorsException e) {
@@ -115,7 +118,7 @@
* checks for an explicit binding. If no explicit binding is found, it looks for a just-in-time
* binding.
*/
- <T> BindingImpl<T> getBindingOrThrow(Key<T> key, Errors errors)
+ <T> BindingImpl<T> getBindingOrThrow(Key<T> key, Errors errors, boolean allowJit)
throws ErrorsException {
// Check explicit bindings, i.e. bindings created by modules.
BindingImpl<T> binding = state.getExplicitBinding(key);
@@ -124,7 +127,7 @@
}
// Look for an on-demand binding.
- return getJustInTimeBinding(key, errors);
+ return getJustInTimeBinding(key, errors, allowJit);
}
public <T> Binding<T> getBinding(Class<T> type) {
@@ -136,7 +139,7 @@
}
public Injector createChildInjector(Iterable<? extends Module> modules) {
- return new InjectorBuilder()
+ return new InjectorBuilderImpl()
.parentInjector(this)
.addModules(modules)
.build();
@@ -145,14 +148,26 @@
public Injector createChildInjector(Module... modules) {
return createChildInjector(ImmutableList.of(modules));
}
+
+ @Override
+ public InjectorBuilder createChildInjectorBuilder() {
+ return new InjectorBuilderImpl()
+ .parentInjector(this);
+ }
/**
* Returns a just-in-time binding for {@code key}, creating it if necessary.
*
* @throws ErrorsException if the binding could not be created.
*/
- private <T> BindingImpl<T> getJustInTimeBinding(Key<T> key, Errors errors)
+ private <T> BindingImpl<T> getJustInTimeBinding(Key<T> key, Errors errors, boolean allowJit)
throws ErrorsException {
+
+
+ if(!isProvider(key) && !allowJit && jitDisabled) {
+ throw errors.jitDisabled(key).toException();
+ }
+
synchronized (state.lock()) {
// first try to find a JIT binding that we've already created
for (InjectorImpl injector = this; injector != null; injector = injector.parent) {
@@ -163,7 +178,7 @@
return binding;
}
}
-
+
return createJustInTimeBindingRecursive(key, errors);
}
}
@@ -217,7 +232,7 @@
@SuppressWarnings("unchecked") // safe because T came from Key<Provider<T>>
Key<T> providedKey = (Key<T>) key.ofType(entryType);
- BindingImpl<T> delegate = getBindingOrThrow(providedKey, errors);
+ BindingImpl<T> delegate = getBindingOrThrow(providedKey, errors, false);
return new ProviderBindingImpl<T>(this, key, delegate);
}
@@ -234,7 +249,7 @@
static <T> InternalFactory<Provider<T>> createInternalFactory(Binding<T> providedBinding) {
final Provider<T> provider = providedBinding.getProvider();
return new InternalFactory<Provider<T>>() {
- public Provider<T> get(Errors errors, InternalContext context, Dependency dependency) {
+ public Provider<T> get(Errors errors, InternalContext context, Dependency dependency, boolean linked) {
return provider;
}
};
@@ -394,7 +409,7 @@
* none is specified.
*/
<T> BindingImpl<T> createUninitializedBinding(Key<T> key, Scoping scoping, Object source,
- Errors errors) throws ErrorsException {
+ Errors errors, boolean jitBinding) throws ErrorsException {
Class<?> rawType = key.getTypeLiteral().getRawType();
// Don't try to inject arrays, or enums.
@@ -424,7 +439,8 @@
return createProvidedByBinding(key, scoping, providedBy, errors);
}
- return ConstructorBindingImpl.create(this, key, null, source, scoping, errors);
+
+ return ConstructorBindingImpl.create(this, key, null, source, scoping, errors, jitBinding && jitDisabled);
}
/**
@@ -473,14 +489,14 @@
final Key<? extends Provider<T>> providerKey
= (Key<? extends Provider<T>>) Key.get(providerType);
final BindingImpl<? extends Provider<?>> providerBinding
- = getBindingOrThrow(providerKey, errors);
+ = getBindingOrThrow(providerKey, errors, false);
InternalFactory<T> internalFactory = new InternalFactory<T>() {
- public T get(Errors errors, InternalContext context, Dependency dependency)
+ 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);
+ errors, context, dependency, linked);
try {
Object o = provider.get();
if (o != null && !rawType.isInstance(o)) {
@@ -527,13 +543,13 @@
// Look up the target binding.
final Key<? extends T> targetKey = Key.get(subclass);
- final BindingImpl<? extends T> targetBinding = getBindingOrThrow(targetKey, errors);
+ final BindingImpl<? extends T> targetBinding = getBindingOrThrow(targetKey, errors, false);
InternalFactory<T> internalFactory = new InternalFactory<T>() {
- public T get(Errors errors, InternalContext context, Dependency<?> dependency)
+ public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
return targetBinding.getInternalFactory().get(
- errors.withSource(targetKey), context, dependency);
+ errors.withSource(targetKey), context, dependency, false);
}
};
@@ -554,7 +570,7 @@
private <T> BindingImpl<T> createJustInTimeBindingRecursive(Key<T> key, Errors errors)
throws ErrorsException {
// ask the parent to create the JIT binding
- if (parent != null) {
+ if (parent != null && !parent.jitDisabled) {
try {
return parent.createJustInTimeBindingRecursive(key, new Errors());
} catch (ErrorsException ignored) {
@@ -622,7 +638,7 @@
if (key.hasAttributes()) {
try {
Errors ignored = new Errors();
- return getBindingOrThrow(key.withoutAttributes(), ignored);
+ return getBindingOrThrow(key.withoutAttributes(), ignored, false);
} catch (ErrorsException ignored) {
// throw with a more appropriate message below
}
@@ -631,15 +647,15 @@
}
Object source = key.getTypeLiteral().getRawType();
- BindingImpl<T> binding = createUninitializedBinding(key, Scoping.UNSCOPED, source, errors);
+ BindingImpl<T> binding = createUninitializedBinding(key, Scoping.UNSCOPED, source, errors, true);
errors.throwIfNewErrors(numErrorsBefore);
initializeJitBinding(binding, errors);
return binding;
}
- <T> InternalFactory<? extends T> getInternalFactory(Key<T> key, Errors errors)
+ <T> InternalFactory<? extends T> getInternalFactory(Key<T> key, Errors errors, boolean allowJit)
throws ErrorsException {
- return getBindingOrThrow(key, errors).getInternalFactory();
+ return getBindingOrThrow(key, errors, allowJit).getInternalFactory();
}
public Map<Key<?>, Binding<?>> getBindings() {
@@ -707,7 +723,7 @@
<T> SingleParameterInjector<T> createParameterInjector(final Dependency<T> dependency,
final Errors errors) throws ErrorsException {
- InternalFactory<? extends T> factory = getInternalFactory(dependency.getKey(), errors);
+ InternalFactory<? extends T> factory = getInternalFactory(dependency.getKey(), errors, false);
return new SingleParameterInjector<T>(dependency, factory);
}
@@ -747,7 +763,9 @@
}
<T> Provider<T> getProviderOrThrow(final Key<T> key, Errors errors) throws ErrorsException {
- final InternalFactory<? extends T> factory = getInternalFactory(key, errors);
+
+
+ final InternalFactory<? extends T> factory = getInternalFactory(key, errors, false);
final Dependency<T> dependency = Dependency.get(key);
return new Provider<T>() {
@@ -758,7 +776,7 @@
public T call(InternalContext context) throws ErrorsException {
Dependency previous = context.setDependency(dependency);
try {
- return factory.get(errors, context, dependency);
+ return factory.get(errors, context, dependency, false);
} finally {
context.setDependency(previous);
}
Index: src/com/google/inject/internal/UntargettedBindingImpl.java
===================================================================
--- src/com/google/inject/internal/UntargettedBindingImpl.java (revision 1113)
+++ src/com/google/inject/internal/UntargettedBindingImpl.java (working copy)
@@ -26,7 +26,7 @@
UntargettedBindingImpl(InjectorImpl injector, Key<T> key, Object source) {
super(injector, key, source, new InternalFactory<T>() {
- public T get(Errors errors, InternalContext context, Dependency<?> dependency) {
+ public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) {
throw new AssertionError();
}
}, Scoping.UNSCOPED);
Index: src/com/google/inject/internal/InjectorShell.java
===================================================================
--- src/com/google/inject/internal/InjectorShell.java (revision 1113)
+++ src/com/google/inject/internal/InjectorShell.java (working copy)
@@ -70,6 +70,7 @@
private InjectorImpl parent;
private Stage stage;
+ private boolean jitDisabled;
/** null unless this exists in a {@link Binder#newPrivateBinder private environment} */
private PrivateElementsImpl privateElements;
@@ -84,6 +85,11 @@
this.stage = stage;
return this;
}
+
+ Builder jitDisabled(boolean jitDisabled) {
+ this.jitDisabled = jitDisabled;
+ return this;
+ }
Builder privateElements(PrivateElements privateElements) {
this.privateElements = (PrivateElementsImpl) privateElements;
@@ -113,7 +119,7 @@
checkState(privateElements == null || parent != null, "PrivateElements with no parent");
checkState(state != null, "no state. Did you remember to lock() ?");
- InjectorImpl injector = new InjectorImpl(parent, state, initializer);
+ InjectorImpl injector = new InjectorImpl(parent, state, initializer, jitDisabled);
if (privateElements != null) {
privateElements.initInjector(injector);
}
@@ -193,7 +199,7 @@
this.injector = injector;
}
- public Injector get(Errors errors, InternalContext context, Dependency<?> dependency)
+ public Injector get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
return injector;
}
@@ -221,7 +227,7 @@
}
private static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
- public Logger get(Errors errors, InternalContext context, Dependency<?> dependency) {
+ public Logger get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked) {
InjectionPoint injectionPoint = dependency.getInjectionPoint();
return injectionPoint == null
? Logger.getAnonymousLogger()
Index: src/com/google/inject/internal/ExposedKeyFactory.java
===================================================================
--- src/com/google/inject/internal/ExposedKeyFactory.java (revision 1113)
+++ src/com/google/inject/internal/ExposedKeyFactory.java (working copy)
@@ -49,8 +49,8 @@
this.delegate = explicitBinding;
}
- public T get(Errors errors, InternalContext context, Dependency<?> dependency)
+ public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
- return delegate.getInternalFactory().get(errors, context, dependency);
+ return delegate.getInternalFactory().get(errors, context, dependency, linked);
}
}
Index: src/com/google/inject/internal/SingleParameterInjector.java
===================================================================
--- src/com/google/inject/internal/SingleParameterInjector.java (revision 1113)
+++ src/com/google/inject/internal/SingleParameterInjector.java (working copy)
@@ -35,7 +35,7 @@
private T inject(Errors errors, InternalContext context) throws ErrorsException {
Dependency previous = context.setDependency(dependency);
try {
- return factory.get(errors.withSource(dependency), context, dependency);
+ return factory.get(errors.withSource(dependency), context, dependency, false);
} finally {
context.setDependency(previous);
}
Index: src/com/google/inject/internal/InjectorBuilderImpl.java
===================================================================
--- src/com/google/inject/internal/InjectorBuilderImpl.java (revision 1113)
+++ src/com/google/inject/internal/InjectorBuilderImpl.java (working copy)
@@ -18,6 +18,7 @@
import com.google.inject.Binding;
import com.google.inject.Injector;
+import com.google.inject.InjectorBuilder;
import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.Module;
@@ -26,6 +27,8 @@
import com.google.inject.TypeLiteral;
import com.google.inject.Scope;
import com.google.inject.spi.Dependency;
+
+import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
@@ -51,7 +54,7 @@
* @author crazybob@google.com (Bob Lee)
* @author jessewilson@google.com (Jesse Wilson)
*/
-public final class InjectorBuilder {
+public final class InjectorBuilderImpl implements com.google.inject.InjectorBuilder {
private final Stopwatch stopwatch = new Stopwatch();
private final Errors errors = new Errors();
@@ -65,20 +68,21 @@
private final InjectorShell.Builder shellBuilder = new InjectorShell.Builder();
private List<InjectorShell> shells;
- public InjectorBuilder() {
+ public InjectorBuilderImpl() {
injectionRequestProcessor = new InjectionRequestProcessor(errors, initializer);
bindingProcesor = new BindingProcessor(errors, initializer);
}
- /**
- * Sets the stage for the created injector. If the stage is {@link Stage#PRODUCTION}, this class
- * will eagerly load singletons.
- */
public InjectorBuilder stage(Stage stage) {
shellBuilder.stage(stage);
this.stage = stage;
return this;
}
+
+ public InjectorBuilder disableJit(boolean jitDisabled) {
+ shellBuilder.jitDisabled(jitDisabled);
+ return this;
+ }
/**
* Sets the parent of the injector to-be-constructed. As a side effect, this sets this injector's
@@ -93,6 +97,12 @@
shellBuilder.addModules(modules);
return this;
}
+
+ @Override
+ public InjectorBuilder addModules(Module... modules) {
+ shellBuilder.addModules(Arrays.asList(modules));
+ return this;
+ }
public Injector build() {
if (shellBuilder == null) {
@@ -200,7 +210,7 @@
Dependency previous = context.setDependency(dependency);
Errors errorsForBinding = errors.withSource(dependency);
try {
- binding.getInternalFactory().get(errorsForBinding, context, dependency);
+ binding.getInternalFactory().get(errorsForBinding, context, dependency, false);
} catch (ErrorsException e) {
errorsForBinding.merge(e.getErrors());
} finally {
@@ -268,6 +278,9 @@
public Injector createChildInjector(Module... modules) {
return delegateInjector.createChildInjector(modules);
}
+ public InjectorBuilder createChildInjectorBuilder() {
+ return delegateInjector.createChildInjectorBuilder();
+ }
public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
return delegateInjector.getScopeBindings();
}
Index: src/com/google/inject/internal/BoundProviderFactory.java
===================================================================
--- src/com/google/inject/internal/BoundProviderFactory.java (revision 1113)
+++ src/com/google/inject/internal/BoundProviderFactory.java (working copy)
@@ -41,16 +41,16 @@
public void notify(Errors errors) {
try {
- providerFactory = injector.getInternalFactory(providerKey, errors.withSource(source));
+ providerFactory = injector.getInternalFactory(providerKey, errors.withSource(source), true);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
}
- public T get(Errors errors, InternalContext context, Dependency<?> dependency)
+ public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
errors = errors.withSource(providerKey);
- javax.inject.Provider<? extends T> provider = providerFactory.get(errors, context, dependency);
+ javax.inject.Provider<? extends T> provider = providerFactory.get(errors, context, dependency, true);
try {
return errors.checkForNull(provider.get(), source, dependency);
} catch(RuntimeException userException) {
Index: src/com/google/inject/internal/InternalFactoryToProviderAdapter.java
===================================================================
--- src/com/google/inject/internal/InternalFactoryToProviderAdapter.java (revision 1113)
+++ src/com/google/inject/internal/InternalFactoryToProviderAdapter.java (working copy)
@@ -34,7 +34,7 @@
this.source = checkNotNull(source, "source");
}
- public T get(Errors errors, InternalContext context, Dependency<?> dependency)
+ public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
try {
return errors.checkForNull(initializable.get(errors).get(), source, dependency);
Index: src/com/google/inject/internal/InjectorBuilder.java
===================================================================
--- src/com/google/inject/internal/InjectorBuilder.java (revision 1113)
+++ src/com/google/inject/internal/InjectorBuilder.java (working copy)
@@ -1,299 +0,0 @@
-/**
- * Copyright (C) 2006 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 com.google.inject.Binding;
-import com.google.inject.Injector;
-import com.google.inject.Key;
-import com.google.inject.MembersInjector;
-import com.google.inject.Module;
-import com.google.inject.Provider;
-import com.google.inject.Stage;
-import com.google.inject.TypeLiteral;
-import com.google.inject.Scope;
-import com.google.inject.spi.Dependency;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.lang.annotation.Annotation;
-
-/**
- * Builds a tree of injectors. This is a primary injector, plus child injectors needed for each
- * {@code Binder.newPrivateBinder() private environment}. The primary injector is not necessarily a
- * top-level injector.
- *
- * <p>Injector construction happens in two phases.
- * <ol>
- * <li>Static building. In this phase, we interpret commands, create bindings, and inspect
- * dependencies. During this phase, we hold a lock to ensure consistency with parent injectors.
- * No user code is executed in this phase.</li>
- * <li>Dynamic injection. In this phase, we call user code. We inject members that requested
- * injection. This may require user's objects be created and their providers be called. And we
- * create eager singletons. In this phase, user code may have started other threads. This phase
- * is not executed for injectors created using {@link Stage#TOOL the tool stage}</li>
- * </ol>
- *
- * @author crazybob@google.com (Bob Lee)
- * @author jessewilson@google.com (Jesse Wilson)
- */
-public final class InjectorBuilder {
-
- private final Stopwatch stopwatch = new Stopwatch();
- private final Errors errors = new Errors();
-
- private Stage stage;
-
- private final Initializer initializer = new Initializer();
- private final BindingProcessor bindingProcesor;
- private final InjectionRequestProcessor injectionRequestProcessor;
-
- private final InjectorShell.Builder shellBuilder = new InjectorShell.Builder();
- private List<InjectorShell> shells;
-
- public InjectorBuilder() {
- injectionRequestProcessor = new InjectionRequestProcessor(errors, initializer);
- bindingProcesor = new BindingProcessor(errors, initializer);
- }
-
- /**
- * Sets the stage for the created injector. If the stage is {@link Stage#PRODUCTION}, this class
- * will eagerly load singletons.
- */
- public InjectorBuilder stage(Stage stage) {
- shellBuilder.stage(stage);
- this.stage = stage;
- return this;
- }
-
- /**
- * Sets the parent of the injector to-be-constructed. As a side effect, this sets this injector's
- * stage to the stage of {@code parent}.
- */
- public InjectorBuilder parentInjector(InjectorImpl parent) {
- shellBuilder.parent(parent);
- return stage(parent.getInstance(Stage.class));
- }
-
- public InjectorBuilder addModules(Iterable<? extends Module> modules) {
- shellBuilder.addModules(modules);
- return this;
- }
-
- public Injector build() {
- if (shellBuilder == null) {
- throw new AssertionError("Already built, builders are not reusable.");
- }
-
- // Synchronize while we're building up the bindings and other injector state. This ensures that
- // the JIT bindings in the parent injector don't change while we're being built
- synchronized (shellBuilder.lock()) {
- shells = shellBuilder.build(initializer, bindingProcesor, stopwatch, errors);
- stopwatch.resetAndLog("Injector construction");
-
- initializeStatically();
- }
-
- // If we're in the tool stage, stop here. Don't eagerly inject or load anything.
- if (stage == Stage.TOOL) {
- return new ToolStageInjector(primaryInjector());
- }
-
- injectDynamically();
-
- return primaryInjector();
- }
-
- /** Initialize and validate everything. */
- private void initializeStatically() {
- bindingProcesor.initializeBindings();
- stopwatch.resetAndLog("Binding initialization");
-
- for (InjectorShell shell : shells) {
- shell.getInjector().index();
- }
- stopwatch.resetAndLog("Binding indexing");
-
- injectionRequestProcessor.process(shells);
- stopwatch.resetAndLog("Collecting injection requests");
-
- bindingProcesor.runCreationListeners();
- stopwatch.resetAndLog("Binding validation");
-
- injectionRequestProcessor.validate();
- stopwatch.resetAndLog("Static validation");
-
- initializer.validateOustandingInjections(errors);
- stopwatch.resetAndLog("Instance member validation");
-
- new LookupProcessor(errors).process(shells);
- for (InjectorShell shell : shells) {
- ((DeferredLookups) shell.getInjector().lookups).initialize(errors);
- }
- stopwatch.resetAndLog("Provider verification");
-
- for (InjectorShell shell : shells) {
- if (!shell.getElements().isEmpty()) {
- throw new AssertionError("Failed to execute " + shell.getElements());
- }
- }
-
- errors.throwCreationExceptionIfErrorsExist();
- }
-
- /**
- * Returns the injector being constructed. This is not necessarily the root injector.
- */
- private Injector primaryInjector() {
- return shells.get(0).getInjector();
- }
-
- /**
- * Inject everything that can be injected. This method is intentionally not synchronized. If we
- * locked while injecting members (ie. running user code), things would deadlock should the user
- * code build a just-in-time binding from another thread.
- */
- private void injectDynamically() {
- injectionRequestProcessor.injectMembers();
- stopwatch.resetAndLog("Static member injection");
-
- initializer.injectAll(errors);
- stopwatch.resetAndLog("Instance injection");
- errors.throwCreationExceptionIfErrorsExist();
-
- for (InjectorShell shell : shells) {
- loadEagerSingletons(shell.getInjector(), stage, errors);
- }
- stopwatch.resetAndLog("Preloading singletons");
- errors.throwCreationExceptionIfErrorsExist();
- }
-
- /**
- * Loads eager singletons, or all singletons if we're in Stage.PRODUCTION. Bindings discovered
- * while we're binding these singletons are not be eager.
- */
- void loadEagerSingletons(InjectorImpl injector, Stage stage, final Errors errors) {
- @SuppressWarnings("unchecked") // casting Collection<Binding> to Collection<BindingImpl> is safe
- Set<BindingImpl<?>> candidateBindings = ImmutableSet.copyOf(Iterables.concat(
- (Collection) injector.state.getExplicitBindingsThisLevel().values(),
- injector.jitBindings.values()));
- for (final BindingImpl<?> binding : candidateBindings) {
- if (isEagerSingleton(injector, binding, stage)) {
- try {
- injector.callInContext(new ContextualCallable<Void>() {
- Dependency<?> dependency = Dependency.get(binding.getKey());
- public Void call(InternalContext context) {
- Dependency previous = context.setDependency(dependency);
- Errors errorsForBinding = errors.withSource(dependency);
- try {
- binding.getInternalFactory().get(errorsForBinding, context, dependency);
- } catch (ErrorsException e) {
- errorsForBinding.merge(e.getErrors());
- } finally {
- context.setDependency(previous);
- }
-
- return null;
- }
- });
- } catch (ErrorsException e) {
- throw new AssertionError();
- }
- }
- }
- }
-
- private boolean isEagerSingleton(InjectorImpl injector, BindingImpl<?> binding, Stage stage) {
- if (binding.getScoping().isEagerSingleton(stage)) {
- return true;
- }
-
- // handle a corner case where a child injector links to a binding in a parent injector, and
- // that binding is singleton. We won't catch this otherwise because we only iterate the child's
- // bindings.
- if (binding instanceof LinkedBindingImpl) {
- Key<?> linkedBinding = ((LinkedBindingImpl<?>) binding).getLinkedKey();
- return isEagerSingleton(injector, injector.getBinding(linkedBinding), stage);
- }
-
- return false;
- }
-
- /** {@link Injector} exposed to users in {@link Stage#TOOL}. */
- static class ToolStageInjector implements Injector {
- private final Injector delegateInjector;
-
- ToolStageInjector(Injector delegateInjector) {
- this.delegateInjector = delegateInjector;
- }
- public void injectMembers(Object o) {
- throw new UnsupportedOperationException(
- "Injector.injectMembers(Object) is not supported in Stage.TOOL");
- }
- public Map<Key<?>, Binding<?>> getBindings() {
- return this.delegateInjector.getBindings();
- }
- public Map<Key<?>, Binding<?>> getAllBindings() {
- return this.delegateInjector.getAllBindings();
- }
- public <T> Binding<T> getBinding(Key<T> key) {
- return this.delegateInjector.getBinding(key);
- }
- public <T> Binding<T> getBinding(Class<T> type) {
- return this.delegateInjector.getBinding(type);
- }
- public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
- return this.delegateInjector.findBindingsByType(type);
- }
- public Injector getParent() {
- return delegateInjector.getParent();
- }
- public Injector createChildInjector(Iterable<? extends Module> modules) {
- return delegateInjector.createChildInjector(modules);
- }
- public Injector createChildInjector(Module... modules) {
- return delegateInjector.createChildInjector(modules);
- }
- public Map<Class<? extends Annotation>, Scope> getScopeBindings() {
- return delegateInjector.getScopeBindings();
- }
- public <T> Provider<T> getProvider(Key<T> key) {
- throw new UnsupportedOperationException(
- "Injector.getProvider(Key<T>) is not supported in Stage.TOOL");
- }
- public <T> Provider<T> getProvider(Class<T> type) {
- throw new UnsupportedOperationException(
- "Injector.getProvider(Class<T>) is not supported in Stage.TOOL");
- }
- public <T> MembersInjector<T> getMembersInjector(TypeLiteral<T> typeLiteral) {
- throw new UnsupportedOperationException(
- "Injector.getMembersInjector(TypeLiteral<T>) is not supported in Stage.TOOL");
- }
- public <T> MembersInjector<T> getMembersInjector(Class<T> type) {
- throw new UnsupportedOperationException(
- "Injector.getMembersInjector(Class<T>) is not supported in Stage.TOOL");
- }
- public <T> T getInstance(Key<T> key) {
- throw new UnsupportedOperationException(
- "Injector.getInstance(Key<T>) is not supported in Stage.TOOL");
- }
- public <T> T getInstance(Class<T> type) {
- throw new UnsupportedOperationException(
- "Injector.getInstance(Class<T>) is not supported in Stage.TOOL");
- }
- }
-}
Index: src/com/google/inject/internal/ConstructorBindingImpl.java
===================================================================
--- src/com/google/inject/internal/ConstructorBindingImpl.java (revision 1113)
+++ src/com/google/inject/internal/ConstructorBindingImpl.java (working copy)
@@ -50,7 +50,7 @@
public ConstructorBindingImpl(Key<T> key, Object source, Scoping scoping,
InjectionPoint constructorInjectionPoint, Set<InjectionPoint> injectionPoints) {
super(source, key, scoping);
- this.factory = new Factory<T>();
+ this.factory = new Factory<T>(false, key);
ConstructionProxy<T> constructionProxy
= new DefaultConstructionProxyFactory<T>(constructorInjectionPoint).create();
this.constructorInjectionPoint = constructorInjectionPoint;
@@ -60,9 +60,12 @@
/**
* @param constructorInjector the constructor to use, or {@code null} to use the default.
+ * @param failIfNotLinked true if this ConstructorBindingImpl's InternalFactory should
+ * only succeed if retrieved from a linked binding
*/
static <T> ConstructorBindingImpl<T> create(InjectorImpl injector, Key<T> key,
- InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors)
+ InjectionPoint constructorInjector, Object source, Scoping scoping, Errors errors,
+ boolean failIfNotLinked)
throws ErrorsException {
int numErrors = errors.size();
Class<? super T> rawType = key.getTypeLiteral().getRawType();
@@ -100,7 +103,7 @@
errors.throwIfNewErrors(numErrors);
- Factory<T> factoryFactory = new Factory<T>();
+ Factory<T> factoryFactory = new Factory<T>(failIfNotLinked, key);
InternalFactory<? extends T> scopedFactory
= Scoping.scope(key, injector, factoryFactory, source, scoping);
@@ -168,12 +171,23 @@
}
private static class Factory<T> implements InternalFactory<T> {
+ private final boolean failIfNotLinked;
+ private final Key<?> key;
private ConstructorInjector<T> constructorInjector;
+
+ Factory(boolean failIfNotLinked, Key<?> key) {
+ this.failIfNotLinked = failIfNotLinked;
+ this.key = key;
+ }
@SuppressWarnings("unchecked")
- public T get(Errors errors, InternalContext context, Dependency<?> dependency)
+ public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
checkState(constructorInjector != null, "Constructor not ready");
+
+ if(failIfNotLinked && !linked) {
+ throw errors.jitDisabled(key).toException();
+ }
// 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.
Index: src/com/google/inject/internal/FactoryProxy.java
===================================================================
--- src/com/google/inject/internal/FactoryProxy.java (revision 1113)
+++ src/com/google/inject/internal/FactoryProxy.java (working copy)
@@ -21,6 +21,7 @@
/**
* A placeholder which enables us to swap in the real factory once the injector is created.
+ * Used for a linked binding, so that getting the linked binding returns the link's factory.
*/
final class FactoryProxy<T> implements InternalFactory<T>, BindingProcessor.CreationListener {
@@ -40,15 +41,15 @@
public void notify(final Errors errors) {
try {
- targetFactory = injector.getInternalFactory(targetKey, errors.withSource(source));
+ targetFactory = injector.getInternalFactory(targetKey, errors.withSource(source), true);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
}
- public T get(Errors errors, InternalContext context, Dependency<?> dependency)
+ public T get(Errors errors, InternalContext context, Dependency<?> dependency, boolean linked)
throws ErrorsException {
- return targetFactory.get(errors.withSource(targetKey), context, dependency);
+ return targetFactory.get(errors.withSource(targetKey), context, dependency, true);
}
@Override public String toString() {
Index: src/com/google/inject/internal/ConstantFactory.java
===================================================================
--- src/com/google/inject/internal/ConstantFactory.java (revision 1113)
+++ src/com/google/inject/internal/ConstantFactory.java (working copy)
@@ -29,7 +29,7 @@
this.initializable = initializable;
}
- public T get(Errors errors, InternalContext context, Dependency dependency)
+ public T get(Errors errors, InternalContext context, Dependency dependency, boolean linked)
throws ErrorsException {
return initializable.get(errors);
}
Index: src/com/google/inject/internal/SingleFieldInjector.java
===================================================================
--- src/com/google/inject/internal/SingleFieldInjector.java (revision 1113)
+++ src/com/google/inject/internal/SingleFieldInjector.java (working copy)
@@ -37,7 +37,7 @@
// Ewwwww...
field.setAccessible(true);
- factory = injector.getInternalFactory(dependency.getKey(), errors);
+ factory = injector.getInternalFactory(dependency.getKey(), errors, false);
}
public InjectionPoint getInjectionPoint() {
@@ -49,7 +49,7 @@
Dependency previous = context.setDependency(dependency);
try {
- Object value = factory.get(errors, context, dependency);
+ Object value = factory.get(errors, context, dependency, false);
field.set(o, value);
} catch (ErrorsException e) {
errors.withSource(injectionPoint).merge(e.getErrors());
Index: src/com/google/inject/Guice.java
===================================================================
--- src/com/google/inject/Guice.java (revision 1113)
+++ src/com/google/inject/Guice.java (working copy)
@@ -17,8 +17,10 @@
package com.google.inject;
import java.util.Arrays;
-import com.google.inject.internal.InjectorBuilder;
+import com.google.inject.internal.InjectorBuilderImpl;
+
+
/**
* The entry point to the Guice framework. Creates {@link Injector}s from
* {@link Module}s.
@@ -90,9 +92,14 @@
*/
public static Injector createInjector(Stage stage,
Iterable<? extends Module> modules) {
- return new InjectorBuilder()
+ return new InjectorBuilderImpl()
.stage(stage)
.addModules(modules)
.build();
}
+
+ /** Creates an {@link InjectorBuilder} which can be used to create an injector. */
+ public static InjectorBuilder createInjectorBuilder() {
+ return new InjectorBuilderImpl().stage(Stage.DEVELOPMENT);
+ }
}
Index: src/com/google/inject/Injector.java
===================================================================
--- src/com/google/inject/Injector.java (revision 1113)
+++ src/com/google/inject/Injector.java (working copy)
@@ -20,6 +20,7 @@
import java.util.List;
import java.util.Map;
+
/**
* Builds the graphs of objects that make up your application. The injector tracks the dependencies
* for each type and uses bindings to inject them. This is the core of Guice, although you rarely
@@ -223,6 +224,11 @@
* @since 2.0
*/
Injector createChildInjector(Module... modules);
+
+ /**
+ * Creates an InjectorBuilder for a child injector.
+ */
+ InjectorBuilder createChildInjectorBuilder();
/**
* Returns a map containing all scopes in the injector. The maps keys are scoping annotations
Index: src/com/google/inject/Scopes.java
===================================================================
--- src/com/google/inject/Scopes.java (revision 1113)
+++ src/com/google/inject/Scopes.java (working copy)
@@ -16,12 +16,12 @@
package com.google.inject;
-import com.google.inject.internal.InjectorBuilder;
+import java.lang.annotation.Annotation;
+
+import com.google.inject.internal.InjectorBuilderImpl;
import com.google.inject.internal.LinkedBindingImpl;
import com.google.inject.spi.BindingScopingVisitor;
-import java.lang.annotation.Annotation;
-
/**
* Built-in scope implementations.
*
@@ -56,7 +56,7 @@
* Maybe one of these days we will identify independent graphs of
* objects and offer to load them in parallel.
*/
- synchronized (InjectorBuilder.class) {
+ synchronized (InjectorBuilderImpl.class) {
if (instance == null) {
T nullableInstance = creator.get();
instance = (nullableInstance != null) ? nullableInstance : NULL;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment