Better J2EEing with Spring
by Peter Braswell
07/11/2005
Abstract
J2EE programming is becoming trickier—much trickier. J2EE has exploded into a complex network of APIs, complicating programming, and configuration. To address some of this complexity, new frameworks and methodologies are emerging. These frameworks rely heavily on a concept called IoC, or Inversion of Control. This article explores some features and benefits of this approach as it relates to, and eases, J2EE programming.
Introduction
Mark Twain said in his famous quote "...the report of my death was an exaggeration." There have been rumblings around the Net and popular geek culture of the irrecoverable complexities of the J2EE API and the imminent death of EJB as a component architecture. This is an easy position to take from the standpoint of academic or perhaps fanciful thinking, but the truth is that the J2EE/EJB API has experienced a Darwinian evolution over what once was. Those of you who have a DCOM or CORBA project under your belts know what I mean. Back in the day, the promise of the EJB component model was a welcome breath of fresh air. The fact is, there is a tremendous investment out there in "corporateville" in all things J2EE. It may feel righteous to declare that we must throw away all previous work and retool, but that kind of thinking isn't grounded in good business acumen. EJB continues to evolve and with it idioms, practices, and frameworks spring up that complement the J2EE API. I didn't really say "spring up," did I?
I earn my lunch money as a consultant building large-scale, distributed, and often, J2EE applications. As such, I have the opportunity to see many projects go through their full lifecycle. I have the added benefit of carrying what I learned right out the door of a completed engagement straight into a brand new project. In a sense my "natural selection" process is accelerated. I can say that recently Spring (and in more general terms IoC or Inversion of Control) has been creeping into my projects more and more. What I propose in this article is Spring as an enabler or enhancer for J2EE projects. Spring, quite literally, is a framework that can standardize a lot of J2EE best practices and can homogenize many ubiquitous J2EE patterns. What follows is a tour of a small part of the Spring universe highlighting those things that, in my humble opinion, can help make your J2EE applications better J2EE apps.
A Brief Introduction to What It Means to IoC
In general, IoC is a technique for managing associations between classes. Yes, it's that simple! No person is an island and the same is true for individual objects. Objects in applications depend on other objects. Rendering this programmatically is often tedious and error-prone. A good IoC framework will assist you in stitching together the dependencies of your application declaratively (via an XML configuration file), not programmatically, which tends to be brittle.
The liberal use of interfaces is a mantra of IoC development. Programming to interfaces augments declarative associations in a way that adds tremendous flexibility to your application. Interface implementation is declared at runtime through your IoC configuration, allowing associations to be "rewired" with little or no impact to the actual application code. This is a recurring theme in the various IoC frameworks and, in general, is a good practice to follow.
A Small Example
I tend to gain an understanding of concepts more quickly by looking at examples. What follows is a series of examples that utilize IoC; as you'll see, the examples get progressively more complex. The way most of us start out with IoC containers is by exploiting their ability to inject dependencies—that is, to declaratively associate one object with another. Utilizing IoC helps you create cleaner code that is generally more flexible and easier to rewire should that become necessary. IoC benefits extend beyond dependency injection, but extended capabilities really start with dependency injectors.
We'll start by building a simple dependency injection example. This first example showcases two concepts already mentioned. The first is IoC's ability to build and wire objects at runtime, and the second is the flexibility this yields when combined with coding to interfaces. Let's start by assuming an architect hands you the UML show in Figure 1.
Figure 1. Pluggability via interfaces
This small example represents a system whose purpose is to measure temperatures. Sensor objects are of several different types but all implement the
ProtocolAdapterIfc
interface, therefore they are interchangeable with one another as they are plugged into the
TemperatureSensor
object. When a
TemperatureSensor
is needed, some entity in the system must know what concrete type of
ProtocolAdapterIfc
to produce and associate with the sensor object. In this trivial case, our sensor could be configured based on a command-line argument, a row in a database, or from a property file. This example isn't really interesting enough to pose a challenge or justify a complex framework, but it will serve well to demonstrate IoC basics.
Imagine, however, this happening many, many times in a reasonably complex application in which you want to vary object wiring dynamically or at the very least externally. Perhaps you have a
DummyProtocolAdapter
that always returns a value of 42 and you use this for testing purposes. Wouldn't it be nice to have a simple, unified framework that a developer could count on to orchestrate the wiring of associations among classes and do this in such a way that it is consistent, externally configured, and doesn't cause an unsightly proliferation of factory singleton classes? This may not sound like a big deal, but the power lies in IoC's simplicity.
We have a class
TemperatureSensor
that holds an association to a class that implements the
ProtocolAdapterIfc
interface.
TemperatureSensor
will use this delegate class to get the temperature value. As illustrated in the UML diagram, there are several classes in our application that implement
ProtocolAdapterIfc
and subsequently can be used in this association. We're going to use our IoC framework (in this case Spring) to declare the implementation of
ProtocolAdaperIfc
to be used. Spring will handle wiring the associations at runtime. First, let's have a look at the XML stanza that will instantiate a
TemperatureSensor
object and associate an implementation of
ProtocolAdapterIfc
with it. That stanza looks like this:
<bean id="tempSensor"
class="yourco.project.sensor.TemperatureSensor">
<property name="sensorDelegate">
<ref bean="sensor"/>
</property>
</bean>
<!-- Sensor to associate with tempSensor -->
<bean id="sensor" class="yourco.project.comm.RS232Adapter"/>
After looking at this code, the intent should be very clear. We're configuring Spring to instantiate a
TemperatureSensor
object and associate an
RS232Adapter
as our class that implements the
ProtocolAdapterIfc
interface. If we want to alter the implementation that gets associated with our
TemperatureSensor
, the only thing that needs to be changed is the
class
value in the
sensor
bean tag.
TemperatureSensor
doesn't care what gets associated as long as it implements the
ProtocolAdapterIfc
interface.
Putting this to work inside the application is fairly straightforward. We must first access the Spring framework, next point it to the correct configuration file, and then ask Spring for an instance of our
tempSensor
object by name. Here's how this can be done:
ClassPathXmlApplicationContext appContext =
new ClassPathXmlApplicationContext(
new String[]
{ "simpleSensor.xml" });
BeanFactory bf = (BeanFactory) appContext;
TemperatureSensor ts = (TemperatureSensor)
bf.getBean("tempSensor");
System.out.println("The temp is: "+
ts.getTemperature());
As you can see, this is not terribly difficult. We begin by bootstrapping Spring and specifying the configuration file we want it to use. Next we reference our bean by name (
tempSensor
). Spring handles the mechanics of creating that object and wiring together the objects based on what was described in the
simpleSensor.xml
file. It injects the dependencies for us—in this case instantiating the RS232Adapter object and associating it with the
TemperatureSensor
object by passing it in as a parameter to the
sensorDelegate()
method.
By contrast, accomplishing the same thing using programmatic Java is not terribly difficult. It would look something like this:
TemperatureSensor ts2 = new TemperatureSensor();
ts2.setSensorDelegate(new RS232Adapter());
The purists among us might argue this is actually better. There are few lines of code and these are probably more readable. While this is true, the solution is far less flexible.
- You can swap in and out different implementations of the various objects in the various tiers at will. For example, if a component in the Web tier needs additional functionality from a new business object, all you have to do is associate the business object with the Web tier object much like we did in the
TemperatureSensor
example above. It will be "injected" into the Web object for immediate use. - This ability to reconfigure the structure of entire applications means that you can change data sources really easily, for example, or create different configuration files for different deployment scenarios, or even create more useful, different configuration files for test scenarios. In a test scenario you may inject mock objects implementing the interfaces instead of the real objects. We'll see an example of this shortly.
What has been illustrated here is perhaps the simplest form of dependency injection. Using this same strategy not only can we associate classes with other classes, but we can install properties in classes. Properties such as strings, integers, or floats can be injected into classes through the Spring configuration file as long as they have JavaBean style accessors. We can also create objects and install properties or bean references via the constructor. They syntax for this is only slightly more complicated than setting via a property.
All of this is done using a flexible, declarative form of configuration. No code changes are necessary, and all the hard work of wiring the dependencies is done for you by Spring.
Spring as a Standardized Locator Pattern
I've always thought of the Service Locator pattern as a staple of good J2EE etiquette. For those of you not familiar with this idiom, it goes something like this: Generally we think of a typical J2EE application as being composed of tiers. Typically there is a Web tier, a services tier (EJB, JMS, WS, WLS control), and of course the database. Often, some mechanics are involved in "finding" services that are required to complete a request. The Service Locator pattern suggests that it is a good idea to wrap these mechanics in some sort of factory class that hides the complexity of producing or finding a given service. This cuts down on the proliferation of JNDI or other service production code that does nothing but muddy the Web tier action class. Prior to the advent of Spring, this was commonly accomplished by the tried-and-true Singleton class. The Singleton/Locator/Factory pattern goes something like this:
Figure 2. Sequence diagram of the Locator pattern
This is a vast improvement over a proliferation JNDI lookup code scattered throughout your Web controller code. Instead it is neatly hidden within the collaborating classes inside the factory. We can improve this idiom with Spring. Incidentally, this solution will have applicability for EJBs, Web services, asynchronous JMS calls and even for WLS Control-based services. This variant of the Locator Pattern as implemented by Spring allows for a bit of abstraction and homogeneity among your business services. In other words, your Web controller developers really shouldn't care what kind of service they are talking to, a concept similar to "WLS Controls" but a bit more universal.
An IoC framework vastly improves the utility of this pattern and virtually does away with complicated, specialty, singleton code to get it accomplished. By borrowing from concepts that have been introduced in the previous example, we can build a very powerful and ubiquitous Service Locator pattern with virtually no extra code. Implementing this begins with a simple mandate that Web action developers should deal exclusively with things that implement interfaces. By and large we already do this with EJB programming, but there is nothing that says services that the Web actions developers deal with must be implemented in terms of EJB. They could easily be plain Java objects or Web services. The main point is that services should be programmed in terms of interfaces (so implementations can be swapped in and swapped out) and the runtime configuration can be handled by Spring.
The thing that makes Spring so well suited to the Service Locator pattern is the ability to treat different types of objects more or less uniformly. With a little planning and liberal use of IoC, we can handle most objects, regardless of their nature (EJB, POJO, and so on) in a more or less universal manner and all without having a proliferation of Singleton factory classes. This makes programming in the Web tier easier and more flexible.
Let's look first at an example of how this pattern would be applied in the case of EJB. We all are aware that using EJB is probably the most complicated approach due to what it takes to get a live reference to an EJB. It is recommended in the Spring world that EJB interfaces extend a non-EJB-specific business interface. This serves two purposes: It keeps the two interfaces automatically in sync, and it also helps to keep business services swappable to non-EJB implementations for testing or stubbing purposes. We can rely on Spring's intrinsic utilities to locate and create the EJB instance and at the same time handle all the heavy lifting for us. The way this is configured is as follows:
<bean id="myBizServiceRef"
class="org.springframework.ejb.access.
LocalStatelessSessionProxyFactoryBean">
<property name="jndiName">
<value>myBizComponent</value>
</property>
<property name="businessInterface">
<value>
yourco.project.biz.MyBizInterface
</value>
</property>
</bean>
You can then retrieve the bean and start working on it like this:
MyBizInterface myService = bf.getBean("myBizServiceRef");
This returns an object that Spring dynamically creates for us and that wraps the underlying target, in this case, a local EJB instance. This is nice because it completely hides the fact that we are dealing with an EJB. Instead we interact with a proxied object that implements the plain business interface. Spring has thoughtfully and dynamically generated this object for us around the "real" business object. The object that is wrapped is of course the local EJB that Spring has located and retrieved a reference for. Moreover, you'll notice that this form of code is identical to that used earlier to retrieve the
tempSensor
object.
So what if we change our mind and want our business component to be implemented via a plain Java object, or perhaps we're testing and we want to replace the heavyweight EJB with a stubbed object that returns "canned" responses? Using IoC and Spring it's pretty easy to accomplish this by changing the Spring context file. We simply replace the wiring of the EJB proxy with something a little more conventional as we saw in the first Spring example:
<bean id="myBizServiceRef"
class="yourco.project.biz.MyStubbedBizService">
</bean>
Notice I've only changed the details of what gets handed back by the Spring framework, and not the bean id. The net result is that the resolution of the business object does not change; it looks exactly the same as it did before:
MyBizInterface myService =
bf.getBean("myBizServiceRef");
The biggest difference obviously is that the object that implements the business interface now is backed by a plain Java object (POJO) and is simply a stubbed version of the interface. This is extremely handy for unit testing or altering the nature of business services with little impact on client code.
Uh Oh! Using Spring to Standardize Exceptions
One really great thing that Spring has managed to accomplish is to "templatize" blocks of code. Nowhere is this more evident than with straight JDBC programming. We've all written code that does something like the following:
- Create a database connection, hopefully from a pool somewhere.
- Construct a query string and submit.
- Iterate over the results and marshal the data into a domain object.
- Handle the prolific exceptions that can occur at various stages.
- Make sure we remember to code a
finally
block that closes the connection.
What tends to happen is that this code gets more or less "boilerplated" all over the place. This is generally bad, not only because of a proliferation of unneeded code, but because things have a tendency to be missed, such as the all-important closing of the connection, which, if not done, can cause leaks in your data source pool.
Although I'm sure we've all written this type of "boilerplate" code many times, seeing it again paints an interesting and stark contrast between Spring's methodology and a straight JDBC implementation. The "traditional" JDBC implementation might look something like this:
Connection con = null;
try
{
String url = "jdbc://blah.blah.blah;";
con = myDataSource().getConnection();
Statement stmt = con.createStatement();
String query = "SELECT TYPE FROM SENSORS";
ResultSet rs = stmt.executeQuery(query);
while (rs.next()) {
String s = rs.getString("TYPE);
logger.debug(s + " " + n);
}
} catch (SQLException ex)
{
logger.error("SQL ERROR!",ex);
}
finally
{
con.close();
}
A few comments regarding this approach. First, it works! There is absolutely nothing wrong with this code. It will connect to a database and get the data you want from the 'SENSOR' table. The basic problem with this approach stems from a lack of abstraction. In a large application you will have to cut and paste this chunk of code over and over again, or at least something similar. The larger problem is relying on programmers to do the "right thing." As we all know, it is imperative to have a
finally
statement that closes the database regardless of the outcome of the database operations. Sometimes we forget to do the right thing. I'm as guilty as anyone!
It would be easy enough to write a small framework to fix this problem. I'm sure we've all done that too, but why not let Spring handle it for us? I doubt I could come up with a cleaner, more elegant solution. Let's look at how the Spring framework handles this boilerplate JDBC scenario.
Spring supports a variety of templates for JDBC, Hibernate, JDO, and iBatis. Templates take the concept of boilerplating and transform it into a legitimate programming idiom. For example, the following snippet of code encapsulates the constituent steps outlined above:
DataSource ds = (DataSource) bf.getBean("myDataSource");
JdbcTemplate temp = new JdbcTemplate(ds);
List sensorList = temp.query("select sensor.type FROM sensors",
new RowMapper() {
public Object mapRow(ResultSet rs, int rowNum)
throws SQLException;
return rs.getString(1);
}
});
This handy bit of code addresses all the tedium of JDBC programming and represents the idea of boilerplating as mentioned earlier. Notice the use of Spring's IoC to look up the data source for this query. Spring also favors the use of unchecked exceptions over checked exceptions; therefore many checked JDBC exceptions get remapped into a generally more useful and friendly unchecked exception hierarchy. Configuring this data source in Spring's context file might look something like this:
<bean id="myDataSource"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName">
<value>org.gjt.mm.mysql.Driver</value>
</property>
<property name="url">
<value>jdbc:mysql://romulus/sensors</value>
</property>
<property name="username">
<value>heater</value>
</property>
<property name="password">
<value>hotshot</value>
</property>
</bean>
In this case, we've configured a basic data source from the Apache commons toolkit. But we are not limited to using just this. We could switch the configuration to use a data source configured and stowed in JNDI. Spring has utility and IoC functionality to configure and hand back objects stored in JNDI. If, for example, you wanted to configure and use a data source associated with a JNDI context, you would enter in the following stanza, replacing the previous data source configuration:
<bean id="myDataSource"
class="org.springframework.jndi.
JndiObjectFactoryBean">
<property name="tempSensorDS">
<value>ConnectionFactory</value>
</property>
</bean>
This highlights the flexibility that Spring brings to the table with respect to testing. Your code can run "in the container" (look up a data source from JNDI), and, with a little change, "outside the container" too.