Perhaps one the hardest parts of actually explaining Spring as a technology is classifying exactly what it is. Typically, Spring is described as a lightweight framework for building Java applications, but that statement brings up two interesting points. First, you can use Spring to build any application in Java and, unlike many other frameworks such as Apache Struts, it is not just limited to web applications. Second, the lightweight part of the description doesn't really refer to the number of classes or the size of the distribution, but rather, it defines the principle of the Spring philosophy as a whole—that is, minimal impact. Spring is lightweight in the sense that you have to make few, if any, changes to your application code to gain the benefits of the Spring core, and should you choose to discontinue using Spring at any point, you will find doing so quite simple. Notice that we qualified that last statement to refer to the Spring core only—many of the extra Spring components, such as data access, require a much closer coupling to the Spring framework. However, the benefits of this coupling are quite clear, and throughout the book we present techniques for minimizing the impact this has on your application.
The core of the Spring framework is based on the principle of Inversion of Control (IoC). IoC is a technique that externalizes the creation and management of component dependencies. Consider an example where class Foo depends on an instance of class Bar to perform some kind of processing. Traditionally, Foo creates an instance of Bar using the new operator or obtains one from some kind of factory class. Using the IoC approach, an instance of Bar (or a subclass) is provided to Foo at runtime by some external process. This behavior, the injection of dependencies at runtime, leads to IoC being renamed the much more descriptive Dependency Injection (DI). The precise nature of the dependencies managed by DI is discussed in Chapter 4.
As you will see in Chapter 4, using the term Dependency Injection when referring to Inversion of Control is always correct. In the context of Spring, you can use the terms interchangeably, without any loss of meaning.
Spring's DI implementation is based around two core Java concepts: JavaBeans and interfaces. When you use DI, you allow dependency configuration to be externalized from your code. JavaBeans provide a standard mechanism for creating Java resources that are configurable in a standard way. In Chapter 4.
Interfaces and DI are technologies that are mutually beneficial. We are sure that no one reading this book will argue that designing and coding an application to interfaces makes for a flexible application, but the complexity of wiring together an application that is designed using interfaces is quite high and places an additional coding burden on developers. By using DI, you reduce the amount of code you need to utilize an interface-based design in your application to almost zero. Likewise, by using interfaces, you can get the most out of DI because your beans can utilize any interface implementation to satisfy their dependency.
In the context of DI, Spring acts more like a container than a framework—providing instances of your application classes with all the dependencies they need—but it does so in a much less intrusive way than, say, the EJB container that allows you to create persistent entity beans. Using Spring for DI relies on nothing more than following the JavaBeans naming conventions (a requirement that, as you will see in Chapter 5, you can bypass using Spring's method injection support) within your classes—there are no special classes from which to inherit or proprietary naming schemes to follow. If anything, the only change you make in an application that uses DI is to expose more properties on your JavaBeans, thus allowing more dependencies to be injected at runtime.
Although we leave the full discussion of DI until
Reduce glue code: One of the biggest plus points of DI is its ability to reduce dramatically the amount of code you have to write to glue the different components of your application together. Often this code is trivial—where creating a dependency involves simply creating a new instance of an object. However, the glue code can get quite complex when you need to look up dependencies in a JNDI repository or when the dependencies cannot be invoked directly, as is the case with remote resources. In these cases, DI can really simplify the glue code by providing automatic JNDI lookup and automatic proxying of remote resources.
Externalize dependencies: You can externalize the configuration of dependencies, which allows you to reconfigure easily without needing to recompile your application. This gives you two interesting benefits. First, as you will see in Chapter 4, DI in Spring gives you the ideal mechanism for externalizing all the configuration options of your application for free. Second, this externalization of dependencies makes it much simpler to swap one implementation of a dependency for another. Consider the case where you have a DAO component that performs data operations against a PostgreSQL database and you want to upgrade to Oracle. Using DI, you can simply reconfigure the appropriate dependency on your business objects to use the Oracle implementation rather than the PostgreSQL one.
Manage dependencies in a single place: Using a traditional approach to dependency management, you create instances of your dependencies where they are needed—within the dependent class. In all but the most trivial of applications, you will have dependencies spread across the classes in your application, and changing them can prove problematic. When you use DI, all the information about dependencies is contained in a single repository, making the management of dependencies much simpler and less error prone.
Improve testability: When you design your classes for DI, you make it possible to replace dependencies easily. This comes in especially handy when you are testing your application. Consider a business object that performs some complex processing; for part of this, it uses a DAO object to access data stored in a relational database. For your test, you are not interested in testing the DAO; you simply want to test the business object with various sets of data. In a traditional approach, where the business object is responsible for obtaining an instance of the DAO itself, you have a hard time testing this, because you are unable to replace the DAO implementation easily with a mock implementation that returns your test data sets. Instead, you need to make sure that your test database contains the correct data and uses the full DAO implementation for your tests. Using DI, you can create a mock implementation of the DAO object that returns the test data sets and then you can pass this to your business object for testing. This mechanism can be extended for testing any tier of your application and is especially useful for testing web components where you can create mock implementations of HttpServletRequest and HttpServletResponse.
Foster good application design: Designing for DI means, in general, designing against interfaces. A typical injection-oriented application is designed so that all major components are defined as interfaces, and then concrete implementations of these interfaces are created and hooked together using the DI container. This kind of design was possible in Java before the advent of DI and DI-based containers such as Spring, but by using Spring, you get a whole host of DI features for free, and you are able to concentrate on building your application logic, not a framework to support it.
As you can see from this list, DI provides a lot of benefits for your application, but it is not without its drawbacks. In particular, DI can make it difficult for someone not intimately familiar with the code to see just what implementation of a particular dependency is being hooked into which objects. Typically, this is only a problem when developers are inexperienced with DI; after becoming more experienced, developers find that the centralized view of an application given by Spring DI enables them to see the whole picture. For the most part, the massive benefits far outweigh this small drawback but you should consider this when planning your application.
The Spring core alone, with its advanced DI capabilities, is a worthy tool, but where Spring really excels is in its myriad of additional features, all elegantly designed and built using the principles of DI. Spring provides features for all layers of an application, from helper application programming interfaces (APIs) for data access right through to advanced Model View Controller (MVC) capabilities. What is great about these features in Spring is that, although Spring often provides its own approach, you can easily integrate them with other tools in Spring, making these tools first-class members of the Spring family.
Aspect Oriented Programming (AOP) is one of the "technologies of the moment" in the Java space. AOP provides the ability to implement crosscutting logic—that is, logic that applies to many parts of your application—in a single place and to have that logic applied across your application automatically. AOP is enjoying an immense amount of time in the limelight at the moment; however, behind all the hype is a truly useful technology that has a place in any Java developer's toolbox.
There are two main kinds of AOP implementation. Static AOP, such as AspectJ (www.aspectj.org), provides a compile-time solution for building AOP-based logic and adding it to an application. Dynamic AOP, such as that in Spring, allows crosscutting logic to be applied arbitrarily to any other code at runtime. Both kinds of AOP have their place, and indeed, Spring provides features to integrate with AspectJ. This is covered in more detail in Chapter 7.
There are many applications for AOP. The typical one given in many of the traditional AOP examples involves performing some kind of logging, but AOP has found uses well beyond the trivial logging applications. Indeed, within the Spring framework itself, AOP is used for many purposes, particularly in transaction management. Spring AOP is covered in full detail in 9, where we show you typical uses of AOP within the Spring framework and your own application, as well as AOP performance and areas where traditional technologies are better suited than AOP.
Data access and persistence seems to be the most discussed topic in the Java world. It seems that you cannot visit a community site such as www.theserverside.com without being bombarded with articles and blog entries for the latest, greatest data access tool. Spring provides excellent integration with a choice selection of these data access tools. In addition to this, Spring makes plain vanilla JDBC a viable option for many projects with its simplified wrapper APIs around the standard API.
As of Spring version 1.1, you have support for JDBC, Hibernate, iBATIS, and Java Data Objects (JDO), along with newly introduced support for Apache Jakarta's ObJect Relational Bridge (OJB).
The JDBC support in Spring makes building an application on top of JDBC realistic, even for more complex applications. The support for Hibernate, iBATIS, and JDO makes already simple APIs even simpler, thus easing the burden on developers. When using the Spring APIs to access data via any tool, you are able to take advantage of Spring's excellent transaction support. A full discussion of this is outside the scope of this chapter but is covered in full detail in Chapter 12.
One of the nicest features we find with Spring is the ability to mix and match data access technologies easily within an application. For instance, you may be running an application with Oracle, using Hibernate for much of your data access logic. However, if you want to take advantage of some Oracle-specific features, then it is simple to implement that part of your data access tier using Spring's JDBC APIs.
Spring provides an excellent abstraction layer for transaction management, allowing for programmatic and declarative transaction control. By using the Spring abstraction layer for transactions, you can make changing the underlying transaction protocol and resource managers simple. You can start with simple, local, resource-specific transactions and move to global, multi-resource transactions without having to change your code.
Transactions are covered in full detail in Chapter 12.
There has been a lot of discussion recently about the complexity of various J2EE APIs, especially those of EJB. It is evident from the proposed EJB 3.0 specification that this discussion has been taken on board by the expert group, hopefully with the result of a much more simplified EJB API.
In the meantime, however, you can use Spring to provide simplified support for many J2EE technologies. For EJB, there is a selection of classes for both building and accessing EJB resources. These classes reduce a lot of the grunt work from both tasks and provide a more DI-oriented API for EJBs.
For any resources stored in a JNDI-accessible location, Spring allows you to do away with the complex lookup code and have JNDI-managed resources injected as dependencies into other objects at runtime. As a side effect of this, your application becomes decoupled from JNDI, allowing you more scope for code reuse in the future.
As of version 1.0.2, Spring does not support JMS access. However, the CVS repository already contains a large array of classes that are to be introduced in 1.1. Using these classes simplifies all interaction with JMS destinations and should reduce a lot of the boilerplate code you need to write in order to use JMS from your Spring applications.
Chapter 13 covers all J2EE integration APIs, including the forthcoming JMS APIs.
Although Spring can be used in almost any setting from desktop to Web, it provides a rich array of classes to support the creation of web-based applications. Using Spring, you have maximum flexibility when you are choosing how to implement your web front end.
For a web application of any complexity, it makes sense to use a framework that uses the MVC paradigm, a pattern that is proven to simplify application maintenance and increase code reuse. Perhaps the most well-known MVC framework for the Web is Apache Struts (http://struts.apache.org). Spring integrates fully with Struts, allowing you to apply Spring DI principles within your Struts classes.
In addition to Struts support, Spring provides its own excellent MVC implementation that you can use with a large array of different view technologies, from JSP and Apache's Jakarta Velocity to Microsoft's Excel and Adobe's PDF. The Spring MVC implementation is quite comprehensive and provides support classes that address 99 percent of all your requirements. For the remaining 1 percent, you can easily extend the MVC framework to add in your own functionality.
View support in Spring MVC is extensive and is constantly improving. In addition to standard support for JSP, which is greatly bolstered by the Spring tag libraries, you can take advantage of fully integrated support for Jakarta Velocity, FreeMarker, Jakarta Tiles (separate from Struts), and XSLT. In addition to this, you will find a set of base view classes that make it simple to add Excel and PDF output to your applications.
We cover the Spring MVC implementation in Chapter 19.
Accessing or exposing remote components in Java has never been the simplest of jobs. Using Spring, you can take advantage of extensive support for a wide range of remoting techniques to quickly expose and access remote services.
Spring provides support for a variety of remote access mechanisms, including Java RMI, JAXRPC, Caucho Hessian, and Caucho Burlap. In addition to these remoting protocols, Spring 1.1 has its own HTTP-based protocol that is based on standard Java serialization. By applying Spring's dynamic proxying capabilities, you can have a proxy to a remote resource injected as a dependency into one of your classes, thus removing the need to couple your application to a specific remoting implementation and also reducing the amount of code you need to write for your application.
In addition to making accessing remote components easy, Spring provides excellent support for exposing a Spring-managed resource as a remote service. Using this mechanism, you can export your service using any of the remoting mechanisms mentioned earlier without needing to have implementation-specific code in your application.
Sending e-mail is a typical requirement for many different kinds of application and is given first- class treatment within the Spring framework. Spring provides a simplified API for sending e-mail messages that fits nicely with the Spring DI capabilities. Spring supports pluggable implementations of the mail API and comes complete with two implementations: one uses JavaMail and the other uses Jason Hunter's MailMessage class from the com.oreilly.servlet package available from http://servlets.com/cos.
Spring provides the ability to create a prototype message in the DI container and use this as the base for all messages sent from your application. This allows for easy customization of mail parameters such as the subject and sender address. However, there is no support for customizing the message body outside of the code. In Chapter 15, we look at the mail support in detail and at a solution that combines Velocity and Spring, which allows the mail content to be externalized from the Java code.
Most nontrivial applications require some kind of scheduling capability. Whether this is for sending updates to customers or performing housekeeping tasks, the ability to schedule code to run at a predefined point in time is an invaluable tool for developers.
Spring provides support for two scheduling mechanisms: one uses the Timer class, which has been available since Java 1.3; and the other uses the Quartz scheduling engine. Scheduling based on the Timer class is quite primitive and is limited to fixed periods defined in milliseconds. When you are using Quartz, you can build complex schedules using the Unix cron format to define when tasks should be run.
The scheduling support in Spring is covered in full in Chapter 14.
One area where Spring really helps to reduce the amount of repetitive, boilerplate code you need to write is in exception handling. The core of the Spring philosophy in this respect is that checked exceptions are overused in Java, and that a framework should not force you to catch any exception from which you are unlikely to be able to recover—a point of view that the authors agree with wholeheartedly.
In reality, many frameworks are designed to reduce the impact of having to write code to handle checked exceptions. However, many of these frameworks take the approach of sticking with checked exceptions but artificially reducing the granularity of the exception class hierarchy. One thing you will notice with Spring is that because of the convenience afforded to the developer from using unchecked exceptions, the exception hierarchy is remarkably granular.
Throughout the book you will see examples of where the Spring exception handling mechanisms can reduce the amount of code you have to write and, at the same time, improve your ability to identify, classify, and diagnose errors within your application.
Source level metadata, often referred to as attributes, is getting quite a bit of coverage in the Java world at the moment, mainly due to the announcement that JSR-175, a standardized specification for attributes, will make it into Java 5.0. In addition to JSR-175, a wide range of open source projects for attributes, such as Jakarta Commons Attributes, provide the same functionality for current JVMs. Given that many applications continue to run on a 1.3 or 1.4 virtual machine and will for some time, this ability to support attributes in those applications is desired.
Spring provides a pluggable metadata solution so that you can plug any implementation into your application at any future point. Currently the only implementation available out of the box is Commons Attributes, but Spring will definitely have support for JSR-175 Attributes before Java 5.0 is released.
Spring's support for attributes is discussed at various points throughout the book, but it is first introduced during the discussions of AOP in Chapter 7.