如何配置Guice,它注入类我想要的绑定?

时间:2012-12-10 19:27:08

标签: java guice

我有以下情况:

public class User {

@Inject
private MusicInterface movie;

}

public interface MusicInterface {
public void getTitle();
}

并拥有Guice配置类:

public class Module extends AbstractModule {

    @Override
    protected void configure() {

        bind(MusicInterface.class).annotatedWith(Names.named("rock")).to(RockMusic.class);
        bind(MusicInterface.class).annotatedWith(Names.named("pop")).to(PopMusic.class);
        bind(User.class).annotatedWith(Names.named("user1").to(User.class);
        bind(User.class).annotatedWith(Names.named("user2").to(User.class);
    }
}

我的问题是: 如何向用户类注入我想要的音乐绑定?

例如:

在我的主要内容我希望User1的注入类名为rock:

Injector injector = Guice.createInjector(new Module());

User user1 = injector.getInstance(Key.get(User.class,Names.named("user1")));

让我们说user1有属性RockMusic.class,稍后我希望用流行音乐获得user2:

User user2 = injector.getInstance(Key.get(User.class,Names.named("user2")));

这个类将具有PopMusic.class属性。

我该怎么做?

我知道我可以在User类中使用注释@Named(...),但在我的情况下它不是解决方案。

2 个答案:

答案 0 :(得分:2)

听起来你要做的就是根据用户的注释绑定不同种类的音乐。这也称为the Robot Legs problem,但您可以将“Leg”替换为“User”,将“Foot”替换为“Music”。这可以在Guice中使用Private Modules,但您可能会发现更容易创建UserFactory。我会告诉你两种方式。

首先, Private Module方式。私有模块隐藏了来自外部世界的所有绑定,因此您可以暴露某些绑定而不会发生其他绑定。

public class MyModule extends AbstractModule {
  @Override protected void configure() {
    install(privateModuleFor("user1", RockMusic.class));
    install(privateModuleFor("user2", PopMusic.class));
  }

  private static Module privateModuleFor(
      final String userName, final Class<? extends MusicInterface> musicClass) {
    return new PrivateModule() {
      @Override protected void configure() {
        // Bind the annotated user.
        bind(User.class).annotatedWith(Names.named(userName)).to(User.class);
        // Now bind the music class as if it's the only music class ever.
        bind(MusicInterface.class).to(musicClass);
        // The above two bindings are hidden, and available only in this module,
        // so Guice doesn't complain about two different competing
        // MusicInterfaces. In order to get access to the User binding
        // outside the module, expose it.
        expose(User.class).annotatedWith(Names.named(userName));
      }
    };
  }
}

现在,您可以请求@Named("user1") User让喜欢摇滚音乐的用户。当然,您不必像我对方法那样组织它:如果您愿意,您仍然可以将MusicInterface的类型绑定到名称,然后将无注释的MusicInterface绑定到您的Key.get(MusicInterface.class, yourMusicTypeHere) PrivateModule,但除非您在其他地方需要名为MusicInterfaces,否则您可以跳过该步骤。

另一种方式是跳过使用Guice进行此绑定。 Guice是一个很好的解决方案,但对于一些复杂的绑定情况,它可能比设置它的价值更麻烦。您可以选择创建一个轻量级UserFactory,为您选择正确类型的音乐。 (ImmutableMap来自Guava。)

public class UserFactory {
  private Map<String, Class<? extends MusicInterface>> musicMap =
      ImmutableMap.builder()
        .put("user1", RockMusic.class)    // You could also put instances here
        .put("user2", PopMusic.class)     // and skip using Guice entirely!
        .build();

  // Never inject Injector unless you don't know what you need until runtime,
  // which is exactly what is happening here.
  @Inject private Injector injector;
  @Inject private Provider<Dependency> someOtherUserDependency;

  public User createUser(String musicType) {
    Class<? extends MusicInterface> clazz = musicMap.get(musicType);
    return new User(injector.getInstance(clazz), someOtherUserDependency.get());
  }
}

现在,您可以通过注入UserFactory并致电userFactory.create()来获取您的用户。当然,也许您的用户需要十几个其他注入的依赖项。在这种情况下,第一个选项看起来不错,或者您可以调查assisted injection。所有这些都是可行的选择,因此您需要考虑哪一种是适合您情况的灵活性和可读性的正确组合。

希望有所帮助!

答案 1 :(得分:0)

鉴于@Named不是一个选项我认为你也不能使用Binding Annotations。您可以向喷油器询问:

Injector injector = Guice.createInjector(new Module());
MusicInterface musicInterface = injector.getInstance(Key.get(String.class, Names.named("rock"))