Dynamically generating injections by annotation

前端 未结 1 1870
南方客
南方客 2021-01-25 12:10

Suppose I have a class that looks like this:

public class MyClass {
    @Inject
    public MyClass(@Foo(\"whatever\") Bar dependency) {
        // ...
    }
}
         


        
1条回答
  •  生来不讨喜
    2021-01-25 12:32

    What you're describing isn't possible through normal Guice: Providers are intended to be zero-argument pure functions and there's no way to plumb the injection site information into them as you would a flexible callback function.

    You can approximate what you want, though, two different ways:

    • If you know every single possible value of @Foo's parameter, you can make your @Foo a binding annotation and bind it by providing a Annotation-compatible equals and hashCode. This provides the most intuitive experience: You can do anything with your @Foo you can do with any other type, such as using @Foo in constructors or injecting @Foo("value") Provider barProvider.

      @Override public void configure() {
        for (String value : PREDEFINED_VALUES) {
          bind(Bar.class)
              .annotatedWith(new FooImpl(value))
              .toProvider(new BarProvider(value));
        }
      }
      
    • If you want @Foo to work for arbitrary parameters, you'll need to extend Guice with custom injections. This won't work for constructor injection or alongside any other @Inject annotations, but it will allow you to inspect types after Guice injection is finished to augment them as you see fit (e.g. detecting and reacting to @Foo annotations on fields).

      See the example in the Guice docs for more information there.

    Internally, Guice's core is effectively a Map, where a Key represents a pair of a possibly-parameterized type and an optional binding annotation. The former binding annotation trick works because Guice can map your injection request to a Provider all on its own, where the latter skips Guice's map so you can inspect/construct/inject instances all on your own.


    If you're willing to skip the annotation part of your solution, you could inject a BarProvider or BarFactory that exposes a forFoo(String) method, which would give you consistent injection without knowing all your String values ahead of time. This would allow you to use assisted injection or AutoFactory to generate your factory (if you want to generate one instance per call), or let you write a straightforward factory yourself for added flexibility.

    public class MyClass {
        private final Bar dependency;
    
        @Inject
        public MyClass(BarProvider barProvider) {
            dependency = barProvider.forFoo("whatever");
            // ...
        }
    }
    

    0 讨论(0)
提交回复
热议问题