Guice: czy możliwe jest wstrzykiwanie modułów?

Mam moduł, który wymaga Depedency. Czy istnieje sposób na wstrzyknięcie samych modułów? Zdaję sobie sprawę, że to sytuacja z kurczakiem i jajkiem...

Przykład:

public class MyModule implements Module {

    private final Dependency d_;

    @Inject public MyModule(Dependency d) {
        d_ = d;
    }

    public void configure(Binder b) { }

    @Provides Something provideSomething() {
        // this requires d_
    }
}

Przypuszczam, że w tym przypadku rozwiązaniem byłoby przekształcenie metody @Provides w pełnowartościową klasę Provider<Something>. Jest to wyraźnie uproszczony przykład; kod, z którym mam do czynienia, ma wiele takich metod @Provides, więc cięcie ich na poszczególne klasy Provider<...> i wprowadzenie modułu do ich konfiguracji dodaje spora ilość bałaganu - a myślałem, że Guice ma na celu zmniejszenie bałaganu?

Być może jest to odbicie mojej względnej noobyness do Guice, ale natknąłem się na kilka uczciwych przypadków, w których byłem kuszony, aby zrobić powyższe. Muszę coś przeoczyć...

Author: sxc731, 2011-03-31

4 answers

@Provides metody mogą przyjmować zależności jako parametry, podobnie jak parametry do konstruktora lub metody z adnotacją @Inject:

@Provides Something provideSomething(Dependency d) {
   return new Something(d); // or whatever
}

Jest to udokumentowane tutaj , choć być może można by to bardziej wyróżnić.

 27
Author: ColinD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-31 19:00:47

Używanie metod provider lub @ Provides jest świetne, jeśli potrzebujesz zależności do ręcznego konstruowania obiektu. Co jednak zrobić, jeśli potrzebujesz czegoś, co pomoże Ci zdecydować, jak skonfigurować same wiązania? Okazuje się, że możesz użyć Guice do stworzenia (i skonfigurowania) swojego modułu.

Oto (wymyślony) przykład. Po pierwsze, moduł, który chcemy skonfigurować:

/**
 * Creates a binding for a Set<String> which represents the food in a pantry.
 */
public class PantryModule extends AbstractModule {
  private final boolean addCheese;

  @Inject
  public ConditionalModule(@Named("addCheese") boolean addCheese) {
    this.addCheese = addCheese;
  }

  @Override
  protected void configure() {
    Multibinder<String> pantryBinder = Multibinder
      .newSetBinder(binder(), String.class);

    pantryBinder.addBinding().toInstance("milk");

    if (addCheese) {
      pantryBinder.addBinding().toInstance("cheese");
    }

    pantryBinder.addBinding().toInstance("bread");
  }
}

PantryModule oczekuje, że zostanie wprowadzona wartość logiczna, aby zdecydować, czy powinien zawierać ser w spiżarnia.

Następnie użyjemy Guice do konfiguracji modułu:

// Here we use an anonymous class as the "configuring" module. In real life, you would 
// probably use a standalone module.
Injector injector = Guice.createInjector(new AbstractModule() {
  @Override
  protected void configure() {
    // No cheese please!
    bindConstant().annotatedWith(Names.named("addCheese")).to(false);
    bind(PantryModule.class);
  }
});

Module configuredConditionalModule = injector.getInstance(PantryModule.class);
Teraz, gdy mamy skonfigurowany moduł, zaktualizujemy nasz wtryskiwacz, aby go użyć...
//...continued from last snippet...
injector = injector.createChildInjector(configuredConditionalModule);

I na koniec otrzymamy zestaw sznurków, które reprezentują naszą spiżarnię:

//...continued from last snippet...
Set<String> pantry = injector.getInstance(new Key<Set<String>>() {});

for (String food : pantry) {
  System.out.println(food);
}

Jeśli połączysz wszystkie elementy w główną metodę i uruchomisz ją, otrzymasz następujące wyjście:

milk
bread

Jeśli zmienisz Wiązanie na wartość logiczną "addCheese" na true, otrzymasz:

milk
cheese
bread

Ta technika jest fajna, ale prawdopodobnie przydatne tylko wtedy, gdy masz kontrolę nad instancją wtryskiwacza i tylko wtedy, gdy moduł wymaga złożonych zależności. Nie wiem, znalazłem prawdziwą potrzebę tego na prawdziwym projekcie w pracy. Jeśli tak, to ktoś inny też.

 9
Author: Jim Hurne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-20 18:48:35

Pytanie jest już dobrze odpowiedział, Ale chciałem tylko dodać odmianę do przykładu Colina:

class MyModule extends AbstractModule { 
  public void configure() {
    bind(Something.class).toProvider(new Provider<Something>() {
       @Inject Dependency d;
       Something get() { return d.buildSomething(); }
    }
  }
}

Podejście metody @ Zapewnia jest jaśniejsze niż to, co mam powyżej w tym prostym przypadku, ale odkryłem, że tworzenie instancji rzeczywistego dostawcy może być przydatne również w niektórych sytuacjach. Coś, co ukradłem z listy mailingowej; nie przyszło by mi do głowy samo ;)

 5
Author: CJC,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-02-01 00:58:15

Jaki jest problem z inicjalizacją modułu przez wywołanie new MyModule(d) lub przez utworzenie Provider<Something> to ma zastrzyk Injector? Wydaje się, że są to standardowe sposoby radzenia sobie z tego rodzaju problemem. Jak już wspomniano, można również używać metod @Provides z argumentami.

Jeśli zależność jest opcjonalna, możesz utworzyć moduł, a następnie wywołać setter, aby zainicjalizować wartość w razie potrzeby (np. com.google.inject.persist.jpa.JpaPersistModule robi to z właściwościami, używając new JpaPersistModule(String), aby załadować poprawną wartość konfiguracja).

W Przeciwnym Razie przypuszczam, że może być to możliwe (a następnie zadzwonić createChildInjector(Modules... modules)), ale prawie zawsze wolałbym jedno z innych podejść do tego.

 0
Author: David H. Clements,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-03-31 21:51:28