Rick has posted 25 posts at DZone. You can read more from them at their website. View Full User Profile

Spring Dependency Injection - An Introductory Tutorial

11.11.2008
| 188827 views |
  • submit to reddit

Spring and Annotation driven DI

Seam, and Guice pioneered the use of DI using annotation instead of XML. Spring also added this support and in typical Spring fashion, it does this in a flexible non-invasive manner.

Let's start off with a simple example. Let's say that you misconfigured the AutomatedTellerMachineImpl and forgot to inject a dependency as follows:
Opps forgot to inject the transport

        <bean id="atmTransport" class="com.arcmind.springquickstart.SoapAtmTransport" />


        <bean id="atm" class="com.arcmind.springquickstart.AutomatedTellerMachineImpl">
        </bean>

You might get an error like this:

Typical error from misconfiguring a bean

Exception in thread "main" java.lang.NullPointerException
        at com.arcmind.springquickstart.AutomatedTellerMachineImpl.withdraw(AutomatedTellerMachineImpl.java:25)
        at com.arcmind.springquickstart.AtmMain.main(AtmMain.java:14)

In a deployed application, this error could be quite cryptic. If you used the @Required annotation, you could ask Spring to scan the beans and look for missing dependencies as follows:

AutomatedTellerMachineImpl using @Required on the setter method of the transport property

import org.springframework.beans.factory.annotation.Required;

public class AutomatedTellerMachineImpl implements AutomatedTellerMachine{
        
        private ATMTransport transport;

        @Required
        public void setTransport(ATMTransport transport) {
                this.transport = transport;
        }

 

Now, when you run this after forgetting to configure a transport, you would get this message:

Caused by: org.springframework.beans.factory.BeanInitializationException: Property 'transport' is required for bean 'atm'

This is clearer and makes it easier to develop and debug applications. To enable this dependency checking feature, you must use context:component-scan or the context:annotation-config tags. This is discussed in more detail later. Here is the last example using context:annotation-config:

Application context file using annotation-config tag

<?xml version="1.0" encoding="UTF-8"?>
<beans 
        xmlns="http://www.springframework.org/schema/beans" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
                http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

        <context:annotation-config/>
        
        <bean id="atmTransport" class="com.arcmind.springquickstart.SoapAtmTransport" />


        <bean id="atm" class="com.arcmind.springquickstart.AutomatedTellerMachineImpl">
                <property name="transport" ref="atmTransport"/>
        </bean>

</beans>

Using @Autowire to define a default transport

You may want to define a default transport for an AutomatedTellerMachine. You could do this with the @Autowire and @Qualifier annotations as follows:

Using @Autowire and @Qualifier annotations to do DI

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;

public class AutomatedTellerMachineImpl implements AutomatedTellerMachine{
        
        @Autowired (required=true)
        @Qualifier ("standardTransport")
        private ATMTransport transport;

 When using Spring annotations for DI, you do not need to have setter methods (or special constructors) any longer. Spring can inject directly into private fields or you have the option of annotating the setter methods instead. The applicationContext for this example looks like this:

Many transports configured in applicationContext, no injection specified in XML

<?xml version="1.0" encoding="UTF-8"?>
<beans 
        xmlns="http://www.springframework.org/schema/beans" 
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="
                http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

        <context:annotation-config/>
        
        <bean id="soapTransport" class="com.arcmind.springquickstart.SoapAtmTransport" />
        <bean id="standardTransport" class="com.arcmind.springquickstart.StandardAtmTransport" />
        <bean id="simulationTransport" class="com.arcmind.springquickstart.SimulationAtmTransport" />


        <bean id="atm" class="com.arcmind.springquickstart.AutomatedTellerMachineImpl"/>


</beans>

Notice that no transport for injection is specified in this file. The annotations specify which transport gets injected by default. Figure 3 illustrates injection using this technique. 

 

You could override which bean gets set by using standard Spring injection. In this way, you have a default (standardTransport) that can be overridden. Here is an example of overriding with another transport when you have a setter method for transport.

Overriding the annotation in the application context file
       

        <bean id="soapTransport" class="com.arcmind.springquickstart.SoapAtmTransport" />
        <bean id="standardTransport" class="com.arcmind.springquickstart.StandardAtmTransport" />
        <bean id="simulationTransport" class="com.arcmind.springquickstart.SimulationAtmTransport" />


        <bean id="atm" class="com.arcmind.springquickstart.AutomatedTellerMachineImpl">
                <property name="transport" ref="simulationTransport"/>
        </bean>

The XML DI injection takes precedence over the annotation. Therefore, the annotation is the "reasonable default", but the application context file has the final word.

 

Avoiding hard-wiring beans directly to other beans with @Qualifier and qualifier tag


For an extra level of indirection, you can add a qualifier to a bean in the configuration file and then specify which type of transport is needed in the AutomatedTellerMachineImpl as follows:

Using @Qualifier for an extra level of indirection

public class AutomatedTellerMachineImpl implements AutomatedTellerMachine{
        
        @Autowired (required=true)
        @Qualifier ("default")
        private ATMTransport transport;

Using qualifier tag in applicationContext.xml

        <bean id="soapTransport" class="com.arcmind.springquickstart.SoapAtmTransport" />
        <bean id="standardTransport" class="com.arcmind.springquickstart.StandardAtmTransport">
                <qualifier value="default"/> 
                <!-- NOTE ADDED THIS QUALIFIER that marks this as default -->
        </bean>
        <bean id="simulationTransport" class="com.arcmind.springquickstart.SimulationAtmTransport" />


        <bean id="atm" class="com.arcmind.springquickstart.AutomatedTellerMachineImpl"/>

With this extra level of indirection, you are not hard-wiring beans directly to other beans, and if you decide that you should use a new default transport object you don't have to rewire every dependent bean.
Figure 4 illustrates injection using this technique. 



About the author

Rick Hightower is CTO of Mammatus and is an expert on Java and Cloud Computing. Rick is invovled in Java CDI advocacy and Java EE. CDI Implementations - Resin Candi - Seam Weld - Apache OpenWebBeans

Published at DZone with permission of its author, Rick Hightower.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)

Comments

Murphree Mukada replied on Thu, 2008/11/27 - 7:48am

Brilliant! Concise and clear. Thanks a million Rick.

Rick Hightower replied on Sun, 2008/11/30 - 6:33pm in response to: George Jiang

The Spring AOP article has been written and is waiting to be copy edited.

Viraf Karai replied on Sat, 2009/02/21 - 3:14pm

Well written, but it might still be a tad difficult for newcomers to Spring to grasp fully.

I'm not in the anti-XML camp, so I don't feel very comfortable with this new trend towards using annotations willy-nilly. In fact I feel that it diminishes the ability to view the big picture. I do like the new 'context' and 'p' namespaces. Some of the annotations like @Required and @Transactional have been well-thought out by the SpringSource folks and I commend them for that.

Just one final note and it's not meant to be a sales pitch - IntelliJ IDEA 8.x has brilliant Spring support and provides really good code completion as well as linking Java interfaces with Spring bean definitions. I've been using it for about 8 months now and am continually amazed at it's capabilities.

Looking forward to your future articles - especially AOP - a great passion of mine.

Rick Reumann replied on Tue, 2009/09/01 - 2:54pm

I'm really curious though, how often do people have that many different implementations at application startup time? I've been using Spring and Guice for a while now and often when I've completed my app I look back and wonder "ok, what did I really gain here having my concrete implemenations defined in an xml or single Java file?"

 I don't care much about using Mock objects in Testing either, but if I did, I could definitely see the advantage of DI there. 

 Of course I like containers doing some DI for me, like the EJB3 DI stuff etc, but in your typical CRUD app I simply find that I've added one more layer of abstraction that isn't needed. In the early days I was also always a purist and coded everything to an Interface - but in real life, how often, for example, are you swapping out Service class or DAO implemnations? 

 I feel like a heretic and wondering if I'm alone :)

 

Pradeep Kumar replied on Wed, 2012/09/19 - 12:59pm

Very nice, no words thanks much..!!

But please add spring MVC 3.0 also.

Krishna Pokala replied on Tue, 2012/10/02 - 9:08am

Superb intro to DI. Thanks Rick

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.