Mark Renouf
Mark Renouf

Reputation: 31010

Has anyone used ServiceLoader together with Guice?

I keep wanting to try this on a larger scale with our app + build system, but higher priorities keep pushing it to the back burner. It seems like a nice way to load Guice modules and avoids the common complaint about "hard coded configuration". Individual configuration properties rarely change on their own, but you will almost always have a set of profiles, usually for different environments (Debug, Production, etc).

ServiceLoader lets you pull a list of all implementations defined as a service for a given type. Putting this together with Guice, you end up with:

import java.util.ServiceLoader;

import com.google.inject.AbstractModule;
import com.google.inject.Module;

public class ModuleLoader<M extends Module> extends AbstractModule {

    private final Class<M> type;

    public ModuleLoader(Class<M> type) {
        this.type = type;
    }

    public static <M extends Module> ModuleLoader<M> of(Class<M> type) {
        return new ModuleLoader<M>(type);
    }

    @Override
    protected void configure() {
        ServiceLoader<M> modules = ServiceLoader.load(type);
        for (Module module : modules) {
            install(module);
        }
    }
}

Usage example (as a dynamic servlet loader in a guice-servlet project):

import com.google.inject.servlet.ServletModule;

public class ServletLoader extends GuiceServletContextListener {
    @Override
    protected final Injector getInjector() {
       return Guice.createInjector(ModuleLoader.of(ServletModule.class);
    }
}

The services (packaged as modules) would be packaged in seperate jar files. Within each one you'd define the class(es) in the meta-data:

Within servlets.jar: META-INF/services/com.google.inject.Module

com.example.webapps.MyServletModuleA
com.example.webapps.MyServletModuleB

Since we use Maven, we think this would be ideal as we could pull in different implementations at runtime via profile dependencies. Is anyone using Guice like this?

If not, feel free to use this example and see how it works for you. (ServiceLoader is only supported in JDK6+)

Upvotes: 16

Views: 5214

Answers (3)

Det
Det

Reputation: 435

"because its impossible to bind the same interface twice."

This is indeed wrong! With Guice's Multibinder there is a way to work with different implementations of the same interface, possibly bound in different modules.

I came to a slightly different solution for the actual loading than Mark Renouf (his ModuleLoader looks indeed better), but my blog post may show a bit more about the environment where this approach is applicable (Plugins) and what the extension points look like:

Guice 2.0 Multibinder + Java ServiceLoader = Plugin mechanism

Upvotes: 0

whiskeysierra
whiskeysierra

Reputation: 5120

I considered this way already but i didnt use it because i was afraid, that i have to keep my modules extremely small, because its impossible to bind the same interface twice. My problem is, that if i want to use an interface/class/enum/whatever from another jar and that jar defined a services/* file i am screwed, because i cant make use of the content of the jar without loading it as a module.

I hope my concern is clear.

Upvotes: 1

bkdougan
bkdougan

Reputation:

We're doing almost exactly this at my work. We're currently stuck in java 5 due to some internal limitations, so we do it a bit differently using Service Provider (due to not having access to ServiceLocator until java 6 like you mentioned), but it essentially works the same.

I remember reading somewhere that this was one of the preferred ways the Guice developers recommended, though they want to leave this open for flexibility.

Upvotes: 3

Related Questions