Rewrite of parent injectors.
This implementation works okay, but there's still a lot of work to be done:
- cross-injector concurrency is still weak. I've left TODOs in a few places where this needs to be addressed.
- Initializer can be simplified further
- we might be able to axe State/InheritingState. These were mostly for my sanity to constrain where InjectorImpl gets its data
- we can always have more test cases
git-svn-id: https://google-guice.googlecode.com/svn/trunk@629 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/BindingProcessor.java b/src/com/google/inject/BindingProcessor.java
index 430dd6b..a9f0386 100644
--- a/src/com/google/inject/BindingProcessor.java
+++ b/src/com/google/inject/BindingProcessor.java
@@ -28,7 +28,6 @@
import java.lang.reflect.Constructor;
import java.lang.reflect.Type;
import java.util.List;
-import java.util.Map;
import java.util.Set;
/**
@@ -59,22 +58,17 @@
};
private final InjectorImpl injector;
- private final Map<Class<? extends Annotation>, Scope> scopes;
+ private final State state;
private final List<CreationListener> creationListeners = Lists.newArrayList();
- private final Map<Key<?>, BindingImpl<?>> bindings;
- private final CreationTimeMemberInjector memberInjector;
+ private final Initializer initializer;
private final List<Runnable> untargettedBindings = Lists.newArrayList();
- BindingProcessor(Errors errors,
- InjectorImpl injector,
- Map<Class<? extends Annotation>, Scope> scopes,
- Map<Key<?>, BindingImpl<?>> bindings,
- CreationTimeMemberInjector memberInjector) {
+ BindingProcessor(Errors errors, InjectorImpl injector, State state,
+ Initializer initializer) {
super(errors);
this.injector = injector;
- this.scopes = scopes;
- this.bindings = bindings;
- this.memberInjector = memberInjector;
+ this.state = state;
+ this.initializer = initializer;
}
@Override public <T> Boolean visitBinding(Binding<T> command) {
@@ -106,7 +100,7 @@
}
public Scope visitScopeAnnotation(Class<? extends Annotation> scopeAnnotation) {
- Scope scope = scopes.get(scopeAnnotation);
+ Scope scope = state.getScope(scopeAnnotation);
if (scope != null) {
return scope;
} else {
@@ -122,8 +116,8 @@
command.acceptTargetVisitor(new BindingTargetVisitor<T, Void>() {
public Void visitInstance(T instance, Set<InjectionPoint> injectionPoints) {
- ConstantFactory<? extends T> factory = new ConstantFactory<T>(instance);
- memberInjector.requestInjection(instance, source, injectionPoints);
+ Initializable<T> ref = initializer.requestInjection(instance, source, injectionPoints);
+ ConstantFactory<? extends T> factory = new ConstantFactory<T>(ref);
InternalFactory<? extends T> scopedFactory = Scopes.scope(key, injector, factory, scope);
putBinding(new InstanceBindingImpl<T>(injector, key, source, scopedFactory, injectionPoints,
instance));
@@ -132,11 +126,10 @@
public Void visitProvider(Provider<? extends T> provider,
Set<InjectionPoint> injectionPoints) {
- InternalFactoryToProviderAdapter<? extends T> factory
- = new InternalFactoryToProviderAdapter<T>(provider, source);
- memberInjector.requestInjection(provider, source, injectionPoints);
- InternalFactory<? extends T> scopedFactory
- = Scopes.scope(key, injector, factory, scope);
+ Initializable<Provider<? extends T>> initializable = initializer
+ .<Provider<? extends T>>requestInjection(provider, source, injectionPoints);
+ InternalFactory<T> factory = new InternalFactoryToProviderAdapter<T>(initializable, source);
+ InternalFactory<? extends T> scopedFactory = Scopes.scope(key, injector, factory, scope);
putBinding(new ProviderInstanceBindingImpl<T>(injector, key, source, scopedFactory, scope,
provider, loadStrategy, injectionPoints));
return null;
@@ -246,7 +239,6 @@
private void putBinding(BindingImpl<?> binding) {
Key<?> key = binding.getKey();
- Binding<?> original = bindings.get(key);
Class<?> rawType = key.getRawType();
if (FORBIDDEN_TYPES.contains(rawType)) {
@@ -254,11 +246,17 @@
return;
}
- if (bindings.containsKey(key)) {
+ Binding<?> original = state.getExplicitBinding(key);
+ if (original != null) {
errors.bindingAlreadySet(key, original.getSource());
- } else {
- bindings.put(key, binding);
+ return;
}
+
+ // TODO: make getExplicitBinding() and blacklist() atomic
+
+ // prevent the parent from creating a JIT binding for this key
+ state.parent().blacklist(key);
+ state.putBinding(key, binding);
}
// It's unfortunate that we have to maintain a blacklist of specific
@@ -268,7 +266,6 @@
AbstractModule.class,
Binder.class,
Binding.class,
- // Injector.class,
Key.class,
Module.class,
Provider.class,
diff --git a/src/com/google/inject/ConstantFactory.java b/src/com/google/inject/ConstantFactory.java
index 32e6801..50b67f2 100644
--- a/src/com/google/inject/ConstantFactory.java
+++ b/src/com/google/inject/ConstantFactory.java
@@ -26,21 +26,20 @@
*/
class ConstantFactory<T> implements InternalFactory<T> {
- private final T value;
+ private final Initializable<T> initializable;
- public ConstantFactory(T value) {
- this.value = value;
+ public ConstantFactory(Initializable<T> initializable) {
+ this.initializable = initializable;
}
public T get(Errors errors, InternalContext context, Dependency dependency)
throws ErrorsException {
- context.ensureMemberInjected(errors, value);
- return value;
+ return initializable.get(errors);
}
public String toString() {
return new ToStringBuilder(ConstantFactory.class)
- .add("value", value)
+ .add("value", initializable)
.toString();
}
}
diff --git a/src/com/google/inject/Guice.java b/src/com/google/inject/Guice.java
index fa3b282..5a776c2 100644
--- a/src/com/google/inject/Guice.java
+++ b/src/com/google/inject/Guice.java
@@ -89,48 +89,8 @@
*/
public static Injector createInjector(Stage stage,
Iterable<? extends Module> modules) {
- return createInjector(null, stage, modules);
- }
-
-
- /**
- * Creates an injector for the given set of modules, with the given parent
- * injector.
- *
- * @throws CreationException if one or more errors occur during Injector
- * construction
- */
- public static Injector createInjector(Injector parent,
- Iterable<? extends Module> modules) {
- return createInjector(parent, Stage.DEVELOPMENT, modules);
- }
-
-
- /**
- * Creates an injector for the given set of modules, with the given parent
- * injector.
- *
- * @throws CreationException if one or more errors occur during Injector
- * construction
- */
- public static Injector createInjector(Injector parent,
- Module... modules) {
- return createInjector(parent, Stage.DEVELOPMENT, Arrays.asList(modules));
- }
-
- /**
- * Creates an injector for the given set of modules, in a given development
- * stage, with the given parent injector.
- *
- * @throws CreationException if one or more errors occur during Injector
- * construction
- */
- public static Injector createInjector(
- Injector parent, Stage stage,
- Iterable<? extends Module> modules) {
return new InjectorBuilder()
.stage(stage)
- .parentInjector(parent)
.addModules(modules)
.build();
}
diff --git a/src/com/google/inject/InheritingState.java b/src/com/google/inject/InheritingState.java
new file mode 100644
index 0000000..0d08429
--- /dev/null
+++ b/src/com/google/inject/InheritingState.java
@@ -0,0 +1,119 @@
+/**
+ * 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;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.inject.internal.Errors;
+import com.google.inject.internal.MatcherAndConverter;
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Map;
+import java.util.Collections;
+
+/**
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class InheritingState implements State {
+
+ // TODO(jessewilson): think about what we need to do w.r.t. concurrency
+
+ private final State parent;
+ private final Map<Key<?>, Binding<?>> explicitBindingsMutable = Maps.newHashMap();
+ private final Map<Key<?>, Binding<?>> explicitBindings
+ = Collections.unmodifiableMap(explicitBindingsMutable);
+ private final Map<Class<? extends Annotation>, Scope> scopes = Maps.newHashMap();
+ private final List<MatcherAndConverter> converters = Lists.newArrayList();
+ private final List<MethodAspect> methodAspects = Lists.newArrayList();
+ private final WeakKeySet blacklistedKeys = new WeakKeySet();
+
+ InheritingState(State parent) {
+ this.parent = checkNotNull(parent, "parent");
+ }
+
+ public State parent() {
+ return parent;
+ }
+
+ @SuppressWarnings("unchecked") // we only put in BindingImpls that match their key types
+ public <T> BindingImpl<T> getExplicitBinding(Key<T> key) {
+ Binding<?> binding = explicitBindings.get(key);
+ return binding != null ? (BindingImpl<T>) binding : parent.getExplicitBinding(key);
+ }
+
+ public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() {
+ return explicitBindings;
+ }
+
+ public void putBinding(Key<?> key, BindingImpl<?> binding) {
+ explicitBindingsMutable.put(key, binding);
+ }
+
+ public Scope getScope(Class<? extends Annotation> annotationType) {
+ Scope scope = scopes.get(annotationType);
+ return scope != null ? scope : parent.getScope(annotationType);
+ }
+
+ public void putAnnotation(Class<? extends Annotation> annotationType, Scope scope) {
+ scopes.put(annotationType, scope);
+ }
+
+ public Iterable<MatcherAndConverter> getConvertersThisLevel() {
+ return converters;
+ }
+
+ public void addConverter(MatcherAndConverter matcherAndConverter) {
+ converters.add(matcherAndConverter);
+ }
+
+ public MatcherAndConverter getConverter(
+ String stringValue, TypeLiteral<?> type, Errors errors, Object source) {
+ MatcherAndConverter matchingConverter = null;
+ for (State s = this; s != State.NONE; s = s.parent()) {
+ for (MatcherAndConverter converter : s.getConvertersThisLevel()) {
+ if (converter.getTypeMatcher().matches(type)) {
+ if (matchingConverter != null) {
+ errors.ambiguousTypeConversion(stringValue, source, type, matchingConverter, converter);
+ }
+ matchingConverter = converter;
+ }
+ }
+ }
+ return matchingConverter;
+ }
+
+ public void addMethodAspect(MethodAspect methodAspect) {
+ methodAspects.add(methodAspect);
+ }
+
+ public List<MethodAspect> getMethodAspects() {
+ List<MethodAspect> result = Lists.newArrayList();
+ result.addAll(parent.getMethodAspects());
+ result.addAll(methodAspects);
+ return result;
+ }
+
+ public void blacklist(Key<?> key) {
+ parent.blacklist(key);
+ blacklistedKeys.add(key);
+ }
+
+ public boolean isBlacklisted(Key<?> key) {
+ return blacklistedKeys.contains(key);
+ }
+}
diff --git a/src/com/google/inject/Initializable.java b/src/com/google/inject/Initializable.java
new file mode 100644
index 0000000..a418722
--- /dev/null
+++ b/src/com/google/inject/Initializable.java
@@ -0,0 +1,33 @@
+/**
+ * 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;
+
+import com.google.inject.internal.Errors;
+import com.google.inject.internal.ErrorsException;
+
+/**
+ * Holds a reference that requires initialization to be performed before it can be used.
+ *
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+interface Initializable<T> {
+
+ /**
+ * Ensures the reference is initialized, then returns it.
+ */
+ T get(Errors errors) throws ErrorsException;
+}
diff --git a/src/com/google/inject/Initializables.java b/src/com/google/inject/Initializables.java
new file mode 100644
index 0000000..a041c74
--- /dev/null
+++ b/src/com/google/inject/Initializables.java
@@ -0,0 +1,41 @@
+/**
+ * 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;
+
+import com.google.inject.internal.Errors;
+import com.google.inject.internal.ErrorsException;
+
+/**
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class Initializables {
+
+ /**
+ * Returns an initializable for an instance that requires no initialization.
+ */
+ static <T> Initializable<T> of(final T instance) {
+ return new Initializable<T>() {
+ public T get(Errors errors) throws ErrorsException {
+ return instance;
+ }
+
+ @Override public String toString() {
+ return String.valueOf(instance);
+ }
+ };
+ }
+}
diff --git a/src/com/google/inject/Initializer.java b/src/com/google/inject/Initializer.java
new file mode 100644
index 0000000..643492f
--- /dev/null
+++ b/src/com/google/inject/Initializer.java
@@ -0,0 +1,159 @@
+/**
+ * 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;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.collect.Maps;
+import com.google.inject.internal.Errors;
+import com.google.inject.internal.ErrorsException;
+import com.google.inject.spi.InjectionPoint;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * Manages and injects instances at injector-creation time.
+ *
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+class Initializer {
+ /** the only thread that we'll use to inject members. */
+ private final Thread creatingThread = Thread.currentThread();
+
+ /** zero means everything is injected. */
+ private final CountDownLatch ready = new CountDownLatch(1);
+
+ /** Maps instances that need injection to a source that registered them */
+ private final Map<Object, Object> outstandingInjections = Maps.newIdentityHashMap();
+ private final InjectorImpl injector;
+
+ Initializer(InjectorImpl injector) {
+ this.injector = injector;
+ }
+
+ /**
+ * Registers an instance for member injection when that step is performed.
+ *
+ * @param instance an instance that optionally has members to be injected (each annotated with
+ * @Inject).
+ * @param source the source location that this injection was requested
+ */
+ public <T> Initializable<T> requestInjection(T instance, Object source,
+ Set<InjectionPoint> injectionPoints) {
+ checkNotNull(source);
+ outstandingInjections.put(instance, source);
+ return new InjectableReference<T>(this, instance);
+ }
+
+ /**
+ * Prepares member injectors for all injected instances. This prompts Guice to do static analysis
+ * on the injected instances.
+ */
+ void validateOustandingInjections(Errors errors) {
+ for (Map.Entry<Object, Object> entry : outstandingInjections.entrySet()) {
+ try {
+ Object toInject = entry.getKey();
+ Object source = entry.getValue();
+ injector.injectors.get(toInject.getClass(), errors.withSource(source));
+ } catch (ErrorsException e) {
+ errors.merge(e.getErrors());
+ }
+ }
+ }
+
+ /**
+ * Performs creation-time injections on all objects that require it. Whenever fulfilling an
+ * injection depends on another object that requires injection, we use {@link
+ * #ensureInjected(Object,com.google.inject.internal.Errors)} to inject that member first.
+ *
+ * <p>If the two objects are codependent (directly or transitively), ordering of injection is
+ * arbitrary.
+ */
+ void injectAll(final Errors errors) {
+ // loop over a defensive copy since ensureInjected() mutates the set. Unfortunately, that copy
+ // is made complicated by a bug in IBM's JDK, wherein entrySet().toArray(Object[]) doesn't work
+ for (Object entryObject : outstandingInjections.entrySet().toArray()) {
+ @SuppressWarnings("unchecked")
+ Entry<Object, Object> entry = (Entry<Object, Object>) entryObject;
+ try {
+ Object toInject = entry.getKey();
+ Object source = entry.getValue();
+ ensureInjected(toInject, errors.withSource(source));
+ } catch (ErrorsException e) {
+ errors.merge(e.getErrors());
+ }
+ }
+
+ if (!outstandingInjections.isEmpty()) {
+ throw new AssertionError("Failed to satisfy " + outstandingInjections);
+ }
+
+ ready.countDown();
+ }
+
+ private class InjectableReference<T> implements Initializable<T> {
+ private final Initializer initializer;
+ private final T instance;
+
+ public InjectableReference(Initializer initializer, T instance) {
+ this.initializer = checkNotNull(initializer, "initializer");
+ this.instance = checkNotNull(instance, "instance");
+ }
+
+ /**
+ * Ensures that the instance has been injected, and returns it.
+ */
+ public T get(Errors errors) throws ErrorsException {
+ initializer.ensureInjected(instance, errors);
+ return instance;
+ }
+
+ @Override public String toString() {
+ return instance.toString();
+ }
+ }
+
+
+ /**
+ * Reentrant. If {@code toInject} was registered for injection at injector-creation time, this
+ * method will ensure that all its members have been injected before returning. This method is
+ * used both internally, and by {@code InternalContext} to satisfy injections while satisfying
+ * other injections.
+ */
+ void ensureInjected(Object toInject, Errors errors) throws ErrorsException {
+ if (ready.getCount() == 0) {
+ return;
+ }
+
+ // just wait for everything to be injected by another thread
+ if (Thread.currentThread() != creatingThread) {
+ try {
+ ready.await();
+ return;
+ } catch (InterruptedException e) {
+ // Give up, since we don't know if our injection is ready
+ throw new RuntimeException(e);
+ }
+ }
+
+ // toInject needs injection, do it right away
+ if (outstandingInjections.remove(toInject) != null) {
+ injector.injectMembersOrThrow(errors, toInject);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/com/google/inject/InjectionRequestProcessor.java b/src/com/google/inject/InjectionRequestProcessor.java
index f7c8042..933defa 100644
--- a/src/com/google/inject/InjectionRequestProcessor.java
+++ b/src/com/google/inject/InjectionRequestProcessor.java
@@ -38,10 +38,10 @@
class InjectionRequestProcessor extends AbstractProcessor {
private final List<StaticInjection> staticInjections = Lists.newArrayList();
- private final CreationTimeMemberInjector memberInjector;
+ private final Initializer memberInjector;
InjectionRequestProcessor(Errors errors,
- CreationTimeMemberInjector memberInjector) {
+ Initializer memberInjector) {
super(errors);
this.memberInjector = memberInjector;
}
diff --git a/src/com/google/inject/Injector.java b/src/com/google/inject/Injector.java
index ede77eb..a2db575 100644
--- a/src/com/google/inject/Injector.java
+++ b/src/com/google/inject/Injector.java
@@ -86,7 +86,7 @@
<T> Binding<T> getBinding(Class<T> type);
/**
- * Finds all bindings to the given type. This method is part of the Injector
+ * Returns all explicit bindings for the given type. This method is part of the Injector
* Introspection API and is primarily intended for use by tools.
*/
<T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type);
@@ -120,4 +120,38 @@
* dependencies ahead of time.
*/
<T> T getInstance(Class<T> type);
+
+ /**
+ * Returns a new injector that inherits all state from this injector. All
+ * bindings, scopes, interceptors and type converters are inherited -- they
+ * are visible to the child injector. Elements of the child injector are not
+ * visible to its parent.
+ *
+ * <p>Just-in-time bindings created for child injectors will be created in an
+ * ancestor injector whenever possible. This allows for scoped instances to be
+ * shared between injectors. Use explicit bindings to prevent bindings from
+ * being shared with the parent injector.
+ *
+ * <p>No key may be bound by both an injector and one of its ancestors. This
+ * includes just-in-time bindings. The lone exception is the key for {@code
+ * Injector.class}, which is bound by each injector to itself.
+ */
+ Injector createChildInjector(Iterable<? extends Module> modules);
+
+ /**
+ * Returns a new injector that inherits all state from this injector. All
+ * bindings, scopes, interceptors and type converters are inherited -- they
+ * are visible to the child injector. Elements of the child injector are not
+ * visible to its parent.
+ *
+ * <p>Just-in-time bindings created for child injectors will be created in an
+ * ancestor injector whenever possible. This allows for scoped instances to be
+ * shared between injectors. Use explicit bindings to prevent bindings from
+ * being shared with the parent injector.
+ *
+ * <p>No key may be bound by both an injector and one of its ancestors. This
+ * includes just-in-time bindings. The lone exception is the key for {@code
+ * Injector.class}, which is bound by each injector to itself.
+ */
+ Injector createChildInjector(Module... modules);
}
diff --git a/src/com/google/inject/InjectorBuilder.java b/src/com/google/inject/InjectorBuilder.java
index ea9dffa..4adfa1f 100644
--- a/src/com/google/inject/InjectorBuilder.java
+++ b/src/com/google/inject/InjectorBuilder.java
@@ -30,6 +30,7 @@
import com.google.inject.spi.Element;
import com.google.inject.spi.Elements;
import com.google.inject.spi.InjectionPoint;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -45,7 +46,7 @@
private final Stopwatch stopwatch = new Stopwatch();
- private Injector parent;
+ private InjectorImpl parent = null;
private Stage stage;
private Factory reflectionFactory = new RuntimeReflectionFactory();
private final List<Module> modules = Lists.newLinkedList();
@@ -72,7 +73,7 @@
return this;
}
- InjectorBuilder parentInjector(Injector parent) {
+ InjectorBuilder parentInjector(InjectorImpl parent) {
this.parent = parent;
return this;
}
@@ -91,7 +92,10 @@
injector = new InjectorImpl(parent);
- modules.add(0, new BuiltInModule(injector, stage));
+ // bind Stage and Singleton if this is a top-level injector
+ if (parent == null) {
+ modules.add(0, new RootModule(stage));
+ }
elements.addAll(Elements.getElements(stage, modules));
@@ -122,22 +126,22 @@
.processCommands(elements);
InterceptorBindingProcessor interceptorCommandProcessor
- = new InterceptorBindingProcessor(errors);
+ = new InterceptorBindingProcessor(errors, injector.state);
interceptorCommandProcessor.processCommands(elements);
ConstructionProxyFactory proxyFactory = interceptorCommandProcessor.createProxyFactory();
injector.reflection = reflectionFactory.create(proxyFactory);
stopwatch.resetAndLog("Interceptors creation");
- new ScopeBindingProcessor(errors, injector.scopes).processCommands(elements);
+ new ScopeBindingProcessor(errors, injector.state).processCommands(elements);
stopwatch.resetAndLog("Scopes creation");
- new TypeConverterBindingProcessor(errors, injector.converters).processCommands(elements);
+ new TypeConverterBindingProcessor(errors, injector.state).processCommands(elements);
stopwatch.resetAndLog("Converters creation");
+ bindInjector();
bindLogger();
bindCommandProcesor = new BindingProcessor(errors,
- injector, injector.scopes, injector.explicitBindings,
- injector.memberInjector);
+ injector, injector.state, injector.initializer);
bindCommandProcesor.processCommands(elements);
bindCommandProcesor.createUntargettedBindings();
stopwatch.resetAndLog("Binding creation");
@@ -145,8 +149,7 @@
injector.index();
stopwatch.resetAndLog("Binding indexing");
- injectionCommandProcessor
- = new InjectionRequestProcessor(errors, injector.memberInjector);
+ injectionCommandProcessor = new InjectionRequestProcessor(errors, injector.initializer);
injectionCommandProcessor.processCommands(elements);
stopwatch.resetAndLog("Static injection");
}
@@ -159,7 +162,7 @@
injectionCommandProcessor.validate(injector);
stopwatch.resetAndLog("Static validation");
- injector.memberInjector.validateOustandingInjections(errors);
+ injector.initializer.validateOustandingInjections(errors);
stopwatch.resetAndLog("Instance member validation");
new ProviderLookupProcessor(errors, injector).processCommands(elements);
@@ -173,7 +176,7 @@
injectionCommandProcessor.injectMembers(injector);
stopwatch.resetAndLog("Static member injection");
- injector.memberInjector.injectAll(errors);
+ injector.initializer.injectAll(errors);
stopwatch.resetAndLog("Instance injection");
errors.throwCreationExceptionIfErrorsExist();
@@ -185,8 +188,10 @@
public void loadEagerSingletons() {
// load eager singletons, or all singletons if we're in Stage.PRODUCTION.
// Bindings discovered while we're binding these singletons are not be eager.
- Set<BindingImpl<?>> candidateBindings = ImmutableSet.copyOf(
- Iterables.concat(injector.explicitBindings.values(), injector.jitBindings.values()));
+ @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 ((stage == Stage.PRODUCTION && binding.getScope() == SINGLETON)
|| binding.getLoadStrategy() == LoadStrategy.EAGER) {
@@ -215,40 +220,51 @@
}
}
- private static class BuiltInModule implements Module {
- final Injector injector;
+ private static class RootModule implements Module {
final Stage stage;
- private BuiltInModule(Injector injector, Stage stage) {
- this.injector = checkNotNull(injector, "injector");
+ private RootModule(Stage stage) {
this.stage = checkNotNull(stage, "stage");
}
public void configure(Binder binder) {
binder = binder.withSource(SourceProvider.UNKNOWN_SOURCE);
-
binder.bind(Stage.class).toInstance(stage);
binder.bindScope(Singleton.class, SINGLETON);
- // Create default bindings.
- // We use toProvider() instead of toInstance() to avoid infinite recursion
- // in toString().
- binder.bind(Injector.class).toProvider(new InjectorProvider(injector));
+ }
+ }
+
+ /**
+ * The Injector is a special case because we allow both parent and child injectors to both have
+ * a binding for that key.
+ */
+ private void bindInjector() {
+ Key<Injector> key = Key.get(Injector.class);
+ InjectorFactory injectorFactory = new InjectorFactory(injector);
+ injector.state.putBinding(key,
+ new ProviderInstanceBindingImpl<Injector>(injector, key, SourceProvider.UNKNOWN_SOURCE,
+ injectorFactory, Scopes.NO_SCOPE, injectorFactory, LoadStrategy.LAZY,
+ ImmutableSet.<InjectionPoint>of()));
+ }
+
+ static class InjectorFactory implements InternalFactory<Injector>, Provider<Injector> {
+ private final Injector injector;
+
+ private InjectorFactory(Injector injector) {
+ this.injector = injector;
}
- class InjectorProvider implements Provider<Injector> {
- final Injector injector;
+ public Injector get(Errors errors, InternalContext context, Dependency<?> dependency)
+ throws ErrorsException {
+ return injector;
+ }
- InjectorProvider(Injector injector) {
- this.injector = injector;
- }
+ public Injector get() {
+ return injector;
+ }
- public Injector get() {
- return injector;
- }
-
- public String toString() {
- return "Provider<Injector>";
- }
+ public String toString() {
+ return "Provider<Injector>";
}
}
@@ -259,7 +275,7 @@
private void bindLogger() {
Key<Logger> key = Key.get(Logger.class);
LoggerFactory loggerFactory = new LoggerFactory();
- injector.explicitBindings.put(key,
+ injector.state.putBinding(key,
new ProviderInstanceBindingImpl<Logger>(injector, key,
SourceProvider.UNKNOWN_SOURCE, loggerFactory, Scopes.NO_SCOPE,
loggerFactory, LoadStrategy.LAZY, ImmutableSet.<InjectionPoint>of()));
@@ -305,6 +321,12 @@
public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
return this.delegateInjector.findBindingsByType(type);
}
+ public Injector createChildInjector(Iterable<? extends Module> modules) {
+ return delegateInjector.createChildInjector(modules);
+ }
+ public Injector createChildInjector(Module... modules) {
+ return delegateInjector.createChildInjector(modules);
+ }
public <T> Provider<T> getProvider(Key<T> key) {
throw new UnsupportedOperationException(
"Injector.getProvider(Key<T>) is not supported in Stage.TOOL");
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index cca4ffd..fa14548 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -16,9 +16,9 @@
package com.google.inject;
+import com.google.common.base.Nullable;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.ImmutableList;
-import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimap;
@@ -60,45 +60,42 @@
* @see InjectorBuilder
*/
class InjectorImpl implements Injector {
- final Injector parentInjector;
- final Map<Key<?>, BindingImpl<?>> explicitBindings = Maps.newHashMap();
+ final State state;
+ final InjectorImpl parent;
final BindingsMultimap bindingsMultimap = new BindingsMultimap();
- final Map<Class<? extends Annotation>, Scope> scopes = Maps.newHashMap();
- final List<MatcherAndConverter> converters = Lists.newArrayList();
- final Map<Key<?>, BindingImpl<?>> parentBindings = Maps.newHashMap();
- final CreationTimeMemberInjector memberInjector = new CreationTimeMemberInjector(this);
+ final Initializer initializer = new Initializer(this);
Reflection reflection;
- InjectorImpl(Injector parentInjector) {
- this.parentInjector = parentInjector;
+ InjectorImpl(@Nullable InjectorImpl parent) {
+ this.parent = parent;
+ this.state = new InheritingState(parent != null ? parent.state : State.NONE);
+
+ if (parent != null) {
+ localContext = parent.localContext;
+ } else {
+ localContext = new ThreadLocal<InternalContext[]>() {
+ protected InternalContext[] initialValue() {
+ return new InternalContext[1];
+ }
+ };
+ }
}
/** Indexes bindings by type. */
void index() {
- for (BindingImpl<?> binding : explicitBindings.values()) {
+ for (Binding<?> binding : state.getExplicitBindingsThisLevel().values()) {
index(binding);
}
}
- <T> void index(BindingImpl<T> binding) {
+ <T> void index(Binding<T> binding) {
bindingsMultimap.put(binding.getKey().getTypeLiteral(), binding);
}
- // not test-covered
public <T> List<Binding<T>> findBindingsByType(TypeLiteral<T> type) {
return Collections.<Binding<T>>unmodifiableList(bindingsMultimap.getAll(type));
}
- // not test-covered
- <T> List<String> getNamesOfBindingAnnotations(TypeLiteral<T> type) {
- List<String> names = Lists.newArrayList();
- for (Binding<T> binding : findBindingsByType(type)) {
- Key<T> key = binding.getKey();
- names.add(key.hasAnnotationType() ? key.getAnnotationName() : "[no annotation]");
- }
- return names;
- }
-
/** Returns the binding for {@code key} */
public <T> BindingImpl<T> getBinding(Key<T> key) {
Errors errors = new Errors(key.getRawType());
@@ -120,15 +117,8 @@
*/
public <T> BindingImpl<T> getBindingOrThrow(Key<T> key, Errors errors)
throws ErrorsException {
- if (parentInjector != null) {
- BindingImpl<T> bindingImpl = getParentBinding(key);
- if (bindingImpl != null) {
- return bindingImpl;
- }
- }
-
// Check explicit bindings, i.e. bindings created by modules.
- BindingImpl<T> binding = getExplicitBindingImpl(key);
+ BindingImpl<T> binding = state.getExplicitBinding(key);
if (binding != null) {
return binding;
}
@@ -137,55 +127,20 @@
return getJustInTimeBinding(key, errors);
}
- /**
- * Checks the parent injector for a scoped binding, and if available, creates an appropriate
- * binding local to this injector and remembers it.
- *
- * TODO: think about this wrt parent jit bindings
- */
- @SuppressWarnings("unchecked")
- private <T> BindingImpl<T> getParentBinding(Key<T> key) {
- synchronized (parentBindings) {
- // null values will mean that the parent doesn't have this binding
- BindingImpl<T> binding = (BindingImpl<T>) parentBindings.get(key);
- if (binding != null) {
- return (BindingImpl<T>) binding;
- }
- try {
- binding = (BindingImpl) parentInjector.getBinding(key);
- }
- catch (ProvisionException e) {
- // if this happens, the parent can't create this key, and we ignore it
- }
-
- BindingImpl<T> bindingImpl = null;
- if (binding != null
- && binding.getScope() != null
- && !binding.getScope().equals(Scopes.NO_SCOPE)) {
- // TODO: this binding won't report its injection points or scoping properly
- bindingImpl = new ProviderInstanceBindingImpl(
- this,
- key,
- binding.getSource(),
- new InternalFactoryToProviderAdapter(binding.getProvider(), binding.getSource()),
- Scopes.NO_SCOPE,
- binding.getProvider(),
- LoadStrategy.LAZY,
- ImmutableSet.<InjectionPoint>of());
- }
- parentBindings.put(key, bindingImpl); // this kinda scares me
- return bindingImpl;
- }
- }
-
public <T> Binding<T> getBinding(Class<T> type) {
return getBinding(Key.get(type));
}
- /** Gets a binding which was specified explicitly in a module. */
- @SuppressWarnings("unchecked")
- <T> BindingImpl<T> getExplicitBindingImpl(Key<T> key) {
- return (BindingImpl<T>) explicitBindings.get(key);
+ public Injector createChildInjector(Iterable<? extends Module> modules) {
+ return new InjectorBuilder()
+ .parentInjector(this)
+ .stage(getInstance(Stage.class))
+ .addModules(modules)
+ .build();
+ }
+
+ public Injector createChildInjector(Module... modules) {
+ return createChildInjector(ImmutableList.of(modules));
}
/**
@@ -194,17 +149,33 @@
* @throws com.google.inject.internal.ErrorsException if the binding could not be created.
*/
@SuppressWarnings("unchecked")
- <T> BindingImpl<T> getJustInTimeBinding(Key<T> key, Errors errors) throws ErrorsException {
+ private <T> BindingImpl<T> getJustInTimeBinding(Key<T> key, Errors errors)
+ throws ErrorsException {
+
+ // TODO: synch should span parent and child
+
synchronized (jitBindings) {
- // Support null values.
- if (!jitBindings.containsKey(key)) {
- BindingImpl<T> binding = createJustInTimeBinding(key, errors);
- jitBindings.put(key, binding);
- return binding;
+ // try to get the JIT binding from the parent injector
+ if (parent != null) {
+ try {
+ return parent.getJustInTimeBinding(key, new Errors());
+ } catch (ErrorsException ignored) {
+ }
}
- else {
+
+ // Support null values.
+ if (jitBindings.containsKey(key)) {
return (BindingImpl<T>) jitBindings.get(key);
}
+
+ if (state.isBlacklisted(key)) {
+ throw errors.childBindingAlreadySet(key).toException();
+ }
+
+ BindingImpl<T> binding = createJustInTimeBinding(key, errors);
+ state.parent().blacklist(key);
+ jitBindings.put(key, binding);
+ return binding;
}
}
@@ -278,7 +249,7 @@
throws ErrorsException {
// Find a constant string binding.
Key<String> stringKey = key.ofType(String.class);
- BindingImpl<String> stringBinding = getExplicitBindingImpl(stringKey);
+ BindingImpl<String> stringBinding = state.getExplicitBinding(stringKey);
if (stringBinding == null || !stringBinding.isConstant()) {
return null;
}
@@ -288,15 +259,7 @@
// Find a matching type converter.
TypeLiteral<T> type = key.getTypeLiteral();
- MatcherAndConverter matchingConverter = null;
- for (MatcherAndConverter converter : converters) {
- if (converter.getTypeMatcher().matches(type)) {
- if (matchingConverter != null) {
- errors.ambiguousTypeConversion(stringValue, source, type, matchingConverter, converter);
- }
- matchingConverter = converter;
- }
- }
+ MatcherAndConverter matchingConverter = state.getConverter(stringValue, type, errors, source);
if (matchingConverter == null) {
// No converter can handle the given type.
@@ -336,8 +299,8 @@
ConvertedConstantBindingImpl(
InjectorImpl injector, Key<T> key, T value, Binding<String> originalBinding) {
- super(injector, key, originalBinding.getSource(), new ConstantFactory<T>(value),
- Scopes.NO_SCOPE, LoadStrategy.LAZY);
+ super(injector, key, originalBinding.getSource(),
+ new ConstantFactory<T>(Initializables.of(value)), Scopes.NO_SCOPE, LoadStrategy.LAZY);
this.value = value;
provider = Providers.of(value);
this.originalBinding = originalBinding;
@@ -360,14 +323,6 @@
}
}
- <T> BindingImpl<T> createBindingFromType(
- Key<T> key, LoadStrategy loadStrategy, Errors errors) throws ErrorsException {
- BindingImpl<T> binding = createUnitializedBinding(
- key, null, loadStrategy, errors);
- initializeBinding(binding, errors);
- return binding;
- }
-
<T> void initializeBinding(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.
@@ -389,13 +344,6 @@
}
}
- <T> BindingImpl<T> createUnitializedBinding(Key<T> key, Scope scope,
- LoadStrategy loadStrategy, Errors errors) throws ErrorsException {
- @SuppressWarnings("unchecked")
- Class<T> type = (Class<T>) key.getRawType();
- return createUnitializedBinding(key, type, scope, type, loadStrategy, errors);
- }
-
/**
* Creates a binding for an injectable type with the given scope. Looks for a scope on the type if
* none is specified.
@@ -441,7 +389,7 @@
if (scope == null) {
Class<? extends Annotation> scopeAnnotation = Annotations.findScopeAnnotation(errors, type);
if (scopeAnnotation != null) {
- scope = scopes.get(scopeAnnotation);
+ scope = state.getScope(scopeAnnotation);
if (scope == null) {
errors.withSource(type).scopeNotFound(scopeAnnotation);
}
@@ -573,14 +521,23 @@
}
/**
- * Returns a new just-in-time binding created by resolving {@code key}. This could be an
- * injectable class (including those with @ImplementedBy, etc.), an automatically converted
- * constant, a {@code Provider<X>} binding, etc.
+ * Returns a new just-in-time binding created by resolving {@code key}. The strategies used to
+ * create just-in-time bindings are:
+ * <ol>
+ * <li>Internalizing Providers. If the requested binding is for {@code Provider<T>}, we delegate
+ * to the binding for {@code T}.
+ * <li>Converting constants.
+ * <li>ImplementedBy and ProvidedBy annotations. Only for unannotated keys.
+ * <li>The constructor of the raw type. Only for unannotated keys.
+ * </ol>
*
* @throws com.google.inject.internal.ErrorsException if the binding cannot be created.
*/
- <T> BindingImpl<T> createJustInTimeBinding(Key<T> key, Errors errors)
- throws ErrorsException {
+ <T> BindingImpl<T> createJustInTimeBinding(Key<T> key, Errors errors) throws ErrorsException {
+ if (state.isBlacklisted(key)) {
+ throw errors.childBindingAlreadySet(key).toException();
+ }
+
// Handle cases where T is a Provider<?>.
if (isProvider(key)) {
// These casts are safe. We know T extends Provider<X> and that given Key<Provider<X>>,
@@ -612,7 +569,12 @@
}
// Create a binding based on the raw type.
- return createBindingFromType(key, LoadStrategy.LAZY, errors);
+ @SuppressWarnings("unchecked")
+ Class<T> rawType = (Class<T>) key.getRawType();
+ BindingImpl<T> binding = createUnitializedBinding(key, rawType, null /* scope */, rawType,
+ LoadStrategy.LAZY, errors);
+ initializeBinding(binding, errors);
+ return binding;
}
<T> InternalFactory<? extends T> getInternalFactory(Key<T> key, Errors errors)
@@ -658,7 +620,7 @@
// not test-covered
public Map<Key<?>, Binding<?>> getBindings() {
- return Collections.<Key<?>, Binding<?>>unmodifiableMap(explicitBindings);
+ return state.getExplicitBindingsThisLevel();
}
interface SingleInjectorFactory<M extends Member & AnnotatedElement> {
@@ -669,7 +631,7 @@
private static class BindingsMultimap {
final Multimap<TypeLiteral<?>, Binding<?>> multimap = Multimaps.newArrayListMultimap();
- <T> void put(TypeLiteral<T> type, BindingImpl<T> binding) {
+ <T> void put(TypeLiteral<T> type, Binding<T> binding) {
multimap.put(type, binding);
}
@@ -975,17 +937,13 @@
return getProvider(type).get();
}
- final ThreadLocal<InternalContext[]> localContext = new ThreadLocal<InternalContext[]>() {
- protected InternalContext[] initialValue() {
- return new InternalContext[1];
- }
- };
+ final ThreadLocal<InternalContext[]> localContext;
/** Looks up thread local context. Creates (and removes) a new context if necessary. */
<T> T callInContext(ContextualCallable<T> callable) throws ErrorsException {
InternalContext[] reference = localContext.get();
if (reference[0] == null) {
- reference[0] = new InternalContext(this);
+ reference[0] = new InternalContext();
try {
return callable.call(reference[0]);
} finally {
@@ -1007,7 +965,7 @@
public String toString() {
return new ToStringBuilder(Injector.class)
- .add("bindings", explicitBindings)
+ .add("bindings", state.getExplicitBindingsThisLevel())
.toString();
}
}
diff --git a/src/com/google/inject/InterceptorBindingProcessor.java b/src/com/google/inject/InterceptorBindingProcessor.java
index e48f67b..ef1c2f9 100644
--- a/src/com/google/inject/InterceptorBindingProcessor.java
+++ b/src/com/google/inject/InterceptorBindingProcessor.java
@@ -27,20 +27,20 @@
*/
class InterceptorBindingProcessor extends AbstractProcessor {
- private final ProxyFactoryBuilder proxyFactoryBuilder;
+ private final State state;
- InterceptorBindingProcessor(Errors errors) {
+ InterceptorBindingProcessor(Errors errors, State state) {
super(errors);
- proxyFactoryBuilder = new ProxyFactoryBuilder();
+ this.state = state;
}
@Override public Boolean visitInterceptorBinding(InterceptorBinding command) {
- proxyFactoryBuilder.intercept(
- command.getClassMatcher(), command.getMethodMatcher(), command.getInterceptors());
+ state.addMethodAspect(new MethodAspect(
+ command.getClassMatcher(), command.getMethodMatcher(), command.getInterceptors()));
return true;
}
ProxyFactory createProxyFactory() {
- return proxyFactoryBuilder.create();
+ return new ProxyFactory(state.getMethodAspects());
}
}
diff --git a/src/com/google/inject/InternalContext.java b/src/com/google/inject/InternalContext.java
index bcb4e59..4183d0b 100644
--- a/src/com/google/inject/InternalContext.java
+++ b/src/com/google/inject/InternalContext.java
@@ -16,8 +16,6 @@
package com.google.inject;
-import com.google.inject.internal.Errors;
-import com.google.inject.internal.ErrorsException;
import com.google.inject.spi.Dependency;
import java.util.HashMap;
import java.util.Map;
@@ -30,14 +28,9 @@
*/
class InternalContext {
- private final InjectorImpl injector;
private Map<Object, ConstructionContext<?>> constructionContexts;
private Dependency dependency;
- public InternalContext(InjectorImpl injector) {
- this.injector = injector;
- }
-
@SuppressWarnings("unchecked")
public <T> ConstructionContext<T> getConstructionContext(Object key) {
if (constructionContexts == null) {
@@ -64,12 +57,4 @@
public void setDependency(Dependency dependency) {
this.dependency = dependency;
}
-
- /**
- * Ensures that an object requiring injection at Injector-creation time has
- * been injected before its use.
- */
- public void ensureMemberInjected(Errors errors, Object toInject) throws ErrorsException {
- injector.memberInjector.ensureInjected(toInject, errors);
- }
}
diff --git a/src/com/google/inject/InternalFactoryToProviderAdapter.java b/src/com/google/inject/InternalFactoryToProviderAdapter.java
index b3a19f8..c506d79 100644
--- a/src/com/google/inject/InternalFactoryToProviderAdapter.java
+++ b/src/com/google/inject/InternalFactoryToProviderAdapter.java
@@ -27,24 +27,23 @@
*/
class InternalFactoryToProviderAdapter<T> implements InternalFactory<T> {
- private final Provider<? extends T> provider;
+ private final Initializable<Provider<? extends T>> initializable;
private final Object source;
- public InternalFactoryToProviderAdapter(Provider<? extends T> provider) {
- this(provider, SourceProvider.UNKNOWN_SOURCE);
+ public InternalFactoryToProviderAdapter(Initializable<Provider<? extends T>> initializable) {
+ this(initializable, SourceProvider.UNKNOWN_SOURCE);
}
public InternalFactoryToProviderAdapter(
- Provider<? extends T> provider, Object source) {
- this.provider = checkNotNull(provider, "provider");
+ Initializable<Provider<? extends T>> initializable, Object source) {
+ this.initializable = checkNotNull(initializable, "provider");
this.source = checkNotNull(source, "source");
}
public T get(Errors errors, InternalContext context, Dependency<?> dependency)
throws ErrorsException {
try {
- context.ensureMemberInjected(errors, provider);
- return errors.checkForNull(provider.get(), source, dependency);
+ return errors.checkForNull(initializable.get(errors).get(), source, dependency);
} catch (RuntimeException userException) {
Errors userErrors = ProvisionException.getErrors(userException);
throw errors.withSource(source)
@@ -53,6 +52,6 @@
}
@Override public String toString() {
- return provider.toString();
+ return initializable.toString();
}
}
diff --git a/src/com/google/inject/MethodAspect.java b/src/com/google/inject/MethodAspect.java
index 30515cc..de9e1c9 100644
--- a/src/com/google/inject/MethodAspect.java
+++ b/src/com/google/inject/MethodAspect.java
@@ -16,11 +16,11 @@
package com.google.inject;
-import com.google.inject.matcher.Matcher;
import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.inject.matcher.Matcher;
import java.lang.reflect.Method;
+import java.util.Arrays;
import java.util.List;
-
import org.aopalliance.intercept.MethodInterceptor;
/**
@@ -34,6 +34,13 @@
final Matcher<? super Method> methodMatcher;
final List<MethodInterceptor> interceptors;
+ /**
+ * @param classMatcher matches classes the interceptor should apply to. For example: {@code
+ * only(Runnable.class)}.
+ * @param methodMatcher matches methods the interceptor should apply to. For example: {@code
+ * annotatedWith(Transactional.class)}.
+ * @param interceptors to apply
+ */
MethodAspect(Matcher<? super Class<?>> classMatcher,
Matcher<? super Method> methodMatcher, List<MethodInterceptor> interceptors) {
this.classMatcher = checkNotNull(classMatcher, "class matcher");
@@ -41,6 +48,11 @@
this.interceptors = checkNotNull(interceptors, "interceptors");
}
+ MethodAspect(Matcher<? super Class<?>> classMatcher,
+ Matcher<? super Method> methodMatcher, MethodInterceptor... interceptors) {
+ this(classMatcher, methodMatcher, Arrays.asList(interceptors));
+ }
+
boolean matches(Class<?> clazz) {
return classMatcher.matches(clazz);
}
diff --git a/src/com/google/inject/ProxyFactory.java b/src/com/google/inject/ProxyFactory.java
index 07c9227..eef3aef 100644
--- a/src/com/google/inject/ProxyFactory.java
+++ b/src/com/google/inject/ProxyFactory.java
@@ -40,8 +40,7 @@
import org.aopalliance.intercept.MethodInterceptor;
/**
- * Proxies classes applying interceptors to methods as specified in
- * {@link ProxyFactoryBuilder}.
+ * Proxies classes applying interceptors to methods.
*
* @author crazybob@google.com (Bob Lee)
*/
diff --git a/src/com/google/inject/ProxyFactoryBuilder.java b/src/com/google/inject/ProxyFactoryBuilder.java
deleted file mode 100644
index 36a3482..0000000
--- a/src/com/google/inject/ProxyFactoryBuilder.java
+++ /dev/null
@@ -1,62 +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;
-
-import com.google.common.collect.Lists;
-import com.google.inject.matcher.Matcher;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.List;
-import org.aopalliance.intercept.MethodInterceptor;
-
-/**
- * Creates a {@link com.google.inject.ProxyFactory}.
- *
- * @author crazybob@google.com (Bob Lee)
- */
-class ProxyFactoryBuilder {
-
- final List<MethodAspect> methodAspects = Lists.newArrayList();
-
- /**
- * Applies the given method interceptor to the methods matched by the class and method matchers.
- *
- * @param classMatcher matches classes the interceptor should apply to. For example: {@code
- * only(Runnable.class)}.
- * @param methodMatcher matches methods the interceptor should apply to. For example: {@code
- * annotatedWith(Transactional.class)}.
- * @param interceptors to apply
- */
- public ProxyFactoryBuilder intercept(Matcher<? super Class<?>> classMatcher,
- Matcher<? super Method> methodMatcher,
- List<MethodInterceptor> interceptors) {
- methodAspects.add(new MethodAspect(classMatcher, methodMatcher, interceptors));
- return this;
- }
-
- public ProxyFactoryBuilder intercept(Matcher<? super Class<?>> classMatcher,
- Matcher<? super Method> methodMatcher,
- MethodInterceptor... interceptors) {
- return intercept(classMatcher, methodMatcher, Arrays.asList(interceptors));
- }
-
- /** Creates a {@code ProxyFactory}. */
- public ProxyFactory create() {
- return new ProxyFactory(new ArrayList<MethodAspect>(methodAspects));
- }
-}
diff --git a/src/com/google/inject/ScopeBindingProcessor.java b/src/com/google/inject/ScopeBindingProcessor.java
index 1458df5..5936fe9 100644
--- a/src/com/google/inject/ScopeBindingProcessor.java
+++ b/src/com/google/inject/ScopeBindingProcessor.java
@@ -21,7 +21,6 @@
import com.google.inject.internal.Errors;
import com.google.inject.spi.ScopeBinding;
import java.lang.annotation.Annotation;
-import java.util.Map;
/**
* Handles {@link Binder#bindScope} commands.
@@ -31,12 +30,12 @@
*/
class ScopeBindingProcessor extends AbstractProcessor {
- private final Map<Class<? extends Annotation>, Scope> scopes;
+ private final State state;
ScopeBindingProcessor(Errors errors,
- Map<Class<? extends Annotation>, Scope> scopes) {
+ State state) {
super(errors);
- this.scopes = scopes;
+ this.state = state;
}
@Override public Boolean visitScopeBinding(ScopeBinding command) {
@@ -54,11 +53,11 @@
// Go ahead and bind anyway so we don't get collateral errors.
}
- Scope existing = scopes.get(checkNotNull(annotationType, "annotation type"));
+ Scope existing = state.getScope(checkNotNull(annotationType, "annotation type"));
if (existing != null) {
errors.duplicateScopes(existing, annotationType, scope);
} else {
- scopes.put(annotationType, checkNotNull(scope, "scope"));
+ state.putAnnotation(annotationType, checkNotNull(scope, "scope"));
}
return true;
diff --git a/src/com/google/inject/Scopes.java b/src/com/google/inject/Scopes.java
index e2597cc..f8a668f 100644
--- a/src/com/google/inject/Scopes.java
+++ b/src/com/google/inject/Scopes.java
@@ -93,6 +93,7 @@
}
Provider<T> scoped = scope.scope(key,
new ProviderToInternalFactoryAdapter<T>(injector, creator));
- return new InternalFactoryToProviderAdapter<T>(scoped);
+ return new InternalFactoryToProviderAdapter<T>(
+ Initializables.<Provider<? extends T>>of(scoped));
}
}
diff --git a/src/com/google/inject/State.java b/src/com/google/inject/State.java
new file mode 100644
index 0000000..580eeae
--- /dev/null
+++ b/src/com/google/inject/State.java
@@ -0,0 +1,129 @@
+/**
+ * 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;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.inject.internal.Errors;
+import com.google.inject.internal.MatcherAndConverter;
+import java.lang.annotation.Annotation;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The inheritable data within an injector. This class is intended to allow parent and local
+ * injector data to be accessed as a unit.
+ *
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+interface State {
+
+ static final State NONE = new State() {
+ public State parent() {
+ throw new UnsupportedOperationException();
+ }
+
+ public <T> BindingImpl<T> getExplicitBinding(Key<T> key) {
+ return null;
+ }
+
+ public Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel() {
+ throw new UnsupportedOperationException();
+ }
+
+ public void putBinding(Key<?> key, BindingImpl<?> binding) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Scope getScope(Class<? extends Annotation> scopingAnnotation) {
+ return null;
+ }
+
+ public void putAnnotation(Class<? extends Annotation> annotationType, Scope scope) {
+ throw new UnsupportedOperationException();
+ }
+
+ public void addConverter(MatcherAndConverter matcherAndConverter) {
+ throw new UnsupportedOperationException();
+ }
+
+ public MatcherAndConverter getConverter(String stringValue, TypeLiteral<?> type, Errors errors,
+ Object source) {
+ throw new UnsupportedOperationException();
+ }
+
+ public Iterable<MatcherAndConverter> getConvertersThisLevel() {
+ return ImmutableSet.of();
+ }
+
+ public void addMethodAspect(MethodAspect methodAspect) {
+ throw new UnsupportedOperationException();
+ }
+
+ public List<MethodAspect> getMethodAspects() {
+ return ImmutableList.of();
+ }
+
+ public void blacklist(Key<?> key) {
+ }
+
+ public boolean isBlacklisted(Key<?> key) {
+ return true;
+ }
+ };
+
+ State parent();
+
+ /** Gets a binding which was specified explicitly in a module, or null. */
+ <T> BindingImpl<T> getExplicitBinding(Key<T> key);
+
+ /** Returns the explicit bindings at this level only. */
+ Map<Key<?>, Binding<?>> getExplicitBindingsThisLevel();
+
+ void putBinding(Key<?> key, BindingImpl<?> binding);
+
+ /** Returns the matching scope, or null. */
+ Scope getScope(Class<? extends Annotation> scopingAnnotation);
+
+ void putAnnotation(Class<? extends Annotation> annotationType, Scope scope);
+
+ void addConverter(MatcherAndConverter matcherAndConverter);
+
+ /** Returns the matching converter for {@code type}, or null if none match. */
+ MatcherAndConverter getConverter(
+ String stringValue, TypeLiteral<?> type, Errors errors, Object source);
+
+ /** Returns all converters at this level only. */
+ Iterable<MatcherAndConverter> getConvertersThisLevel();
+
+ void addMethodAspect(MethodAspect methodAspect);
+
+ List<MethodAspect> getMethodAspects();
+
+ /**
+ * Forbids the corresponding injector from creating a binding to {@code key}. Child injectors
+ * blacklist their bound keys on their parent injectors to prevent just-in-time bindings on the
+ * parent injector that would conflict.
+ */
+ void blacklist(Key<?> key);
+
+ /**
+ * Returns true if {@code key} is forbidden from being bound in this injector. This indicates that
+ * one of this injector's descendent's has bound the key.
+ */
+ boolean isBlacklisted(Key<?> key);
+}
diff --git a/src/com/google/inject/TypeConverterBindingProcessor.java b/src/com/google/inject/TypeConverterBindingProcessor.java
index 1f2b291..b9d1fd3 100644
--- a/src/com/google/inject/TypeConverterBindingProcessor.java
+++ b/src/com/google/inject/TypeConverterBindingProcessor.java
@@ -29,7 +29,6 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
-import java.util.List;
/**
* Handles {@link Binder#convertToTypes} commands.
@@ -39,11 +38,11 @@
*/
class TypeConverterBindingProcessor extends AbstractProcessor {
- private final List<MatcherAndConverter> converters;
+ private final State state;
- TypeConverterBindingProcessor(Errors errors, List<MatcherAndConverter> converters) {
+ TypeConverterBindingProcessor(Errors errors, State state) {
super(errors);
- this.converters = converters;
+ this.state = state;
// Configure type converters.
convertToPrimitiveType(int.class, Integer.class);
@@ -162,11 +161,12 @@
private void internalConvertToTypes(Matcher<? super TypeLiteral<?>> typeMatcher,
TypeConverter converter) {
- converters.add(new MatcherAndConverter(typeMatcher, converter, SourceProvider.UNKNOWN_SOURCE));
+ state.addConverter(
+ new MatcherAndConverter(typeMatcher, converter, SourceProvider.UNKNOWN_SOURCE));
}
@Override public Boolean visitTypeConverterBinding(TypeConverterBinding command) {
- converters.add(new MatcherAndConverter(
+ state.addConverter(new MatcherAndConverter(
command.getTypeMatcher(), command.getTypeConverter(), command.getSource()));
return true;
}
diff --git a/src/com/google/inject/WeakKeySet.java b/src/com/google/inject/WeakKeySet.java
new file mode 100644
index 0000000..b1c9799
--- /dev/null
+++ b/src/com/google/inject/WeakKeySet.java
@@ -0,0 +1,45 @@
+/**
+ * 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;
+
+import com.google.common.collect.Sets;
+import java.util.Set;
+
+/**
+ * Minimal set that doesn't hold strong references to the contained keys.
+ *
+ * @author jessewilson@google.com (Jesse Wilson)
+ */
+public final class WeakKeySet {
+
+ /**
+ * We store strings rather than keys so we don't hold strong references.
+ *
+ * <p>One potential problem with this approach is that parent and child injectors cannot define
+ * keys whose class names are equal but class loaders are different. This shouldn't be an issue
+ * in practice.
+ */
+ private Set<String> backingSet = Sets.newHashSet();
+
+ public boolean add(Key<?> key) {
+ return backingSet.add(key.toString());
+ }
+
+ public boolean contains(Object o) {
+ return o instanceof Key && backingSet.contains(o.toString());
+ }
+}
diff --git a/src/com/google/inject/internal/Errors.java b/src/com/google/inject/internal/Errors.java
index 228d2d1..e3a3cb3 100644
--- a/src/com/google/inject/internal/Errors.java
+++ b/src/com/google/inject/internal/Errors.java
@@ -241,6 +241,10 @@
return addMessage("A binding to %s was already configured at %s.", key, source);
}
+ public Errors childBindingAlreadySet(Key<?> key) {
+ return addMessage("A binding to %s already exists on a child injector.", key);
+ }
+
public Errors errorInjectingMethod(Throwable cause) {
return addMessage(cause, "Error injecting method, %s", cause);
}
diff --git a/test/com/google/inject/ParentInjectorTest.java b/test/com/google/inject/ParentInjectorTest.java
index 41e8118..953549b 100644
--- a/test/com/google/inject/ParentInjectorTest.java
+++ b/test/com/google/inject/ParentInjectorTest.java
@@ -16,83 +16,204 @@
package com.google.inject;
+import com.google.common.collect.ImmutableList;
+import static com.google.inject.Asserts.assertContains;
+import com.google.inject.matcher.Matchers;
+import com.google.inject.name.Names;
+import com.google.inject.spi.TypeConverter;
+import static java.lang.annotation.ElementType.TYPE;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+import java.util.List;
import junit.framework.TestCase;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
/**
- *
+ * @author jessewilson@google.com (Jesse Wilson)
*/
public class ParentInjectorTest extends TestCase {
- Module baseModule = new AbstractModule() {
+ public void testParentAndChildCannotShareExplicitBindings() {
+ Injector parent = Guice.createInjector(bindsA);
+ try {
+ parent.createChildInjector(bindsA);
+ fail("Created the same explicit binding on both parent and child");
+ } catch (CreationException e) {
+ assertContains(e.getMessage(), "A binding to ", A.class.getName(), " was already configured",
+ " at ", getClass().getName(), ".configure(ParentInjectorTest.java:",
+ " at ", getClass().getName(), ".configure(ParentInjectorTest.java:");
+ }
+ }
+
+ public void testParentJitBindingWontClobberChildBinding() {
+ Injector parent = Guice.createInjector();
+ parent.createChildInjector(bindsA);
+ try {
+ parent.getInstance(A.class);
+ fail("Created a just-in-time binding on the parent that's the same as a child's binding");
+ } catch (ProvisionException e) {
+ assertContains(e.getMessage(), "A binding to ", A.class.getName(),
+ " already exists on a child injector.");
+ }
+ }
+
+ public void testJustInTimeBindingsAreSharedWithParentIfPossible() {
+ Injector parent = Guice.createInjector();
+ Injector child = parent.createChildInjector();
+ assertSame(child.getInstance(A.class), parent.getInstance(A.class));
+
+ Injector anotherChild = parent.createChildInjector();
+ assertSame(anotherChild.getInstance(A.class), parent.getInstance(A.class));
+
+ Injector grandchild = child.createChildInjector();
+ assertSame(grandchild.getInstance(A.class), parent.getInstance(A.class));
+ }
+
+ public void testBindingsInherited() {
+ Injector parent = Guice.createInjector(bindsB);
+ Injector child = parent.createChildInjector();
+ assertSame(RealB.class, child.getInstance(B.class).getClass());
+ }
+
+ public void testChildBindingsNotVisibleToParent() {
+ Injector parent = Guice.createInjector();
+ parent.createChildInjector(bindsB);
+ try {
+ parent.getBinding(B.class);
+ fail();
+ } catch (ProvisionException expected) {
+ }
+ }
+
+ public void testScopesInherited() {
+ Injector parent = Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ bindScope(MyScope.class, Scopes.SINGLETON);
+ }
+ });
+ Injector child = parent.createChildInjector(new AbstractModule() {
+ @Override protected void configure() {
+ bind(A.class).in(MyScope.class);
+ }
+ });
+ assertSame(child.getInstance(A.class), child.getInstance(A.class));
+ }
+
+ public void testInterceptorsInherited() {
+ Injector parent = Guice.createInjector(new AbstractModule() {
+ protected void configure() {
+ super.bindInterceptor(Matchers.any(), Matchers.returns(Matchers.identicalTo(A.class)),
+ returnNullInterceptor);
+ }
+ });
+
+ Injector child = parent.createChildInjector(new AbstractModule() {
+ protected void configure() {
+ bind(C.class);
+ }
+ });
+
+ assertNull(child.getInstance(C.class).interceptedMethod());
+ }
+
+ public void testTypeConvertersInherited() {
+ Injector parent = Guice.createInjector(bindListConverterModule);
+ Injector child = parent.createChildInjector(bindStringNamedB);
+
+ assertEquals(ImmutableList.of(), child.getInstance(Key.get(List.class, Names.named("B"))));
+ }
+
+ public void testTypeConvertersConflicting() {
+ Injector parent = Guice.createInjector(bindListConverterModule);
+ Injector child = parent.createChildInjector(bindListConverterModule, bindStringNamedB);
+
+ try {
+ child.getInstance(Key.get(List.class, Names.named("B")));
+ fail();
+ } catch (ProvisionException expected) {
+ Asserts.assertContains(expected.getMessage(), "Multiple converters can convert");
+ }
+ }
+
+ public void testInjectorInjectionSpanningInjectors() {
+ Injector parent = Guice.createInjector();
+ Injector child = parent.createChildInjector(new AbstractModule() {
+ protected void configure() {
+ bind(D.class);
+ }
+ });
+
+ D d = child.getInstance(D.class);
+ assertSame(d.injector, child);
+
+ E e = child.getInstance(E.class);
+ assertSame(e.injector, parent);
+ }
+
+ @Singleton
+ static class A {}
+
+ private final Module bindsA = new AbstractModule() {
protected void configure() {
- bind(Foo.class).to(FooImpl.class).in(Scopes.SINGLETON);
- bind(Bar.class).to(BarOne.class);
+ bind(A.class).toInstance(new A());
}
};
- Injector injector = Guice.createInjector(baseModule);
- Injector childInjector = Guice.createInjector(injector,
- new AbstractModule() {
- protected void configure() {
- bind(Bar.class).to(BarTwo.class);
- bind(Bus.class).to(BusImpl.class);
- }
- });
+ interface B {}
+ static class RealB implements B {}
- /** Make sure that singletons are properly handled **/
- public void testExplicitSingleton() throws Exception {
- Foo fooParent = injector.getInstance(Foo.class);
- Foo fooChild = childInjector.getInstance(Foo.class);
+ private final Module bindsB = new AbstractModule() {
+ protected void configure() {
+ bind(B.class).to(RealB.class);
+ }
+ };
- assertTrue(fooChild instanceof FooImpl);
- assertSame(fooParent, fooChild);
+ @Target(TYPE) @Retention(RUNTIME) @ScopeAnnotation
+ public @interface MyScope {}
+
+ private final MethodInterceptor returnNullInterceptor = new MethodInterceptor() {
+ public Object invoke(MethodInvocation methodInvocation) throws Throwable {
+ return null;
+ }
+ };
+
+ private final TypeConverter listConverter = new TypeConverter() {
+ public Object convert(String value, TypeLiteral<?> toType) {
+ return ImmutableList.of();
+ }
+ };
+
+ private final Module bindListConverterModule = new AbstractModule() {
+ protected void configure() {
+ convertToTypes(Matchers.any(), listConverter);
+ }
+ };
+
+ private final Module bindStringNamedB = new AbstractModule() {
+ protected void configure() {
+ bind(String.class).annotatedWith(Names.named("B")).toInstance("buzz");
+ }
+ };
+
+ public static class C {
+ public A interceptedMethod() {
+ return new A();
+ }
}
- /**
- * Make sure that when there are non scoped bindings in the parent,
- * they are not used.
- */
- public void testNonSingletons() throws Exception {
- Bar barParent = injector.getInstance(Bar.class);
- Bar barChild = childInjector.getInstance(Bar.class);
-
- assertNotSame(barParent, barChild);
- assertTrue(barParent instanceof BarOne);
- assertTrue(barChild instanceof BarTwo);
+ static class D {
+ @Inject Injector injector;
}
- public void testImplicitSingleton() throws Exception {
- Car carParent = injector.getInstance(Car.class);
- Car carChild = childInjector.getInstance(Car.class);
-
- assertNotNull(carParent);
- assertNotNull(carChild);
- assertSame(carParent, carChild);
+ static class E {
+ @Inject Injector injector;
}
- public void testImplicitSingletonFromChild() throws Exception {
- Truck truck = childInjector.getInstance(Truck.class);
- assertNotNull(truck);
- }
-
- interface Foo {}
- @Singleton
- static class FooImpl implements Foo {}
-
- interface Bar {}
- static class BarOne implements Bar {}
- static class BarTwo implements Bar {}
-
- @Singleton
- static class Car {}
-
- interface Bus {}
- static class BusImpl implements Bus {}
-
- @Singleton
- private static class Truck {
- @Inject
- Truck(Bus bus) {}
- }
-
+ private final Module bindsD = new AbstractModule() {
+ protected void configure() {
+ bind(D.class);
+ }
+ };
}
diff --git a/test/com/google/inject/ProxyFactoryTest.java b/test/com/google/inject/ProxyFactoryTest.java
index 18be9d1..4a0d214 100644
--- a/test/com/google/inject/ProxyFactoryTest.java
+++ b/test/com/google/inject/ProxyFactoryTest.java
@@ -17,6 +17,7 @@
package com.google.inject;
+import com.google.common.collect.Lists;
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
import static com.google.inject.matcher.Matchers.annotatedWith;
@@ -27,6 +28,7 @@
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.reflect.InvocationTargetException;
+import java.util.List;
import junit.framework.TestCase;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
@@ -36,13 +38,14 @@
*/
public class ProxyFactoryTest extends TestCase {
+ List<MethodAspect> aspects = Lists.newArrayList();
+
public void testSimpleCase()
throws NoSuchMethodException, InvocationTargetException, ErrorsException {
SimpleInterceptor interceptor = new SimpleInterceptor();
- ProxyFactoryBuilder builder = new ProxyFactoryBuilder();
- builder.intercept(any(), any(), interceptor);
- ProxyFactory factory = builder.create();
+ aspects.add(new MethodAspect(any(), any(), interceptor));
+ ProxyFactory factory = new ProxyFactory(aspects);
ConstructionProxy<Simple> constructionProxy = factory
.createConstructionProxy(new Errors(), InjectionPoint.forConstructorOf(Simple.class));
@@ -74,11 +77,8 @@
throws NoSuchMethodException, InvocationTargetException, ErrorsException {
SimpleInterceptor interceptor = new SimpleInterceptor();
- ProxyFactoryBuilder builder = new ProxyFactoryBuilder();
-
- builder.intercept(
- only(Bar.class), annotatedWith(Intercept.class), interceptor);
- ProxyFactory factory = builder.create();
+ aspects.add(new MethodAspect(only(Bar.class), annotatedWith(Intercept.class), interceptor));
+ ProxyFactory factory = new ProxyFactory(aspects);
ConstructionProxy<Foo> fooFactory =
factory.get(new Errors(), InjectionPoint.forConstructorOf(Foo.class));
@@ -131,9 +131,8 @@
throws InvocationTargetException, NoSuchMethodException, ErrorsException {
SimpleInterceptor interceptor = new SimpleInterceptor();
- ProxyFactoryBuilder builder = new ProxyFactoryBuilder();
- builder.intercept(any(), any(), interceptor);
- ProxyFactory factory = builder.create();
+ aspects.add(new MethodAspect(any(), any(), interceptor));
+ ProxyFactory factory = new ProxyFactory(aspects);
ConstructionProxy<A> constructor =
factory.get(new Errors(), InjectionPoint.forConstructorOf(A.class));
@@ -147,9 +146,8 @@
throws NoSuchMethodException, InvocationTargetException, ErrorsException {
SimpleInterceptor interceptor = new SimpleInterceptor();
- ProxyFactoryBuilder builder = new ProxyFactoryBuilder();
- builder.intercept(not(any()), not(any()), interceptor);
- ProxyFactory factory = builder.create();
+ aspects.add(new MethodAspect(not(any()), not(any()), interceptor));
+ ProxyFactory factory = new ProxyFactory(aspects);
ConstructionProxy<A> constructor =
factory.get(new Errors(), InjectionPoint.forConstructorOf(A.class));
@@ -171,9 +169,8 @@
DoubleInterceptor doubleInterceptor = new DoubleInterceptor();
CountingInterceptor countingInterceptor = new CountingInterceptor();
- ProxyFactoryBuilder builder = new ProxyFactoryBuilder();
- builder.intercept(any(), any(), doubleInterceptor, countingInterceptor);
- ProxyFactory factory = builder.create();
+ aspects.add(new MethodAspect(any(), any(), doubleInterceptor, countingInterceptor));
+ ProxyFactory factory = new ProxyFactory(aspects);
ConstructionProxy<Counter> constructor =
factory.get(new Errors(), InjectionPoint.forConstructorOf(Counter.class));