Using DI has become an increasingly standard practice for the well-grounded Java developer, and several popular containers provide excellent DI capabilities. But, in the not too distant past, the various DI frameworks all had differing standards for how you should configure your code to take advantage of their IoC containers. Even if the various frameworks had followed a similar configuration style (for example, XML or Java annotations) there was still the question of what the common annotations or configuration would be. The new standardized approach to DI for Java (JSR-330) solves this issue. This article discusses this standardized approach.
This article is based on The Well-Grounded Java Developer published in July 2. Download the eBook instantly from manning.com. All print book purchases include free digital formats (PDF, ePub and Kindle). Visit the book’s page for more information based on The Well-Grounded Java Developer. This content is being reproduced here by permission from Manning Publications.
Authors: Benjamin J. Evans and Martijn Verburg
Since 2004 there have been several widely used IoC containers for the purposes of DI (Guice, Spring, and PicoContainer to name a few). Up until recently, all of the implementations have had different approaches to configuring DI for your code, which made it difficult for developers to swap between frameworks. A resolution of sorts came about in May 2009 when two leading members of the DI community, Bob Lee (from Guice) and Rod Johnson (from SpringSource) announced that they had come together to work on a standard set of interface annotations. [1] Subsequently JSR-330 (javax.inject) was raised to provide standardized DI for Java SE with effectively 100 percent support from all major players in that space.
What about Enterprise Java?
Enterprise Java is already getting its own DI in JEE 6 (a.k.a. CDI), covered under JSR-299 (“Contexts and Dependency Injection for the Java EE platform”). You can find out more by searching for JSR-299 at http://jcp.org/. In short, JSR-299 builds on top of JSR-330 in order to provide standardized configuration for enterprise scenarios.
With the addition of javax.inject into Java (Java SE versions 5, 6, and 7 are supported), it’s now possible to use standardized DI and move between DI frameworks as required. For example, you can run your code within the Guice framework as a lightweight solution for your DI needs, and then perhaps move to the Spring framework in order to use its richer set of features.
WARNING In practice, this isn’t as easy as it sounds. As soon as your code utilizes a feature that’s only supported by a particular DI framework, you are locked in to that framework. The javax.inject package provides a subset of common DI functionality, but you may need to use more advanced DI features than that. As you can imagine, there was quite a bit of debate as to what should be part of the common standard and what should not. The situation isn’t perfect, but at least there is now a way to avoid framework lock-in.
To understand how the latest DI frameworks utilize the new standard, you need to investigate the javax.inject package. A key thing to remember is that the javax.inject package simply provides an interface and several annotation types that the various DI frameworks implement. You wouldn’t typically implement these yourself unless you’re creating your own JSR-330 compatible IoC container for Java. (And, if you are, then hats off to you!)
Why should I care how this stuff works?
The well-grounded Java developer doesn’t simply use libraries and frameworks without understanding at least the basics of how they work under the hood. In the DI space, a lack of understanding can lead to incorrectly configured dependencies, dependencies mysteriously falling out of scope, dependencies being shared when they shouldn’t be, step debugging mysteriously dying, and a whole host of other insidious problems.
The Javadoc for javax.inject does an excellent job of explaining the purpose of this package, so we’ll quote it verbatim:
Package javax.inject [2]
This package specifies a means for obtaining objects in such a way as to maximize reusability, testability and maintainability compared to traditional approaches such as constructors, factories, and service locators (e.g., JNDI). This process, known as dependency injection, is beneficial to most nontrivial applications.
The javax.inject package consists of five annotation types (@Inject, @Qualifier, @Named, @Scope, and @Singleton) and a single Provider<T> interface. These are explained over the next few sections, starting with the @Inject annotation.
The @Inject annotation
The @Inject annotation can be used with three class member types to indicate where you’d like a dependency to be injected. The class member types that can be injected, in the order that they’re processed at runtime are:
1. Constructors
2. Methods
3. Fields
You can annotate a constructor with @Inject and expect its parameters to be provided at runtime by your configured IoC container. For example, the Header and Content objects are injected into the MurmurMessage when the constructor is invoked.
@Inject public MurmurMessage(Header header, Content content)
{
this.header = header;
this.content = content;
}
The specification allows for zero or more parameters to be injected for constructors, so injecting a zero-parameter constructor is still valid.
WARNING As per the specification, there can only be one constructor in a class with an @Inject annotation. This makes sense, as the JRE would not be able to decide which injected constructor took precedence.
You can annotate a method with @Inject and, like a constructor, expect its zero or more parameters to be injected at runtime. There are some restrictions in that injected methods can’t be declared abstract and can’t declare type parameters of their own. [3] The following short code sample demonstrates the use of @Inject with a setter method, a common technique when using DI to set optional fields.
@Inject public void setContent(Content content)
{
this.content = content;
}
This technique of method parameter injection is especially powerful when it comes to providing service methods with the resources they need to do their jobs. For example, you could pass a data access object (DAO) argument to a finder service method that was tasked to retrieve some data.
TIP It has become a default best practice to use constructor injection for setting mandatory dependencies for a class and to use setter injection for nonmandatory dependencies, such as fields that already have sensible defaults.
It’s also possible to inject fields (as long as they aren’t final), but the practice isn’t common because it makes unit testing more difficult. The syntax again is quite simple.
public class MurmurMessenger
{
@Inject private MurmurMessage murmurMessage;
...
}
You can read further about the @Inject annotation in the Javadoc [4], where you can discover some nuances about what types of values can be injected and how circular dependencies are dealt with.
You should now be comfortable with the @Inject annotation, so it’s time to look at how you can qualify (further identify) those injected objects for use in your code.
The @Qualifier annotation
The @Qualifier annotation defines the contract for implementing frameworks that can be used to qualify (identify) the objects you wish to inject into your code. For example, if you had two objects of the same type configured in your IoC container, you’d want to be able to distinguish between those two objects when it came to injecting them into your code.
The visual representation in figure 1 helps explain this concept.
Figure 1 A @Qualifier annotation used to differentiate between two beans of the same MusicGenre type
When you use an implementation provided by one of the frameworks, you should be aware that there are rules around creating an implementation of the @Qualifier annotation:
* It must be annotated with the @Qualifier and @Retention(RUNTIME) annotations. This ensures that the qualifier is retained at runtime.
* It should typically be @Documented so that the implementation is added as part of the public Javadoc for that API.
* It can have attributes.
* It may have restricted usage if annotated with @Target; for example, it might restrict usage to fields as opposed to the default of fields and method parameters.
To bring the preceding list into perspective, here’s a brief hypothetical example of a @Qualifier implementation that an IoC container might provide for you. A music library framework might provide a @MusicGenre qualifier, which can be used by developers when they create a MetalRecordAlbumns class. The qualifier ensures that the injected Genre is of the right type.
@Documented
@Retention(RUNTIME)
@Qualifier
public @interface MusicGenre
{
Genre genre() default Genre.TRANCE;
public enum GENRE { CLASSICAL, METAL, ROCK, TRANCE }
}
public class MetalRecordAlbumns
{
@Inject @MusicGenre(GENRE.METAL) Genre genre;
}
It’s unlikely that you’ll be creating your own @Qualifier annotations, but it’s important to have a basic understanding of how the various IoC container implementations work.
One type of @Qualifier that the specification defines for all IoC containers to implement is the @Named annotation interface.
The @Named annotation
The @Named annotation interface is a specific @Qualifier that provides a contract for implementers to qualify injected objects by their names. When you combine the @Inject annotation with the qualifying @Named annotation, that specifically named object of the correct type will be injected.
In the following example, the MurmurMessage that’s named “murmur” as well as one named “broadcast” will be injected.
public class MurmurMessenger
{
@Inject @Named("murmur") private MurmurMessage murmurMessage;
@Inject @Named("broadcast") private MurmurMessage broadcastMessage;
...
}
Although there are other qualifiers that could be seen as common, it was decided that only the @Named qualifier would be implemented by all of the DI frameworks as part of JSR-330.
Another area that the various backers of the original specification came to agreement on was having a standardized interface to deal with what scopes the injected objects can live in.
The @Scope annotation
The @Scope annotation defines a contract that can be used to define how the injector (that is, the IoC container) reuses
* When no implementation of the @Scope annotation interface is declared, the injector should create an instance of the object to inject but only use that instance once for injection purposes.
* If an implementation of the @Scope annotation interface is declared, the lifespan of that injected object is defined by the implementation of that scope.
* If an injected object can be used by multiple threads in an implementation of @Scope, that injected object needs to be thread-safe.
* The IoC container should generate an exception if there is more than one @Scope annotation declared in the same class or if it discovers a @Scope annotation that it doesn’t support.
Those default behaviors give the DI frameworks some boundaries to work within when managing the lifecycles of their injected objects. Several IOC containers do support their own @Scope implementations, especially in the web frontend space (at least until JSR-299 is universally adopted in that area). Only the @Singleton annotation was deemed to be a common @Scope implementation for JSR-330, and it’s therefore also defined as a specific annotation in the specification.
The @Singleton annotation
The @Singleton annotation interface is a widely used annotation in DI frameworks. More often than not, you’re wanting to inject a value object that doesn’t change, and a singleton is an efficient solution.
The Singleton pattern
A Singleton is simply a design pattern that enforces that the instantiation of a class occurs once and once only. For more details, see Design Patterns: Elements of Reusable Object-Oriented Software, by Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides (Addison-Wesley Professional, 1994), p. 127. Do take care with the Singleton pattern, it can be an antipattern in some cases!
Most DI frameworks treat @Singleton as a hidden default. For example, if you don’t declare a scope, then by default the framework assumes you want to use a singleton. If you do declare it explicitly, you can do so in the following manner.
public MurmurMessage
{
@Inject @Singleton MessageHeader defaultHeader;
}
In this example, the assumption is that the defaultHeader never changes (it’s effectively static data) and can therefore be injected as a singleton.
Last, we’ll cover the most flexible option for when one of the standard annotations isn’t enough.
The Provider<T> interface
To give you extra control over the object being injected into your code by the DI framework, you can ask the DI framework to inject an implementation of the Provider<T> interface for that object (T) instead. This gives you the following benefits in controlling that injected object:
* You can retrieve multiple instances of that object.
* You can defer the retrieval of the object to when it’s needed (lazy loading) or even not at all.
* You can break circular dependencies.
* You can define the scope, allowing you to look up objects in a smaller scope than the entire loaded application.
The interface contains only one method, T get(), which is expected to provide a fully constructed, injected instance of the object (T). For example, you can inject an implementation of the Provider<T> interface (Provider<Message>) into the Murmur-Message constructor. This will get different Message objects based on arbitrary criteria, as the following listing demonstrates.
import com.google.inject.Inject;
import com.google.inject.Provider;
class MurmurMessage
{
@Inject MurmurMessage (Provider<Message> messageProvider)
{
Message msg1 = messageProvider.get(); #A
if (someGlobalCondition)
{
Message copyOfMsg1 = messageProvider.get(); #1
}
...
}
}
#A Get a Message
#1 Get copy of Message
Notice how you can grab further instances of the injected Message object from the Provider<Message>, as opposed to just the single instance if you had injected a Message directly. In this case, you’re using a second copy of that injected Message object, only loading it in when you need it (#1).
Summary
JSR-330 isn’t only an important standard that unifies common DI functionality, it also provides behind-the-scenes rules and limitations that you should be aware of. By studying the standard set of DI annotations, you can gain a much greater appreciation for how the various DI frameworks implement the specification, and therefore how you can use them most effectively.
1. Bob Lee. “Announcing @javax.inject.Inject” (8 May 2009). www.theserverside.com/news/thread.tss?thread_id=54499.
2. “Package javax.inject,” Javadoc, http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/packagesummary.html
3. By this, we mean you can’t use the “Generic Methods” trick as discussed in The Java Tutorials on Oracle’s website: http://download.oracle.com/javase/tutorial/extra/generics/methods.html.
4. “Annotation Type Inject,” Javadoc, http://atinject.googlecode.com/svn/trunk/javadoc/javax/inject/Inject.html.
Pingback: JavaPins
Pingback: Software Development Linkopedia July-August 2012