Yaron
Yaron

Reputation: 2143

Explicitly add binder dependency when binding to Guice provider instance

Say I want to create a general provider that returns the first item in a collection. Something like:

class P extends Provider<T> {
  private final Provider<Collection<T>> provider;

  FirstItemProvider(Provider<Collection<T>> provider) {
    this.provider = provider;
  }

  @Override
  public T get() {
    Collection<T> input = provider.get();
    Iterator<T> iter = input.iterator();
    return iter.hasNext() ? iter.next() : null;
  }
}

Now I have various such collections with different annotations and I want to add bindings to this provider, BUT, I also want to be able to traverse bindings with using DefaultBindingTargetVisitor and getDependencies().

I've tried something like:

bind(Key.get(type, annotation))
  .toProvider(new P(
     getProvider(
       Key.get(Types.collectionOf(type.getType()), annotation)
     )
  ));

This works fine in terms of binding, but the traversal doesn't wok as Guice doesn't detect the dependency.

I couldn't figure out any other way to bind to an annotated producer since it doesn't have an annotation on it.

Is there any way to do what I'm trying to do?

Thanks Yaron

Upvotes: 0

Views: 1713

Answers (1)

Tavian Barnes
Tavian Barnes

Reputation: 12922

This works for me:

import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import javax.inject.Provider;

import com.google.inject.AbstractModule;
import com.google.inject.Binder;
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.name.Names;
import com.google.inject.spi.Dependency;
import com.google.inject.spi.ProviderInstanceBinding;
import com.google.inject.spi.ProviderWithDependencies;

class FirstItemProvider<T> implements ProviderWithDependencies<T> {
    private final Provider<? extends Collection<? extends T>> collectionProvider;
    private final Key<? extends Collection<? extends T>> key;

    public FirstItemProvider(Binder binder, Key<? extends Collection<? extends T>> key) {
        this.collectionProvider = binder.getProvider(key);
        this.key = key;
    }

    @Override
    public T get() {
        return collectionProvider.get().iterator().next();
    }

    @Override
    public Set<Dependency<?>> getDependencies() {
        return Collections.<Dependency<?>>singleton(Dependency.get(key));
    }
}

class FooModule extends AbstractModule {
    @Override
    protected void configure() {
        Key<List<String>> listKey = new Key<List<String>>(Names.named("foo")){};
        Key<String> stringKey = new Key<String>(Names.named("foo")){};

        bind(listKey).toInstance(Arrays.asList("foo", "bar"));
        bind(stringKey).toProvider(new FirstItemProvider<>(binder(), listKey));
    }
}

public class Foo {
    public static void main(String[] args) {
        Injector injector = Guice.createInjector(new FooModule());

        Key<String> stringKey = new Key<String>(Names.named("foo")){};
        ProviderInstanceBinding<String> binding = (ProviderInstanceBinding<String>)injector.getBinding(stringKey);
        System.out.println(binding.getDependencies());
    }
}

The output is

[Key[type=java.util.List<java.lang.String>, [email protected](value=foo)]]

Upvotes: 2

Related Questions