The Spring Framework, a popular Java/J2EE application framework built on a lightweight Inversion-of-Control container, is particularly well-known for its data access and transaction management capabilities. Spring's declarative transaction demarcation can be applied to any POJO target object, with the full sophistication of declarative transactions as found in EJB Container-Managed Transactions (CMT). The choices for a back-end transaction manager range from simple JDBC-based transactions to full-fledged J2EE transactions via JTA.
This article discusses Spring's transaction management facilities in detail. The focus is on leveraging Spring's declarative transactions for POJOs, with JTA as the back-end transaction strategy. The article shows that Spring's transaction services can seamlessly interact with a J2EE server's transaction coordinator, for example the transaction coordinator for BEA WebLogic Server, essentially serving as an alternative to the traditional transaction demarcation style of EJB CMT.
As an illustration of Spring's declarative transaction demarcation style, let's look at the configuration for the central service facade of Spring's PetClinic sample application:
java:comp/env/jdbc/petclinic PROPAGATION_REQUIRED,readOnly PROPAGATION_REQUIRED
This follows Spring's standard XMLBean definition format. It defines:
Note: As an alternative to explicit proxy definitions, Spring also supports an auto-proxying mechanism and the use of source-level metadata either through Commons Attributes or through J2SE 5.0 annotations. A discussion of these alternatives is beyond the scope of this article; refer to the Spring documentation for details.
The service interface and service implementation used are specific to the application and can be implemented without any awareness of Spring or Spring transaction management in particular. Plain Java objects can serve as the target objects, and any plain Java interface can serve as the service interface. Here is an example of the Clinic interface:
public interface Clinic {
Pet loadPet(int id);
void storePet(Pet pet);
...
}
A simple implementation of this interface is shown below, assuming that it uses JDBC for performing the necessary data access. It receives the JDBC DataSource via a bean property setter; this corresponds directly to the dataSource property definition in the configuration above.
public class JdbcClinic implements Clinic {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public Pet loadPet(int id) {
try {
Connection con = this.dataSource.getConnection();
...
}
catch (SQLException ex) {
...
}
}
public void storePet(Pet pet) {
try {
Connection con = this.dataSource.getConnection();
...
}
catch (SQLException ex) {
...
}
}
...
}
As you can see, the code is straightforward. We are using a simple Java object. The transaction management is handled by the transactional proxy, which we examine next.
Note that the actual JDBC-based Clinic implementations in the PetClinic sample application leverage Spring's JDBC support classes to avoid working at the plain JDBC API level. However, Spring's transaction management also will work with a plain JDBC-based implementation, such as the one illustrated above.
In addition to the JdbcClinic instance, the configuration also defines a transactional proxy for it. The actual interfaces exposed by this proxy can be specified explicitly, if desired. By default, all interfaces implemented by the target object will be exposed—in this case, the application's Clinic service interface.
From the point of view of a client, the "clinic" bean is just an implementation of the application's Clinic interface. The client does not have to know that it is dealing with a transactional proxy. This is the power of interfaces at work: A direct reference to the target object can easily be replaced with a proxy that implements the same interface—in this case, a proxy that implicitly creates transactions.
The concrete transactional behavior of a proxy is driven by transaction attributes for specific methods or method name patterns, as shown in the example below:
PROPAGATION_REQUIRED,readOnly PROPAGATION_REQUIRED
The key attribute determines which methods should have transactional behavior added to them by the proxy. The most important part of such an attribute is the propagation behavior. The following options are available:
The first six strategies are analogous to EJB CMT, with the same constant names, and should therefore look immediately familiar to EJB developers. The seventh, PROPAGATION_NESTED, is a special variant provided by Spring: It requires a transaction manager that either works with the JDBC 3.0 Savepoint API to provide nested transaction behavior (such as Spring's DataSourceTransactionManager) or supports nested transactions through JTA.
The readOnly flag in a transaction attribute indicates that the corresponding transaction should be optimized as a read-only transaction. This is an optimization hint: Some transaction strategies will be able to perform significant optimizations in such a case, for example avoiding dirty checking ("flush" attempts) when using an Object/Relational Mapping tool such as Hibernate or TopLink.
There is also the option to define a "timeout" value in the transaction attribute, specifying the timeout for the transaction as number of seconds. In the case of JTA, this will simply be passed down to the J2EE server's transaction coordinator and will get interpreted accordingly there.
At runtime, a client will fetch a reference to "clinic" bean, cast it to the Clinic interface, and invoke operations such as loadPet or storePet on it. This will implicitly go through Spring's transactional proxy, with a "transaction interceptor" registered in front of the target object; a new transaction will be created, and then the call will be delegated to the JdbcClinic target method.
Figure 1 illustrates the underlying concept of an AOP proxy with an "advisor chain" and a target at the end. In this case, the only advisor will be a transaction interceptor that wraps transactional behavior around the target method. This is proxy-based AOP (Aspect-Oriented Programming) at work beneath the hood of Spring's declarative transaction facilities.

Figure 1. An AOP proxy with an advisor chain and a target at the end
For example, a web tier component in the PetClinic web application could perform a ServletContext lookup to get a reference to the Spring WebApplicationContext and in turn to the "clinic" bean managed there:
WebApplicationContext ctx =
WebApplicationContexUtils.getWebApplicationContext(servletContext);
Clinic clinic = (Clinic) ctx.getBean("clinic);
Pet pet = new Pet();
pet.setName("my new cat");
clinic.storePet(pet);
At the beginning of the storePet() call, Spring's transactional proxy will implicitly create a transaction. When the storePet() call returns, the transaction will be committed or rolled back. By default, any RuntimeException or Error thrown will lead to a rollback. The actual rules for when to commit and when to roll back can be specified: Spring's transaction attribute supports a concept called "rollback rules."
For example, we could introduce a checked PetClinicException and tell the transactional proxy to roll back when that exception is thrown:
PROPAGATION_REQUIRED,readOnly,-PetClinicException PROPAGATION_REQUIRED,-PetClinicException
There is also an analogous syntax for "commit rules," indicating specific exceptions that should trigger a commit.
Note that an explicit lookup as shown above is just the generic variant to get access to a Spring-managed bean, working in any web resource such as a servlet or a filter. When building a web tier with Spring's own web MVC framework, such beans can be injected directly into web controllers. Support for convenient access to Spring beans is also available for Struts, WebWork, JSF, and Tapestry, for example. Refer to the Spring documentation for details.
PlatformTransactionManager strategyThe core interface in Spring's transaction support is org.springframework.transaction.PlatformTransactionManager. All of Spring's transaction demarcation facilities delegate to a PlatformTransactionManager for actually executing transactions, passing in appropriate TransactionDefinition instances. While the PlatformTransactionManager interface can be used directly, applications usually just configure a concrete transaction manager and use declarative transactions to demarcate transactions.
Spring comes with a variety of PlatformTransactionManager implementations, which fall into two categories:
org.springframework.jdbc.datasource.DataSourceTransactionManager and org.springframework.orm.hibernate.HibernateTransactionManager. org.springframework.transaction.jta.JtaTransactionManager, delegating to a transaction coordinator that follows the JTA specification (usually but not necessarily, a J2EE server). The main value of the PlatformTransactionManager abstraction is that applications are not tied to a specific transaction management environment. Instead, the transaction strategy can be switched easily—through choosing a different PlatformTransactionManager implementation class. This allows the application code and declarative transaction demarcation to stay the same, no matter which environment an application component is used in.
For example, a basic version of an application might be deployed to Tomcat, talking to a single Oracle database. It can leverage convenient Spring transaction demarcation there, choosing the JDBC DataSourceTransactionManager as its transaction strategy. Spring will demarcate the transactions, and the JDBC driver will execute corresponding plain JDBC transactions.
A different version of the same application might be deployed to WebLogic Server, talking to two Oracle databases. The application code and the transaction demarcation do not have to change. The only adaptation required is to choose JtaTransactionManager as the transaction strategy, letting Spring demarcate the transactions and WebLogic Server's transaction coordinator execute the transactions.
UserTransaction versus JTA TransactionManagerLet's look at some details of how Spring's JTA support works. While it is helpful to understand this mechanism, it is often not necessary to worry about it. For simple use cases like the one shown in the previous section, a standard JtaTransactionManager definition is all that's needed, in combination with XA-aware DataSources provided by the J2EE server.
A default Spring JtaTransactionManager setup will fetch the JTA's javax.transaction.UserTransaction object from the standard JNDI location specified for it by J2EE: java:comp/UserTransaction. This is fine for most use cases in a standard J2EE environment.
However, a default JtaTransactionManager will not be able to perform transaction suspension (that is, it won't support PROPAGATION_REQUIRES_NEW and PROPAGATION_NOT_SUPPORTED). The reason is that the standard JTA UserTransaction interface does not support suspending or resuming transactions; it only supports beginning and completing new transactions.
To make transaction suspension work, a javax.transaction.TransactionManager instance needs to be supplied, which offers standard suspend and resume methods, as specified by JTA. Unfortunately, J2EE does not define a standard JNDI location for the JTA TransactionManager! Consequently, vendor-specific lookup mechanisms have to be used.
vendorSpecificJndiLocation
J2EE essentially does not consider the JTA TransactionManager interface part of its public API. The JTA specification itself declares the TransactionManager interface as intended for container integration. While this is understandable, a standard JNDI location for the JTA TransactionManager would still add significant value, in particular for lightweight containers such as Spring; the JTA TransactionManager of any J2EE server could then be located in a uniform manner.
Not only will Spring's JtaTransactionManager benefit from access to the JTA TransactionManager, but O/R mapping tools such as Hibernate, Apache OJB, and Kodo JDO will benefit too, as they need this to be able to perform cache synchronization in a JTA environment—that is, releasing cache locks at JTA transaction completion. The ability to register transaction synchronizations is only offered by the JTA TransactionManager interface, not by the UserTransaction handle. As a consequence, each of these tools needs to implement its own vendor-specific TransactionManager lookup adapters.
Defining a standard JNDI location for the JTA TransactionManager is high on the J2EE wish list of many infrastructure software providers. It would be great if the J2EE 5.0 specification team would recognize the importance of this feature. Fortunately, advanced J2EE servers such as WebLogic Server already consider their JTA TransactionManager a public API, including vendor-specific extensions!
In the case of WebLogic Server, the official JNDI location of the JTA TransactionManager is javax.transaction.TransactionManager. This value can be specified as "transactionManagerName" on Spring's JtaTransactionManager. In principle, this enables Spring-driven transaction suspension with WebLogic's JTA subsystem, activating the support for PROPAGATION_REQUIRES_NEW and PROPAGATION_NOT_SUPPORTED.
javax.transaction.TransactionManager
Beyond its standard JtaTransactionManager and the general configuration options supported there, Spring also provides a special WebLogicJtaTransactionManager adapter that leverages WebLogic's JTA extensions directly.
In addition to the convenience of auto-detecting WebLogic's JTA TransactionManager, this enables three important features that go beyond standard JTA:
TransactionManager interface beneath, calling the forceResume() method. The following screenshot shows WebLogic Server's transaction monitor, which lists a couple of Spring-driven transactions by name:
|
Effectively, Spring's WebLogicJtaTransactionManager exposes the full power of WebLogic Server's transaction manager to Spring-based applications. This makes Spring transaction demarcation a compelling alternative to EJB CMT and provides the same level of transaction support.
Note that the WebLogic-specific JTA setup is only necessary when there is an actual need for transaction suspension or for leveraging WebLogic's JTA extensions. For standard transaction demarcation such as PROPAGATION_REQUIRED or PROPAGATION_SUPPORTS, the standard JTA setup will be fine.
As illustrated above, Spring's declarative transaction demarcation for POJOs can be considered an alternative to traditional EJB CMT. However, Spring and EJB are not mutually exclusive. A Spring application context can also serve as a back end behind EJB facades, managing data access objects (DAOs) and other fine-grained business objects.
In an EJB scenario, transactions will be driven by EJB CMT. Spring's data access support will automatically detect such an environment and adapt accordingly. For example, Spring's Hibernate support will provide its implicit resource management even with such EJB-driven transactions, just like it would with Spring-driven transactions. It will even provide the same semantics and not require any modification to DAO code.
Spring effectively decouples DAO implementations from the actual runtime environment. DAOs can participate in Spring transactions (with any transaction strategy as the back end) as well as EJB CMT transactions. This not only allows for reuse in other environments, but it also allows for straightforward use in tests outside the J2EE container.
The Spring Framework offers full-fledged transaction demarcation facilities for both J2EE and non-J2EE environments, in particular declarative transactions for plain Java target objects. This enables convenient transaction demarcation without EJB, in a flexible and non-intrusive fashion. In contrast to EJBs, such transactional POJO application objects can easily be tested or reused outside a J2EE container.
Spring provides various out-of-the-box transaction strategies, such as JtaTransactionManager, which delegates to the J2EE server's transaction coordinator, and the JDBC DataSourceTransactionManager, which executes transactions for a single JDBC DataSource (that is, a single target database). Spring can easily adapt the transaction strategy to a different environment through a simple change in back-end configuration.
Beyond standard JTA support, Spring provides sophisticated integration with WebLogic Server's JTA extensions, allowing for advanced features like transaction monitoring and per-transaction isolation levels. Through this special WebLogic Server support, the full power of WebLogic's transaction manager becomes available to Spring-based applications.
Spring transaction demarcation is a compelling alternative to EJB CMT, in particular in combination with a POJO-based lightweight architecture. In a scenario where the only reason for choosing Local Stateless Session Beans would be declarative transactions, a Spring-based POJO service model is a viable option, offering a significantly higher level of flexibility, testability, and reuse.
你可以使用这个链接引用该篇文章 http://publishblog.blogchina.com/blog/tb.b?diaryID=2508088