diff --git a/core/src/com/google/inject/Injector.java b/core/src/com/google/inject/Injector.java
index f485aa4453..a0535405e2 100644
--- a/core/src/com/google/inject/Injector.java
+++ b/core/src/com/google/inject/Injector.java
@@ -16,7 +16,9 @@
package com.google.inject;
+import com.google.common.collect.ListMultimap;
import com.google.inject.spi.Element;
+import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.TypeConverterBinding;
import java.lang.annotation.Annotation;
import java.util.List;
@@ -269,7 +271,32 @@ public interface Injector {
*
The returned list does not include elements inherited from a {@link #getParent() parent
* injector}, should one exist.
*
+ *
The returned list is immutable; it contains only the elements that were present when {@link
+ * #getElements} was invoked. Subsequent calls may return a list with additional elements.
+ *
+ *
The returned list does not include data inherited from a {@link #getParent() parent
+ * injector}, should one exist.
+ *
+ *
This method is part of the Guice SPI and is intended for use by tools and extensions.
+ *
* @since 5.0
*/
List getElements();
+
+ /**
+ * Returns the injection points created for calls to {@link #getMembersInjector} (either directly
+ * or indirectly, e.g. through {@link #injectMembers}.
+ *
+ * This excludes any injection points from elements (which are accessible from each element via
+ * the SPI), unless {@link #getMembersInjector} or {@link #injectMembers} were also called for the
+ * same key.
+ *
+ *
The returned multimap does not include data inherited from a {@link #getParent() parent
+ * injector}, should one exist.
+ *
+ *
This method is part of the Guice SPI and is intended for use by tools and extensions.
+ *
+ * @since 5.0
+ */
+ ListMultimap, InjectionPoint> getAllMembersInjectorInjectionPoints();
}
diff --git a/core/src/com/google/inject/internal/FailableCache.java b/core/src/com/google/inject/internal/FailableCache.java
index e1a461d8f7..def5d980dd 100644
--- a/core/src/com/google/inject/internal/FailableCache.java
+++ b/core/src/com/google/inject/internal/FailableCache.java
@@ -19,6 +19,9 @@
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Maps;
+import java.util.Map;
/**
* Lazily creates (and caches) values for keys. If creating the value fails (with errors), an
@@ -62,4 +65,16 @@ public V get(K key, Errors errors) throws ErrorsException {
boolean remove(K key) {
return delegate.asMap().remove(key) != null;
}
+
+ Map asMap() {
+ return Maps.transformValues(
+ Maps.filterValues(
+ ImmutableMap.copyOf(delegate.asMap()),
+ resultOrError -> !(resultOrError instanceof Errors)),
+ resultOrError -> {
+ @SuppressWarnings("unchecked") // create returned a non-error result, so this is safe
+ V result = (V) resultOrError;
+ return result;
+ });
+ }
}
diff --git a/core/src/com/google/inject/internal/InjectorImpl.java b/core/src/com/google/inject/internal/InjectorImpl.java
index b6c389039c..9c091e23ff 100644
--- a/core/src/com/google/inject/internal/InjectorImpl.java
+++ b/core/src/com/google/inject/internal/InjectorImpl.java
@@ -22,10 +22,12 @@
import com.google.common.base.Objects;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
+import com.google.common.collect.Multimaps;
import com.google.common.collect.Sets;
import com.google.inject.Binder;
import com.google.inject.Binding;
@@ -128,6 +130,9 @@ enum JitLimitation {
Lookups lookups = new DeferredLookups(this);
+ /** The set of types passed to {@link #getMembersInjector} and {@link #injectMembers}. */
+ final Set> userRequestedMembersInjectorTypes = Sets.newConcurrentHashSet();
+
InjectorImpl(InjectorImpl parent, State state, InjectorOptions injectorOptions) {
this.parent = parent;
this.state = state;
@@ -984,6 +989,14 @@ public List getElements() {
return elements.build();
}
+ @Override
+ public ListMultimap, InjectionPoint> getAllMembersInjectorInjectionPoints() {
+ return ImmutableListMultimap.copyOf(
+ Multimaps.filterKeys(
+ membersInjectorStore.getAllInjectionPoints(),
+ userRequestedMembersInjectorTypes::contains));
+ }
+
/** Returns parameter injectors, or {@code null} if there are no parameters. */
SingleParameterInjector>[] getParametersInjectors(List> parameters, Errors errors)
throws ErrorsException {
@@ -1038,6 +1051,8 @@ public void injectMembers(Object instance) {
@Override
public MembersInjector getMembersInjector(TypeLiteral typeLiteral) {
checkNotNull(typeLiteral, "typeLiteral");
+ userRequestedMembersInjectorTypes.add(typeLiteral);
+
Errors errors = new Errors(typeLiteral);
try {
return membersInjectorStore.get(typeLiteral, errors);
diff --git a/core/src/com/google/inject/internal/InternalInjectorCreator.java b/core/src/com/google/inject/internal/InternalInjectorCreator.java
index 43f326de0a..1c063b644b 100644
--- a/core/src/com/google/inject/internal/InternalInjectorCreator.java
+++ b/core/src/com/google/inject/internal/InternalInjectorCreator.java
@@ -16,6 +16,7 @@
package com.google.inject.internal;
+import com.google.common.collect.ListMultimap;
import com.google.inject.Binding;
import com.google.inject.Injector;
import com.google.inject.Key;
@@ -28,6 +29,7 @@
import com.google.inject.internal.util.Stopwatch;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.Element;
+import com.google.inject.spi.InjectionPoint;
import com.google.inject.spi.TypeConverterBinding;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
@@ -312,6 +314,11 @@ public List getElements() {
return delegateInjector.getElements();
}
+ @Override
+ public ListMultimap, InjectionPoint> getAllMembersInjectorInjectionPoints() {
+ return delegateInjector.getAllMembersInjectorInjectionPoints();
+ }
+
@Override
public Provider getProvider(Key key) {
throw new UnsupportedOperationException(
diff --git a/core/src/com/google/inject/internal/MembersInjectorStore.java b/core/src/com/google/inject/internal/MembersInjectorStore.java
index 6328902ddf..4a2f2ec5c6 100644
--- a/core/src/com/google/inject/internal/MembersInjectorStore.java
+++ b/core/src/com/google/inject/internal/MembersInjectorStore.java
@@ -16,7 +16,10 @@
package com.google.inject.internal;
+import static com.google.common.collect.ImmutableListMultimap.flatteningToImmutableListMultimap;
+
import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.google.inject.ConfigurationException;
@@ -26,6 +29,7 @@
import com.google.inject.spi.TypeListenerBinding;
import java.lang.reflect.Field;
import java.util.List;
+import java.util.Map.Entry;
import java.util.Set;
/**
@@ -133,4 +137,11 @@ ImmutableList getInjectors(
}
return ImmutableList.copyOf(injectors);
}
+
+ ImmutableListMultimap, InjectionPoint> getAllInjectionPoints() {
+ return cache.asMap().entrySet().stream()
+ .collect(
+ flatteningToImmutableListMultimap(
+ Entry::getKey, entry -> entry.getValue().getInjectionPoints().stream()));
+ }
}
diff --git a/core/test/com/google/inject/spi/InjectorSpiTest.java b/core/test/com/google/inject/spi/InjectorSpiTest.java
index 91a0bd5851..e592fcd04f 100644
--- a/core/test/com/google/inject/spi/InjectorSpiTest.java
+++ b/core/test/com/google/inject/spi/InjectorSpiTest.java
@@ -2,6 +2,8 @@
import static com.google.common.truth.Truth.assertThat;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Multimap;
import com.google.inject.AbstractModule;
import com.google.inject.Asserts;
import com.google.inject.Binding;
@@ -317,4 +319,79 @@ private static class Baz {
@Inject
Provider fooP;
}
+
+ public void testGetAllMembersInjectorInjectionPoints_injectMembers_returned() {
+ Injector injector =
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Integer.class).toInstance(42);
+ bind(String.class).toInstance("bar");
+ }
+ });
+ injector.injectMembers(new ClassWithInjectableField("foo"));
+ Multimap, InjectionPoint> injectionPoints =
+ injector.getAllMembersInjectorInjectionPoints();
+ TypeLiteral expectedTypeLiteral =
+ TypeLiteral.get(ClassWithInjectableField.class);
+ assertThat(injectionPoints.keySet()).containsExactly(expectedTypeLiteral);
+ Key> actualDependencyKey =
+ Iterables.getOnlyElement(
+ Iterables.getOnlyElement(injectionPoints.get(expectedTypeLiteral))
+ .getDependencies())
+ .getKey();
+ assertEquals(Key.get(Integer.class), actualDependencyKey);
+ }
+
+ public void testGetAllMembersInjectorInjectionPoints_getInstance() {
+ Injector injector =
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Integer.class).toInstance(42);
+ bind(String.class).toInstance("bar");
+ }
+ });
+ injector.getInstance(ClassWithInjectableField.class);
+ Multimap, InjectionPoint> injectionPoints =
+ injector.getAllMembersInjectorInjectionPoints();
+ assertThat(injectionPoints).isEmpty();
+ }
+
+ public void testGetAllMembersInjectorInjectionPoints_getInstanceAndInjectMembers() {
+ Injector injector =
+ Guice.createInjector(
+ new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Integer.class).toInstance(42);
+ bind(String.class).toInstance("bar");
+ }
+ });
+ injector.injectMembers(new ClassWithInjectableField("foo"));
+ injector.getInstance(ClassWithInjectableField.class);
+ Multimap, InjectionPoint> injectionPoints =
+ injector.getAllMembersInjectorInjectionPoints();
+ TypeLiteral expectedTypeLiteral =
+ TypeLiteral.get(ClassWithInjectableField.class);
+ assertThat(injectionPoints.keySet()).containsExactly(expectedTypeLiteral);
+ Key> actualDependencyKey =
+ Iterables.getOnlyElement(
+ Iterables.getOnlyElement(injectionPoints.get(expectedTypeLiteral))
+ .getDependencies())
+ .getKey();
+ assertEquals(Key.get(Integer.class), actualDependencyKey);
+ }
+
+ private static class ClassWithInjectableField {
+
+ @Inject
+ ClassWithInjectableField(String name) {}
+
+ @Inject private Integer instanceField;
+
+ @Inject private static Double staticField;
+ }
}