Disclaimer : I will only focus on issues that I faced and solved in course of my porting of the application. The application did not have many blocker features for Guice - hence I do not claim that *all* applications can be ported completely using Guice 1.0.
Really Guicy!
Before I go into the details, here are some of the guicy attributes of Guice as a Java framework ..
The Java 5 usage - I always believe that backward compatibility is not a do-all end-all in a framework evolution. At some stage u need to educate the users as well, to migrate to newer versions and use the advanced features of your framework, which will make their applications more performant and maintainable. This has been one of my complaints against Java as well. It is really heartening to see Guice designers base their engine on Java 5 and use all advanced features like metadata and generics to the fullest. This has definitely made Guice more concise, precise and DRY.
Type-safety - This is possibly the loudest slogan of Guice as a DI container. It's Java generics all the way and although you can subvert the typesystem (more on this later) and hide some of your bindings from Guice, it is more of an exception. All api s in Guice are strongly typed, hence your application remains blessed with the safety of typed injections that Guice encourages.
Concise, minimal, well-designed api set with extremely verbose and explanatory error messages.
Let's Guice it up ..
Here it is. The application has been running happily in a Spring-Hibernate-JPA architecture. I took up the porting exercise purely out of academic interest and to get a first hand feel of trying to validate Guice against a real life non trivial application. I was somewhat aware of the nuances that I needed to figure out beforehand, and I classified my injection points into the following three groups :
- points that I had complete control of and where I knew I would be able to inject my annotations
- services with multiple implementations being used in the same application - luckily I did not have many such instances
- third party POJOs that I could not invade into
The first ones were pretty cool and I happily added
@Inject
with appropriate bindings in the module. The injection points became very explicit and the class became more readable as far as external dependencies were concerned.I did not have many occurences of multiple implementations of the same service being used in the same application deployment. As far as the application is concerned, we needed different implementations for different deployments, and hence I had different modules in place for them. In one of the cases, I needed to address the problem within the same instance of the application, which I did the usual way, using annotations like the following :
bind(IBarService.class)
.to(BarService.class)
.in(Scopes.SINGLETON);
bind(IBarService.class)
.annotatedWith(Gold.class)
.to(SpecialBarService.class)
.in(Scopes.SINGLETON);
Injecting into third party POJOs is one of the issues that has been debated over a lot in the various blogs and forums. Here are some of the cases and how I addressed them in my application :
Case 1: Use
Provider<>
along with constructor injection : I used this pattern to address POJOs which we were using as part of another component and which used constructor injection. e.g. public class ThirdPartyBeanProvider implements Provider<ThirdPartyBean> {
final private IFooService fooService;
final private IBarService barService;
@Inject
public ThirdPartyBeanProvider(final IFooService fooService, final IBarService barService) {
this.fooService = fooService;
this.barService = barService;
}
public ThirdPartyBean get() {
return new ThirdPartyBean(fooService, barService);
}
}
Case 2: These beans were using setter injection and I had some cases where multiple implementations of a service where being used in the same deployment of the application. Use
Provider<>
along with annotations to differentiate the multiple implementations of an interface. Luckily I did not have many of these cases, otherwise it would have been a bit troublesome with annotation explosion. But, at the same time, I think there may not be lots of use cases which need this in typical applications for a single deployment. e.g.public class AnotherThirdPartyBeanProvider implements Provider<AnotherThirdPartyBean> {
final private IFooService fooService;
final private @Inject @Gold IBarService barService;
@Inject
public AnotherThirdPartyBeanProvider(final IFooService fooService, final IBarService barService) {
this.fooService = fooService;
this.barService = barService;
}
public AnotherThirdPartyBean get() {
AnotherThirdPartyBean atb = new AnotherThirdPartyBean();
atb.setFooService(fooService);
atb.setBarService(barService);
return atb;
}
}
Case 3: Here I had lots of POJOs using setter injections that needed to be handled the same way. I would have to write lots of providers, but for this dynamic gem from Kevin which I dug up in a thread in the developer's mailing list. Here the type system is a bit subverted, and Guice does not have full information of all bindings. But, hey .. for porting applications,
AutowiringProvider<>
gave me a great way to solve this issue. Here's straight out of the class javadoc :A provider which injects the instances it provides using an "auto-wiring" approach, rather than requiring {@link Inject @Inject} annotations. This provider requires a Class to be specified, which is the concrete type of the objects to be provided. It must be hand-instantiated by your {@link com.google.inject.Module}, or subclassed with an injectable constructor (often simply the default constructor).
And I used it like a charm to set up the bindings of my POJOs.
Finally, here is a snapshot of a representative
Module
class, with actual class names changed for demonstration purposes :public class MyModule extends AbstractModule {
@Override
protected void configure() {
bind(IFooService.class)
.to(FooService.class)
.in(Scopes.SINGLETON);
bind(IBarService.class)
.to(BarService.class)
.in(Scopes.SINGLETON);
bind(IBarService.class)
.annotatedWith(Gold.class)
.to(SpecialBarService.class)
.in(Scopes.SINGLETON);
bind(ThirdPartyBean.class)
.toProvider(ThirdPartyBeanProvider.class);
bind(YetAnotherThirdPartyBean.class)
.toProvider(new AutowiringProvider<YetAnotherThirdPartyBean>(YetAnotherThirdPartyBean.class));
bind(AnotherThirdPartyBean.class)
.toProvider(AnotherThirdPartyBeanProvider.class);
}
}
Injecting the EntityManager
In an implementation of the Repository pattern (a la Domain Driven Design), I was using JPA with Hibernate. I had an implementation of a
JpaRepository
, where I was injecting an EntityManager
through the annotation @PersistenceContext
. This was working with normal Java EE application servers where the container injects the appropriate instance of the EntityManager. public class JpaRepository extends RepositoryImpl {
@PersistenceContext
private EntityManager em;
// ..
// ..
}
Spring also supports this annotation both at field and method level if a
PersistenceAnnotationBeanPostProcessor
is enabled. Using Guice I had to write a Provider<>
to have this same functionality implemented in my Java SE application.@Singleton
public class EntityManagerProvider implements Provider<EntityManager> {
private static final EntityManagerFactory emf =
Persistence.createEntityManagerFactory("GuiceJpaGettingStarted");
public EntityManager get() {
return emf.createEntityManager();
}
}
The Guice Way
Guice is opinionated .. yes, it really is. And through this porting exercise I have learnt it. It encourages some practices and adds syntactic vinegars trying to subvert the recommendations. e.g. For injection, you either annotate with
@Inject
or write Provider
s. Provider<>
is a wonderful tiny abstraction and it's amazing how powerful it can get in real life applications. Use Provider
s to implement custom instantiation policies, multiple injections per dependency and even can have custom scopes for injecting providers. For porting applications, you can use AutowiringProvider<>
, but that's not really what Guice encourages a lot.Guicy Performance
In the Spring based version, I had been using quite a few lookup-method-injections to design singleton services that have prototype beans injected. While porting, I didn't have to do anything special in Guice, apart from specifying the appropriate scopes during binding in modules. And these prototype beans were heavily instantiated within the application. I did some benchmarking and found that Guice proved to be much more performant than Spring for these use cases. In some cases I got 10 times better performance in injection and repeated instantiation of prototype beans within singleton services. I admit that Spring has much richer support for lifecycle methods and I would not venture into the hairy territory of trying to compare Spring with Guice. But if I would have to select an IoC container just for dependency injection, Guice will definitely be up there as a strong contender.
5 comments:
Note that with Spring 2.0.4 (released just yesterday), performance when creating prototypes has dramatically improved. Spring is said to be 12 times faster than it used to be for instances of the same bean definition.
Yeah .. I also saw that in the release notes for Spring 2.0.4 today. I will surely run my benchmarks once I download the stuff ..
Hai do u have any article/example related to migration of a spring web app to use guice.I would like to use spring for transaction management and guice for IOC is it possible?
[for aniesh]: I do not have any documentation, but u can refer to here and here for more information on Guice and Spring integration.
Thanks for the great example. I do wonder how you handle closing the EntityManager you get from the Provider, though.
Post a Comment