8.1 What Is Spring?
Spring is an open source
intended to make J2EE development easier. It consists of a container,
a framework for configuring and assembling components, and a set of
snap-in services for transactions, persistence, and web user
interfaces. The container itself is smallonly 90 KB. The
entire framework is much larger, but it does a marvelous job of
shielding that size and complexity from you using many of the
principles in this book. It's a mind-bending
framework, but in a good way. You'll find that once
you learn to give away a whole lot of control, you also lose a lot of
tedious, repetitive detail, simultaneously gaining elegance and
simplicity. Here's how to build something in Spring:Code lower-level Java beans that will go into the container. Some of
your beans will have dependencies on others. Omit some of the
controlling code that you're used to; let the
framework take care of those details.Configure your beans. Most will use Spring's XML
configuration service. Describe each bean, including primarily a
name, your bean's class, its fields, and the initial
values that you want to set for those fields. As you configure, you
can refer to other beans by name.Spring reads the XML files, creating and populating your objects with
reflection to set initial values for your fields. Along the way,
Spring satisfies some dependencies. For example, you might set a
dataSource property to
You'll see a concrete example shortly, but bear with
me. Since Spring is so different from most J2EE programming styles,
you need a firm foundation before you dive in.The centerpiece of Spring is the XML bean factory, a framework that
uses independent beans and configuration to assemble an application.
Alternatively, Spring can make many connections without configuration
information through the autowiring option. For example, if your
application needs a data source and you only specified one kind of
data source, you can let Spring autowire that connection.Atop these factories and configuration services, Spring snaps in some
services. Most of them are simple, high-level abstractions that rely
on J2EE services or other lower-level frameworks to do the work.
These are the major frameworks now supported by Spring:A JDBC persistence framework makes it easy to
basic JDBC applications. You get a
meaningful exception hierarchy rather than the generic
SQLE exception with an SQLState
error code and you get unified, consistent code without managing the
tedious details like JDBC connections and result set mapping.An OR mapping framework makes it easy to use
other OR tools such as Hibernate and JDO with Spring. You get help
managing singleton sessions and resources, consistent configuration,
a flexible abstraction for transactions, and simplified exceptions.Spring offers connection pooling for any POJO.
Spring's clean abstraction of services makes this
possible and convenient.A transaction framework, because declarative
you to configure a transaction
policy rather than writing a lot of supporting code. Contrary to
popular belief, you don't need a monolithic
container to support transactions, even declarative transactions.
With Spring, you can provide transactional support to a POJO. You can
also transparently change the underlying mechanism from JTA to JDBC
or back simply through configuration.While Spring isn't a
full-blown aspect-oriented language, it
has good support for many critical AOP concepts.
Spring offers method interception and introduction, among other AOP
concepts. Rod Johnson, creator of Spring, has this to say about his
Spring AOP is actually fairly capable. It's no
AspectJ, but it compares well to other proxy-based frameworks such as
Nanning. For example, it has a powerful point-cut model and supports
before and after returning and throws advice.
Like Struts, an MVC web framework
lets you cleanly separate model,
view, and controller for web applications. There are some differences
from Struts, but mostly it provides an MVC framework
that's consistent with the rest of Spring.
You don't have to adopt any of these services to use
Spring. Further, unlike EJB, you can adopt them piecemeal or extend
them as needed to accomplish your goals. For the most part, the
authors of Spring relied on existing frameworks to snap into Spring.
They provided the underpinnings and additional abstractions to
simplify a few services as needed. The real power is in the approach.
The single exception is the MVC web framework, which directly
competes with a number of viable alternatives.Spring is part of a growing trend toward lightweight containers.
It's easy to see the contrast between this approach
and heavier containers:Heavyweight containers accept components of a predetermined
type. EJB components, for example, inherit directly from a
number of base classes. OLE from Microsoft enforces a standard
interface. Tomcat accepts only servlets. In contrast, lightweight
containers accept Java objects, using reflection or constructors to
configure objects.Heavyweight containers force more dependencies
on components that go into the container. EJB containers must use a
rigorous API to maintain the EJB lifecycle, and do just about
anything of consequence. EJB components have stringent requirements
for threading, inheritance, and even, in some cases, re-entrance.
Spring forces no dependencies on its components, so they can be run
and tested outside of the container or ported to other architectures
with far less effort.
The contrast, on the surface, is staggering. Build applications with
both models, and you'll find that lightweight
containers do more than save you from tedious details. They change
the way that you think; they change the way that you test. In short,
they change the way that you program from the inside out. The
"secret sauce" combines two
injection (described in Chapter 6) and inversion of
8.1.1 Inversion of Control
Earlier in the book, we discussed
how modern developers get
tremendous benefit by changing who's in control.
Most applications hardwire services to objects or components that use
the service. If you choose to code this way, you're
choosing to couple your code, with all of the inherent disadvantages.
Certainly, sometimes, you'll still want to directly
call a service. After all, what is a method call?Sometimes, though, you want to break all dependencies between the
caller and the service. In the passive domain models in Chapter 3, you saw how to get more power and
flexibility from an architecture that changes who is in control:
specifically, control passes from your application to the framework.
This idea is not new. Older applications used to control the
navigation between screens of an application. When I worked at IBM in
the late 1980s, we had a team of four working full time on the
navigation for a query report writer. When we started building
graphical user interfaces, control passed from the application to the
graphical user interface framework. We stripped out our navigator
component and replaced the navigator team with one part-time
developer. That's the power of inversion of control.Although lightweight containers use inversion of control broadly,
inversion of control alone is not what makes them different. Within a
lightweight container, inversion of control focuses on one particular
aspect of the application: configuration and assembly through
dependency injection. Rely on configuration rather than a hard-wired
method call and trust the framework to wire your application
together. A single assembler reads your configuration file, creates
the beans that you've defined, and then initializes
them, wiring them together in the process.Figure 8-1 repeats the dependency injection figure
from Chapter 6 (Figure 6-8).
The job of creating objects and setting the properties appropriately
passes from the more traditional application to the framework
provided by the lightweight container. In the process, the container
satisfies two dependencies: the dependency of the data source on its
configuration and the dependency of the DAO on a data source. The
coupling moves from code to configuration. Singletons disappear. You
simply write the class and configuration and the framework creates
and assembles instances to form an application.