Inversion of Control and Dependency Injection


In Chapter 1, you learned about Inversion of Control and why it is important. Let's recap briefly what this term really means, and then look at a few examples as they apply to the Spring container.

Software code is normally broken up into logical components or services that interact with each other. In Java, these components are usually instances of Java classes, or objects. Each object must use or work with other objects in order to do its job. For an object A, it can be said that the other objects that object A deals with are its dependencies. Inversion of Control refers to the generally desirable architectural pattern of having an outside entity (the container in this case) wire together objects, such that objects are given their dependencies by the container, instead of directly instantiating them themselves.

Take a look at some examples.

Note 

Most larger examples in this chapter, even when not listed in complete form, are available separately in fully compilable form so that you may experiment with them. See the website.

Assume we have a historical weather data service, coded in a traditional, non-IoC style:

 public class WeatherService {        WeatherDAO weatherDao = new StaticDataWeatherDAOImpl();        public Double getHistoricalHigh(Date date) {     WeatherData wd = weatherDao.find(date);     if (wd != null)       return new Double(wd.getHigh());     return null;   } }      public interface WeatherDAO {   WeatherData find(Date date);   WeatherData save(Date date);   WeatherData update(Date date); }      public class StaticDataWeatherDAOImpl implements WeatherDAO {        public WeatherData find(Date date) {     WeatherData wd = new WeatherData();     wd.setDate((Date) date.clone());     ...     return wd;   }        public WeatherData save(Date date) {     ...   }   public WeatherData update(Date date) {     ...   } } 

Following good practice, we will have a test case to verify that this code works. Based on JUnit, it might look as follows:

 public class WeatherServiceTest extends TestCase {   public void testSample1() throws Exception {     WeatherService ws = new WeatherService();     Double high = ws.getHistoricalHigh(             new GregorianCalendar(2004, 0, 1).getTime());     // ... do more validation of returned value here...   } } 

Our weather service deals with a weather data DAO (Data Access Object) in order to get historical data. It deals with the DAO through a WeatherDAO interface, but in this example, the weather service directly instantiates a specific known type of weather DAO that implements that interface, StaticDataWeatherDAOImpl. Additionally, our driver application, WeatherServiceTest, directly uses a specific WeatherService class, with no possibility of specialization. This example is coded in non-IoC style. While the weather service does deal with the weather DAO through an interface, the weather service is directly instantiating a specific DAO type, and is controlling the lifecycle; thus it has a dependency on both the DAO interface and a specific implementation class. Additionally, the test sample representing the client of the service directly instantiates a specific weather service type, instead of dealing with it through an interface. In a real application, further implicit dependencies are likely — for example, on a particular persistence framework — and with this approach, they will be hard-coded in the caller code.

Now let's look at a simple example of how a Spring container can provide Inversion of Control. We first rework our weather service so that it is split up into an interface and implementation and allow the specific DAO instance to be set into that implementation object as a JavaBean property:

 public interface WeatherService {    Double getHistoricalHigh(Date date); }     public class WeatherServiceImpl implements WeatherService {        private WeatherDAO weatherDao;        public void setWeatherDao(WeatherDAO weatherDao) {     this.weatherDao = weatherDao;   }        public Double getHistoricalHigh(Date date) {     WeatherData wd = weatherDao.find(date);     if (wd != null)       return new Double(wd.getHigh());     return null;   } }     // same class as in previous example  public class StaticDataWeatherDAOImpl implements WeatherDAO {     ...  }

We are going to use a Spring application context, specifically ClasspathXmlApplicationContext, to manage an instance of the weather service and ensure that it is given an instance of the weather DAO to work with. First we define a configuration file in XML format, applicationContext.xml:

 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"     "http://www.springframework.org/dtd/spring-beans.dtd">      <beans>   <bean  >     <property name="weatherDao">       <ref local="weatherDao"/>     </property>   </bean>   <bean  >   </bean> </beans> 

We configure a weatherService object using the bean element, and specify that its weatherDao property should be set to an instance of the weatherDao bean, which we also define. Now we just need to modify the test to create the container and obtain the weather service from it:

public class WeatherServiceTest extends TestCase {   public void testSample2() throws Exception {     ApplicationContext ctx = new ClassPathXmlApplicationContext(         "ch03/sample2/applicationContext.xml");     WeatherService ws = (WeatherService) ctx.getBean("weatherService");         Double high = ws.getHistoricalHigh(         new GregorianCalendar(2004, 0, 1).getTime());     // ... do more validation of returned value here...    } } 

The classes are now coded and deployed in an IoC style. The weather service does not know or care about the implementation details of the actual weather DAO, on which it has a dependency solely through the WeatherDAO interface. Additionally, splitting the original WeatherService class into a WeatherService interface and WeatherServiceImpl implementation class shields clients of the weather service from implementation details such as how the weather DAO is provided to the weather service, through a JavaBeans style setter method in this case. It has the added benefit of allowing the actual weather service implementation itself to be transparently changed. At this point we may switch the actual WeatherDAO implementation and/or the WeatherService implementation, with only configuration file changes and clients of these components being unaware of the changes. This is an example of using interfaces when code in one layer uses code from another layer, as mentioned in Chapter 1.

Different Forms of Dependency Injection

The term Dependency Injection describes the process of providing a component with the dependencies it needs, in an IoC fashion, because the dependencies can be said to effectively be injected into the component. The form of dependency injection we saw in the previous example is called Setter Injection because JavaBean setter methods are used to supply the dependency to the object needing them. Please refer to http://java.sun.com/products/javabeans for more information about JavaBeans and the JavaBeans specification.

Now let's look at a variant of this, called Constructor Injection, where dependencies are instead supplied to an object via that object's own constructor:

public interface WeatherService {    Double getHistoricalHigh(Date date); } public class WeatherServiceImpl implements WeatherService {       private final WeatherDao weatherDao;       public WeatherServiceImpl(WeatherDao weatherDao) {     this.weatherDao = weatherDao;   }       public Double getHistoricalHigh(Date date) {     WeatherData wd = weatherDao.find(date);     if (wd != null)       return new Double(wd.getHigh());     return null;   } }     // WeatherDAO unchanged public interface WeatherDAO {   ... } // StaticDataWeatherDAOImpl unchanged public class StaticDataWeatherDAOImpl implements WeatherDAO {   ... }

WeatherServiceImpl now has a constructor that takes WeatherDAO, instead of a setter method that takes this value. The application context configuration is modified accordingly. The test class is oblivious to the changes and does not have to be modified at all:

<beans>   <bean  >     <constructor-arg>       <ref local="weatherDao"/>     </constructor-arg>   </bean>   <bean  >   </bean> </beans>     // WeatherServiceTest unchanged public class WeatherServiceTest extends TestCase {   ... }

Method Injection, the final form of dependency injection we are going to look at, is the most rarely used. In this form, the container is responsible for implementing methods at runtime. For example, an object might define a protected abstract method, and the container might implement it at runtime to return an object resulting from a container lookup. The aim of method injection is, again, to avoid dependencies on the container APIs, and reduce coupling.

One of the best uses of method injection is to handle the case where a singleton, stateless object, needs to use a non-singleton, stateful, or non-threadsafe object. Consider our weather service, of which there is only one instance needed because our implementation is stateless, and as such it is deployed with the default Spring container handling of being treated as a singleton, with only one instance ever being created, which is then cached and reused. What if the weather service needs to use StatefulWeatherDAO, a WeatherDAO implementation that is not threadsafe? On every invocation of WeatherService.getHistoricalHigh(), the weather service needs to use a fresh instance of the weather DAO (or at least make sure no other weather service invocation is using the same DAO at the same time). As you'll shortly see, we can easily tell the container to make the DAO a non-singleton object, so that each request for it returns a new instance, but we have a problem because the one instance of the weather service is injected with its dependencies only once. One option is for the weather service to be aware of the Spring container, and ask for a new DAO whenever it needs one. This, however, couples it to Spring where ideally it would not know anything about Spring at all. Instead, we can rely on Spring's Lookup Method Injection support, making the weather service access the weather DAO via a getWeatherDAO() JavaBean method, which can remain abstract or concrete as desired. Then in the factory definition, we tell the container to override this method and provide an implementation that returns the new DAO instance as another bean:

 public abstract class WeatherServiceImpl implements WeatherService {        protected abstract WeatherDao getWeatherDao();       public Double getHistoricalHigh(Date date) {     WeatherData wd = getWeatherDao().find(date);     if (wd != null)       return new Double(wd.getHigh());     return null;   } }     <beans>   <bean  >     <lookup-method name="getWeatherDao" bean="weatherDao"/>   </bean>   <bean  singleton="false"         >   </bean> </beans>

Note that we have told the container to make the DAO a non-singleton, so that a new instance can actually be returned on each invocation of getWeatherDao(). Otherwise the same cached singleton instance would be returned each time. While this is legal, that's probably almost never what you want because the main benefit of using Lookup Method Injection, as in this case, is to inject prototypes. In this example, the WeatherServiceImpl class and getWeatherDao method are abstract, but we could override any getter method on any JavaBean. (In fact, candidate methods simply need to have no arguments; it is not necessary for them to follow JavaBeans naming conventions, although it's often a good idea.) The important point is that other code must use that getter method to access the DAO, not a field. This is, in any case, a good practice for any JavaBean property.

The use of Method Injection raises the question of how you would test the code without the container being around to inject the method, such as in a unit test. This may seem especially relevant for the case, such as this one, where the implementation class is abstract. One realistic and relatively simple strategy is for the purposes of the unit test to just subclass the abstract class, providing a test-specific implementation of the normally injected method; for example:

 ... WeatherService ws = new WeatherServiceImpl() {   protected WeatherDao getWeatherDao() {     // return a value for the test     ...   } }; 

The use of some advanced features such as Method Injection should always be subject to scrutiny by Spring users, who ultimately should decide which approach feels cleanest to them, but generally we feel this approach is preferred to putting in a code-level dependency on the container into application code.

Deciding Between Setter Injection and Constructor Injection

When using existing classes, you will sometimes not even have a choice as to whether to use setter injection or constructor injection. If a class has only a multi-argument constructor, and no JavaBean properties, or alternately only a no-arg constructor and simple JavaBean properties, the choice has effectively been made for you. Additionally, some existing classes may actually force you to use both forms in combination, with a multi-arg constructor that needs to be used, followed by the setting of some optional JavaBean properties.

When you do have a choice as to which form to architect for or to use, there are a number of aspects to consider:

  • Using JavaBean properties generally makes it easier to handle default or optional values, in the case that not all values are actually needed. In the constructor case, this usually leads to multiple constructor variants, with one calling another internally. The many variations or long argument lists can become very verbose and unmanageable.

  • JavaBean properties (as long as they are not private) are automatically inherited by subclasses, while constructors are never inherited. The latter limitation often leads to the need to create relatively boilerplate constructors in subclasses, which call superclass constructors. Most IDEs, however, do now include code completion aids that make creation of either constructors or JavaBean properties fairly effortless.

  • JavaBean properties are arguably better at being self-documenting than constructor arguments, at the source level. When adding JavaDocs, properties require less work to document as there is no duplication.

  • At runtime, JavaBean properties may be used for matching based on name because properties have names visible by reflection. However, in a compiled class file, constructor argument names are not retained, so that automatic matching based on name is not possible.

  • JavaBean properties allow getting the current state (as well as setting it) if a getter method is provided. This is useful in a number of situations, such as when the state needs to be stored elsewhere.

  • The JavaBeans PropertyEditor mechanism exists for performing automatic type conversion when needed. This is in fact used and supported by Spring.

  • JavaBean properties can be mutable because a setter method can be called multiple times. This allows changing a dependency if the use case actually supports it. Dependencies passed in via constructors cannot be mutable unless also exposed as properties. If, on the other hand, complete immutability is required, then Constructor Injection allows you to also explicitly declare the field set from a constructor argument as final, whereas the best that a setter method can do is throw an exception if it is called more than once; there is no way to make the field storing the value from the setter method final, so that other class code may not modify it.

  • Constructor arguments make it easier to ensure a valid object is constructed because all required values can be declared and thus must be passed in, and the class is free to use them in the order they are needed, in the process of initialization. With JavaBean properties, there is the possibility that some properties have not been set before the object is used, resulting in an invalid state. Additionally, with JavaBean properties, there is no way to specify and enforce that setters must be called in a certain order, which may mean that initialization may need to take place after property setting, via the use of an init method. If used, such an init method can also verify that all needed properties have been set. Spring can automatically call any declared initialization method after all properties have been set, or alternately beans may implement the InitializingBean interface, which declares an afterPropertiesSet() method that will automatically be called.

  • When there are only a few constructors, this can sometimes be less verbose than many JavaBean property accessor methods. Most IDEs, however, do now include code completion aids, which make creation of either constructors or JavaBean properties fairly effortless.

Generally, the Spring team prefers the use of setter injection over constructor injection for most cases in practice, although this decision should not be a hard and fast one. The aspects mentioned should provide most of the factors used to make the decision in each particular situation. Typically, constructor injection seems to work well for simpler initialization scenarios, with perhaps a constructor taking only a few arguments, which are ideally (easier to match) complex types that are not duplicated. As the configuration complexity scales up, setter injection seems to become more manageable and less work. Note that other major IoC containers also support both constructor and setter injection, so the possibility of Spring lock-in should not be a concern when making this choice.

Of course, whatever form is being used, this should really affect only the implementation class and actual configuration of that class. Most other code in the application should be working against interfaces, and should be completely unaffected by configuration concerns.



Professional Java Development with the Spring Framework
Professional Java Development with the Spring Framework
ISBN: 0764574833
EAN: 2147483647
Year: 2003
Pages: 188

Similar book on Amazon

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net