Am I able to have an injector to use its providers later and request then after createChildInjector
?
Here's an example:
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;
}
}
I think it is fair for the UserProperties module to know how to provide the target user based on a target user name and a list of users but my application in this example doesn't need a target user and it just want to know "MyName".
So basically, I would like to have a module that would know how to behave if my application asks for this information, but if it does not, it will still be able to compile and run with as much information as I have provided.
Does it make sense?
You can do this with 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);
}
}
Notes:
Optional<User>
main()
method to use the child injector to get the user name, but you could get it from either injector@Named
(plus it allows a place to add Javadoc)