With all of the excitement surrounding Guice lately, I thought it might be worthwhile to compare Guice with Spring JavaConfig. Both represent different approaches to annotation-based dependency-injection. I've chosen to reimplement my knight/quest example from chapter 1 of Spring in Action to illustrate the basics of each option.
Before I get started, a few disclaimers:
- As the author of Spring in Action, it should be no surprise to you that I will have a difficult time being unbiased. Therefore, I will make no pretense of impartiality. However, I will do my best to be as fair as I can be to both Guice and Spring JavaConfig.
- As someone who has several years of Spring experience to match his few days of Guice experience, I will admit that my knowledge of Guice isn't up to par with my knowledge of Spring. Therefore, it is quite possible that there are things about Guice that I simply do not know about and have not taken advantage of in my comparison. I assure you that failure to use the best possible Guice technique is not meant to devalue Guice, but merely due to the fact that I'm not as experienced as Guice as I am with Spring. For what it's worth, while I have several years of Spring experience, I'm still relatively new to Spring JavaConfig.
- This is only a comparison of Guice with Spring JavaConfig, the core Spring container, and (to a lesser degree) Spring AOP; it is not a comparison of Guice to the entire Spring Framework. The Spring Framework is much more than just its core dependency injection container, providing functionality that Guice does not provide or claim to provide.
- My comparisons will with Guice 1.0, Spring JavaConfig 1.0-m1, and Spring 2.0.2.
With that out of the way, let's get started. Since Spring is the hometeam in this matchup, I'll let Guice take the field first.
A Knight's tour of Guice
In Guice, much of the configuration is done in the context of a module class. This class effectively binds classes to interfaces within the Guice injector, so that Guice knows how to resolve certain dependencies. Here's what the KnightModule class that I wrote looks like:
(Note that for space considerations I'm only going to show the code that is pertinent to the comparison of Guice with Spring JavaConfig. The full source code for the Guice examples will be available here, while you'll be able to find the Spring JavaConfig examples here. Also notice that I've placed the main() method in KnightModule for convenience only--you may or may not want to do this in a real-world application.)
The first thing you'll see is the configure() method. This method, which is mandated by the AbstractModule class, is used for, among other things, binding interfaces to an implementation classes. This is important, because in Guice, you do not explicitly wire objects together--instead you declare that a property, constructor argument, or even a method argument needs to be injected and Guice will automatically (or semi-automatically, to be more accurate) find an implementation to match the property or argument.
For example, notice that the Knight interface is bound to the KnightOfTheRoundTable class. What this is basically telling Guice is that anytime it needs to inject a property or argument that is of type Knight, it should use the concrete KnightOfTheRoundTable implementation. Similarly, the Quest interface is bound to the HolyGrailQuest class. Thus when KnightOfTheRoundTable needs its quest property wired with a Quest implementation, Guice will oblige with a HolyGrailQuest.
Along with being wired with a Quest implementation, the KnightOfTheRoundTable class is also constructed with a String value that is the knight's name. Unfortunately, I could find no more straightforward way of wiring a String value into KnightOfTheRoundTable's constructor without binding the value through an annotation. Thus, the call to bindConstant() tells Guice to inject the value "Bedivere" into any String that is annotated with @Name (which is a custom annotation that I created as an "anchor" to bind the String injection to).
In the case of both the Quest and Knight bindings, the in() method is called to set the scoping of the bean to be singleton. Optionally, you can leave this out and instead place a @Singleton annotation on the KnightOfTheRoundTable and HolyGrailQuest classes. However, for reasons I'll describe later in this article, I don't particularly recommend the @Singleton annotation.
I'll come back to the call to bindInterceptor() in a moment. But first, it's important to know that KnightModule only tells Guice about the objects and values that it is to inject. It doesn't say where to inject them. For that, I had to place annotations in the KnightOfTheRoundTable class. Here's the modified KnightOfTheRoundTable.java:
Notice the use of the @Inject annotation on both the constructor and the setQuest() method. This annotation tells Guice that it needs to inject some values into the arguments of the constructor and method that it annotates. Without this annotation in place, Guice will pass over those methods and not perform any injection at all.
First look at the setQuest() method. It is annotated with @Inject and has a Quest argument. As you'll recall, Quest is bound to HolyGrailQuest. Therefore, when Guice is configuring the KnightOfTheRoundTable object, it will call setQuest(), passing in a reference to a HolyGrailQuest object.
It should also be noted that although this example doesn't show it, Guice can also inject into any method, not just setters and constructors. So, even if the setQuest() method were renamed to giveMeAQuestPlease(), Guice would still call it, passing in a HolyGrailQuest object.
Also, instead of explicitly binding Quest to HolyGrailQuest in KnightModule.java, I could've used the @ImplementedBy annotation in Quest.java to bind HolyGrailQuest to Quest. For example, here's the Quest interface, annotated with @ImplementedBy:
The @ImplementedBy annotation is effectively equivalent to the bind() call I used in KnightModule. The difference is that it sets up Quest to be bound to a default implementation class, should one not be bound to Quest in KnightModule.
Now look at the constructor. It is also annotated with @Inject. And it has a String argument that is annotated with @Name. The @Inject argument tells Guice that the constructor must be injected, while the @Name annotation (a custom annotation that I created to help this example along) helps Guice know what to inject it with. Back in the KnightModule class, we bound the String value "Bedivere" to the @Name annotation. Therefore, when KnightOfTheRoundTable is constructed, it will be constructed with the argument being set to "Bedivere".
Guice and AOP
At this point you've seen the basics of Guice injection. Now let's see how Guice does AOP.
First, I implemented the MinstrelInterceptor as an AOP Alliance MethodInterceptor:
Then, back in KnightModule.java, I had to bind the interceptor:
This effectively binds the MinstrelInterceptor to all methods that are annotated with @MinstrelIntercepted (another custom annotation) on all types ("any()"). I couldn't find any more straightforward way to create a "pointcut" other than to create a custom annotation and to bind the interceptor to it.
With the interceptor bound to @MinstrelInterceptor (on both types and methods), I had to change the KnightOfTheRoundTable to use the @MinstrelIntercepted annotation on the embarkOnQuest() method:
With the embarkOnQuest() method annotated and MinstrelInterceptor bound to the @MinstrelIntercepted annotation, any call to a KnightOfTheRoundTable's embarkOnQuest() method will result in the minstrel singing about the knight's exploits.
And that is, in a nutshell, how I implemented the knight/quest example using Guice. Again, the full source code for this example is available for download here.
Now let's see how I did the same thing using Spring JavaConfig.
A Knight's tour of Spring JavaConfig
Like Guice, Spring JavaConfig enables you to configure an application's objects in Java code using annotations. But their approaches to dependency injection are vastly different.
To get started, I needed to create a configuration class. In Spring JavaConfig, any class that is annotated with @Configuration can serve as a JavaConfig configuration. Here's the knight example's KnightConfig.java:
(Again, I've placed the main() method in KnightConfig as a convenience. You may or may not want to do this in a real-world application.)
At first glance, you may think that KnightConfig is analogous to the Guice example's KnightModule. It's true, that both serve as the central configuration artifact for both resources. But take a look a little closer and you'll see that they're quite different. More specifically, KnightConfig is more explicit in its configuration than the Guice example's KnightModule.
For example, notice the first method in KnightConfig is simply called knight() and it returns a Knight. This method is annotated with @Bean and is, in fact, the complete set of instructions for how Spring JavaConfig should create and inject a bean named "knight" in the Spring container. In short, it will create a new instance of KnightOfTheRoundTable, passing in "Bedivere" as its constructor argument. Then it will call the setQuest() method to wire a Quest into the quest property. The actual quest used is a reference to the quest() method.
Similarly, the quest() method is also annotated with @Bean and simply returns an instance of HolyGrailQuest.
The method names give the bean its ID, while the value returned is the type. Everything that happens in the method defines how the bean is configured. Therefore, taken together, the knight() and quest() methods are roughly equivalent to the following Spring XML configuration:
I should also mention that because all of the configuration takes place directly in Java code, it's quite easy to inject references into regular methods...not just constructors and setters. Similarly, it's no problem to add conditional and looping logic in your configuration, which is something that pure Spring XML cannot do.
It's also important to realize that Spring JavaConfig is not mutually exclusive to Spring XML configuration. You can easily use both in the same application by configuring the @Configuration-annotated classes as beans in Spring XML and then wiring an org.springframework.beans.factory.java.ConfigurationPostProcessor in your Spring XML. In trying to create an XML-free example, however, I've not exploited the mix-n-match of JavaConfig with XML here.
And, in case you're wondering, Spring's special wiring features, such as autowiring, lazy-loading, init-methods, etc, are all still available as attributes to the @Bean and @Configuration annotations. It's beyond the scope of this article to go into any great detail, but the knight() method could be redefined as follows to be autowired by type:
As with the Guice example, I'll come back to see how the minstrel advice is wired in. First, however, let's see what else needs to be done with KnightOfTheRoundTable and HolyGrailQuest for Spring JavaConfig to wire them together.
Oh wait...actually, there's nothing special that needs to be done with either of those classes. They're perfectly suitable for wiring with Spring JavaConfig, just as they would be using Spring XML. Unlike Guice, Spring JavaConfig keeps its annotations confined to the configuration classes. So I guess I can go ahead and talk about how to use AOP with Spring JavaConfig.
Spring JavaConfig and AOP
The minstrelAdvice() method is similar to the knight() and quest() methods in that it wires a MinstrelAdvice object into the Spring container. But what makes it special is its @SpringAdvice annotation. This annotation tells Spring JavaConfig that the bean configured in the method is actually AOP advice that implements one of Spring's advice interfaces (such as MethodBeforeAdvice or even AOP Alliance's MethodInterceptor). In fact, because the Guice example's MinstrelInterceptor implements the AOP Alliance MethodInterceptor class, it's perfectly suitable for reuse in the Spring JavaConfig example!
The @SpringAdvice annotation takes a pointcut expression as its value attribute. Here I'm using an AspectJ-style pointcut expression to indicate that I want the advice applied to the execution of the embarkOnQuest() method on any class.
And that ends our examination of Spring JavaConfig as applied to Spring in Action's knight/quest example. The full source code for the Spring JavaConfig example can be found here.
Conclusions
Both Guice and Spring JavaConfig offer interesting alternatives to the sometimes verbose XML used to configure many frameworks (including Spring). Each, however, takes a different approach to configuration. Let's examine how they stack up against each other:
- String identifiers - One complaint against Spring's XML configuration is the use of String identifiers and the lack of refactorability therein. It's true, that Spring's XML configuration is heavy with references to classes, properties, and other beans that are merely String values and thus difficult to refactor without a proper tool (such as the SpringIDE for Eclipse). Fortunately, both Guice and Spring JavaConfig register a win in this category, as neither relies on String identifiers in their configuration and both are easily refactorable through modern IDEs.
- Speed/Performance - Guice bills itself as faster than other containers when performing dependency injection. While I didn't run any formal benchmarks to compare Spring JavaConfig and Guice, some informal tests proved Guice's claims to be true. This is not terribly surprising, however, as Spring defines a rich set of lifecycle events around the creation of beans in the container, while I could not find any comparable lifecycle built into Guice. Without the overhead of the lifecycle events, Guice does, in fact, wire up the beans much faster than Spring.
- JAR file footprint - The Guice example above only requires two JAR files: guice-1.0.jar and aopalliance.jar. Together, these two JAR files weigh in at just over half a megabyte. Meanwhile, the Spring example requires several JAR files, including spring-javaconfig.jar, spring-beans.jar, spring-aop.jar, spring-core.jar, spring-context.jar, commons-logging.jar, aopalliance.jar, asm-2.2.2jar, cglib-nodep-2.1_3.jar, and aspectjweaver.jar, together weighing in at several megabytes in size. So, Guice is more lightweight than Spring in terms of JAR file footprint.
- Non-intrusive - Both Spring JavaConfig and Guice rely on proprietary annotations to help them do their injections. Where they differ, however, is that Guice requires placement of @Inject annotations withing the injected application classes, while Spring JavaConfig confines its use of annotations to the configuration-specific classes. Consequently, Guice-injected classes cannot be reused and compiled in a separate application without the Guice JAR file in place. Meanwhile, Spring JavaConfig leaves the application classes unaltered, enabling their unhindered reuse. Thus, in this category, Spring JavaConfig is the clear winner. (Granted, Spring does define a handful of annotations to be placed in application classes--@Required and @Transactional come to mind. But their use is not mandated to configure Spring.)
- Third-party beans - Related to the last category, I could find no direct way to inject values into a third party class using Guice. Guice-style injection requires the use of the @Inject annotation, which is useless in classes for which I do not have the source code (or classes that I do not wish to annotate with @Inject). Guice's documentation indicates that third-party components will require the creation of a custom provider, but in my opinion, this is a hack that adds complexity to something that would be very simple if the @Inject annotation were not required. Spring JavaConfig, however, keeps the configuration separate from the application classes, thus wiring a third-party class in Spring JavaConfig is no different than wiring a custom class. Spring wins again.
- Constant/literal injection - Injecting a String literal (or any other type of literal value) in Spring JavaConfig is rather straightforward. In contrast, literal injection in Guice is cumbersome, requiring the creation and use of a custom annotation to identify where the literal should be injected. Spring JavaConfig wins this category.
- AOP - The Guice documentation acknowledges that Guice's flavor of AOP is less powerful than Spring AOP (both of which are much less powerful than AspectJ). But, the Guice documentation claims that this loss of AOP power is in trade for simplicity. Honestly, I found Guice's approach to AOP (binding interceptors) non-intuitive as compared to Spring JavaConfig. The Spring JavaConfig approach required only a simple annotation with an AspectJ-style pointcut expression, whereas Guice required a confusing binding call involving "matchers" to pick out the target of the interceptor. Maybe I'm too well-versed in Spring AOP to appreciate the simplicity of Guice's interceptor-binding...but for now I must give Spring the win in this category.
Final score: Guice - 3, Spring JavaConfig - 5.
I'm glad that I spent the time to learn a little about Guice, but I'm not all that "juiced" about using Guice. In addition to all of the reasons given above, I'd much rather build my application against a well-established container like Spring that has a wealth of integration points with other frameworks and APIs (Guice only has integration with Struts 2 and Spring, as far as I can tell). Also, to be clear, Guice really doesn't compete with Spring as much as it competes with Spring's application context--Spring's capabilities go beyond dependency injection and AOP.
Also, one more comment on the speed issue: I've given Guice a point in that category, simply because my quick-n-dirty tests confirmed that Guice was faster. But, I've seen a few more detailed performance tests run by others that show that while Guice is faster in creating prototype beans, Spring is faster in creating singletons. Given that singletons are more typically used than prototypes, Spring should probably get the point in this category. But since I have not taken the time to provide the evidence, I'll go ahead and give this one to Guice.
(I'm sure that you're wondering where the numbers are that show Spring is faster in producing singletons. Unfortunately, the work done to produce these numbers is not mine and I am not sure if the author is ready to release his figures yet. If he's reading this, then he knows I'm talking about him here and I ask him to provide the numbers on his blog. When/if he does that, I'll put the link to his blog here.)
Again, let me repeat that I've had only a few days worth of experience with Guice as compared to a few years of experience with Spring. If there's a change that can be made to the Guice examples to make them better and to provide a more fair comparison, I'm eager to hear about it.
Special thanks to Crazy Bob for his gracious offer to review my Guice example. He pointed out a few things about my example that could've been done a bit better. He also suggested moving the ".in(Scopes.SINGLETON)" part of the binding into the HolyGrailQuest and KnightOfTheRoundTable classes as a class-scoped @Singleton annotation. I chose not to do this, because it would be one more intrusion of Guice into my application objects. Finally, Bob wanted me to mention that Guice really shines when you use a dependency more than once (in the knight/quest example, the Quest dependency was only used once).
Thanks also go to Rod Johnson, Colin Sampaleanu, and Costin Leau for going over the Spring JavaConfig bits and providing their feedback. Rod suggested that I mention that the @AspectJ advice style is way nicer than the AOP Alliance stuff (which is basically a legacy programming model in Spring 2.0). I fully agree that the @AspectJ stuff is pretty slick and I recommend it over the AOP Alliance stuff. I only used the AOP Alliance stuff here to provide a slightly more even playing field. (Although, in hindsight, an @AspectJ example would've been a slam-dunk in Spring's favor.)
Finally, and before I get pointless comments to this effect: I realize that a "Knight's Tour" is a series of 64 moves by a knight piece on a chess board, where the knight visits every square once and only once. Had I given an actual "knight's tour" of Guice and Spring, my examples would've been more comprehensive (covering at least 64 points). I know the phrase isn't a perfect fit here, but it sounds cool, so I'm keeping it. :-)
And, in case you didn't catch those download links earlier, you can find the Guice example code here and the Spring JavaConfig example code here.