I am a Guice user who also uses CDI, I like both, but starting firmly in the Guice camp left me confused about how to effectivly use CDI.
The problem was that although Guice and CDI are both dependency injection frameworks, they require a slightly different mindset which means you have to organise your projects differently. Don't expect your Guice application to work in CDI without modification, and moving from CDI to Guice is likely to be painful.
The Guice way
With Guice you are in complete control over how your application is configured, although defaulting is possible, if you need to specify an explicit implementation you can do so via a module in your bootstrap class.
Guice configuration uses a fluent configuration API to achive application specific configuration. This is great if you do a lot of refactoring as the configuration gets refactored along with the rest of your code.
For example, here we have an application that is creating a starship:
Although TranswarpDrive and WarpDrive are available implementations of engines, we are specifying that we want to use WarpDrive.
- Great refactoring support.
- It's easy to see how injection is configured.
The CDI way
With CDI you must let the container take control over what is being injected. If you've been using Guice this will seem unnatural, like letting a plane fly itself, but it the idea is that CDI components should automatically configure themselves as much as possible.
This is fine, except that when converting a Guice application to CDI for the first time you will probably encounter something like:
The fundemental difference between CDI and Guice is that everything on the classpath is a candidate for injection, and the CDI container will tell you if there is an ambiguous injection. In this case if we ask for an Engine the CDI container can't tell which implementation to use.
There are a couple of ways to remedy this situation:
If you have several implementations of a class then you can annotate them with the @Alternative annotation. Any class annotated with @Alternative can be specified in META-INF/benas.xml, if it is then it will override other implementations on the classpath.
In our example, if WarpDrive was annotated with @Alternative then it would be OK for TranswarpDrive to be on the classpath.
If you are prepared to split your implementations into different maven artifacts then you can use Maven to select the implementation that you want to use. This is more applicable to library developers, you can leave it to the client to select what implementation they want to use by including it as a dependency in the pom.
In our example, if Engine, WarpDrive and TranswarpDrive are all in different artifacts, then using the pom we can select to just have the WarpDrive on the classpath. The CDI container doesn't know anything about TranswarpDrive.
CDI sounds like a step backwards, but it isn't
At this point I was wondering why on earth I should be using CDI instead of Guice, the use of XML for alternatives instead of the Guice fluent config feels very wrong. However after working with CDI for a while I realised that it wasn't a problem:
- CDI aware components configure themselves – If a component is CDI aware then you generally just include them on the classpath and start injecting the services you need. No module required.
- Alternatives should hardly ever be used – Instead if the different implementations are required at runtime then use @Instance.
- CDI extensions are first class citizens – With CDI an extension can hook directly into the container injection mechanism, with Guice you only get the opportunity to inject after guice has had first dibs.
- Lifecycle support – Lack of lifecycle support in Guice lifecycle support is sometimes problematic for applications that require a clean shutdown.
- It's the standard – Like it or not, CDI is the default dependency injection that'll be bundled in with modern application servers, at some point you'll find it easier to just write the code to put food on the table rather than worrying about what library is doing dependency injection.
Don’t get me wrong, I am still a big fan of Guice, and and there are still cases where Guice wins on simplicity and performance, just think of CDI as another tool in the toolbox.
CDI Unit testing needs some Guice
There is one fly in the ointment when using CDI. During unit testing you usually want to replace an implementation with an alternative implementation for testing or mock. Using Guice I would create an injector for my unit tests that had the appropriate configuration. However, simply firing up the JavaSE Weld implementation won't work because:
- Everything on the classpath gets included in the container, of you have some code that listens to the startup event then this can slow down running the test.
- There is no opportunity to reconfigure what is being injected without modifying you main application code.
Instead you need to be able to configure what goes into CDI container for the unit test in a similar way that you would with Guice. Take a look at these projects to see how it's done: