Different Annotations to Same Provider

425 views
Skip to first unread message

Bahri Gençsoy

unread,
Mar 9, 2011, 5:25:58 AM3/9/11
to google...@googlegroups.com

Hi, I am trying to achieve some binding with Guice 2.0. I did search the mailing list but couldn't find a similar problem/solution.

I am trying to bind different implementations of same interface. I want to discriminate them against their annotations.

These are my classes to be injected dependencies to, I do not want to change them a lot:

class SingleRunner {
    @Inject
    public void setExecutor(@Named("single") ExecutorService executor) {
        // ...
    }
}

class PoolRunner {
    @Inject
    public void setExecutor(@Named("pool") ExecutorService executor) {
        // ...
    }
}

This is the traditional approach to bind providers for these classes; creating to provider classes and bind them:

class SingleProvider implements Provider<ExecutorService> {
    @Inject
    private SomeOtherDependency other;

    public ExecutorService get() {
        return singleExecutorService;
    }

}

class PoolProvider implements Provider<ExecutorService> {
    @Inject
    private SomeOtherDependency other;

    public ExecutorService get() {
        return poolExecutorService;
    }

}

class MyModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(ExecutorService.class).annotatedWith(Names.named("pool")).toProvider(PoolProvider.class);
        bind(ExecutorService.class).annotatedWith(Names.named("single")).toProvider(SingleProvider.class);
    }
}

But I am not comfortable with creating two provider classes. I want one provider class to provide all instances, ie something like that:

class AllExecutorProvider {
    @Inject
    public void setSomeOtherDependency(SomeOtherdependency dep) {
        // ...
    }

    public ExecutorService getExecutor(String name) {
        if (name.equals("pool")) {
            return poolExecutor;
        } else if (name.equals("single")) {
            return singleExecutor;
        } else {
            return null;
        }
    }
}


Thanks in advance


Sam Berlin

unread,
Mar 9, 2011, 9:41:50 AM3/9/11
to google...@googlegroups.com, Bahri Gençsoy
You can have one provider for both bindings, but the provider can't disambiguate based on what it's providing (that is, there's know of knowing the thing it's providing for).

You could do this with less code with @Provides methods (or also simulate it if you're stuck with classes).  For example:

@Provides @Named("single") ExecutorService provideSingle(AnotherDependency ad) {
   return makeExecutor(ad, Type.SINGLE);
 }

@Provides @Named("pool") ExecutorService  providePool(AnotherDependency ad) {
   return makeExecutor(ad, Type.POOL);
}

private ExecutorService  makeExecutor(AnotherDependency ad, Type type) {   ...  }

---  or, using classes ---

class SingleProvider extends GenericExecutorProvider {
  SingleProvider() {    super(Type.SINGLE);    }
 }

class PoolProvider extends GenericExecutorProvider {
  PoolProvider() {   super(Type.POOL);  }
 }

class GenericExecutorProvider implmenets Provider<ExecutorService> {
   final Type type;
   GenericExecutorProvider(Type type) { this.type = type; }

   @Inject injectDependency(AnotherDependency ad) { .... }

   ExecutorService get() {
      switch(type) { ... }
   }
 }


sam

2011/3/9 Bahri Gençsoy <bahri....@gmail.com>


--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To post to this group, send email to google...@googlegroups.com.
To unsubscribe from this group, send email to google-guice...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-guice?hl=en.

Russ Milliken

unread,
Mar 9, 2011, 10:18:08 AM3/9/11
to google...@googlegroups.com, Sam Berlin, Bahri Gençsoy
I would do the "class" solution in a more OO way (eliminate the "switch"):

public class MyModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(GenericExecutorProvider.class)
            .annotatedWith(Names.named("single"))
            .to(SingleProvider.class);

        bind(GenericExecutorProvider.class)
            .annotatedWith(Names.named("pool"))
            .to(PoolProvider.class);
    }
}

abstract class GenericExecutorProvider implements Provider<ExecutorService> {
    public abstract ExecutorService get();
}

class SingleProvider extends GenericExecutorProvider {

    @Inject
    SingleProvider(final SomeDependency d) { }

    public ExecutorService get() {
        // construct and return an ExecutorService
        // based on SomeDependency
        return null;
    }
}

class PoolProvider extends GenericExecutorProvider {

    @Inject
    PoolProvider(final AnotherDependency d) { }

    public ExecutorService get() {
        // construct and return an ExecutorService
        // based on AnotherDependency
        return null;
    }
}


2011/3/9 Sam Berlin <sbe...@gmail.com>

Russ Milliken

unread,
Mar 9, 2011, 11:06:27 AM3/9/11
to google...@googlegroups.com, Sam Berlin, Bahri Gençsoy
I got the bindings wrong, correcting that.  Also illustrating how the superclass of Providers gets common dependencies.


public class MyModule extends AbstractModule {

    @Override
    protected void configure() {
        bind(ExecutorService.class)
            .annotatedWith(Names.named("single"))
            .toProvider(SingleProvider.class);

        bind(ExecutorService.class)
            .annotatedWith(Names.named("pool"))
            .toProvider(PoolProvider.class);
    }
}

class SomeDependency {}
class AnotherDependency {}
class CommonDependency1 {}
class CommonDependency2 {}
class CommonDependency3 {}


abstract class GenericExecutorProvider implements Provider<ExecutorService> {

    @Inject
    GenericExecutorProvider(final CommonDependency1 d1,
                            final CommonDependency2 d2,
                            final CommonDependency3 d3) {}


    public abstract ExecutorService get();
}

class SingleProvider extends GenericExecutorProvider {

    @Inject
    SingleProvider(final SomeDependency d,
                   final CommonDependency1 d1,
                   final CommonDependency2 d2,
                   final CommonDependency3 d3) {
        super(d1,d2,d3);

    }

    public ExecutorService get() {
        // construct and return an ExecutorService
        // based on SomeDependency
        return null;
    }
}

class PoolProvider extends GenericExecutorProvider {

    @Inject
    PoolProvider(final AnotherDependency d,
                 final CommonDependency1 d1,
                 final CommonDependency2 d2,
                 final CommonDependency3 d3) {
        super(d1,d2,d3);

Bahri Gençsoy

unread,
Mar 10, 2011, 5:04:20 AM3/10/11
to google...@googlegroups.com

AFAIK Provides annotation can only be used in Modules, thus is not a preferable choice for me.

Base class approach is actually introducing another class, I just want to reduce the provider class definitions.

I created custom injector, but this time I feel like I am doing guice's own work, somewhat obnoxiously:

class SomeExecutorDependentClass{
    @InjectExecutor("pool")
    void setExecutor(ScheduledExecutorService e) {
        this.executor = e;
    }
}

class MyExecutors{

    public ExecutorService getExecutor(String name) {
        if (name.equals("pool")) {
            return pool;
        } else if (name.equals("sync")) {
            return synch;
        } else {
            throw new IllegalArgumentException("Executor name not found " + name);
        }
    }
}

    public @interface InjectExecutor {
        String value();
    }

    public static TypeListener getInjectionListener(Provider<MyExecutors> provider) {
        return new ExecutorTypeListener(provider);
    }

    static class ExecutorTypeListener implements TypeListener {

        private Provider<MyExecutors> provider;

        public ExecutorTypeListener(Provider<MyExecutors> provider) {
            this.provider = provider;
        }

        public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {
            for (Method m : typeLiteral.getRawType().getDeclaredMethods()) {
                if (m.isAnnotationPresent(InjectExecutor.class)) {
                    typeEncounter.register(new ExecutorInjector<I>(m, provider));
                }
            }
        }

    }

    static class ExecutorInjector<T> implements MembersInjector<T> {

        private Method method;

        private Provider<MyExecutors> provider;

        public ExecutorInjector(Method m, Provider<MyExecutors> provider) {
            this.method = m;
            this.provider = provider;
        }

        public void injectMembers(T instance) {
            boolean accessible = method.isAccessible();
            try {
                method.setAccessible(true);
                method.invoke(instance,
                        provider.get().getExecutor(method.getAnnotation(InjectExecutor.class).value()));
            } catch (Exception e) {
                throw new RuntimeException(e);
            } finally {
                method.setAccessible(accessible);
            }
        }
    }
Reply all
Reply to author
Forward
0 new messages