Recently, I had to delve into the play framework for a particular microservice at work. Now it is not exactly new, nor is Guice, nor DI, but coming from Spring world it was still kind of a shift in approach. There is a lot of documentation comparing Spring with Guice, stating which is better, why, and how.
In general, these articles discuss specific points where these two frameworks differ in their approaches and which approach seems better to the author. I am not sure these articles really help someone trying to take a dip in the other framework. We know the differing opinions, as they are stated by the authors of the respective frameworks in their own documentation as well.
Another person (the article’s author) reiterating it with an incomplete comparison of these frameworks does not sound helpful. What would work much better is a direct mapping of features, without the author’s opinion (didn’t this sound like an opinion?). That should help someone getting into Spring from Guice or vice versa.
Now let me warn you, since these are different frameworks for the same purpose, DI (Dependency Injection), they exist for their differences. Hence, there cannot be one-to-one mapping of features/differences in these frameworks. What we can get instead is mapping of similar features. If nothing else, the comparison below should help someone find the right documentation for what they are trying to do, instead of wondering what to look for.
To another point, we are here discussing Spring and Guice only on their dependency injection approaches and not as web frameworks, AOP, JPA abilities, their ecosystem or any other features they provide. That is for another time maybe, but not today.
Application level @Configuration
Extend AbstractModule, comes closest to that. It defines a part of your application; multiple modules can depend on each other in an application. (Unless your service is too small)
There is no classpath scanning in Guice. (keep reading...)
@Component, @Service etc.
@Singleton with “bind() with/without .to()” in Module
@Scope(“”); singleton (DEFAULT), prototype, request, session, global-session
Default is unscoped, similar to prototype in Spring, @Singleton, @SessionScoped, @RequestScoped, custom; eager/lazy differ for production and development.
@Inject (from javax or from guice package)
@Qualifier / @Named, Names.named, annotatedWith and @BindingAnnotation
@Provides or implement Provider<T>
@Bean with @Autowired field in it
Explicit constructor binding: .toConstructor(A.class.getConstructor())
@Named with Names.bindProperties() in your module
Injecting static fields can be achieved with @Autowired on non-static setter method.
For static fields, use .requestStaticInjection() in your Module
ApplicationContext (BeanFactory to be precise)
@Autowired with context.getBean(Clazz, Object...)
@AssistedInject. Allows for using params, with injected beans to instantiate objects.
Provider<T> with FactoryProvider; FactoryModuleBuilder
No Support for lifecycle events (extensions)
Let’s also see a few more points, which would not fit well in a tabular form:
One can add more capabilities to Guice with plugins, and there are a few actively maintained ones like Governator from Netflix. Spring DI can be extended using BeanPostProcessor or BeanFactoryPostProcessor in your application, but I was unable to find a plugin for extending Spring’s core DI abilities.
Unlike Spring, wiring in Guice (called binding) is plain Java, and so Guice has compile-time verification of any wiring we do. Spring depends on metadata through annotations, which are not checked during compilation. It does not have this feature, and exceptions are at runtime.
Classpath scanning can be achieved in Guice by extending it. (Some plugins provide this, but Governator, for one, has deprecated it.)
Lack of classpath scanning in Guice, most likely, considerably reduces the application startup time in comparison to Spring.
In Guice, an Interface can declare the default implementation class (is it odd, Spring people?) via the @ImplementedBy annotation, which can be overridden by .bind() if found in a module. Similarly, the interface can declare the configuration class, which generates the instance: @ProvidedBy.
I know I said we are not going to discuss any other abilities, but this one is a little interesting; Guice has built-in support for AOP, but in Spring, we need an additional dependency.
Not a difference, but a point to note: Both frameworks have similar injection types, Constructor, method, and field.
I have tried to be as opinionless as possible when writing the above piece; although there are a few things that I find important to note.
Guice is very much a non-magical (in the words of Guice authors) dependency injection framework, you can literally see DI happen, with the code that you write and can read.
Thankfully, Guice has no beans. NO BEANS! How many beans do we have to remember and disambiguate before it is too much? Javabeans, Enterprise Javabeans, Spring Beans, Coffee Beans, Mr. Bean, and I might still have missed a few others!
Guice still feels like Java. It believes in extending classes. Spring nowadays seems to believe only in annotations, so much so that a few folks I asked around can’t even remember what ‘extends’ keyword stands for!
So which one is better? Now, that was not the question we were hoping to answer!