DekGenius.com
Previous Section  < Day Day Up >  Next Section

8.1 What Is Spring?

Spring is an open source framework 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 small—only 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:

  1. 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.

  2. 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.

  3. 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 MyDataSource.

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 create 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 transactions allow 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 AOP support:

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 ingredients, dependency injection (described in Chapter 6) and inversion of control.

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.

Figure 8-1. Lightweight inversion of control containers
figs/bflJ_0801.gif


    Previous Section  < Day Day Up >  Next Section