Tuesday, June 21, 2011

Type-safe dependency injection in Spring 3 IoC-Container

CDI provides the javax.inject.Qualifier annotation. This annotation is used to identify the bean being searched during dependency injection by type (instead by name). In this article I provide a quick guide to set-up Spring IoC to do the same thing with the org.springframework.beans.factory.annotation.Qualifier annotation.

The following examples are taken from the Spring 3 reference documentation. 

Use component scan and autowiring

The first thing to do is to set-up Spring for "component scans". Component scan is an "option for implicitly detecting the beans by scanning the classpath. [...] This removes the need to use XML to perform bean registration, instead you can use annotations (for example @Component) or your own custom filter criteria to select which classes will have bean definitions registered with the container" (from the Spring reference dosumentation). See here for more information on component scans. In your Spring beans.xml you need to set-up the Spring IoC-Container for component scanning. The figure below illustrates the typical beans.xml setup.

The base-package is the package where your candidate components reside. To instintiate the container simply use the following code snippet somewhere in your application:

ApplicationContext context =
new ClassPathXmlApplicationContext(new String[] {"beans.xml"})
Note: if you are in a web application there are simple ways to integrate Spring declaratively without any programming effort. See here for further instructions.
When you instintiate the container, it performs a scan on the base-package to look up any bean definitions.

Define a custom annotation using @Qualifier

Next you need to define a custom annotation that is used to identify the injected bean. This is an equivalent procedure to the use of JSR 330 annotations in CDI.
Assign the custom annotation to a bean that implements the service

The annotation that you just defined can be used to mark the bean that implements the required MovieCatalog service. 

You use @Component to let Spring know that this a bean definition. The @Offline mark up annotation is used by Spring IoC to identify this bean when it is injected into a parent object.
Inject the bean using @Autowire and your custom qualifier

When you need to inject the CachingMovieCatalog service into a parent bean then you do the following.

That's it in terms of type-safe dependency injection. There was no "String" naming for bean identification involved here. All used concepts were strict Java types.

Why does all this makes sense at all?  

That's indeed a good question. Let's assume that the MovieCatalog is implemented by different (known component-)classes A and B and we would not create and use the custom qualifier annotation @Offline. In that case the dependency injection process for the property offlineCatalog would lead to ambiguous results, it would return A and B as bean candidates. Now, since you marked the CachingMovieCatalog implementation and the offlineCatalog property with your @Offline annotation Spring IoC can uniquely identify the bean that should be injected. Furthermore, instead of using a "String" name to lookup the bean to inject we use ordinary Java annotations. This is a type-safe way to wire and look up beans (all the nice development time features in your IDE work!).

1 comment:

  1. Nice Explanation. Great job
    I find this post very useful too.