42 votes

Passer un 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;              
    }

}

C'est la définition de Foo :

class Foo
{
   Foo(String bar)
   {

   }

}

OK. Je ne suis pas sûr de pouvoir passer ce paramètre au constructeur de Foo avec Guice ?

Des idées ?

67voto

DougA Points 315

Toutes les réponses à la question "Guice Constructor Parameter" semblent être incomplètes d'une certaine manière. Voici une solution complète, incluant l'utilisation et un visuel :

enter image description here

interface FooInterface {
    String getFooName();
}

// Annotez 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
    public String 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);
}

// Lier cette usine à un fournisseur créé par AssistedInject.

class BinderModule implements Module {

    public 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;

    public String doFoo() {
        // Send bar details through the Factory, not the "injector"
        Foo f = fooFactory.create("This foo is named bar. How lovely!");
        return 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

10voto

David H. Clements Points 1401

Ce que vous recherchez probablement, c'est d'utiliser une usine Guice. C'est particulièrement facile avec le AssistedInject mais ils ont une exemple de manuel en haut de la page . En résumé, pour l'exemple du manuel, vous obtenez l'usine sous une forme non statique. getFoo à laquelle vous passez les paramètres dont vous avez besoin et construisez l'objet à partir de là.

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

Pour utiliser AssistedInject qui, à mon avis, a une sémantique un peu plus propre et implique moins de câblage manuel, vous aurez besoin de l'option Injection assistée par un guide dans le classpath, alors lors de la création de l'extension Foo (bien, FooImpl nous devrions utiliser des interfaces) :

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

Ensuite, vous créez un FooFactory interface :

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

Ensuite, 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.

10voto

NejcT Points 80

Je sais qu'il s'agit d'un vieux sujet, mais j'ai moi-même rencontré ce problème aujourd'hui. Je n'ai besoin que de deux ou trois instances différentes de 'Foo' et je n'avais pas envie d'écrire tout le code bolierplate de Factory. En cherchant un peu sur Google, j'ai trouvé ceci Stubbismes - Le blog de Tony Je vous suggère 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 dans le constructeur de votre service où vous avez besoin des instances de 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 aidera quelqu'un.

2voto

JB Nizet Points 250258

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

new Foo(bar)

Toutes les classes n'ont pas besoin d'être instanciées par Guice.

Voir aussi Injection assistée Pour éviter de créer cette usine vous-même, laissez Guice en créer une pour vous.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X