This CL changes how singletons are loaded. Previously we registered a callback at bind-time. Now we keep track of whether bindings were eager (using the new enum LoadStrategy) and we load 'em all afterwards.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@498 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/BindCommandProcessor.java b/src/com/google/inject/BindCommandProcessor.java
index 008f6c8..f1fab1e 100644
--- a/src/com/google/inject/BindCommandProcessor.java
+++ b/src/com/google/inject/BindCommandProcessor.java
@@ -20,7 +20,10 @@
import com.google.inject.commands.BindConstantCommand;
import com.google.inject.commands.BindScoping;
import com.google.inject.commands.BindTarget;
-import com.google.inject.internal.*;
+import com.google.inject.internal.Annotations;
+import com.google.inject.internal.ErrorMessages;
+import com.google.inject.internal.ResolveFailedException;
+import com.google.inject.internal.StackTraceElements;
import java.lang.annotation.Annotation;
import java.lang.reflect.Type;
@@ -38,8 +41,6 @@
private final Map<Class<? extends Annotation>, Scope> scopes;
private final List<CreationListener> creationListeners
= new ArrayList<CreationListener>();
- private final List<ContextualCallable<Void>> eagerSingletonCreators
- = new ArrayList<ContextualCallable<Void>>();
private final Stage stage;
private final Map<Key<?>, BindingImpl<?>> bindings;
private final Map<Object, Void> outstandingInjections;
@@ -71,8 +72,9 @@
validateKey(command.getSource(), command.getKey());
- // TODO(jessewilson): Scope annotation on type, like @Singleton
- final boolean shouldPreload = command.getScoping().isEagerSingleton();
+ final LoadStrategy loadStrategy = command.getScoping().isEagerSingleton()
+ ? LoadStrategy.EAGER
+ : LoadStrategy.LAZY;
final Scope scope = command.getScoping().acceptVisitor(new BindScoping.Visitor<Scope>() {
public Scope visitEagerSingleton() {
return Scopes.SINGLETON;
@@ -104,8 +106,8 @@
outstandingInjections.put(instance, null);
InternalFactory<? extends T> scopedFactory
= Scopes.scope(key, injector, factory, scope);
- createBinding(source, shouldPreload, new InstanceBindingImpl<T>(
- injector, key, source, scopedFactory, instance));
+ putBinding(new InstanceBindingImpl<T>(
+ injector, key, source, scopedFactory, instance));
return null;
}
@@ -115,8 +117,8 @@
outstandingInjections.put(provider, null);
InternalFactory<? extends T> scopedFactory
= Scopes.scope(key, injector, factory, scope);
- createBinding(source, shouldPreload, new ProviderInstanceBindingImpl<T>(
- injector, key, source, scopedFactory, scope, provider));
+ putBinding(new ProviderInstanceBindingImpl<T>(
+ injector, key, source, scopedFactory, scope, provider, loadStrategy));
return null;
}
@@ -126,8 +128,8 @@
creationListeners.add(boundProviderFactory);
InternalFactory<? extends T> scopedFactory = Scopes.scope(
key, injector, (InternalFactory<? extends T>) boundProviderFactory, scope);
- createBinding(source, shouldPreload, new LinkedProviderBindingImpl<T>(
- injector, key, source, scopedFactory, scope, providerKey));
+ putBinding(new LinkedProviderBindingImpl<T>(
+ injector, key, source, scopedFactory, scope, providerKey, loadStrategy));
return null;
}
@@ -140,8 +142,8 @@
creationListeners.add(factory);
InternalFactory<? extends T> scopedFactory
= Scopes.scope(key, injector, factory, scope);
- createBinding(source, shouldPreload, new LinkedBindingImpl<T>(
- injector, key, source, scopedFactory, scope, targetKey));
+ putBinding(new LinkedBindingImpl<T>(
+ injector, key, source, scopedFactory, scope, targetKey, loadStrategy));
return null;
}
@@ -154,7 +156,7 @@
// @ImplementedBy annotation or something.
if (key.hasAnnotationType() || !(type instanceof Class<?>)) {
addError(source, ErrorMessages.MISSING_IMPLEMENTATION);
- createBinding(source, shouldPreload, invalidBinding(injector, key, source));
+ putBinding(invalidBinding(injector, key, source));
return null;
}
@@ -163,11 +165,11 @@
Class<T> clazz = (Class<T>) type;
final BindingImpl<T> binding;
try {
- binding = injector.createUnitializedBinding(clazz, scope, source);
- createBinding(source, shouldPreload, binding);
+ binding = injector.createUnitializedBinding(clazz, scope, source, loadStrategy);
+ putBinding(binding);
} catch (ResolveFailedException e) {
injector.errorHandler.handle(source, e.getMessage());
- createBinding(source, shouldPreload, invalidBinding(injector, key, source));
+ putBinding(invalidBinding(injector, key, source));
return null;
}
@@ -224,31 +226,33 @@
return true;
}
- private <T> void createBinding(Object source, boolean shouldPreload,
- BindingImpl<T> binding) {
- putBinding(binding);
-
- // Register to preload if necessary.
- if (binding.getScope() == Scopes.SINGLETON) {
- if (stage == Stage.PRODUCTION || shouldPreload) {
- eagerSingletonCreators.add(new EagerSingletonCreator(binding.key, binding.internalFactory));
- }
- } else {
- if (shouldPreload) {
- addError(source, ErrorMessages.PRELOAD_NOT_ALLOWED);
- }
- }
- }
-
public void createUntargettedBindings() {
for (Runnable untargettedBinding : untargettedBindings) {
untargettedBinding.run();
}
}
- public void createEagerSingletons(InjectorImpl injector) {
- for (ContextualCallable<Void> preloader : eagerSingletonCreators) {
- injector.callInContext(preloader);
+ public void loadEagerSingletons(InjectorImpl injector) {
+ // load eager singletons, or all singletons if we're in Stage.PRODUCTION.
+ for (final BindingImpl<?> binding : bindings.values()) {
+ if (stage == Stage.PRODUCTION || binding.getLoadStrategy() == LoadStrategy.EAGER) {
+ injector.callInContext(new ContextualCallable<Void>() {
+ public Void call(InternalContext context) {
+ InjectionPoint<?> injectionPoint
+ = InjectionPoint.newInstance(binding.key, context.getInjector());
+ context.setInjectionPoint(injectionPoint);
+ try {
+ binding.internalFactory.get(context, injectionPoint);
+ return null;
+ } catch(ProvisionException provisionException) {
+ provisionException.addContext(injectionPoint);
+ throw provisionException;
+ } finally {
+ context.setInjectionPoint(null);
+ }
+ }
+ });
+ }
}
}
@@ -258,31 +262,6 @@
}
}
- private static class EagerSingletonCreator implements ContextualCallable<Void> {
- private final Key<?> key;
- private final InternalFactory<?> factory;
-
- public EagerSingletonCreator(Key<?> key, InternalFactory<?> factory) {
- this.key = key;
- this.factory = Objects.nonNull(factory, "factory");
- }
-
- public Void call(InternalContext context) {
- InjectionPoint<?> injectionPoint
- = InjectionPoint.newInstance(key, context.getInjector());
- context.setInjectionPoint(injectionPoint);
- try {
- factory.get(context, injectionPoint);
- return null;
- } catch(ProvisionException provisionException) {
- provisionException.addContext(injectionPoint);
- throw provisionException;
- } finally {
- context.setInjectionPoint(null);
- }
- }
- }
-
private void putBinding(BindingImpl<?> binding) {
Key<?> key = binding.getKey();
Binding<?> original = bindings.get(key);
diff --git a/src/com/google/inject/BindingImpl.java b/src/com/google/inject/BindingImpl.java
index 771d13d..ce6e955 100644
--- a/src/com/google/inject/BindingImpl.java
+++ b/src/com/google/inject/BindingImpl.java
@@ -30,14 +30,16 @@
final Object source;
final InternalFactory<? extends T> internalFactory;
final Scope scope;
+ final LoadStrategy loadStrategy;
BindingImpl(InjectorImpl injector, Key<T> key, Object source,
- InternalFactory<? extends T> internalFactory, Scope scope) {
+ InternalFactory<? extends T> internalFactory, Scope scope, LoadStrategy loadStrategy) {
this.injector = injector;
this.key = key;
this.source = source;
this.internalFactory = internalFactory;
this.scope = scope;
+ this.loadStrategy = loadStrategy;
}
public Key<T> getKey() {
@@ -80,6 +82,10 @@
return internalFactory instanceof ConstantFactory<?>;
}
+ LoadStrategy getLoadStrategy() {
+ return loadStrategy;
+ }
+
/**
* Perform any post-creation initialization, that could require construction
* of other bindings.
diff --git a/src/com/google/inject/ClassBindingImpl.java b/src/com/google/inject/ClassBindingImpl.java
index 36fb272..10a1d03 100644
--- a/src/com/google/inject/ClassBindingImpl.java
+++ b/src/com/google/inject/ClassBindingImpl.java
@@ -36,8 +36,9 @@
ClassBindingImpl(InjectorImpl injector, Key<T> key, Object source,
InternalFactory<? extends T> internalFactory, Scope scope,
- InjectorImpl.LateBoundConstructor<T> lateBoundConstructor) {
- super(injector, key, source, internalFactory, scope);
+ InjectorImpl.LateBoundConstructor<T> lateBoundConstructor,
+ LoadStrategy loadStrategy) {
+ super(injector, key, source, internalFactory, scope, loadStrategy);
this.lateBoundConstructor = lateBoundConstructor;
}
diff --git a/src/com/google/inject/ConstantBindingImpl.java b/src/com/google/inject/ConstantBindingImpl.java
index 7ff5aa9..b6d9c4f 100644
--- a/src/com/google/inject/ConstantBindingImpl.java
+++ b/src/com/google/inject/ConstantBindingImpl.java
@@ -33,13 +33,12 @@
ConstantBindingImpl(InjectorImpl injector, Key<T> key, Object source,
InternalFactory<T> internalFactory, T value) {
- super(injector, key, source, internalFactory, Scopes.NO_SCOPE);
+ super(injector, key, source, internalFactory, Scopes.NO_SCOPE, LoadStrategy.LAZY);
this.value = value;
this.provider = Providers.of(value);
}
- @Override
- public Provider<T> getProvider() {
+ @Override public Provider<T> getProvider() {
return this.provider;
}
@@ -51,8 +50,7 @@
return this.value;
}
- @Override
- public String toString() {
+ @Override public String toString() {
return new ToStringBuilder(ConstantBinding.class)
.add("key", key)
.add("value", value)
diff --git a/src/com/google/inject/InjectorBuilder.java b/src/com/google/inject/InjectorBuilder.java
index 6bd907a..39e23a1 100644
--- a/src/com/google/inject/InjectorBuilder.java
+++ b/src/com/google/inject/InjectorBuilder.java
@@ -187,7 +187,7 @@
injector.fulfillOutstandingInjections();
stopwatch.resetAndLog("Instance injection");
- bindCommandProcesor.createEagerSingletons(injector);
+ bindCommandProcesor.loadEagerSingletons(injector);
stopwatch.resetAndLog("Preloading");
}
@@ -241,7 +241,7 @@
injector.explicitBindings.put(key,
new ProviderInstanceBindingImpl<Logger>(injector, key,
SourceProviders.UNKNOWN_SOURCE, loggerFactory, Scopes.NO_SCOPE,
- loggerFactory));
+ loggerFactory, LoadStrategy.LAZY));
}
static class LoggerFactory implements InternalFactory<Logger>, Provider<Logger> {
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index 2731361..82e3cc4 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -190,7 +190,8 @@
new InternalFactoryToProviderAdapter(binding.getProvider(),
binding.getSource()),
Scopes.NO_SCOPE,
- binding.getProvider());
+ binding.getProvider(),
+ LoadStrategy.LAZY);
} else {
bindingImpl = null;
}
@@ -250,7 +251,7 @@
* from Binding<T>.
*/
private <T> BindingImpl<Provider<T>> createProviderBinding(
- Key<Provider<T>> key) throws ResolveFailedException {
+ Key<Provider<T>> key, LoadStrategy loadStrategy) throws ResolveFailedException {
Type providerType = key.getTypeLiteral().getType();
// If the Provider has no type parameter (raw Provider)...
@@ -265,7 +266,7 @@
@SuppressWarnings("unchecked")
Key<T> providedKey = (Key<T>) key.ofType(entryType);
- return new ProviderBindingImpl<T>(this, key, getBindingOrThrow(providedKey));
+ return new ProviderBindingImpl<T>(this, key, getBindingOrThrow(providedKey), loadStrategy);
}
void handleMissingBinding(Object source, Key<?> key) {
@@ -290,9 +291,9 @@
final Binding<T> providedBinding;
ProviderBindingImpl(InjectorImpl injector, Key<Provider<T>> key,
- Binding<T> providedBinding) {
+ Binding<T> providedBinding, LoadStrategy loadStrategy) {
super(injector, key, SourceProviders.UNKNOWN_SOURCE,
- createInternalFactory(providedBinding), Scopes.NO_SCOPE);
+ createInternalFactory(providedBinding), Scopes.NO_SCOPE, loadStrategy);
this.providedBinding = providedBinding;
}
@@ -403,7 +404,7 @@
ConvertedConstantBindingImpl(InjectorImpl injector, Key<T> key, T value,
Binding<String> originalBinding) {
super(injector, key, SourceProviders.UNKNOWN_SOURCE,
- new ConstantFactory<T>(value), Scopes.NO_SCOPE);
+ new ConstantFactory<T>(value), Scopes.NO_SCOPE, LoadStrategy.LAZY);
this.value = value;
this.provider = Providers.of(value);
this.originalBinding = originalBinding;
@@ -436,14 +437,10 @@
}
}
- <T> BindingImpl<T> createBindingFromType(Class<T> type)
+ <T> BindingImpl<T> createBindingFromType(Class<T> type, LoadStrategy loadStrategy)
throws ResolveFailedException {
- return createBindingFromType(type, null, SourceProviders.defaultSource());
- }
-
- <T> BindingImpl<T> createBindingFromType(Class<T> type, Scope scope,
- Object source) throws ResolveFailedException {
- BindingImpl<T> binding = createUnitializedBinding(type, scope, source);
+ BindingImpl<T> binding = createUnitializedBinding(
+ type, null, SourceProviders.defaultSource(), loadStrategy);
initializeBinding(binding);
return binding;
}
@@ -474,7 +471,7 @@
* a scope on the type if none is specified.
*/
<T> BindingImpl<T> createUnitializedBinding(Class<T> type,
- Scope scope, Object source) throws ResolveFailedException {
+ Scope scope, Object source, LoadStrategy loadStrategy) throws ResolveFailedException {
// Don't try to inject arrays, or enums.
if (type.isArray() || type.isEnum()) {
throw new ResolveFailedException(ErrorMessages.MISSING_BINDING, type);
@@ -484,14 +481,14 @@
ImplementedBy implementedBy = type.getAnnotation(ImplementedBy.class);
if (implementedBy != null) {
// TODO: Scope internal factory.
- return createImplementedByBinding(type, implementedBy);
+ return createImplementedByBinding(type, implementedBy, loadStrategy);
}
// Handle @ProvidedBy.
ProvidedBy providedBy = type.getAnnotation(ProvidedBy.class);
if (providedBy != null) {
// TODO: Scope internal factory.
- return createProvidedByBinding(type, providedBy);
+ return createProvidedByBinding(type, providedBy, loadStrategy);
}
// We can't inject abstract classes.
@@ -512,12 +509,12 @@
Key<T> key = Key.get(type);
- LateBoundConstructor<T> lateBoundConstructor
- = new LateBoundConstructor<T>();
+ LateBoundConstructor<T> lateBoundConstructor = new LateBoundConstructor<T>();
InternalFactory<? extends T> scopedFactory
= Scopes.scope(key, this, lateBoundConstructor, scope);
- return new ClassBindingImpl<T>(this, key, source, scopedFactory, scope, lateBoundConstructor);
+ return new ClassBindingImpl<T>(this, key, source, scopedFactory, scope, lateBoundConstructor,
+ loadStrategy);
}
static class LateBoundConstructor<T> implements InternalFactory<T> {
@@ -547,7 +544,7 @@
* Creates a binding for a type annotated with @ProvidedBy.
*/
<T> BindingImpl<T> createProvidedByBinding(final Class<T> type,
- ProvidedBy providedBy) throws ResolveFailedException {
+ ProvidedBy providedBy, LoadStrategy loadStrategy) throws ResolveFailedException {
final Class<? extends Provider<?>> providerType = providedBy.value();
// Make sure it's not the same type. TODO: Can we check for deeper loops?
@@ -583,14 +580,14 @@
return new LinkedProviderBindingImpl<T>(this, Key.get(type),
StackTraceElements.forType(type), internalFactory, Scopes.NO_SCOPE,
- providerKey);
+ providerKey, loadStrategy);
}
/**
* Creates a binding for a type annotated with @ImplementedBy.
*/
<T> BindingImpl<T> createImplementedByBinding(Class<T> type,
- ImplementedBy implementedBy) throws ResolveFailedException {
+ ImplementedBy implementedBy, LoadStrategy loadStrategy) throws ResolveFailedException {
// TODO: Use scope annotation on type if present. Right now, we always
// use NO_SCOPE.
@@ -622,7 +619,7 @@
return new LinkedBindingImpl<T>(this, Key.get(type),
StackTraceElements.forType(type), internalFactory, Scopes.NO_SCOPE,
- Key.get(subclass));
+ Key.get(subclass), loadStrategy);
}
<T> BindingImpl<T> createBindingJustInTime(Key<T> key) throws ResolveFailedException {
@@ -633,7 +630,7 @@
// BindingImpl<Provider<X>>.
@SuppressWarnings({ "UnnecessaryLocalVariable", "unchecked" })
BindingImpl<T> binding
- = (BindingImpl<T>) createProviderBinding((Key) key);
+ = (BindingImpl<T>) createProviderBinding((Key) key, LoadStrategy.LAZY);
return binding;
}
@@ -659,7 +656,7 @@
// Create a binding based on the raw type.
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) key.getTypeLiteral().getRawType();
- return createBindingFromType(clazz);
+ return createBindingFromType(clazz, LoadStrategy.LAZY);
}
<T> InternalFactory<? extends T> getInternalFactory(Key<T> key)
@@ -917,9 +914,6 @@
catch (IllegalAccessException e) {
throw new AssertionError(e);
}
- catch (ProvisionException e) {
- throw e;
- }
catch (InvocationTargetException e) {
Throwable cause = e.getCause() != null ? e.getCause() : e;
throw new ProvisionException(cause,
diff --git a/src/com/google/inject/InstanceBindingImpl.java b/src/com/google/inject/InstanceBindingImpl.java
index f6ab05f..c4ac1a0 100644
--- a/src/com/google/inject/InstanceBindingImpl.java
+++ b/src/com/google/inject/InstanceBindingImpl.java
@@ -33,13 +33,12 @@
InstanceBindingImpl(InjectorImpl injector, Key<T> key, Object source,
InternalFactory<? extends T> internalFactory, T instance) {
- super(injector, key, source, internalFactory, Scopes.NO_SCOPE);
+ super(injector, key, source, internalFactory, Scopes.NO_SCOPE, LoadStrategy.EAGER);
this.instance = instance;
this.provider = Providers.of(instance);
}
- @Override
- public Provider<T> getProvider() {
+ @Override public Provider<T> getProvider() {
return this.provider;
}
@@ -55,8 +54,7 @@
return injector.getFieldAndMethodDependenciesFor(instance.getClass());
}
- @Override
- public String toString() {
+ @Override public String toString() {
return new ToStringBuilder(InstanceBinding.class)
.add("key", key)
.add("instance", instance)
diff --git a/src/com/google/inject/InvalidBindingImpl.java b/src/com/google/inject/InvalidBindingImpl.java
index f2213b1..4a2705f 100644
--- a/src/com/google/inject/InvalidBindingImpl.java
+++ b/src/com/google/inject/InvalidBindingImpl.java
@@ -25,7 +25,7 @@
public T get(InternalContext context, InjectionPoint<?> injectionPoint) {
throw new AssertionError();
}
- }, Scopes.NO_SCOPE);
+ }, Scopes.NO_SCOPE, LoadStrategy.LAZY);
}
public void accept(BindingVisitor<? super T> bindingVisitor) {
diff --git a/src/com/google/inject/LinkedBindingImpl.java b/src/com/google/inject/LinkedBindingImpl.java
index 3693fd9..5b920f0 100644
--- a/src/com/google/inject/LinkedBindingImpl.java
+++ b/src/com/google/inject/LinkedBindingImpl.java
@@ -31,8 +31,9 @@
LinkedBindingImpl(InjectorImpl injector, Key<T> key, Object source,
InternalFactory<? extends T> internalFactory, Scope scope,
- Key<? extends T> targetKey) {
- super(injector, key, source, internalFactory, scope);
+ Key<? extends T> targetKey,
+ LoadStrategy loadStrategy) {
+ super(injector, key, source, internalFactory, scope, loadStrategy);
this.targetKey = targetKey;
}
diff --git a/src/com/google/inject/LinkedProviderBindingImpl.java b/src/com/google/inject/LinkedProviderBindingImpl.java
index 15f6a71..35263cc 100644
--- a/src/com/google/inject/LinkedProviderBindingImpl.java
+++ b/src/com/google/inject/LinkedProviderBindingImpl.java
@@ -31,8 +31,9 @@
LinkedProviderBindingImpl(InjectorImpl injector, Key<T> key, Object source,
InternalFactory<? extends T> internalFactory, Scope scope,
- Key<? extends Provider<? extends T>> providerKey) {
- super(injector, key, source, internalFactory, scope);
+ Key<? extends Provider<? extends T>> providerKey,
+ LoadStrategy loadStrategy) {
+ super(injector, key, source, internalFactory, scope, loadStrategy);
this.providerKey = providerKey;
}
diff --git a/src/com/google/inject/LoadStrategy.java b/src/com/google/inject/LoadStrategy.java
new file mode 100644
index 0000000..326e69c
--- /dev/null
+++ b/src/com/google/inject/LoadStrategy.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (C) 2008 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;
+
+/**
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+enum LoadStrategy {
+ EAGER, LAZY
+}
diff --git a/src/com/google/inject/ProviderInstanceBindingImpl.java b/src/com/google/inject/ProviderInstanceBindingImpl.java
index 81263b1..d65253d 100644
--- a/src/com/google/inject/ProviderInstanceBindingImpl.java
+++ b/src/com/google/inject/ProviderInstanceBindingImpl.java
@@ -34,8 +34,9 @@
ProviderInstanceBindingImpl(InjectorImpl injector, Key<T> key,
Object source,
InternalFactory<? extends T> internalFactory, Scope scope,
- Provider<? extends T> providerInstance) {
- super(injector, key, source, internalFactory, scope);
+ Provider<? extends T> providerInstance,
+ LoadStrategy loadStrategy) {
+ super(injector, key, source, internalFactory, scope, loadStrategy);
this.providerInstance = providerInstance;
}
diff --git a/src/com/google/inject/internal/ErrorMessages.java b/src/com/google/inject/internal/ErrorMessages.java
index 213f341..3bb807e 100644
--- a/src/com/google/inject/internal/ErrorMessages.java
+++ b/src/com/google/inject/internal/ErrorMessages.java
@@ -223,9 +223,6 @@
public static final String BINDING_ALREADY_SET = "A binding to %s was already"
+ " configured at %s.";
- public static final String PRELOAD_NOT_ALLOWED = "Preloading is only supported for"
- + " singleton-scoped bindings.";
-
public static final String ERROR_INJECTING_FIELD = "Error injecting field";
public static final String ERROR_INJECTING_METHOD = "Error injecting method";