Guice with ServiceTracker feature like OSGi

109 views
Skip to first unread message

Karsten Schulz

unread,
Feb 10, 2015, 2:47:08 PM2/10/15
to google...@googlegroups.com
Hello guys,

i have a handling question about guice. i like really dependency injection and use this in the most of private projects. But currently i have some problems with generics and multiple binding for one interface. For the first problem i have another solution. i don't know if thats works, but i will test it in the future.

So the second problem is for me currently a little bit difficult to understand.

The starting point is:

I have an Interface thats supports Generic, for Example:
public interface GenericInterface<T> {}

So i have also an implemented class, for Example:
public class GenericClass<T> implements GenericInterface<T> {}

So this interface and class have some generic methods.

In my Modul i define some bindings:

public class ExampleModule extends AbstractModule {

   
public void configure() {
        bind
(GenericInterface.class).annotatedWith(Names.named("Example")).toInstance(new GenericClass<String>());
        bind(GenericInterface.class).annotatedWith(Names.named("Second")).toInstance(new GenericClass<Integer>());
   
}
}

Now my Question. In my opinion, i have to different instances from GenericInterface. So i can inject this with the annotation @Named

But in another case, i have a class, that will add all implementations of GenericInterface without specify the name.

public class ExmapleClass {

    @Inject
    public <T> void addGeneric(GenericInterface<T> genericInterface) {}
}

Ok first, i find out, that generic methods doesn't work, but it's also not possible to add automaticly all GenericInterfaces to this method. So i searching for an solutions, that has the feature like the ServiceTracker functionality from OSGi.

Nate Bauernfeind

unread,
Feb 10, 2015, 3:16:18 PM2/10/15
to google...@googlegroups.com
Couple of comments.

1. Resolving generic type dependencies can be done without named annotations. The trick is to use a Guice TypeLiteral. See: http://google.github.io/guice/api-docs/latest/javadoc/index.html?com/google/inject/TypeLiteral.html

Your bind becomes something more like:
bind(new TypeLiteral<GenericInterface<String>>() {}).toInstance(new GenericClass<String>());

2. You probably want to use the multibinder extension to inject all implementations into a single location. See: https://github.com/google/guice/wiki/Multibindings

Your binding becomes more like:
Multibinder<GenericInterface> mBinder = Multibinder.newSetBinder(binder(), GenericInterface.class);
mBinder.addBinding().toInstance(new GenericClass<String>());

3. Instead of using multibinder you might enjoy something of an auto-registration approach.

public abstract class BaseGenericInterface<T> extends GenericInterface<T> {
    @Inject
    public void registerThis(GenericInterfaceRegistry registry) {
        registry.registerThis(this);
    }
}

I like this approach the most because in Scala I can actually put the method (and implementation) in the interface and it just automagically happens. (When I do this, I also almost always pass a scala manifest/typetag to the registry too.)

Happy guicing!
Nate

--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-guice...@googlegroups.com.
To post to this group, send email to google...@googlegroups.com.
Visit this group at http://groups.google.com/group/google-guice.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-guice/293f68ba-b645-493d-9dcb-1f6165564e2f%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Karsten Schulz

unread,
Feb 11, 2015, 3:41:52 PM2/11/15
to google...@googlegroups.com
Hello,

ok i have try this and thanks a lot. it's work very well. my solutions is a combine from multibindings and the the single add method. I use multibinding and inject the collection to the class and iterate over this and call for every single element the add method. your third option works also very well. in the first phase i have implement this on this way. but in my opinion the uncouple class design will be broken, because the bean class have to be know about the register class. but i understand the feature, that the automaticly add, maybe in scala is this possibility very well, but i think it's a little bit bad small, cause the strict couple between bean and register.

Nate Bauernfeind

unread,
Feb 11, 2015, 4:08:15 PM2/11/15
to google...@googlegroups.com
Cool! Thanks for sharing back your success! I'm glad I was able to help you.

I agree that option #3 couples the container and the instance. However this is slightly easy to get around if you introduce one layer of abstractedness. Let me give you a concrete example.

Imagine you have a base module:
abstract class GenericInterface<T> {
private final Class<T> myClass;
public GenericInterface(Class<T> myClass) {
this.myClass = myClass;
}
@Inject
private registerThis(GenericRegistry registry) {
registry.register(myClass, this);
}
}
 
class GenericRegistry {
private final HashMap<Class<?>, GenericInterface<?>> map = new HashMap<>();
// Package private may be preferred.
public <T> void register(Class<T> cls, GenericInterface<T> impl) {
// Error handle prior mapping. (also ignore warnings)
map.put(cls, impl);
}
public <T> GenericInterface<T> getImpl(Class<T> cls) {
return (GenericInterface<T>)map.get(cls);
}
// or get an iterator of all GenericInterfaces... or any other kind of API that makes sense for your situation
}

Then you have another module the depends only on the base and you inject the registry there:
class API {
@Inject
public API(GenericRegistry registry) { ... }
// ...
}

And your implementations can come from any module that depends on the base (consider the potential of having guice modules that extend your application):
class A extends GenericInterface<SomeClass> { ... }
class B extends GenericInterface<SomeClass'> { ... }

Hope that helps you see how you can decouple the coupling =). You need to be aware that you can't use the Registry until the entire injector is created. If you use it in any constructor or injection method then it may not have finished registering all bindings. This is a problem I solve by introducing start/stop methods on all services -- it's a good practice anyways since the injector is even faster to construct (to find errors before any initialization occurs).

Nate

Reply all
Reply to author
Forward
0 new messages