Guice @Provides Methods vs Provider Classes

后端 未结 2 978
轮回少年
轮回少年 2020-12-10 14:12

I\'m working on a fairly large project that has a lot of injections. We\'re currently using a class that implements Provider for each injection that needs one,

相关标签:
2条回答
  • 2020-12-10 14:56

    As far as I know, they're exactly equivalent for most simple cases.

    /**
     * Class-style provider.
     * In module: bind(Foo.class).annotatedWith(Quux.class).toProvider(MyProvider.class);
     */
    class MyProvider implements Provider<Foo> {
      @Inject Dep dep;  // All sorts of injection work, including constructor injection.
    
      @Override public Foo get() {
        return dep.provisionFoo("bar", "baz");
      }
    }
    
    /**
     * Method-style provider. configure() can be empty, but doesn't have to be.
     */
    class MyModule extends AbstractModule {
      /** Name doesn't matter. Dep is injected automatically. */
      @Provides @Quux public Foo createFoo(Dep dep) {
        return dep.provisionFoo("bar", "baz");
      }
    
      @Override public void configure() { /* nothing needed in here */ }
    }
    

    In either style, Guice lets you inject Foo and Provider<Foo>, even if the key is bound to a class or instance. Guice automatically calls get if getting an instance directly and creates an implicit Provider<Foo> if one doesn't exist. Binding annotations work in both styles.

    The main advantage of @Provides is compactness, especially in comparison to anonymous inner Provider implementations. Note, however, that there might be a few cases where you'd want to favor Provider classes:

    • You can create your own long-lived Provider instances, possibly with constructor parameters, and bind keys to those instances instead of to class literals.

      bind(Foo.class).toProvider(new FooProvisioner("bar", "baz"));
      
    • If you're using a framework compatible with JSR 330 (javax.inject), you can easily bind to javax.inject.Provider classes or instances. com.google.inject.Provider extends that interface.

      bind(Foo.class).toProvider(SomeProviderThatDoesntKnowAboutGuice.class);
      
    • Your Provider may be complex enough to factor into its own class. Depending on how you've structured your tests, it may be easier to test your Provider this way.

    • Providers can extend abstract classes. It may not be easy or intuitive to do this with @Provides methods.

    • You can bind several keys to the same Provider directly. Each @Provides method produces exactly one binding, though you could bind other keys to the key (@Quux Foo here) and let Guice do a second lookup.

    • Providers are easy to decorate or wrap, if you wanted to (for instance) cache or memoize instances without using Guice scopes or bindings.

      bind(Foo.class).toProvider(new Cache(new FooProvisioner("bar", "baz")));
      

    IMPORTANT: Though this is a good strategy for classes that Guice can't create, bear in mind that Guice can automatically create and inject a Provider<T> for any T that you bind in any way, including to a class name, key, or instance. No need to create an explicit provider unless there's actual logic of your own involved.

    0 讨论(0)
  • 2020-12-10 14:58

    There is also a difference in how classes are instantiated. For Eg :

    public class GumProvider implements Provider<Gum> {
    
      public Gum get() {
        return new Gum();
      }
    }
    

    public class GumModule extends AbstractModule {
    
        protected void configure() {
    
            bind(Gum.class).toProvider(GumProvider.class);
            //bind(Gum.class).to(GumballMachine.class);
        }
    }
    

    public class GumballMachine {
    
        @Inject
        private Provider<Gum> gumProvider;
    
        Gum gum;
    
    
        public Gum dispense() {
            return gumProvider.get();
        }
    }
    

    public class App {
    
        public static void main(String[] args) {
          // TODO Auto-generated method stub
          Injector injector = Guice.createInjector(new GumModule());
          GumballMachine m = injector.getInstance(GumballMachine.class);
          System.out.println(m.dispense());
          System.out.println(m.dispense());
    
    
        }
    
    }
    

    This would create an Instance of Gum Per Invocation. Whereas if @Provides were used the same Instance of Gum would be passed to both the Injectors

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