The Guicer DSL

Abstract

Guicer is an alternative Domain Specific Language (DSL) for configuring Guice injectors which is simpler and more concise than the original Guice DSL. This article provides a brief introduction to the Guicer DSL.

Overview

The Guicer DSL uses simple method chaining for configuring a Guice Injector with modules and bindings. It does not require you to implement or subclass anything (e.g. a Module).

The Guicer DSL supports…

sub-smodules,

private-modules,

annotated bindings,

bindings for classes,

bindings for type literals,

bindings for constants and

scoped bindings.

The Guicer DSL doesn’t yet support…

@Provides methods.

For everything which isn’t supported yet, you can add custom modules to the Guice injector via the Guicer DSL, so that you can exploit Guice to the fullest.

If you like to add support for a specific feature to the Guicer DSL, I welcome pull requests. Please note that to be acceptable, the changes/extensions in the pull request need to maintain the general concept of method chaining because this is what sets the Guicer DSL apart from the original Guice DSL.

Prerequisites

You need Java SE 6 and Maven 3.0.4 or higher to use this project. Furthermore, you need to checkout the source code repository and build the project yourself because the artifacts are not yet available on Maven Central.

Your First Configuration

The following class creates a minimally configured Guice injector with the Guicer DSL:

With the Guicer DSL, you start by creating a new GuiceContext. In fact, this is the only import and the only new statement you need with the Guicer DSL. Everything else gets configured via method chaining / drilling down from the GuiceContex.

After creating the GuiceContext, you need to call injector() in order to create a builder for a Guice Injector. When you are done with using the builder, you need to call build() in order to obtain the configured Injector.

Adding Modules

In the previous example, the configuration is empty and so you couldn’t use the configured injector to do many useful things. Let’s start adding a module to the injector:

The call to module() creates an injection for a Guice Module. When you are done with using the injection, you need to call inject() in order to make it inject the configured Module into the builder for the Injector.

Adding Sub-Modules

This is a recursive pattern, so you can add modules to modules like this:

Think of the call to inject() as a closing brace which returns control to the parent scope. If you know XML, this should be familiar to you. In XML, the preceeding code could have been expressed like this:

<injector>
<module>
<module>
</module>
</module>
</injector>

As you can see, inject() and build() in the Guicer DSL are the equivalent to </module> and </injector> in XML. I prefer to have inject() and build() indented one level more, however. Of course, this is a matter of style and you might as well write this:

This feature not only saves some lines of code, it also eliminates a point of failure because the generated exposing and binding will always match.

Qualifying Bindings With Annotations

In the previous example, I’ve also added calls to annotatedWith(named(...)) in order to disambiguate between the primary and secondary printer for the TeePrinter. The named(String) function is a static import from com.google.inject.name.Names which creates an annotation which is comparable to the annotation type javax.inject.Named. With the given parameters, I specifically ask for an injection point of the type Printer which is respectively annotated with @Named("primary") or @Named("secondary").

Binding Constants

The secondary printer in the previous example needs a boolean parameter further down its decorator chain. You can use the same syntax as with the original Guice DSL to configure this. Here’s the complete configuration for the secondary printer, including the constant binding:

The sub-module exposes a binding for Printer to a BanneredPrinter with a CheckedPrinter and a FilePrinter as their respective delegates. The FilePrinter will append its output to the file named print.log in the current directory.