Five Things I Love About Spring
by Bruce A. Tate, coauthor of Spring: A Developer's Notebook05/11/2005
On a sweltering June morning more than 15 years ago, I climbed into an old fiberglass kayak. It was so old that splinters would break off into my fingers, and the paddle was about twice as long as conventional whitewater paddles. I swam more than I boated, but it didn't matter. Fifteen years later, I am still hooked.
About two years ago, I tried out that Spring project that was prominently mentioned on the Hibernate site. It felt just like that old kayak: it fit me perfectly. For hardcore enterprise development, Spring became so deeply woven into my programming that I made it the topic of my fourth Java book, Spring: A Developer's Notebook. In this article, I'll tell you why.
1. Spring Provides Better Leverage
On a river, I've learned to paddle with more of my waist and back, because my arm muscles can't hold up to whole days of paddling on the river. I'm more efficient; I get better leverage. With Spring, I can do more work with each line of code. You'll find extra leverage in many places with Spring, but the biggest is in persistence. Here's a method from the Hibernate data access object:
public List getReservations( ) {
return getHibernateTemplate( ).find("from Reservation");
}
Notice what you don't see. There's no transaction processing. Spring lets you build configuration code to handle that. You don't have to manage resources by closing the session. You don't have to do your own configuration. You don't have to manage exceptions at this level, because the exceptions are unchecked. You're free to manage them at the most appropriate place. Here's how another Hibernate method would look, without Spring:
public List getBikesOldWay( ) throws Exception {
List bikes = null;
Session s = null;
try {
s = mySessionFactory.openSession( );
bikes = s.find("from Bike");
}catch (Exception ex) {
//handle exception gracefully
}finally {
s.close( );
}
return bikes;
}
Spring gives me leverage. I code faster, and maintain less.
2. Spring Enables POJO Programming
After the EJB 2.x debacle, we all look to find ways to express enterprise services without invading each and every bean with a cumbersome model. Sure, we need transactions, security, persistence, and sometimes, remoting. With EJB, I had to learn an extensive API and work through the new tools and deployment process. I was a slave to the services provided by the container. With Spring, I can choose my own services and persistence framework. I program in POJOs and add enterprise services to them with configuration files.
In Spring: A Developer's Notebook, I built a RentaBike application. Instead of a
session bean or an entity bean, I called my POJO hibRentaBike; it
serves as my data access object. I add the services elsewhere. The Spring
configuration file, called a context, is an XML file with all of the beans in the
container, along with their properties and the services the beans need. Here's a look.
The target:
<bean id="rentaBikeTarget" class="com.springbook.HibRentABike">
<property name="storeName">
<value>Bruce's Bikes</value>
</property>
<property name="sessionFactory">
<ref local="sessionFactory"/>
</property>
<property name="transactionManager">
<ref local="transactionManager"/>
</property>
</bean>
The interceptor:
<bean name="transactionInterceptor"
class="org.springframework.transaction.interceptor.TransactionInterceptor">
<property name="transactionManager">
<ref local="transactionManager"/>
</property>
<property name="transactionAttributeSource">
<value>
com.springbook.RentABike.transferReservation=
PROPAGATION_REQUIRED,-ReservationTransferException
com.springbook.RentABike.save*=PROPAGATION_REQUIRED
com.springbook.RentABike.*=PROPAGATION_REQUIRED,readOnly
</value>
</property>
</bean>
The proxy:
<bean id="rentaBike" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<value>com.springbook.RentABike</value>
</property>
<property name="interceptorNames">
<value>transactionInterceptor,rentaBikeTarget</value>
</property>
</bean>
Notice three different beans: the proxy, the target, and the interceptors. The proxy will call the POJO, and also any services needed by the POJO. Interceptors contain glue code that invokes the service. They also specify how to treat each method in the target. Anyone needing to access RentaBike calls the proxy, which calls the transactional interceptor, which begins a transaction, and calls the target (the POJO). The target does its thing, returns to the interceptor (which commits the transaction), and returns to the proxy and the caller of the proxy.

Figure 1. POJO programming in action
You build your program out of POJOs and configure it, and Spring hides the rest from you. I'm a POJO programmer.
3. Dependency Injection Helps Testability
Spring greatly improves your testability through a design pattern called Dependency Injection (DI). When a consumer depends on a dependency (we'll call it a service), you'll create a property on the consumer. Spring will create the consumer and the service, and set the consumer's property to the value of the service. Said another way, Spring manages the lifecycle of the beans in the context, and resolves dependencies. Here's an example of dependency injection, without Spring. First, here's the consumer, which serves as a primitive view for the application:
public class CommandLineView {
private RentABike rentaBike;
public CommandLineView( ) {rentaBike = new ArrayListRentABike("Bruce's Bikes"); }
public void setRentABike(RentABike rentABike){
this.rentABike = rentABike;
}
public void printAllBikes( ) {
System.out.println(rentaBike.toString( ));
Iterator iter = rentaBike.getBikes().iterator( );
while(iter.hasNext( )) {
Bike bike = (Bike)iter.next( );
System.out.println(bike.toString( ));
}
}
public static final void main(String[] args) {
CommandLineView clv = new CommandLineView( );
clv.printAllBikes( );
}
}
Next, here's the service, which is the model. It's a simple implementation with
an array list. It has a dependency upon the model (RentaBike):
interface RentABike {
List getBikes( );
Bike getBike(String serialNo);
}
public class ArrayListRentABike implements RentABike {
private String storeName;
final List bikes = new ArrayList();
public ArrayListRentABike(String storeName) {
this.storeName = storeName;
bikes.add(new Bike("Shimano", "Roadmaster", 20, "11111", 15, "Fair"));
bikes.add(new Bike("Cannondale", "F2000 XTR", 18, "22222",12,"Excellent"));
bikes.add(new Bike("Trek","6000", 19, "33333", 12.4, "Fair"));
}
public String toString() { return "RentABike: " + storeName; }
public List getBikes() { return bikes; }
public Bike getBike(String serialNo) {
Iterator iter = bikes.iterator();
while(iter.hasNext()) {
Bike bike = (Bike)iter.next();
if(serialNo.equals(bike.getSerialNo())) return bike;
}
return null;
}
}
Here's an assembler. The code in bold is the dependency injection. The
assembler instantiates the service and the consumer, and resolves the dependency
by setting the rentaBike property:
public class RentABikeAssembler {
public static final void main(String[] args) {
CommandLineView clv = new CommandLineView( );
RentABike rentaBike = new ArrayListRentABike("Bruce's Bikes");
clv.setRentaBike(rentaBike);
clv.printAllBikes( );
}
}
Of course, Spring will eventually fill the role of the assembler. If you wrap the service in an interface, then you'll be able to inject any implementation of the interface into the container.
Dependency injection lets you code a production dependency and a test dependency. For example, this example creates a stub object that makes testing the view easier. (For more about stubs and mocks, read "Mocks Aren't Stubs.")
You've already seen the Hibernate implementation of RentaBike, and the array list version. I may not want to run all of my user interface tests on the full Hibernate implementation. Instead, I simply implement the interface another way, with an array list.
Dependency injection lets me wire up a production version (with HibRentaBike),
a development version (with an ArrayListRentaBike list), and a test version (with
a mock object). When I'm coding in Java, I've got to have Dependency Injection
to get those mocks into hard-to-reach places.
4. Inversion of Control Simplifies JDBC
JDBC applications are ugly, verbose, and tedious. A good abstraction layer can help. Spring lets you customize a default JDBC method with a query and an anonymous inner class to eliminate much of the drudge work. Here's a simple JDBC example:
JdbcTemplate template = new JdbcTemplate(dataSource);
final List names = new LinkedList();
template.query("SELECT USER.NAME FROM USER",
new RowCallbackHandler() {
public void processRow(ResultSet rs) throws SQLException {
names.add(rs.getString(1));
}
}
);
|
Related Reading
Spring: A Developer's Notebook |
Think of the template.query method as a default JDBC method. Spring will
execute the processRow method in the anonymous inner class for each line in
the result set. You configure the data source in the context. You don't have to
worry about opening or closing the statement or connection, configuring the data
source, or managing transactions. You don't specify an outer result set, or
manage exceptions at the lowest levels, because Spring folds your
SQLException to a common set of unchecked exceptions. Other languages
like Ruby and Smalltalk often use inversion of control with code blocks, but it's
not very common in Java. Inversion of control simply rocks.
5. Spring's Community Thrives
Some open source projects don't need to be particularly active to be useful. JUnit, for example, does a targeted job, and has basically everything that you need, if you like the programming model. Lightweight containers like Spring need a vibrant community. Spring has one of the most active communities you can find, and you get lots of benefits:
Services: With Spring, you can find hundreds of different services, from security, to systems management, to workflow. For persistence, you can plug in JDO, Hibernate, Top Link, JDBC, or OJB.
Support and education: Dozens of independent consultants offer Spring services, and you can get exceptional training around the world.
Enhancements: Spring puts out several major releases a year. The excellent testing within the framework and the cleanly factored extensions mean that each release is of good quality. Spring already has support for Hibernate 3 underway, and offers a new powerful web flow framework, all in the last major release.
Commercial support: Authors like me write books on Spring. To date, you can find five books on Spring, and several more with some Spring content. Several product vendors also support Spring. Dozens of open source frameworks, such as Geronimo and Hibernate, have special support for Spring.
The Spring community makes using the framework much easier. I can hire Spring developers and get them training. I can read books to supplement my knowledge, and get components for just about everything I need to do. I can't find a community for any other lightweight container that comes close.
Resources
If you want to read more, there are a whole lot of places you can go.
Martinfowler.com has some great articles on stubs and mocks and dependency injection.
You can find the Spring framework here.
My first book, Better, Faster, Lighter Java, summarizes the lightweight development movement.
Spring: A Developer's Notebook and its code formed the foundation of this article. It's a developer notebook, so pay close attention to the errata page, and definitely use the sample code to make things easier.
There's also O'Reilly's Hibernate: A Developer's Notebook.
In April 2005, O'Reilly Media, Inc., released Spring: A Developer's Notebook.
Sample Chapter 6, "Services and AOP" (PDF), is available free online.
You can also look at the Table of Contents, the Index, and the full description of the book.
For more information, or to order the book, click here.
Bruce A. Tate is a kayaker, mountain biker, and father of two. In his spare time, he is an independent consultant in Austin, Texas. He is the author of four books, including the bestselling "Bitter Java", and the recently released Better, Faster, Lighter Java, from O'Reilly.
Return to ONJava.com.
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 8 of 8.
-
JdbcTemplace
2005-06-03 07:07:47 laker2000 [Reply | View]
On the surface JdbcTemplace looks easy. But as soon as you want to start doing something more complex like parameterized queries or Stored procs, then Spring no cleaner than an Abstract DAO approach. Has anybody else has similar experiences?
-
less config file is expected
2005-05-17 02:38:20 He_Kun [Reply | View]
I think configuration file is nessesary in Spring,Hibernate. But so many configuration files are not pure programming with java. And we have to learn how to use a new config file for a considerable time. Espesially hibernate config file make me headache whenever I see it.
It would be better if no config file exist any longer except for basic envirioment property file. :) -
less config file is expected
2005-05-18 06:35:11 kelzer [Reply | View]
If you start out by defining business objects in Java, then use Hibernate XDoclet tags to describte how those objects will be persisted, you can automatically generate that Hibernate config file and never have to manually touch it.
You don't have to use a config file with Spring. You can do everything in Java code if you want to. But most developers feel the advantages of using Spring's XML config files justify their use.
-
Some errors in the code
2005-05-12 15:44:17 Russell_Healy [Reply | View]
The constructor for CommandLineView constructs a RentABike using new RentABike - there is no concrete implementation of RentABike.
More importantly, clv does not have a setRentABike method, so the Assembler is not able to do the injection.
Also, I do not understand why CommandLineView has a main method.
CommandLineView should look more like this:
public class CommandLineView {
private RentABike rentaBike;
public CommandLineView( ) {
//nothing to do here
}
public void setRentABike(RentABike rentABike){
this.rentABike = rentABike;
}
public void printAllBikes( ) {
System.out.println(rentaBike.toString( ));
Iterator iter = rentaBike.getBikes().iterator( );
while(iter.hasNext( )) {
Bike bike = (Bike)iter.next( );
System.out.println(bike.toString( ));
}
}
}
I hope this helps
Russell
-
What is a dependency again?
2005-05-11 21:28:25 shmert [Reply | View]
It seems that there should be a glossary of terms for this article, I got a little lost with the terminology. Or perhaps some examples might be helpful. What does the interceptor intercept? Why exactly do we need one? And what is all this stuff, perl?:
com.springbook.RentABike.transferReservation=
PROPAGATION_REQUIRED,-ReservationTransferException
com.springbook.RentABike.save*=PROPAGATION_REQUIRED com.springbook.RentABike.*=PROPAGATION_REQUIRED,readOnly
p.s. My wife tells me I'm a genius, and she's always right, so it's definitely not me. -
What is a dependency again?
2005-05-18 07:22:14 kelzer [Reply | View]
Apologies to O'Reilly for recommending a competitor's book, but Agile Software Development, Principles, Patterns, and Practices by Robert C. Martin (aka "Uncle Bob") has a very good section on the topic of dependencies. His name for the concept, the "Dependency-Inversion Principle", is less common than "Inversion of Control" (IoC) or "Dependency Injection", but they all deal with the same issue.
You can do a Google search to learn more on this topic, but in a nutshell, it says that your high level important business classes shouldn't be dependent upon lower level (less important) classes. You may have heard the terms "tightly coupled" and "loosely coupled" describing this same type of thing. Sometimes the dependencies are even cyclical, which is the worst condition (though it's acceptable, as is tight coupling, when the classes involved truely are very closely related and expected to always be used together. Normally, you'd expect these classes to exist in the same package.)
-
What is a dependency again?
2005-05-12 07:35:09 barryhawkins [Reply | View]
If dependency injection and transaction interception are foreign to you, it might be good to check out the Spring documentation from the website and/or some of the articles that focus more on introducing the concepts used by Inversion of Control lightweight containers. The short answers to your questions would be:
1.) The interceptor intercepts calls to specific methods, listed in the value tag shown in the XML excerpt you guoted. The values to the right of the equals sign specify what sort of transactional attributes are assigned to the method(s) specified. Figure 1 in item 2 of the article is a pretty good diagram of that.
2.) You mostly need the interceptor mentioned in the article if you want to enable transactions for your DAO operations, which is usually the case.
3.) The format of the block you quoted is the standard Java(TM) proporties format of key/value pairs, delimited by the equals sign and newlines. That is contained within the XML tag <value>
P.S. Even geniuses have to RTM sometimes ;-)





public List getBikesOldWay() throws Exception {
List bikes = null;
Session s = null;
try {
s = mySessionFactory.openSession( );
bikes = s.find("from Bike");
}catch (Exception ex) {
//handle exception gracefully
}finally {
s.close( );
}
return bikes;
}
This will throw a NullPointerException in the finally block if the session factory throws an exception when opening a session, i.e. it will conceal the true exception.
Is this how Spring does it too?