2016-03-03 4 views
2

Я изучаю Google Guice. Вы знаете, как реализовать проблему «ноги робота»? Позвольте мне объяснить это на примере.Как использовать «футляр для ног робота» с помощью Google Guice?

Давайте предположим, что у меня есть некоторый класс называется Service:

@Singleton 
public class Service { 
    @Inject 
    Source source; 

} 

Интерфейс Source имеет две реализации:

public class SourceDatabase implements Source { 

} 

public class SourceFileSystem implements Source { 

} 

Мой модуль реализована следующим образом:

public class MyModule extends AbstractModule { 
    @Override 
    protected void configure() { 
     bind(Service.class).asEagerSingleton(); 
    } 
} 

Ну , Я хотел бы знать, возможно ли это:

public class MainClass {  

    @Inject @SomeAnnotation("database") 
    Service serviceWithADatabaseSource; 

    @Inject @SomeAnnotation("file-system") 
    Service serviceWithAFileSystemSource; 

} 

Существует ли аннотацию или связывание, которые позволяют мне сделать это, позвольте мне аннотировать член как serviceWithADatabaseSource, и это помогает Guice знать, что внутренний элемент source должен быть введен с SourceDatabase реализации?

Редактировать: Спасибо Даниэлю Мартину за то, что вы дали нам имя такого рода проблем на его комментарии.

+2

https://github.com/google/guice/wiki/FrequentlyAskedQuestions # how-do-i-build-two-like-but-little-different-trees-of-objects (То, что вы описали, это проблема «ноги робота») –

ответ

3

Как documented in the Guice Wiki, вам необходимо установить два PrivateModule s, каждый из которых предоставляет вам сервис с правой аннотацией.

public class MyModule extends AbstractModule { 
    @Override 
    protected void configure() { 
    install(new PrivateModule() { 
     @Override public void configure() { 
     // Bind Source to SourceDatabase. 
     bind(Source.class).to(SourceDatabase.class); 
     // Bind @Named("database") Service to Service. 
     bind(Service.class).annotatedWith(Names.named("database")) 
      .to(Service.class); 
     // Now expose @Named("database") Service without exposing 
     // either of the other two conflicting bindings. 
     expose(Service.class).annotatedWith(Names.named("database")); 
     } 
    }); 
    install(new PrivateModule() { 
     @Override public void configure() { 
     // Same as above. 
     bind(Source.class).to(SourceFileSystem.class); 
     bind(Service.class).annotatedWith(Names.named("file-system")) 
      .to(Service.class); 
     expose(Service.class).annotatedWith(Names.named("file-system")); 
     } 
    }); 
    } 
} 

Если модули не были экземплярами PrivateModule, эти привязки к источнику и службе конфликтуют друг с другом. Однако вместо этого каждое связывание наследует все публичные привязки от Инжектора, но только предоставляет @Named(...) Service внешнему миру. Таким образом, одна и та же реализация Service может вводить тот же не аннотированный Source, но при этом он возвращает разные полностью инъецируемые типы.

Также обратите внимание, что вы не сможете запросить Source или Service (без аннотации) за пределами PrivateModules, так как вы не установили привязку в любом не-частном модуле. Этого следует ожидать: привязки PrivateModule не должны конфликтовать с любыми публичными привязками и, не входя в одну из открытых привязок PrivateModule, Guice не будет знать, к какому Source или Service, чтобы вернуться.

Наконец, учитывая, что экземпляры модулей могут принимать параметры конструктора, это может быть хорошей идеей, чтобы извлечь два анонимных внутренних PrivateModules в именованный эквивалент:

public class MyModule extends AbstractModule { 
    @Override 
    protected void configure() { 
    install(new SourcePrivateModule(SourceDatabase.class, "database")); 
    install(new SourcePrivateModule(SourceFileSystem.class, "file-system")); 
    } 
} 
Смежные вопросы