web-dev-qa-db-fra.com

Passer le paramètre au constructeur avec Guice

J'ai une usine comme ci-dessous,

public final class Application {

    private static IFoo foo;

    public static IFoo getFoo(String bar)
    {
        // i need to inject bar to the constructor of Foo
        // obvious i have to do something, not sure what
        Injector injector = Guice.createInjector();
        logger = injector.getInstance(Foo.class);
        return logger;              
    }

}

Voici la définition de Foo:

class Foo
{
   Foo(String bar)
   {

   }

}

D'ACCORD. Je ne sais pas comment je peux passer ce paramètre au constructeur Foo avec Guice?

Des idées?

31
DarthVader

Toutes les réponses "Guice Constructor Parameter" semblent être incomplètes d'une manière ou d'une autre. Voici une solution complète, y compris l'utilisation:

interface FooInterface{
  String getFooName();
}

// Annoter le constructeur et les paramètres assistés sur la classe d'implémentation

class Foo implements FooInterface {
   String bar;

   @Inject
   Foo(@Assisted String bar)
   {
      this.bar = bar;
   }

   // return the final name
   getFooName(){
     return this.bar;
   }

}

// Créez une interface d'usine avec une méthode create () qui ne prend que les paramètres assistés.

// L'interface FooFactory n'a pas de classe d'implémentation explicite (Guice Magic)

interface FooFactory{
   Foo create(String bar);
}

// Liez cette fabrique à un fournisseur créé par AssistedInject

binderModule implements Module{

 void configure(Binder binder) {
   binder.install(new FactoryModuleBuilder()
         .implement(FooInterface.class, Foo.class)
         .build(FooFactory.class));
 }
}

// Maintenant, utilisez-le:

class FooAction{

   @Inject private FooFactory fooFactory;

   doFoo(){
      // Send bar details through the Factory, not the "injector" 
      Foo f = fooFactory.create("This foo is named bar. How lovely!");
      f.getFooName(); // "This foo is named bar. How lovely!"
   }
}

Beaucoup d'aide ici: https://google.github.io/guice/api-docs/latest/javadoc/index.html?com/google/inject/assistedinject/FactoryModuleBuilder.html

52
DougA

Ce que vous cherchez probablement, c'est d'utiliser une usine Guice. Particulièrement facile avec la fonctionnalité AssistedInject , mais ils ont un exemple manuel en haut de la page . Le raccourci pour l'exemple manuel est que vous obtenez l'usine sous la méthode non statique getFoo que vous passez tous les paramètres dont vous avez besoin et construisez l'objet à partir de là.

Cela ne fonctionnera pas directement si vous avez une interception de méthode dans Foo, mais cela fonctionnera dans de nombreux autres cas.

Pour utiliser AssistedInject, qui pour moi a une sémantique quelque peu plus propre et signifie moins de câblage manuel, vous aurez besoin de l'extension guice-aidinject dans le chemin de classe, puis lors de la création de Foo (enfin, FooImpl, nous devrions utiliser des interfaces):

@Inject
public FooImpl(@Assisted String bar)
{
    this.baz = bar;
}

Ensuite, vous créez une interface FooFactory:

public interface FooFactory {
    public Foo create(String bar);
}

Puis dans votre module guice:

install(new FactoryModuleBuilder()
    .implement(Foo.class, FooImpl.class)
    .build(FooFactory.class));

Vous pouvez consulter le javadoc pour FactoryModuleBuilder pour des exemples avec des usines plus complexes.

8
David H. Clements

Je sais que c'est du vieux fil, mais je viens de frapper le problème moi-même aujourd'hui. Je n'ai besoin que de deux ou maximum trois instances différentes de 'Foo' et je ne voulais vraiment pas écrire tout le code bolierplate de Factory. Avec un peu de recherche sur Google, j'ai trouvé cela Stubbisms - Tony’s Weblog Je suggérerais cette solution qui est parfaite si vous savez exactement de quelles instances vous avez besoin.

Dans le module Guice:

    bind(Foo.class).annotatedWith(Names.named("firstFoo")).toProvider(new Provider<Foo>() {
        @Override
        public Foo get() {
            return new FooImpl("topic A");
        }
    });
    bind(Foo.class).annotatedWith(Names.named("secondFoo")).toProvider(new Provider<Foo>() {
        @Override
        public Foo get() {
            return new FooImpl("topic B");
        }
    });

Ou en Java 8:

    bind(Foo.class).annotatedWith(Names.named("firstFoo")).toProvider(() -> new FooImpl("first"));
    bind(Foo.class).annotatedWith(Names.named("secondFoo")).toProvider(() -> new FooImpl("second"));

Et en constructeur de votre service où vous avez besoin d'instances Foo:

@Inject
public MyService (
    @Named("firstFoo") Foo firstFoo,
    @Named("secondFoo") Foo secondFoo) {
}

Et Foo dans mon cas:

public class FooImpl implements Foo {

    private String name;

    public FooImpl(String name) {
        this.name = name;
    }

    @Override
    public String getName() {
        return name;
    }
}

J'espère que cela aide quelqu'un.

7
NejcT

Si cette classe est une fabrique, ce devrait être un objet géré par Guice, ayant une méthode getFoo non statique, et la méthode getFoo utiliserait simplement

new Foo(bar)

Guice n'a pas besoin d'instancier chaque classe.

Voir aussi AssistedInject , pour éviter de créer vous-même cette usine et laissez Guice en créer une pour vous.

1
JB Nizet