Domanda

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

  1. extend assisted injection (via copy and paste) to add my functionality
  2. Write something very similar to assisted injection that does it for me
  3. write a lot of boilerplate that does this without guices help

I'm looking for a solution along the lines of:

  1. Some guice configuration or pattern that does this
  2. Some extension that does this
  3. Documentation/examples of places I can look that will help me write this myself
  4. Alternate patterns for the example app to accomplish what I want to do
È stato utile?

Soluzione

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);
    
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top