How to get all implementors/subclasses of an interface with Guice?
Asked Answered
M

3

24

With Spring, you can define an array property and have Spring inject one of every (@Component) class that derives from the given type.

Is there an equivalent for this in Guice? Or an extension point to add this behavior?

Mortician answered 22/5, 2011 at 0:3 Comment(0)
I
21

This looks like a use case for Guice MultiBinder. You could have something like that:

interface YourInterface {
    ...
}

class A implements YourInterface {
    ...
}

class B implements YourInterface {
    ...
}

class YourModule extends AbstractModule {
    @Override protected void configure() {
        Multibinder.newSetBinder(YourInterface.class).addBinding().to(A.class):
        Multibinder.newSetBinder(YourInterface.class).addBinding().to(B.class):
    }
}

And you can inject a Set<YourInterface> anywhere:

class SomeClass {
    @Inject public SomeClass(Set<YourInterface> allImplementations) {
        ...
    }
}

That should match with what you need.

Interface answered 24/5, 2011 at 19:57 Comment(3)
It does indeed. Thanks for pointing me in the right direction.Mortician
You'll find Multibindings in a separate jar ("guice extension"): search.maven.org/…Derman
May i ask how do we do this in spring?Papeete
C
16

Guice Multibindings require you to explicitly addBinding() for A & B to YourInterface. If you would like a more "transparent" (automatic) solution such as what AFAIK Spring offers out-of-the-box, then assuming that Guice already knows about A & B because you already have a binding for A & B elsewhere anyway, even if not explicit but just implicit e.g. through an @Inject somewhere else, then and only then you alternatively could use something like this for auto-discovery (inspired by as done here, based on accessing Guice injector in a Module):

class YourModule extends AbstractModule {
   @Override protected void configure() { }

   @Provides
   @Singleton
   SomeClass getSomeClass(Injector injector) {
       Set<YourInterface> allYourInterfaces = new HashSet<>();
       for (Key<?> key : injector.getAllBindings().keySet()) {
           if (YourInterface.class.isAssignableFrom(key.getTypeLiteral().getRawType())) {
            YourInterface yourInterface = (YourInterface) injector.getInstance(key);
            allYourInterfaces.add(yourInterface);
       }
       return new SomeClass(allYourInterfaces);
   }
}

Note again that this approach does NOT require any classpath scanning; it just looks at all already known bindings in the Injector for anything that IS-A YourInterface.

Cladophyll answered 12/11, 2016 at 1:54 Comment(1)
I am trying same. But It is not working. #43705646Tellurize
C
1

Kotlin

Class SomeModule : AbstractModule() {
        
            override fun configure() {
                val myBinder: Multibinder<MyInterface> = Multibinder.newSetBinder(binder(), MyInterface::class.java)
                myBinder.addBinding().to(Implementation1::class.java)
                 myBinder.addBinding().to(Implementation2::class.java)
}

Usage

@Inject constructor(private val someVar:Set<@JvmSuppressWildcards MyInterface>)
Chancroid answered 9/6, 2021 at 18:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.