Introducing the Spring Framework --3(an article posted on TheServerSide.com)

AOP

Since 2003 there has been much interest in applying AOP solutions to those enterprise concerns, such as transaction management, which have traditionally been addressed by EJB.

The first goal of Spring's AOP support is to provide J2EE services to POJOs. Spring AOP is portable between application servers, so there's no risk of vendor lock in. It works in either web or EJB container, and has been used successfully in WebLogic, Tomcat, JBoss, Resin, Jetty, Orion and many other application servers and web containers.

Spring AOP supports method interception. Key AOP concepts supported include:

  • Interception: Custom behaviour can be inserted before or after method invocations against any interface or class. This is similar to "around advice" in AspectJ terminology.
  • Introduction: Specifying that an advice should cause an object to implement additional interfaces. This can amount to mixin inheritance.
  • Static and dynamic pointcuts: Specifying the points in program execution at which interception should take place. Static pointcuts concern method signatures; dynamic pointcuts may also consider method arguments at the point where they are evaluated. Pointcuts are defined separately from interceptors, enabling a standard interceptor to be applied in different applications and code contexts.

Spring supports both stateful (one instance per advised object) and stateless interceptors (one instance for all advice).

Spring does not support field interception. This is a deliberate design decision. I have always felt that field interception violates encapsulation. I prefer to think of AOP as complementing, rather than conflicting with, OOP. In five or ten years time we will probably have travelled a lot farther on the AOP learning curve and feel comfortable giving AOP a seat at the top table of application design. (At that point language-based solutions such as AspectJ may be far more attractive than they are today.)

Spring implements AOP using dynamic proxies (where an interface exists) or CGLIB byte code generation at runtime (which enables proxying of classes). Both these approaches work in any application server, or in a standalone environment.

Spring was the first AOP framework to implement the AOP Alliance interfaces (www.sourceforge.net/projects/aopalliance). These represent an attempt to define interfaces allowing interoperability of interceptors between AOP frameworks.

Spring integrates with AspectJ, providing the ability to seamlessly include AspectJ aspects into Spring applications . Since Spring 1.1 it has been possible to dependency inject AspectJ aspects using the Spring IoC container, just like any Java class. Thus AspectJ aspects can depend on any Spring-managed objects. The integration with the forthcoming AspectJ 5 release is still more exciting, with AspectJ set to provide the ability to dependency inject any POJO using Spring, based on an annotation-driven pointcut.

Because Spring advises objects at instance, rather than class loader, level, it is possible to use multiple instances of the same class with different advice, or use unadvised instances along with advised instances.

Perhaps the commonest use of Spring AOP is for declarative transaction management. This builds on the transaction abstraction described above, and can deliver declarative transaction management on any POJO. Depending on the transaction strategy, the underlying mechanism can be JTA, JDBC, Hibernate or any other API offering transaction management.

The following are the key differences from EJB CMT:

  • Transaction management can be applied to any POJO. We recommend that business objects implement interfaces, but this is a matter of good programming practice, and is not enforced by the framework.
  • Programmatic rollback can be achieved within a transactional POJO through using the Spring transaction API. We provide static methods for this, using ThreadLocal variables, so you don't need to propagate a context object such as an EJBContext to ensure rollback.
  • You can define rollback rules declaratively. Whereas EJB will not automatically roll back a transaction on an uncaught application exception (only on unchecked exceptions, other types of Throwable and "system" exceptions), application developers often want a transaction to roll back on any exception. Spring transaction management allows you to specify declaratively which exceptions and subclasses should cause automatic rollback. Default behaviour is as with EJB, but you can specify automatic rollback on checked, as well as unchecked exceptions. This has the important benefit of minimizing the need for programmatic rollback, which creates a dependence on the Spring transaction API (as EJB programmatic rollback does on the EJBContext).
  • Because the underlying Spring transaction abstraction supports savepoints if they are supported by the underlying transaction infrastructure, Spring's declarative transaction management can support nested transactions, in addition to the propagation modes specified by EJB CMT (which Spring supports with identical semantics to EJB). Thus, for example, if you have doing JDBC operations on Oracle, you can use declarative nested transactions using Spring.
  • Transaction management is not tied to JTA. As explained above, Spring transaction management can work with different transaction strategies.

It's also possible to use Spring AOP to implement application-specific aspects. Whether or not you choose to do this depends on your level of comfort with AOP concepts, rather than Spring's capabilities, but it can be very useful. Successful examples we've seen include:

  • Custom security interception, where the complexity of security checks required is beyond the capability of the standard J2EE security infrastructure. (Of course, before rolling your own security infrastructure, you should check the capabilities of Acegi Security for Spring, a powerful, flexible security framework that integrates with Spring using AOP, and reflects Spring's architectural approach.
  • Debugging and profiling aspects for use during development
  • Aspects that apply consistent exception handling policies in a single place
  • Interceptors that send emails to alert administrators or users of unusual scenarios

Application-specific aspects can be a powerful way of removing the need for boilerplate code across many methods.

Spring AOP integrates transparently with the Spring BeanFactory concept. Code obtaining an object from a Spring BeanFactory doesn't need to know whether or not it is advised. As with any object, the contract will be defined by the interfaces the object implements.

The following XML stanza illustrates how to define an AOP proxy:

<bean id="myTest"
	class="org.springframework.aop.framework.ProxyFactoryBean">
	<property name="proxyInterfaces">
		<value>org.springframework.beans.ITestBean</value>
	</property>
	<property name="interceptorNames">
		<list>
			<value>txInterceptor</value>
			<value>target</value>
		</list>
	</property>
</bean>

Note that the class of the bean definition is always the AOP framework's ProxyFactoryBean, although the type of the bean as used in references or returned by the BeanFactory getBean() method will depend on the proxy interfaces. (Multiple proxy methods are supported.) The "interceptorNames" property of the ProxyFactoryBean takes a list of String. (Bean names must be used rather than bean references, as new instances of stateful interceptors may need to be created if the proxy is a "prototype", rather than a singleton bean definition.) The names in this list can be interceptors or pointcuts (interceptors and information about when they should apply). The "target" value in the list above automatically creates an "invoker interceptor" wrapping the target object. It is the name of a bean in the factory that implements the proxy interface. The myTest bean in this example can be used like any other bean in the bean factory. For example, other objects can reference it via <ref> elements and these references will be set by Spring IoC.

There are a number of ways to set up proxying more concisely, if you don't need the full power of the AOP framework, such as using Java 5.0 annotations to drive transactional proxying without XML metadata, or the ability to use a single piece of XML to apply a consistent proxying strategy to many beans defined in a Spring factory.

It's also possible to construct AOP proxies programmatically without using a BeanFactory, although this is more rarely used:

TestBean target = new TestBean();
DebugInterceptor di = new DebugInterceptor();
MyInterceptor mi = new MyInterceptor();
ProxyFactory factory = new ProxyFactory(target);
factory.addInterceptor(0, di);
factory.addInterceptor(1, mi);
// An "invoker interceptor" is automatically added to wrap the target
ITestBean tb = (ITestBean) factory.getProxy();

We believe that it's generally best to externalize the wiring of applications from Java code, and AOP is no exception.

The use of AOP as an alternative to EJB (version 2 or above) for delivering enterprise services is growing in importance. Spring has successfully demonstrated the value proposition.

MVC web framework

Spring includes a powerful and highly configurable MVC web framework.

Spring's MVC model is most similar to that of Struts, although it is not derived from Struts. A Spring Controller is similar to a Struts Action in that it is a multithreaded service object, with a single instance executing on behalf of all clients. However, we believe that Spring MVC has some significant advantages over Struts. For example:

  • Spring provides a very clean division between controllers, JavaBean models, and views.
  • Spring's MVC is very flexible. Unlike Struts, which forces your Action and Form objects into concrete inheritance (thus taking away your single shot at concrete inheritance in Java), Spring MVC is entirely based on interfaces. Furthermore, just about every part of the Spring MVC framework is configurable via plugging in your own interface. Of course we also provide convenience classes as an implementation option.
  • Spring, like WebWork, provides interceptors as well as controllers, making it easy to factor out behavior common to the handling of many requests.
  • Spring MVC is truly view-agnostic. You don't get pushed to use JSP if you don't want to; you can use Velocity, XLST or other view technologies. If you want to use a custom view mechanism - for example, your own templating language - you can easily implement the Spring View interface to integrate it.
  • Spring Controllers are configured via IoC like any other objects. This makes them easy to test, and beautifully integrated with other objects managed by Spring.
  • Spring MVC web tiers are typically easier to test than Struts web tiers, due to the avoidance of forced concrete inheritance and explicit dependence of controllers on the dispatcher servlet.
  • The web tier becomes a thin layer on top of a business object layer. This encourages good practice. Struts and other dedicated web frameworks leave you on your own in implementing your business objects; Spring provides an integrated framework for all tiers of your application.

As in Struts 1.1 and above, you can have as many dispatcher servlets as you need in a Spring MVC application.

The following example shows how a simple Spring Controller can access business objects defined in the same application context. This controller performs a Google search in its handleRequest() method:

public class GoogleSearchController
		implements Controller {

	private IGoogleSearchPort google;

	private String googleKey;

	public void setGoogle(IGoogleSearchPort google) {
		this.google = google;
	}

	public void setGoogleKey(String googleKey) {
		this.googleKey = googleKey;
	}

	public ModelAndView handleRequest(
				HttpServletRequest request, HttpServletResponse response)
		throws ServletException, IOException {
		String query = request.getParameter("query");
		GoogleSearchResult result =
			// Google property definitions omitted...

			// Use google business object
			google.doGoogleSearch(this.googleKey, query,
				start, maxResults, filter, restrict,
				safeSearch, lr, ie, oe);

		return new ModelAndView("googleResults", "result", result);
	}
}

In the prototype this code is taken from, IGoogleSearchPort is a GLUE web services proxy, returned by a Spring FactoryBean. However, Spring IoC isolates this controller from the underlying web services library. The interface could equally be implemented by a plain Java object, test stub, mock object, or EJB proxy, as discussed below. This controller contains no resource lookup; nothing except code necessary to support its web interaction.

Spring also provides support for data binding, forms, wizards and more complex workflow. A forthcoming article in this series will discuss Spring MVC in detail.

If your requirements are really complex, you should consider Spring Web Flow, a powerful framework that provides a higher level of abstraction for web flows than any traditional web MVC framework, and was discussed in a recent TSS article by its architect, Keith Donald.

A good introduction to the Spring MVC framework is Thomas Risberg's Spring MVC tutorial (http://www.springframework.org/docs/MVC-step-by-step/Spring-MVC-step-by-step.html). See also "Web MVC with the Spring Framework" (http://www.springframework.org/docs/web_mvc.html).

If you're happy with your favourite MVC framework, Spring's layered infrastructure allows you to use the rest of Spring without our MVC layer. We have Spring users who use Spring for middle tier management and data access but use Struts, WebWork, Tapestry or JSF in the web tier.

Implementing EJBs

If you choose to use EJB, Spring can provide important benefits in both EJB implementation and client-side access to EJBs.

It's now widely regarded as a best practice to refactor business logic into POJOs behind EJB facades. (Among other things, this makes it much easier to unit test business logic, as EJBs depend heavily on the container and are hard to test in isolation.) Spring provides convenient superclasses for session beans and message driven beans that make this very easy, by automatically loading a BeanFactory based on an XML document included in the EJB Jar file.

This means that a stateless session EJB might obtain and use a collaborator like this:

import org.springframework.ejb.support.AbstractStatelessSessionBean;

public class MyEJB extends AbstractStatelessSessionBean
			implements MyBusinessInterface {
	private MyPOJO myPOJO;

	protected void onEjbCreate() {
		this.myPOJO = getBeanFactory().getBean("myPOJO");
	}

	public void myBusinessMethod() {
		this.myPOJO.invokeMethod();
	}
}

Assuming that MyPOJO is an interface, the implementing class - and any configuration it requires, such as primitive properties and further collaborators - is hidden in the XML bean factory definition.

We tell Spring where to load the XML document via an environment variable definition named ejb/BeanFactoryPath in the standard ejb-jar.xml deployment descriptor, as follows:

<session>
	<ejb-name>myComponent</ejb-name>
	<local-home>com.test.ejb.myEjbBeanLocalHome</local-home>
	<local>com.mycom.MyComponentLocal</local>
	<ejb-class>com.mycom.MyComponentEJB</ejb-class>
	<session-type>Stateless</session-type>
	<transaction-type>Container</transaction-type>

	<env-entry>
		<env-entry-name>ejb/BeanFactoryPath</env-entry-name>
		<env-entry-type>java.lang.String</env-entry-type>
		<env-entry-value>/myComponent-ejb-beans.xml</env-entry-value></env-entry>
	</env-entry>
</session>

The myComponent-ejb-beans.xml file will be loaded from the classpath: in this case, in the root of the EJB Jar file. Each EJB can specify its own XML document, so this mechanism can be used multiple times per EJB Jar file.

The Spring superclasses implement EJB lifecycle methods such as setSessionContext() and ejbCreate(), leaving the application developer to optionally implement the Spring onEjbCreate() method.

When EJB 3.0 is available in public draft, we will offer support for the use of the Spring IoC container to provide richer Dependency Injection semantics in that environment. We will also integrate the JSR-220 O/R mapping API with Spring as a supported data access API.

Using EJBs

Spring also makes it much easier to use, as well as implement EJBs. Many EJB applications use the Service Locator and Business Delegate patterns. These are better than spraying JNDI lookups throughout client code, but their usual implementations have significant disadvantages. For example:

  • Typically code using EJBs depends on Service Locator or Business Delegate singletons, making it hard to test.
  • In the case of the Service Locator pattern used without a Business Delegate, application code still ends up having to invoke the create() method on an EJB home, and deal with the resulting exceptions. Thus it remains tied to the EJB API and the complexity of the EJB programming model.
  • Implementing the Business Delegate pattern typically results in significant code duplication, where we have to write numerous methods that simply call the same method on the EJB.

For these and other reasons, traditional EJB access, as demonstrated in applications such as the Sun Adventure Builder and OTN J2EE Virtual Shopping Mall, can reduce productivity and result in significant complexity.

Spring steps beyond this by introducing codeless business delegates. With Spring you'll never need to write another Service Locator, another JNDI lookup, or duplicate methods in a hand-coded Business Delegate unless you're adding real value.

For example, imagine that we have a web controller that uses a local EJB. We'll follow best practice and use the EJB Business Methods Interface pattern, so that the EJB's local interface extends a non EJB-specific business methods interface. (One of the main reasons to do this is to ensure that synchronization between method signatures in local interface and bean implementation class is automatic.) Let's call this business methods interface MyComponent. Of course we'll also need to implement the local home interface and provide a bean implementation class that implements SessionBean and the MyComponent business methods interface.

With Spring EJB access, the only Java coding we'll need to do to hook up our web tier controller to the EJB implementation is to expose a setter method of type MyComponent on our controller. This will save the reference as an instance variable like this:

private MyComponent myComponent;

public void setMyComponent(MyComponent myComponent) {
	this.myComponent = myComponent;
}

We can subsequently use this instance variable in any business method.

Spring does the rest of the work automatically, via XML bean definition entries like this. LocalStatelessSessionProxyFactoryBean is a generic factory bean that can be used for any EJB. The object it creates can be cast by Spring to the MyComponent type automatically.

<bean id="myComponent"
class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">

	<property name="jndiName" value="myComponent" />
	<property name="businessInterface" value="com.mycom.MyComponent" />
</bean>

<bean id="myController"
	class = "com.mycom.myController"
>
	<property name="myComponent" ref="myComponent" />
</bean>

There's a lot of magic happening behind the scenes, courtesy of the Spring AOP framework, although you aren't forced to work with AOP concepts to enjoy the results. The "myComponent" bean definition creates a proxy for the EJB, which implements the business method interface. The EJB local home is cached on startup, so there's normally only a single JNDI lookup. (There is also support for retry on failure, so an EJB redeployment won't cause the client to fail.) Each time the EJB is invoked, the proxy invokes the create() method on the local EJB and invokes the corresponding business method on the EJB.

The myController bean definition sets the myController property of the controller class to this proxy.

This EJB access mechanism delivers huge simplification of application code:

  • The web tier code has no dependence on the use of EJB. If we want to replace this EJB reference with a POJO or a mock object or other test stub, we could simply change the myComponent bean definition without changing a line of Java code
  • We haven't had to write a single line of JNDI lookup or other EJB plumbing code as part of our application.

We can also apply the same approach to remote EJBs, via the similar org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean factory bean. However, it's trickier to conceal the RemoteExceptions on the business methods interface of a remote EJB. (Spring does let you do this, if you wish to provide a client-side service interface that matches the EJB remote interface but without the "throws RemoteException" clause in the method signatures.)

Testing

As you've probably gathered, I and the other Spring developers are firm believers in the importance of comprehensive unit testing. We believe that it's essential that frameworks are thoroughly unit tested, and that a prime goal of framework design should be to make applications built on the framework easy to unit test.

Spring itself has an excellent unit test suite. We've found the benefits of test first development to be very real on this project. For example, it has made working as an internationally distributed team extremely efficient, and users comment that CVS snapshots tend to be stable and safe to use.

We believe that applications built on Spring are very easy to test, for the following reasons:

  • IoC facilitates unit testing
  • Applications don't contain plumbing code directly using J2EE services such as JNDI, which is typically hard to test
  • Spring bean factories or contexts can be set up outside a container

The ability to set up a Spring bean factory outside a container offers interesting options for the development process. In several web application projects using Spring, work has started by defining the business interfaces and integration testing their implementation outside a web container. Only after business functionality is substantially complete is a thin layer added to provide a web interface.

Since Spring 1.1.1, Spring has provided powerful and unique support for a form of integration testing outside the deployed environment. This is not intended as a substitute for unit testing or testing against the deployed environment. However, it can significantly improve productivity.

The org.springframework.test package provides valuable superclasses for integration tests using a Spring container, but not dependent on an application server or other deployed environment. Such tests can run in JUnit--even in an IDE--without any special deployment step. They will be slower to run than unit tests, but much faster to run than Cactus tests or remote tests relying on deployment to an application server. Typically it is possible to run hundreds of tests hitting a development database--usually not an embedded database, but the product used in production--within seconds, rather than minutes or hours. Such tests can quickly verify correct wiring of your Spring contexts, and data access using JDBC or ORM tool, such as correctness of SQL statements. For example, you can test your DAO implementation classes.

The enabling functionality in the org.springframework.test package includes:

  • The ability to populate JUnit test cases via Dependency Injection. This makes it possible to reuse Spring XML configuration when testing, and eliminates the need for custom setup code for tests.
  • The ability to cache container configuration between test cases, which greatly increases performance where slow-to-initialize resources such as JDBC connection pools or Hibernate SessionFactories are concerned.
  • Infrastructure to create a transaction around each test method and roll it back at the conclusion of the test by default. This makes it possible for tests to perform any kind of data access without worrying about the effect on the environments of other tests. In my experience across several complex projects using this functionality, the productivity and speed gain of such a rollback-based approach is very significant.
Who's using Spring?

There are many production applications using Spring. Users include investment and retail banking organizations, well-known dotcoms, global consultancies, academic institutions, government departments, defence contractors, several airlines, and scientific research organizations (including CERN).

Many users use all parts of Spring, but some use components in isolation. For example, a number of users begin by using our JDBC or other data access functionality.

Roadmap

Since the first version of this article, in October 2003, Spring has progressed through its 1.0 final release (March 2004) through version 1.l (September 2004) to 1.2 final (May 2005). We believe in a philosophy of "release early, release often," so maintenance releases and minor enhancements are typically released every 4-6 weeks.

Since that time enhancements include:

  • The introduction of a remoting framework supporting multiple protocols including RMI and various web services protocols
  • Support for Method Injection and other IoC container enhancements such as the ability to manage objects obtained from calls to static or instance factory methods
  • Integration with more data access technologies, including TopLink and Hibernate 3 as well as Hibernate 2 in the recent 1.2 release
  • Support for declarative transaction management configured by Java 5.0 annotations (1.2), eliminating the need for XML metadata to identify transactional methods
  • Support for JMX management of Spring-managed objects (1.2).
  • Integration with Jasper Reports, the Quartz scheduler and AspectJ
  • Integration with JSF as a web layer technology

We intend to continue with rapid innovation and enhancement. The next major release will be 1.3 (final release expected Q3, 2005). Planned enhancements include:

  • XML configuration enhancements (planned for release 1.3), which will allow custom XML tags to extend the basic Spring configuration format by defining one or more objects in a single, validated tag. This not only has the potential to simplify typical configurations significantly and reduce configuration errors, but will be ideal for developers of third-party products that are based on Spring.
  • Integration of Spring Web Flow into the Spring core (planned for release 1.3)
  • Support for dynamic reconfiguration of running applications
  • Support for the writing of application objects in languages other than Java, such as Groovy, Jython or other scripting languages running on the Java platform. Such objects will benefit from the full services of the Spring IoC container and will allow dynamic reloading when the script changes, without affecting objects that were given references to them by the IoC container.

As an agile project, Spring is primarily driven by user requirements. So we don't develop features that no one has a use for, and we listen carefully to our user community.

Spring Modules is an associated project, led by Rob Harrop of Interface21, which extends the reach of the Spring platform to areas that are not necessarily integral to the Spring core, while still valuable to many users. This project also serves as an incubator, so some of this functionality will probably eventually migrate into the Spring core. Spring Modules presently includes areas such as integration with the Lucene search engine and OSWorkflow workflow engine, a declarative, AOP-based caching solution, and integration with the Commons Validator framework.

Interestingly, although the first version of this article was published six months before the release of Spring 1.0 final, almost all the code and configuration examples would still work unchanged in today's 1.2 release. We are proud of our excellent record on backward compatibility. This demonstrates the ability of Dependency Injection and AOP to deliver a non-invasive API, and also indicates the seriousness with which we take our responsibility to the community to provide a stable framework to run vital applications.

Summary

Spring is a powerful framework that solves many common problems in J2EE. Many Spring features are also usable in a wide range of Java environments, beyond classic J2EE.

Spring provides a consistent way of managing business objects and encourages good practices such as programming to interfaces, rather than classes. The architectural basis of Spring is an Inversion of Control container based around the use of JavaBean properties. However, this is only part of the overall picture: Spring is unique in that it uses its IoC container as the basic building block in a comprehensive solution that addresses all architectural tiers.

Spring provides a unique data access abstraction, including a simple and productive JDBC framework that greatly improves productivity and reduces the likelihood of errors. Spring's data access architecture also integrates with TopLink, Hibernate, JDO and other O/R mapping solutions.

Spring also provides a unique transaction management abstraction, which enables a consistent programming model over a variety of underlying transaction technologies, such as JTA or JDBC.

Spring provides an AOP framework written in standard Java, which provides declarative transaction management and other enterprise services to be applied to POJOs or - if you wish - the ability to implement your own custom aspects. This framework is powerful enough to enable many applications to dispense with the complexity of EJB, while enjoying key services traditionally associated with EJB.

Spring also provides a powerful and flexible MVC web framework that is integrated into the overall IoC container.

More information

See the following resources for more information about Spring:

We pride ourselves on excellent response rates and a helpful attitude to queries on the forms and mailing lists. We hope to welcome you into our community soon!

About the Author

Rod Johnson has almost ten years experience as a Java developer and architect and has worked with J2EE since the platform emerged. He is the author of the best-selling Expert One-on-One J2EE Design and Development (Wrox, 2002), and J2EE without EJB (Wrox, 2004, with Juergen Hoeller) and has contributed to several other books on J2EE. Rod serves on two Java specification committees and is a regular conference speaker. He is CEO of Interface21, an international consultancy that leads Spring Framework development and offers expert services on the Spring Framework and J2EE in general.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值