是否可以使用不完整的依赖树创建 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 的地方)
我能否让注入器稍后使用其提供程序并在 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 的地方)