Monday, 23 August 2010

Dependency Injection in Java 7

Hi all,


So apart from recovering from a long bout of the stomach flu, enjoying good food again at a "Come dine with me" night (our parents would be so proud) and planning a rush trip to Vegas I've managed to get out a first draft of the Dependency Injection chapter for the Well Grounded Java 7 Developer.


Java 7 unifies some of the basic standards that the various Dependency Injection frameworks (Spring, Guice, PicoContainer etc) have, making it easier for developers to move between the frameworks as needed.


A reminder of what DI is with regards to Java[1]

The javax.inject package specifies a means for obtaining objects in such a way as to maximize re-usability, 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.


So without further preamble, here's a short section from the chapter, let me know what you think!


Inject annotation

The @Inject annotation interface can be used in three places to indicate where you'd like a dependency to be injected.  Below are the types of members that can be injected, in the order that they are processed at runtime:

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 e.g:

@Inject public MurmurMessage(Header header, Content content)
{
    this.header = header;
    this.content = content;
}

In this case both the Header and Content parameters would be injected at runtime.  The specification allows for 0+ 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 0+ number of parameters to be injected at runtime.  There are some restrictions in that injected methods cannot be declared abstract and cannot declare type parameters of their own[1].  The short code sample below demonstrates the use of @Inject with a setter method, a common technique when using Dependency Injection.

@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 job.  For example you could pass a 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 used setter injection for non-mandatory dependencies, e.g. fields that already have sensible defaults.

It is also possible to inject fields (as long as they are not final), however the practice is not common.  The syntax again is quite simple.

public class MurmurMessenger
{
    @Inject private MurmurMessage murmurMessage;
    ...
}

You can read further about the @Inject annotation in the Javadoc, where you can discover some nuances about what types of values can be injected and how circular dependencies are dealt with.

Cheers,
Martijn

4 comments:

  1. Hi Martijn,

    Just a couple of nit picks.

    - Your example has Content as a parameter, which you later refer to as Context.

    - You use the term argument, when it might be strictly more correct to use the term parameter (See: http://en.wikipedia.org/wiki/Parameter_(computer_science)#Parameters_and_arguments )

    ReplyDelete
  2. Thanks for the catch Stephen! I've updated the entry.

    ReplyDelete
  3. Hi Martijn

    You wrote: "You can annotate a constructor with @Inject and expect its parameters to be provided at runtime by your "configured" IoC container"

    Does it mean that Java 7, in order to provide dependency injection, relies on external IoC containers like Spring, Pico, etc?

    Can Java 7 provide DI on its own? Could I remove my IoC containers and use a pure Java 7 implementation?

    Thanks for the post. Look forward for your book.

    ReplyDelete
  4. Hi Sandro,

    Yes that's correct, the Java 7 DI specification relies on an external IoC container. However, you can annotate your POJOs with pure Java 7 annotations which means you can move between containers at will (no container lock-in).

    Spring 3 and Guice 2 already support this standard as it were, so you can try it out early.

    Cheers,
    Martijn

    ReplyDelete