From a0b4d160a81de97712c156b07053002a97e66373 Mon Sep 17 00:00:00 2001 From: Jesse Van Beurden Date: Thu, 14 Mar 2024 08:30:48 -0700 Subject: [PATCH] Add a moduleInstance parameter to the ModuleAnnotatedMethodScanner. This is useful in some cases where the scanner bindings need more information from the module currently being scanned. PiperOrigin-RevId: 615788414 --- .../internal/ProviderMethodsModule.java | 3 +- .../com/google/inject/spi/ElementSource.java | 2 +- .../spi/ModuleAnnotatedMethodScanner.java | 43 ++++++++++++++++- .../spi/ModuleAnnotatedMethodScannerTest.java | 48 +++++++++++++++++++ 4 files changed, 92 insertions(+), 4 deletions(-) diff --git a/core/src/com/google/inject/internal/ProviderMethodsModule.java b/core/src/com/google/inject/internal/ProviderMethodsModule.java index 14c2233f2a..ffde0cadcc 100644 --- a/core/src/com/google/inject/internal/ProviderMethodsModule.java +++ b/core/src/com/google/inject/internal/ProviderMethodsModule.java @@ -295,7 +295,8 @@ private ProviderMethod createProviderMethod( Key key = getKey(errors, returnType, method, method.getAnnotations()); boolean prepareMethodError = false; try { - key = scanner.prepareMethod(binder, annotation, key, point); + key = + scanner.prepareMethod(binder, annotation, key, point, isStaticModule() ? null : delegate); } catch (Throwable t) { prepareMethodError = true; binder.addError(t); diff --git a/core/src/com/google/inject/spi/ElementSource.java b/core/src/com/google/inject/spi/ElementSource.java index 30b762949a..076ff5f6a2 100644 --- a/core/src/com/google/inject/spi/ElementSource.java +++ b/core/src/com/google/inject/spi/ElementSource.java @@ -47,7 +47,7 @@ public final class ElementSource { final ElementSource originalElementSource; /** - * Wheather the originalElementSource was set externaly (untrusted) or by Guice internals + * Whether the originalElementSource was set externally (untrusted) or by Guice internals * (trusted). * *

External code can set the originalElementSource to an arbitrary ElementSource via diff --git a/core/src/com/google/inject/spi/ModuleAnnotatedMethodScanner.java b/core/src/com/google/inject/spi/ModuleAnnotatedMethodScanner.java index 02d1a8f87a..ba3bffa108 100644 --- a/core/src/com/google/inject/spi/ModuleAnnotatedMethodScanner.java +++ b/core/src/com/google/inject/spi/ModuleAnnotatedMethodScanner.java @@ -20,6 +20,7 @@ import com.google.inject.Key; import java.lang.annotation.Annotation; import java.util.Set; +import javax.annotation.Nullable; /** * Allows extensions to scan modules for annotated methods and bind those methods as providers, @@ -53,7 +54,45 @@ public abstract class ModuleAnnotatedMethodScanner { *

If {@code injectionPoint} represents an {@code abstract} method, {@code null} must be * returned from this method. This scanner can use {@code binder} to bind alternative bindings in * place of the abstract method. + * + * @deprecated Prefer overriding the overload that takes `Object moduleInstance` instead. If not + * overridden, that method will invoke this one to aid in backwards compatibility. + */ + @Deprecated + public Key prepareMethod( + Binder binder, Annotation annotation, Key key, InjectionPoint injectionPoint) { + throw new IllegalStateException( + "'prepareMethod' not implemented, one override must be implemented."); + } + + /** + * Prepares a method for binding. This {@code key} parameter is the key discovered from looking at + * the binding annotation and return value of the method. Implementations can modify the key to + * instead bind to another key. For example, Multibinder may want to change + * {@code @ProvidesIntoSet String provideFoo()} to bind into a unique Key within the multibinder + * instead of binding {@code String}. + * + *

The injection point and annotation are provided in case the implementation wants to set the + * key based on the property of the annotation or if any additional preparation is needed for any + * of the dependencies. The annotation is guaranteed to be an instance of one the classes returned + * by {@link #annotationClasses}. + * + *

Returning null will cause Guice to skip this method, so that it is not bound to any key. + * + *

If {@code injectionPoint} represents an {@code abstract} method, {@code null} must be + * returned from this method. This scanner can use {@code binder} to bind alternative bindings in + * place of the abstract method. + * + *

The {@code moduleInstance} parameter contains the Module instance that is currently being + * scanned, which may be null if the methods were discovered on Module class objects (as opposed + * to module instances). */ - public abstract Key prepareMethod( - Binder binder, Annotation annotation, Key key, InjectionPoint injectionPoint); + public Key prepareMethod( + Binder binder, + Annotation annotation, + Key key, + InjectionPoint injectionPoint, + @Nullable Object moduleInstance) { + return prepareMethod(binder, annotation, key, injectionPoint); + } } diff --git a/core/test/com/google/inject/spi/ModuleAnnotatedMethodScannerTest.java b/core/test/com/google/inject/spi/ModuleAnnotatedMethodScannerTest.java index aa2e458ebe..c23b6f825f 100644 --- a/core/test/com/google/inject/spi/ModuleAnnotatedMethodScannerTest.java +++ b/core/test/com/google/inject/spi/ModuleAnnotatedMethodScannerTest.java @@ -1035,6 +1035,54 @@ String privateString() { .isEqualTo(scanner); } + static class DelegatingScanner extends ModuleAnnotatedMethodScanner { + Object delegate = null; + + @Override + public Set> annotationClasses() { + return ImmutableSet.of(TestProvides.class); + } + + @Override + public Key prepareMethod( + Binder binder, + Annotation annotation, + Key key, + InjectionPoint injectionPoint, + Object delegate) { + this.delegate = delegate; + return key; + } + } + + @Test + public void scannerPropagatesTheDelegateObject() { + DelegatingScanner scanner = new DelegatingScanner(); + AbstractModule module = + new AbstractModule() { + @TestProvides + String provideString() { + return "foo"; + } + }; + Guice.createInjector(scannerModule(scanner), module); + assertThat(scanner.delegate).isSameInstanceAs(module); + } + + static class StaticProvider extends AbstractModule { + @TestProvides + static String provideString() { + return "foo"; + } + } + + @Test + public void staticScannerPropagatesNullDelegateObject() { + DelegatingScanner scanner = new DelegatingScanner(); + Guice.createInjector(ProviderMethodsModule.forModule(StaticProvider.class, scanner)); + assertThat(scanner.delegate).isNull(); + } + ModuleAnnotatedMethodScanner getSourceScanner(Binding binding) { return ((ElementSource) binding.getSource()).scanner; }