If you have a ton of settings, and want to avoid creating a new binding annotation for each of them, you might try putting them in an enum and using that enum in a common binding annotation. This might be a little complex of a solution, but it may also save the boilerplate you're trying to avoid.
With this way, you can match object references (IDE-friendly) instead of Strings (slow and brittle), and still only create one binding annotation.
public enum Config {
DB_NAME("db_name"),
DB_HOST("db_host_name_specified_in_file"),
SOME_NUMBER("some_number"),
;
private final String propertyName;
private Config(String propertyName) {
this.propertyName = propertyName;
}
public String getPropertyName() {
return propertyName;
}
public InjectConfig annotation() {
// Create an implementation of InjectConfig for ease of binding.
return new InjectConfig() {
@Override public Class<? extends Annotation> annotationType() {
return InjectConfig.class;
}
@Override public Config value() {
return Config.this;
}
@Override public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (!(obj instanceof InjectConfig)) {
return false;
}
return value() == ((InjectConfig) obj).value();
}
/** @see Annotation#hashCode */
@Override public int hashCode() {
return (127 * "value".hashCode()) ^ value().hashCode();
}
};
}
@Retention(RetentionPolicy.RUNTIME)
@BindingAnnotation
public static @interface InjectConfig {
Config value();
}
}
Now you can iterate through and bind each one in a loop:
public class YourModule extend AbstractModule {
@Override public void configure() {
// You can get a Provider in a Module as long as you
// don't call get() before the injector exists.
Provider<Settings> settingsProvider = binder().getProvider(Settings.class);
for (Config config : Config.values()) {
String propertyName = config.getPropertyName();
// Guice's TypeConverter will convert Strings to the right type.
bind(String.class).annotatedWith(config.annotation()).toProvider(
new GetValueFromSettingsProvider(settingsProvider, propertyName));
}
}
}
And inject only what you need, directly:
/** Your constructor */
YourClass(@InjectConfig(DB_USER) String user,
@InjectConfig(SOME_NUMBER) int number) { }
I didn't get a chance to test this, but as far as I know it should work. Given your specific settings use-case you may need to massage the GetValueFromSettingsProvider
you write, or write an overridable getConfigValueFromSettings
method in the enum. Remember, though, that one way or another you're still going to need to store the (enum key, property name in file, property type) tuple, and it seems like an Enum is the best way to manage that programmatically.