Skip to main content

Dependency injection in Apache Geronimo, Part 1: A new way to look at decoupling in J2EE applications

Neal Ford, Application Architect, ThoughtWorks Inc.
Neal Ford
Neal Ford is an application architect at ThoughtWorks, a global IT consultancy with an exclusive focus on end-to-end software development and delivery. He is the author of Developing with Delphi: Object-Oriented Techniques, JBuilder 3 Unleashed, and Art of Java Web Development. His primary consulting focus is on building large-scale enterprise applications. You can reach Neal through his Web site at www.nealford.com or at nford@thoughtworks.com.

Summary:  Get familiar with how dependency injection (DI) works in Apache Geronimo in this article, the first in a two-part series. Through a simple example using the lightest-weight DI container available, PicoContainer, you'll discover the theoretical underpinnings of DI without getting caught up in too much detail.

View more content in this series

Date:  07 Feb 2006
Level:  Introductory
Activity:  1907 views

Software developers have always pursued code reuse -- and for obvious reasons. The means of this pursuit have changed over time, from functions in Fortran through object-oriented programming (OOP) and inheritance toward interfaces. At each step, we've found techniques better than the previous to let us decouple code from hard dependencies. One of the best ways to promote code reuse is to decouple interfaces from implementation. In the book Art of UNIX Programming, Eric Raymond makes this point in several of the UNIX® philosophies:

  • Rule of Modularity: Write simple parts connected by clean interfaces.
  • Rule of Separation: Separate policy from mechanism; separate interfaces from engines.
  • Rule of Representation: Fold knowledge into data so that program logic can be stupid and robust.

While these ideas are old, we keep finding new ways to realize them in Java™ technology. The latest round of decoupling, dependency injection, reflects the ideals stated above. Like many new concepts implemented in different ways in different places, a lot of confusion has arisen between the concept and the implementation. In this two-part article series, I discuss the concepts of DI (also known as Inversion of Control, or IoC), then demonstrate how it's implemented in Apache Geronimo.

DI vs. IoC

One aspect of DI frameworks that causes confusion is the terminology used to describe them. You hear both Inversion of Control and dependency injection used almost interchangeably. However, they don't mean the same thing.

IoC is a general term that encompasses most frameworks. Literally, it means inverting the controlling element to what is traditionally the element it controls (the controllee). In other words, what is typically the controlling part of the framework becomes the thing controlled. For example, in the Model-View-Controller (MVC) design pattern, the controller invokes methods. In event-driven environments, the view invokes code through event handlers. Thus, the view (that is, the controllee) is taking the role of the controller.

I find the term Inversion of Control too all-encompassing. It's like lumping Java 2 Platform, Enterprise Edition (J2EE) development into software development. Yes, J2EE is software development, but it's a highly specialized version of it.

Fortunately, Martin Fowler has come to the rescue and, in collaboration with several framework builders, named this style of decoupling dependency injection. This term is much better, because it specifically describes what developers are doing. Thus, throughout this article and the next, I refer to this style of coding as dependency injection. If you read other sources that use IoC, make sure you know what the author means.


Dependency injection

Software components that rely on one another to perform work are said to be coupled together. Some level of coupling is inevitable in software development. However, you should try to keep coupling to a bare minimum. For example, when building library code, it's better to define types as interfaces rather than as concrete classes. In this way, you can change the concrete class later without changing the library code, which relies solely on the definition you created in the interface. DI provides yet another way to eliminate high coupling between components, because the container can inject a component at run time into a component that relies on it. Consider the example shown in Figure 1.


Figure 1. Simple, naive customer persistence framework
Naive Persistence Framework

In this example, the Customer Workflow class is hopelessly coupled to the Customer Persistence class, which in turn is tied to a database. The technical term for this architecture is yikes! Fortunately, developers in the Java world avoid this type of high coupling. In fact, avoiding this style of coupling was one of the motivating factors behind the creation of Enterprise JavaBeans (EJB) technology.

You can achieve some code separation if you represent the concrete Customer Persistence class as an interface instead. Figure 2 shows an improved version of the same structure.


Figure 2. Using interfaces to decouple relationships
Decoupling with interfaces

Forcing the dependencies on interfaces allows you to provide a variety of implementation classes that implement the interface's contract. In this case, you could have one Customer Persistence class that reads from an XML document and another that uses a relational database. The workflow class doesn't know (or care) which mechanism you use.

Diversion into EJB land

Fast-forward some logical conclusions later, and you arrive at code reuse by means of DI through interfaces, factories, and pooled (proxied) objects, otherwise known as EJB technology. Figure 3 shows the same relationship depicted in a typical EJB relationship.


Figure 3. EJB's version of decoupling with interfaces
EJB-style dependencies

Do you really need all that? EJB technology certainly provided a level of decoupling, but at what price? The authors of the EJB specification used the tools that were in vogue at the time: inheritance, interfaces, and design patterns. And they tried to solve every problem that a developer would face when writing an enterprise application by baking it into the framework. What you don't see in Figure 3 is object pooling, automatic transaction processing, security, and all the other good things that EJB technology provides. The only problem is that I didn't ask for those facilities -- the EJB framework just included them. Consequently, my simple decoupling problem is now a big problem.

You can see why there's a backlash against this style of decoupling. As Bruce Tate so eloquently stated in a blog entry, to create a simple application, "I don't want to have to eat the whole elephant." Prescriptive approaches that force you to implement elements that you don't need never work. They tend to drag a lot of complexity along with their benefit; sooner or later, the complexity outweighs the benefit you thought you were getting from the framework.


Decoupling through DI

With the perspective of hindsight, EJB 2.0 is not the correct way to solve these problems. There must be a simpler way to decouple applications without adding needless bulk and complexity. The current thinking on how to do this lies with DI. In fact, EJB 3 takes this approach, as does Geronimo, which eschews all the heavyweight framework in favor of a cleaner technique.

Before looking at Geronimo and how it implements DI, take a look at a simpler container. The following examples use PicoContainer, an open source container developed by ThoughtWorks that performs DI and nothing else. This container shows how injection works in isolation, which leads to how Geronimo works and the second article in this series.


Constructor injection and PicoContainer

Two major schools of thought revolve around how to perform DI: constructor injection and setter injection. Constructor injection uses a constructor to determine what type of concrete object to return. Setter injection injects types through set() methods.

In PicoContainer, you inject dependencies through the constructor. For example, Figure 2 shows a Workflow class that needed to create customer objects by means of a persistence mechanism. For this example, there are two different finder classes: FinderFromFile, which uses an XML file for its searching, and FinderFromDb, which uses a relational database. Even though this example is small, it uses several files, which are summarized in Table 1.

Table 1. Files in the PicoContainer example
FileTypePurpose
CustomerListerClassClass that uses one of the finder types to locate customers by name
FinderFromFileClassImplementation of a CustomerFinder interface, which looks for customers in a text file
CustomerFinderInterfaceInterface that defines the semantics of something that can find Customer objects
CustomerClassThe subject of this search; encapsulates customer information
CustomerWorkflowClassController class for the application; initializes the container
TestCustomerListingClassUnit test to exercise the customer finder

Figure 4 illustrates the relationships between these classes.


Figure 4. Relationship between PicoContainer example classes
Class relationships

The code

The following code listings show how DI works in PicoContainer.

CustomerFinder

The CustomerFinder interface makes DI possible. For DI to work, you must have an interface whose concrete class you can inject into the consumer of the behavior you desire. In this case, the CustomerFinder interface defines the method find(String name), as shown in Listing 1.


Listing 1. Interface defining how to find customers
public interface CustomerFinder {
    Customer find(String name);
}                           


This interface defines the semantics of how you find customers without exposing the details of performing the actual search. (The details are different depending on whether you use an XML file or a database.)

FinderFromFile

The FinderFromFile concrete class implements the FinderFromFile interface. This class, shown in Listing 2, is responsible for finding a customer by name from within a flat file.


Listing 2. Class implementing find(String name) for flat files
public class FinderFromFile implements CustomerFinder {
    private String fileName;
    
    public FinderFromFile(String fileName) {
        this.fileName = fileName;
    }
    public Customer find(String name) {
        // . . . details omitted
    }
}


CustomerLister

Next, you define the class that is responsible for calling the finder class -- CustomerLister, shown in Listing 3. This is the class whose dependency (that is, which implementation of the CustomerFinder interface to use) is injected.


Listing 3. Class whose finder behavior the container injects
public class CustomerLister {
    private CustomerFinder finder;

    public CustomerLister(CustomerFinder finder) {
        this.finder = finder;
    }

    public Customer findCustomerByName(String name) {
        return finder.find(name);
    }
}

In Listing 3, you can see that the CustomerLister constructor accepts an instance of a class that implements the CustomerFinder interface. The container injects this instance to this CustomerLister. This behavior is referred to in the DI world as constructor injection, because the instance is passed through one of the constructors.

The alternative (and more widely used) behavior is setter injection, where dependent classes are injected through set() methods. PicoContainer supports both types of injection, and the only real difference is whether you allow the construction of a class with explicit dependencies when those dependent classes may not be available. The theory here is that if you can't inject the dependency, you can't construct the dependent class. Theory aside, functionally there is no difference between these two styles of injection.

Listing 4 shows what the CustomerFinder interface would look like if setter injection were used instead.


Listing 4. CustomerFinder with setter injection
public class CustomerLister {
    private CustomerFinder finder;

    public CustomerLister() {
    }

    public void setFinder(CustomerFinder finder) {
        this.finder = finder;
    }
}

CustomerWorkflow

The next class of interest is the CustomerWorkflow class, which acts as the controller in this example. This class configures PicoContainer in its configureContainer() method. This method in turn creates the container (which acts as a singleton) and registers components and their parameters. The CustomerWorkflow class is shown in Listing 5.


Listing 5. Controller for this example (CustomerWorkflow configures the container)
public class CustomerWorkflow {

    public MutablePicoContainer configureContainer() {
        MutablePicoContainer pico = new DefaultPicoContainer();
        Parameter[] finderParams =  
	        {new ConstantParameter("customerListing.xml")};
        pico.registerComponentImplementation(CustomerFinder.class, 
	        FinderFromFile.class, finderParams);
        pico.registerComponentImplementation(CustomerLister.class);
        return pico;
           }
}

Notice that the configuration of PicoContainer allows you to specify parameters to the components you inject. (In Geronimo, these components are referred to as GBeans.) Note also that the configuration is done in Java code, not in an XML document (like the Spring container). If you prefer to work in XML, consider using NanoContainer, which is an open source container similar to PicoContainer. In either case, you have now set up your dependencies for injection. Now you need merely to call the code that needs those dependencies and allow the container to perform its work.

TestCustomerFinder

The last piece of the puzzle is the class that drives the process. This example uses a unit test to verify that everything works as expected. The TestCustomerListing class (shown in Listing 6) ties everything together.


Listing 6. Test class that demonstrates injection
public class TestCustomerListing extends TestCase {
    private CustomerWorkflow workflow;

    public void setup() {
        workflow = new CustomerWorkflow();
    }

    public void teardown() {
        workflow = null;
    }

    public void testCustomerFinder() {
        MutablePicoContainer pico = workflow.configureContainer();
        CustomerLister lister = (CustomerLister) 
                pico.getComponentInstance(CustomerLister.class);
        Customer foundCustomer = 
                lister.findCustomerByName("Homer");
        assertEquals("Homer", foundCustomer.getName());
    }
}

The TestCustomerListing class creates an instance of the Workflow class in the setup() method, then invokes the configureContainer() method. The class then uses PicoContainer to deliver the CustomerLister class that has the correct dependency (in this case, the FinderFromFile finder class) to the lister object, which in turn gets a reference to the named customer.

Despite the number of moving parts, this example demonstrates the decoupling power of DI. To create a completely new way to persist customers, you can still locate a customer just by extending the interface and adding configuration code to the container. In this way, you get a level of decoupling that you can't achieve using just OOP primitives built into the language.


Looking ahead

One of the difficulties in understanding a topic like DI is the amount of coupling this topic has with other, unrelated topics. For example, studying Geronimo to learn about DI is difficult, because Geronimo contains a lot of moving parts that have nothing to do with DI. In this article, I decoupled the topic away from the implementation, choosing the smallest software stack available to investigate the characteristics of DI. I discussed the motivation and definition of DI and showed an example of how a container can inject dependencies from one component into another.

In Part 2 of this series, I step away from PicoContainer and move to Geronimo, demonstrating how the same principles at work in this simple example are applicable to something as complex as a J2EE application server. Geronimo gives you the same level of decoupling as PicoContainer but comes with a host of predefined service hooks to allow you to inject much more complex behaviors.


Resources

Learn

Get products and technologies

  • Download and experiment with PicoContainer.

  • Download NanoContainer to create the examples in this article. NanoContainer is related to PicoContainer (written by some of the same developers) but has a slightly different philosophy and more out-of-the-box capabilities.

  • Innovate your next open source development project with IBM trial software, available for download or on DVD.

  • Download Apache Geronimo, Version 1.0.

  • Download your free copy of IBM WebSphere® Application Server Community Edition V1.0 -- a lightweight J2EE application server built on Apache Geronimo open source technology that is designed to help you accelerate your development and deployment efforts.

Discuss

About the author

Neal Ford

Neal Ford is an application architect at ThoughtWorks, a global IT consultancy with an exclusive focus on end-to-end software development and delivery. He is the author of Developing with Delphi: Object-Oriented Techniques, JBuilder 3 Unleashed, and Art of Java Web Development. His primary consulting focus is on building large-scale enterprise applications. You can reach Neal through his Web site at www.nealford.com or at nford@thoughtworks.com.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source, Java technology, WebSphere
ArticleID=103405
ArticleTitle=Dependency injection in Apache Geronimo, Part 1: A new way to look at decoupling in J2EE applications
publish-date=02072006
author1-email=nford@thoughtworks.com
author1-email-cc=nford@thoughtworks.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers