是否可以使用不完整的依赖树创建 Java Guice 依赖注入器?

Is it possible to create a Java Guice dependency injector with an incomplete dependency tree?

我能否让注入器稍后使用其提供程序并在 createChildInjector 之后请求? 这是一个例子:

class App {
  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new UserProperties());
    User user = new User("Some Name");
    injector.createChildInjector(new MyUserModule(user));
    String myName = injector.getInstance(Key.get(String.class, Names.named("MyName")));
    System.out.println(myName);
  }
}

@Value
class User {
  String name;
}

class UserProperties extends AbstractModule {

  @Provides
  @Singleton
  @Named("MyName")
  public String myName(User user) {
    return user.getName();
  }

  // won't be used because we aren't interested now in getting TargetUser
  // List<User> could also be in a separated module that get all users from database
  @Provides
  @Singleton
  @Named("TargetUser")
  public User targetName(@Named("TargetUserName") String name, List<User> users) {
    return users.stream().filter(user -> user.getName().equals(name)).findAny().get();
  }
}

@AllArgsConstructor
class MyUserModule extends AbstractModule {
  private final User user;

  @Singleton
  @Provides
  public User user() {
    return user;
  }
}

@AllArgsConstructor
class TargetUserModule extends AbstractModule {
  private final String name;

  @Provides
  @Singleton
  @Named("TargetUserName")
  public String targetUserName() {
    return name;
  }
}

我认为 UserProperties 模块知道如何根据目标用户名和用户列表提供目标用户是公平的,但我在此示例中的应用程序不需要目标用户,它只需要知道“我的名字”。

所以基本上,我希望有一个模块知道如果我的应用程序要求此信息时如何表现,但如果它不要求,它仍然能够编译并 运行我已经提供了很多信息。

有道理吗?

您可以使用 OptionalBinder (javadoc):

import javax.inject.Qualifier;

/** Binding annotation for the name (a String) of the current User. */
@Qualifier
@Target({ FIELD, PARAMETER, METHOD })
@Retention(RUNTIME)
@interface UserName {}

class App {
  public static void main(String[] args) {
    Injector injector = Guice.createInjector(new UserPropertyModule());
    User user = new User("Some Name");
    Injector childInjector = injector.createChildInjector(new MyUserModule(user));
    String myName = childInjector.getInstance(Key.get(String.class, UserName.class));
    System.out.println(myName);
  }
}

@Value
class User {
  String name;
}

class UserPropertyModule extends AbstractModule {

  @Override
  protected void configure() {
     OptionalBinder.newOptionalBinder(binder(), User.class)
         .setDefault().toProvider(DefaultUserProvider.class);
  }

  @Provides @UserName String provideUserName(User user) {
    return user.getName();
  }
}

class DefaultUserProvider implements Provider<User> {
  @Override
  public User get() {
    // Called to get the default user
  }
}

@AllArgsConstructor
class MyUserModule extends AbstractModule {
  private final User user;

  @Override
  protected void configure() {
     OptionalBinder.newOptionalBinder(binder(), User.class)
         .setBinding().toInstance(user);
  }
}

备注:

  • 以上假定您的主模块可以提供默认值。如果不能,则需要注入 Optional<User>
  • 我修改了你的 main() 方法以使用子注入器获取用户名,但你可以从任一注入器获取它
  • 我使用了绑定注释,因为它比 @Named 类型更安全(而且它允许添加 Javadoc 的地方)