I'd like to be able to discover/inject the name of the method that created an object via assisted injection into the object that was created.
An example of what I want to do:
// what I want guice to create the implementation for this
interface Preferences {
Preference<String> firstName();
Preference<String> lastName();
// other preferences possibly of other types
}
// my interfaces and classes
interface Preference<T> {
T get();
void set(T value);
}
class StringPreference implements Preference<String> {
private final Map<String, Object> backingStore;
private final String key;
@Inject StringPreference(@FactoryMethodName String key,
Map<String, Object> backingStore) {
this.backingStore = backingStore;
this.key = key;
}
public String get() { return backingStore.get(key).toString(); }
public void set(String value) { backingStore.put(key, value); }
}
// usage
public void exampleUsage() {
Injector di = // configure and get the injector (probably somewhere else)
Preferences map = di.createInstance(Preferences.class);
Map<String, Object> backingStore = di.createInstance(...);
assertTrue(backingStore.isEmpty()); // passes
map.firstName().set("Bob");
assertEquals("Bob", map.firstName().get());
assertEquals("Bob", backingStore.get("firstName"));
map.lastName().set("Smith");
assertEquals("Smith", map.lastName().get());
assertEquals("Smith", backingStore.get("lastName"));
}
Unfortunately the only ways I've thought of so far to implement this is to
I'm looking for a solution along the lines of:
Your real request, about injecting the creation context, is not possible and will not be possible in Guice. (direct link to bug)
A couple other ideas:
If your use-case can suffice with read-only properties, use Names.bindProperties
which will allow an entire Properties
instance (or Map<String, String>
) to be bound to constants with appropriate @Named
annotations. Like other bindConstant
calls, this will even cast to appropriate primitive types for you, or anything else you bind using convertToTypes
.
If you're just looking for an individual Map per injecting class, don't forget that you can write your own factory.
class PreferenceMapOracle {
private static final Map<Class<?>, Map<String, String>> prefMap =
Maps.newHashMap();
public Map<String, String> mapForClass(Class<?> clazz) {
if (prefMap.contains(clazz)) {
return prefMap.get(clazz);
}
Map<String, String> newMap = Maps.newHashMap();
prefMap.put(clazz, newMap);
return newMap;
}
}
class GuiceUser {
private final Map<String, String> preferences;
@Inject GuiceUser(PreferenceMapOracle oracle) {
preferences = oracle.mapForClass(getClass());
}
}
Nothing built into Guice will automatically reflect across your Preferences
interface and create a bean-style implementation where there was none. You could probably write your own clever framework with liberal use of dynamic proxy objects, or with a package that provides nifty reflective support like GSON. You will still need to provide those reflectively-created interfaces one way or another, but I could easily imagine a call like:
preferences = oracle.getPrefs(Preferences.class);