July 27, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

A Spring Jump Start

  • April 13, 2005
  • By Craig Walls and Ryan Breidenbach
  • Send Email »
  • More Articles »

The BeanFactory class used here is the Spring container. After loading the hello.xml file into the container, the main() method calls the getBean() method on the BeanFactory to retrieve a reference to the greeting service. With this reference in hand, it finally calls the sayGreeting() method. When we run the Hello application, it prints (not surprisingly)

Buenos Dias!

This is about as simple a Spring-enabled application as we can come up with. But it does illustrate the basics of configuring and using a class in Spring. Unfortunately, it is perhaps too simple because it only illustrates how to configure a bean by injecting a String value into a property. The real power of Spring lies in how beans can be injected into other beans using IoC.

1.4 Understanding inversion of control

Inversion of control is at the heart of the Spring framework. It may sound a bit intimidating, conjuring up notions of a complex programming technique or design pattern. But as it turns out, IoC is not nearly as complex as it sounds. In fact, by applying IoC in your projects, you'll find that your code will become significantly simpler, easier to understand, and easier to test.

But what does "inversion of control" mean?

1.4.1 Injecting dependencies

In an article written in early 2004, Martin Fowler asked what aspect of control is being inverted. He concluded that it is the acquisition of dependent objects that is being inverted. Based on that revelation, he coined a better name for inversion of control: dependency injection.3

Any nontrivial application (pretty much anything more complex than HelloWorld.java) is made up of two or more classes that collaborate with each other to perform some business logic. Traditionally, each object is responsible for obtaining its own references to the objects it collaborates with (its dependencies). As you'll see, this can lead to highly coupled and hard-to-test code.

Applying IoC, objects are given their dependencies at creation time by some external entity that coordinates each object in the system. That is, dependencies are injected into objects. So, IoC means an inversion of responsibility with regard to how an object obtains references to collaborating objects.

1.4.2 IoC in action

If you're like us, then you're probably anxious to see how this works in code. We aim to please, so without further delay...

Suppose that your company's crack marketing team culled together the results of their expert market analysis and research and determined that what your customers need is a knight. That is, they need a Java class that represents a knight. After probing them for requirements, you learn that what they specifically want is for you to implement a class that represents an Arthurian knight of the Round Table that embarks on brave and noble quests to find the Holy Grail.

This is an odd request, but you've become accustomed to the strange notions and whims of the marketing team. So, without hesitation, you fire up your favorite IDE and bang out the class in listing 1.5.

Listing 1.5 KnightOfTheRoundTable.java

In listing 1.5 the knight is given a name as a parameter of its constructor. Its constructor sets the knight's quest by instantiating a HolyGrailQuest. The implementation of HolyGrailQuest is fairly trivial, as shown in listing 1.6.

Listing 1.6 HolyGrailQuest.java

package com.springinaction.chapter01.knight;
public class HolyGrailQuest {
   public HolyGrailQuest() {}

   public HolyGrail embark() throws GrailNotFoundException {
      HolyGrail grail = null;
      // Look for grail
      ...
      return grail;
   }
}

Satisfied with your work, you proudly check the code into version control. You want to show it to the marketing team, but deep down something doesn't feel right. You almost dismiss it as the burrito you had for lunch when you realize the problem: you haven't written any unit tests.

Knightly testing

Unit testing is an important part of development. It not only ensures that each individual unit functions as expected, but it also serves to document each unit in the most accurate way possible. Seeking to rectify your failure to write unit tests, you put together the test case (listing 1.7) for your knight class.

Listing 1.7 Testing the KnightOfTheRoundTable

package com.springinaction.chapter01.knight;

import junit.framework.TestCase;

public class KnightOfTheRoundTableTest extends TestCase {

   public void testEmbarkOnQuest() {
      KnightOfTheRoundTable knight =
         new KnightOfTheRoundTable("Bedivere");

      try {
         HolyGrail grail = knight.embarkOnQuest();

         assertNotNull(grail);

         assertTrue(grail.isHoly());
      } catch (GrailNotFoundException e) {
         fail();
      }
   }
}

After writing this test case, you set out to write a test case for HolyGrailQuest. But before you even get started, you realize that the KnightOfTheRoundTableTest test case indirectly tests HolyGrailQuest. You also wonder if you are testing all contingencies. What would happen if HolyGrailQuest's embark() method returned null? Or what if it were to throw a GrailNotFoundException?

Who's calling who?

The main problem so far with KnightOfTheRoundTable is with how it obtains a HolyGrailQuest. Whether it is instantiating a new HolyGrail instance or obtaining one via JNDI, each knight is responsible for getting its own quest (as shown in figure 1.2). Therefore, there is no way to test the knight class in isolation. As it stands, every time you test KnightOfTheRoundTable, you will also indirectly test HolyGrailQuest.

Figure 1.2 A knight is responsible for getting its own quest, through instantiation or some other means.

What's more, you have no way of telling HolyGrailQuest to behave differently (e.g., return null or throw a GrailNotFoundException) for different tests. What would help is if you could create a mock implementation of HolyGrailQuest that lets you decide how it behaves. But even if you were to create a mock implementation, KnightOfTheRoundTable still retrieves its own HolyGrailQuest, meaning you would have to make a change to KnightOfTheRoundTable to retrieve the mock quest for testing purposes (and then change it back for production).

Decoupling with interfaces

The problem, in a word, is coupling. At this point, KnightOfTheRoundTable is statically coupled to HolyGrailQuest. They're handcuffed together in such a way that you can't have a KnightOfTheRoundTable without also having a HolyGrailQuest.

Coupling is a two-headed beast. On one hand, tightly coupled code is difficult to test, difficult to reuse, difficult to understand, and typically exhibits "whack-a-mole" bugs (i.e., fixing one bug results in the creation of one or more new bugs). On the other hand, completely uncoupled code doesn't do anything. In order to do anything useful, classes need to know about each other somehow. Coupling is necessary, but it should be managed very carefully.

A common technique used to reduce coupling is to hide implementation details behind interfaces so that the actual implementation class can be swapped out without impacting the client class. For example, suppose you were to create a Quest interface:

package com.springinaction.chapter01.knight;

public interface Quest {
   public abstract Object embark() throws QuestException;
}

Then, you change HolyGrailQuest to implement this interface. Also, notice that embark now returns an Object and throws a QuestException.

package com.springinaction.chapter01.knight;

public class HolyGrailQuest implements Quest {
   public HolyGrailQuest() {}

   public Object embark() throws QuestException {
      // Do whatever it means to embark on a quest
      return new HolyGrail();
   }
}

Also, the following method must also change in KnightOfTheRoundTable to be compatible with these Quest types:

private Quest quest;
...
public Object embarkOnQuest() throws QuestException {
   return quest.embark();
}

Likewise, you could also have KnightOfTheRoundTable implement the following Knight interface:

public interface Knight {
   public Object embarkOnQuest() throws QuestException;
}

Hiding your class's implementation behind interfaces is certainly a step in the right direction. But where many developers fall short is in how they retrieve a Quest instance. For example, consider this possible change to KnightOfTheRoundTable:

public class KnightOfTheRoundTable implements Knight {

   private Quest quest;
   ...

   public KnightOfTheRoundTable(String name) {
      quest = new HolyGrailQuest();
   ...
   }

   public Object embarkOnQuest() throws QuestException {
      return quest.embark();
   }
}

Here the KnightOfTheRoundTable class embarks on a quest through the Quest interface. But, the knight still retrieves a specific type of Quest (here a HolyGrailQuest). This isn't much better than before. A KnightOfTheRoundTable is stuck going only on quests for the Holy Grail and no other types of quest.





Page 3 of 4



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel