@Transactional supports qualifier value for choosing between multiple transaction managers

git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@1134 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
Juergen Hoeller 2009-05-08 23:13:43 +00:00
parent b13dfb067e
commit f4f3667679
13 changed files with 208 additions and 75 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2008 the original author or authors. * Copyright 2002-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -18,12 +18,11 @@ package org.springframework.transaction.annotation;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.AnnotatedElement; import java.lang.reflect.AnnotatedElement;
import javax.ejb.ApplicationException; import javax.ejb.ApplicationException;
import javax.ejb.TransactionAttributeType; import javax.ejb.TransactionAttributeType;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionAttribute; import org.springframework.transaction.interceptor.TransactionAttribute;
import org.springframework.transaction.support.DefaultTransactionDefinition;
/** /**
* Strategy implementation for parsing EJB3's {@link javax.ejb.TransactionAttribute} * Strategy implementation for parsing EJB3's {@link javax.ejb.TransactionAttribute}
@ -53,8 +52,7 @@ public class Ejb3TransactionAnnotationParser implements TransactionAnnotationPar
* EJB3-specific TransactionAttribute, implementing EJB3's rollback rules * EJB3-specific TransactionAttribute, implementing EJB3's rollback rules
* which are based on annotated exceptions. * which are based on annotated exceptions.
*/ */
private static class Ejb3TransactionAttribute extends DefaultTransactionDefinition private static class Ejb3TransactionAttribute extends DefaultTransactionAttribute {
implements TransactionAttribute {
public Ejb3TransactionAttribute(TransactionAttributeType type) { public Ejb3TransactionAttribute(TransactionAttributeType type) {
setPropagationBehaviorName(PREFIX_PROPAGATION + type.name()); setPropagationBehaviorName(PREFIX_PROPAGATION + type.name());
@ -62,7 +60,7 @@ public class Ejb3TransactionAnnotationParser implements TransactionAnnotationPar
public boolean rollbackOn(Throwable ex) { public boolean rollbackOn(Throwable ex) {
ApplicationException ann = ex.getClass().getAnnotation(ApplicationException.class); ApplicationException ann = ex.getClass().getAnnotation(ApplicationException.class);
return (ann != null ? ann.rollback() : (ex instanceof RuntimeException || ex instanceof Error)); return (ann != null ? ann.rollback() : super.rollbackOn(ex));
} }
} }

View File

@ -17,8 +17,8 @@
package org.springframework.transaction.annotation; package org.springframework.transaction.annotation;
import java.io.Serializable; import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.lang.annotation.Annotation; import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList; import java.util.ArrayList;
import org.springframework.transaction.interceptor.NoRollbackRuleAttribute; import org.springframework.transaction.interceptor.NoRollbackRuleAttribute;
@ -58,6 +58,7 @@ public class SpringTransactionAnnotationParser implements TransactionAnnotationP
rbta.setIsolationLevel(ann.isolation().value()); rbta.setIsolationLevel(ann.isolation().value());
rbta.setTimeout(ann.timeout()); rbta.setTimeout(ann.timeout());
rbta.setReadOnly(ann.readOnly()); rbta.setReadOnly(ann.readOnly());
rbta.setQualifier(ann.value());
ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>(); ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>();
Class[] rbf = ann.rollbackFor(); Class[] rbf = ann.rollbackFor();
for (Class rbRule : rbf) { for (Class rbRule : rbf) {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2006 the original author or authors. * Copyright 2002-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -49,6 +49,15 @@ import org.springframework.transaction.TransactionDefinition;
@Documented @Documented
public @interface Transactional { public @interface Transactional {
/**
* A qualifier value for the specified transaction.
* <p>May be used to determine the target transaction manager,
* matching the qualifier value (or the bean name) of a specific
* {@link org.springframework.transaction.PlatformTransactionManager}
* bean definition.
*/
String value() default "";
/** /**
* The transaction propagation type. * The transaction propagation type.
* <p>Defaults to {@link Propagation#REQUIRED}. * <p>Defaults to {@link Propagation#REQUIRED}.

View File

@ -95,8 +95,7 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
private static void registerTransactionManager(Element element, BeanDefinition def) { private static void registerTransactionManager(Element element, BeanDefinition def) {
String transactionManagerName = (element.hasAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE) ? String transactionManagerName = (element.hasAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE) ?
element.getAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME); element.getAttribute(TxNamespaceUtils.TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);
def.getPropertyValues().addPropertyValue( def.getPropertyValues().addPropertyValue("transactionManagerBeanName", transactionManagerName);
TxNamespaceUtils.TRANSACTION_MANAGER_PROPERTY, new RuntimeBeanReference(transactionManagerName));
} }

View File

@ -27,6 +27,9 @@ import org.springframework.transaction.support.DefaultTransactionDefinition;
*/ */
public class DefaultTransactionAttribute extends DefaultTransactionDefinition implements TransactionAttribute { public class DefaultTransactionAttribute extends DefaultTransactionDefinition implements TransactionAttribute {
private String qualifier;
/** /**
* Create a new DefaultTransactionAttribute, with default settings. * Create a new DefaultTransactionAttribute, with default settings.
* Can be modified through bean property setters. * Can be modified through bean property setters.
@ -66,10 +69,18 @@ public class DefaultTransactionAttribute extends DefaultTransactionDefinition im
} }
public void setQualifier(String qualifier) {
this.qualifier = qualifier;
}
public String getQualifier() {
return this.qualifier;
}
/** /**
* Default behavior is as with EJB: rollback on unchecked exception. * The default behavior is as with EJB: rollback on unchecked exception.
* Additionally attempt to rollback on Error. * Additionally attempt to rollback on Error.
* Consistent with TransactionTemplate's behavior. * <p>This is consistent with TransactionTemplate's default behavior.
*/ */
public boolean rollbackOn(Throwable ex) { public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error); return (ex instanceof RuntimeException || ex instanceof Error);

View File

@ -64,6 +64,10 @@ public abstract class DelegatingTransactionAttribute implements TransactionAttri
return this.targetAttribute.getName(); return this.targetAttribute.getName();
} }
public String getQualifier() {
return this.targetAttribute.getQualifier();
}
public boolean rollbackOn(Throwable ex) { public boolean rollbackOn(Throwable ex) {
return this.targetAttribute.rollbackOn(ex); return this.targetAttribute.rollbackOn(ex);
} }

View File

@ -17,18 +17,30 @@
package org.springframework.transaction.interceptor; package org.springframework.transaction.interceptor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Map;
import java.util.Properties; import java.util.Properties;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.core.NamedThreadLocal; import org.springframework.core.NamedThreadLocal;
import org.springframework.transaction.NoTransactionException; import org.springframework.transaction.NoTransactionException;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.TransactionSystemException; import org.springframework.transaction.TransactionSystemException;
import org.springframework.util.ClassUtils; import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/** /**
* Base class for transactional aspects, such as the AOP Alliance * Base class for transactional aspects, such as the AOP Alliance
@ -61,7 +73,7 @@ import org.springframework.util.ClassUtils;
* @see #setTransactionAttributes * @see #setTransactionAttributes
* @see #setTransactionAttributeSource * @see #setTransactionAttributeSource
*/ */
public abstract class TransactionAspectSupport implements InitializingBean { public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
// NOTE: This class must not implement Serializable because it serves as base // NOTE: This class must not implement Serializable because it serves as base
// class for AspectJ aspects (which are not allowed to implement Serializable)! // class for AspectJ aspects (which are not allowed to implement Serializable)!
@ -94,8 +106,9 @@ public abstract class TransactionAspectSupport implements InitializingBean {
* @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive() * @see org.springframework.transaction.support.TransactionSynchronizationManager#isActualTransactionActive()
*/ */
protected static TransactionInfo currentTransactionInfo() throws NoTransactionException { protected static TransactionInfo currentTransactionInfo() throws NoTransactionException {
return (TransactionInfo) transactionInfoHolder.get(); return transactionInfoHolder.get();
} }
/** /**
* Return the transaction status of the current method invocation. * Return the transaction status of the current method invocation.
* Mainly intended for code that wants to set the current transaction * Mainly intended for code that wants to set the current transaction
@ -114,23 +127,31 @@ public abstract class TransactionAspectSupport implements InitializingBean {
protected final Log logger = LogFactory.getLog(getClass()); protected final Log logger = LogFactory.getLog(getClass());
/** Delegate used to create, commit and rollback transactions */ private String transactionManagerBeanName;
private PlatformTransactionManager transactionManager; private PlatformTransactionManager transactionManager;
/** Helper used to find transaction attributes */
private TransactionAttributeSource transactionAttributeSource; private TransactionAttributeSource transactionAttributeSource;
private BeanFactory beanFactory;
/** /**
* Set the transaction manager. This will perform actual * Specify the name of the default transaction manager bean.
* transaction management: This class is just a way of invoking it. */
public void setTransactionManagerBeanName(String transactionManagerBeanName) {
this.transactionManagerBeanName = transactionManagerBeanName;
}
/**
* Specify the target transaction manager.
*/ */
public void setTransactionManager(PlatformTransactionManager transactionManager) { public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager; this.transactionManager = transactionManager;
} }
/** /**
* Return the transaction manager. * Return the transaction manager, if specified.
*/ */
public PlatformTransactionManager getTransactionManager() { public PlatformTransactionManager getTransactionManager() {
return this.transactionManager; return this.transactionManager;
@ -160,7 +181,6 @@ public abstract class TransactionAspectSupport implements InitializingBean {
* @see CompositeTransactionAttributeSource * @see CompositeTransactionAttributeSource
* @see MethodMapTransactionAttributeSource * @see MethodMapTransactionAttributeSource
* @see NameMatchTransactionAttributeSource * @see NameMatchTransactionAttributeSource
* @see AttributesTransactionAttributeSource
* @see org.springframework.transaction.annotation.AnnotationTransactionAttributeSource * @see org.springframework.transaction.annotation.AnnotationTransactionAttributeSource
*/ */
public void setTransactionAttributeSources(TransactionAttributeSource[] transactionAttributeSources) { public void setTransactionAttributeSources(TransactionAttributeSource[] transactionAttributeSources) {
@ -174,7 +194,6 @@ public abstract class TransactionAspectSupport implements InitializingBean {
* @see TransactionAttributeSourceEditor * @see TransactionAttributeSourceEditor
* @see MethodMapTransactionAttributeSource * @see MethodMapTransactionAttributeSource
* @see NameMatchTransactionAttributeSource * @see NameMatchTransactionAttributeSource
* @see AttributesTransactionAttributeSource
* @see org.springframework.transaction.annotation.AnnotationTransactionAttributeSource * @see org.springframework.transaction.annotation.AnnotationTransactionAttributeSource
*/ */
public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) { public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
@ -188,22 +207,79 @@ public abstract class TransactionAspectSupport implements InitializingBean {
return this.transactionAttributeSource; return this.transactionAttributeSource;
} }
/**
* Set the BeanFactory to use for retrieving PlatformTransactionManager beans.
*/
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/** /**
* Check that required properties were set. * Check that required properties were set.
*/ */
public void afterPropertiesSet() { public void afterPropertiesSet() {
if (getTransactionManager() == null) { if (this.transactionManager == null && this.beanFactory == null) {
throw new IllegalArgumentException("Property 'transactionManager' is required"); throw new IllegalStateException(
"Setting the property 'transactionManager' or running in a ListableBeanFactory is required");
} }
if (getTransactionAttributeSource() == null) { if (this.transactionAttributeSource == null) {
throw new IllegalArgumentException( throw new IllegalStateException(
"Either 'transactionAttributeSource' or 'transactionAttributes' is required: " + "Either 'transactionAttributeSource' or 'transactionAttributes' is required: " +
"If there are no transactional methods, then don't use a transaction aspect."); "If there are no transactional methods, then don't use a transaction aspect.");
} }
} }
/**
* Determine the specific transaction manager to use for the given transaction.
*/
protected PlatformTransactionManager determineTransactionManager(TransactionAttribute txAttr) {
if (this.transactionManager != null) {
return this.transactionManager;
}
PlatformTransactionManager chosen = null;
String qualifier = txAttr.getQualifier();
if (StringUtils.hasLength(qualifier)) {
if (!(this.beanFactory instanceof ConfigurableListableBeanFactory)) {
throw new IllegalStateException("BeanFactory required to be a ConfigurableListableBeanFactory " +
"for resolution of qualifier '" + qualifier + "': " + this.beanFactory.getClass());
}
ConfigurableListableBeanFactory bf = (ConfigurableListableBeanFactory) this.beanFactory;
Map<String, PlatformTransactionManager> tms =
BeanFactoryUtils.beansOfTypeIncludingAncestors(bf, PlatformTransactionManager.class);
for (String beanName : tms.keySet()) {
if (bf.containsBeanDefinition(beanName)) {
BeanDefinition bd = bf.getBeanDefinition(beanName);
if (bd instanceof AbstractBeanDefinition) {
AbstractBeanDefinition abd = (AbstractBeanDefinition) bd;
AutowireCandidateQualifier candidate = abd.getQualifier(Qualifier.class.getName());
if ((candidate != null && qualifier.equals(candidate.getAttribute(AutowireCandidateQualifier.VALUE_KEY))) ||
qualifier.equals(beanName) || ObjectUtils.containsElement(bf.getAliases(beanName), qualifier)) {
if (chosen != null) {
throw new IllegalStateException("No unique PlatformTransactionManager bean found " +
"for qualifier '" + qualifier + "'");
}
chosen = tms.get(beanName);
}
}
}
}
}
if (chosen != null) {
return chosen;
}
else if (this.transactionManagerBeanName != null) {
return this.beanFactory.getBean(this.transactionManagerBeanName, PlatformTransactionManager.class);
}
else if (this.beanFactory instanceof ListableBeanFactory) {
return BeanFactoryUtils.beanOfTypeIncludingAncestors(((ListableBeanFactory) this.beanFactory), PlatformTransactionManager.class);
}
else {
throw new IllegalStateException(
"Cannot retrieve PlatformTransactionManager beans from non-listable BeanFactory: " + this.beanFactory);
}
}
/** /**
* Create a transaction if necessary, based on the given method and class. * Create a transaction if necessary, based on the given method and class.
* <p>Performs a default TransactionAttribute lookup for the given method. * <p>Performs a default TransactionAttribute lookup for the given method.
@ -217,7 +293,8 @@ public abstract class TransactionAspectSupport implements InitializingBean {
protected TransactionInfo createTransactionIfNecessary(Method method, Class targetClass) { protected TransactionInfo createTransactionIfNecessary(Method method, Class targetClass) {
// If the transaction attribute is null, the method is non-transactional. // If the transaction attribute is null, the method is non-transactional.
TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass); TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
return createTransactionIfNecessary(txAttr, methodIdentification(method)); PlatformTransactionManager tm = determineTransactionManager(txAttr);
return createTransactionIfNecessary(tm, txAttr, methodIdentification(method));
} }
/** /**
@ -245,7 +322,7 @@ public abstract class TransactionAspectSupport implements InitializingBean {
* @see #getTransactionAttributeSource() * @see #getTransactionAttributeSource()
*/ */
protected TransactionInfo createTransactionIfNecessary( protected TransactionInfo createTransactionIfNecessary(
TransactionAttribute txAttr, final String joinpointIdentification) { PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
// If no name specified, apply method identification as transaction name. // If no name specified, apply method identification as transaction name.
if (txAttr != null && txAttr.getName() == null) { if (txAttr != null && txAttr.getName() == null) {
@ -259,7 +336,6 @@ public abstract class TransactionAspectSupport implements InitializingBean {
TransactionStatus status = null; TransactionStatus status = null;
if (txAttr != null) { if (txAttr != null) {
PlatformTransactionManager tm = getTransactionManager();
if (tm != null) { if (tm != null) {
status = tm.getTransaction(txAttr); status = tm.getTransaction(txAttr);
} }
@ -270,7 +346,7 @@ public abstract class TransactionAspectSupport implements InitializingBean {
} }
} }
} }
return prepareTransactionInfo(txAttr, joinpointIdentification, status); return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
} }
/** /**
@ -281,10 +357,10 @@ public abstract class TransactionAspectSupport implements InitializingBean {
* @param status the TransactionStatus for the current transaction * @param status the TransactionStatus for the current transaction
* @return the prepared TransactionInfo object * @return the prepared TransactionInfo object
*/ */
protected TransactionInfo prepareTransactionInfo( protected TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm,
TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) { TransactionAttribute txAttr, String joinpointIdentification, TransactionStatus status) {
TransactionInfo txInfo = new TransactionInfo(txAttr, joinpointIdentification); TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinpointIdentification);
if (txAttr != null) { if (txAttr != null) {
// We need a transaction for this method // We need a transaction for this method
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
@ -319,7 +395,7 @@ public abstract class TransactionAspectSupport implements InitializingBean {
if (logger.isTraceEnabled()) { if (logger.isTraceEnabled()) {
logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]"); logger.trace("Completing transaction for [" + txInfo.getJoinpointIdentification() + "]");
} }
getTransactionManager().commit(txInfo.getTransactionStatus()); txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
} }
} }
@ -337,7 +413,7 @@ public abstract class TransactionAspectSupport implements InitializingBean {
} }
if (txInfo.transactionAttribute.rollbackOn(ex)) { if (txInfo.transactionAttribute.rollbackOn(ex)) {
try { try {
getTransactionManager().rollback(txInfo.getTransactionStatus()); txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
} }
catch (TransactionSystemException ex2) { catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by rollback exception", ex); logger.error("Application exception overridden by rollback exception", ex);
@ -357,7 +433,7 @@ public abstract class TransactionAspectSupport implements InitializingBean {
// We don't roll back on this exception. // We don't roll back on this exception.
// Will still roll back if TransactionStatus.isRollbackOnly() is true. // Will still roll back if TransactionStatus.isRollbackOnly() is true.
try { try {
getTransactionManager().commit(txInfo.getTransactionStatus()); txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
} }
catch (TransactionSystemException ex2) { catch (TransactionSystemException ex2) {
logger.error("Application exception overridden by commit exception", ex); logger.error("Application exception overridden by commit exception", ex);
@ -392,7 +468,9 @@ public abstract class TransactionAspectSupport implements InitializingBean {
* Opaque object used to hold Transaction information. Subclasses * Opaque object used to hold Transaction information. Subclasses
* must pass it back to methods on this class, but not see its internals. * must pass it back to methods on this class, but not see its internals.
*/ */
protected class TransactionInfo { protected final class TransactionInfo {
private final PlatformTransactionManager transactionManager;
private final TransactionAttribute transactionAttribute; private final TransactionAttribute transactionAttribute;
@ -402,11 +480,17 @@ public abstract class TransactionAspectSupport implements InitializingBean {
private TransactionInfo oldTransactionInfo; private TransactionInfo oldTransactionInfo;
public TransactionInfo(TransactionAttribute transactionAttribute, String joinpointIdentification) { public TransactionInfo(PlatformTransactionManager transactionManager,
TransactionAttribute transactionAttribute, String joinpointIdentification) {
this.transactionManager = transactionManager;
this.transactionAttribute = transactionAttribute; this.transactionAttribute = transactionAttribute;
this.joinpointIdentification = joinpointIdentification; this.joinpointIdentification = joinpointIdentification;
} }
public PlatformTransactionManager getTransactionManager() {
return this.transactionManager;
}
public TransactionAttribute getTransactionAttribute() { public TransactionAttribute getTransactionAttribute() {
return this.transactionAttribute; return this.transactionAttribute;
} }
@ -438,7 +522,7 @@ public abstract class TransactionAspectSupport implements InitializingBean {
private void bindToThread() { private void bindToThread() {
// Expose current TransactionStatus, preserving any existing TransactionStatus // Expose current TransactionStatus, preserving any existing TransactionStatus
// for restoration after this transaction is complete. // for restoration after this transaction is complete.
this.oldTransactionInfo = (TransactionInfo) transactionInfoHolder.get(); this.oldTransactionInfo = transactionInfoHolder.get();
transactionInfoHolder.set(this); transactionInfoHolder.set(this);
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2005 the original author or authors. * Copyright 2002-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,7 +19,7 @@ package org.springframework.transaction.interceptor;
import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionDefinition;
/** /**
* This interface adds a <code>rollbackOn</code> specification to TransactionDefinition. * This interface adds a <code>rollbackOn</code> specification to {@link TransactionDefinition}.
* As custom <code>rollbackOn</code> is only possible with AOP, this class resides * As custom <code>rollbackOn</code> is only possible with AOP, this class resides
* in the AOP transaction package. * in the AOP transaction package.
* *
@ -30,6 +30,8 @@ import org.springframework.transaction.TransactionDefinition;
*/ */
public interface TransactionAttribute extends TransactionDefinition { public interface TransactionAttribute extends TransactionDefinition {
String getQualifier();
/** /**
* Should we roll back on the given exception? * Should we roll back on the given exception?
* @param ex the exception to evaluate * @param ex the exception to evaluate

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2007 the original author or authors. * Copyright 2002-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -94,11 +94,12 @@ public class TransactionInterceptor extends TransactionAspectSupport implements
// If the transaction attribute is null, the method is non-transactional. // If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = final TransactionAttribute txAttr =
getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass); getTransactionAttributeSource().getTransactionAttribute(invocation.getMethod(), targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(invocation.getMethod()); final String joinpointIdentification = methodIdentification(invocation.getMethod());
if (txAttr == null || !(getTransactionManager() instanceof CallbackPreferringPlatformTransactionManager)) { if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls. // Standard transaction demarcation with getTransaction and commit/rollback calls.
TransactionInfo txInfo = createTransactionIfNecessary(txAttr, joinpointIdentification); TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null; Object retVal = null;
try { try {
// This is an around advice: Invoke the next interceptor in the chain. // This is an around advice: Invoke the next interceptor in the chain.
@ -120,10 +121,10 @@ public class TransactionInterceptor extends TransactionAspectSupport implements
else { else {
// It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in. // It's a CallbackPreferringPlatformTransactionManager: pass a TransactionCallback in.
try { try {
Object result = ((CallbackPreferringPlatformTransactionManager) getTransactionManager()).execute(txAttr, Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
new TransactionCallback() { new TransactionCallback<Object>() {
public Object doInTransaction(TransactionStatus status) { public Object doInTransaction(TransactionStatus status) {
TransactionInfo txInfo = prepareTransactionInfo(txAttr, joinpointIdentification, status); TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try { try {
return invocation.proceed(); return invocation.proceed();
} }
@ -167,6 +168,15 @@ public class TransactionInterceptor extends TransactionAspectSupport implements
// Serialization support // Serialization support
//--------------------------------------------------------------------- //---------------------------------------------------------------------
private void writeObject(ObjectOutputStream oos) throws IOException {
// Rely on default serialization, although this class itself doesn't carry state anyway...
oos.defaultWriteObject();
// Deserialize superclass fields.
oos.writeObject(getTransactionManager());
oos.writeObject(getTransactionAttributeSource());
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// Rely on default serialization, although this class itself doesn't carry state anyway... // Rely on default serialization, although this class itself doesn't carry state anyway...
ois.defaultReadObject(); ois.defaultReadObject();
@ -178,15 +188,6 @@ public class TransactionInterceptor extends TransactionAspectSupport implements
setTransactionAttributeSource((TransactionAttributeSource) ois.readObject()); setTransactionAttributeSource((TransactionAttributeSource) ois.readObject());
} }
private void writeObject(ObjectOutputStream oos) throws IOException {
// Rely on default serialization, although this class itself doesn't carry state anyway...
oos.defaultWriteObject();
// Deserialize superclass fields.
oos.writeObject(getTransactionManager());
oos.writeObject(getTransactionAttributeSource());
}
/** /**
* Internal holder class for a Throwable, used as a return value * Internal holder class for a Throwable, used as a return value

View File

@ -23,7 +23,6 @@ import org.springframework.aop.framework.AbstractSingletonProxyFactoryBean;
import org.springframework.aop.support.DefaultPointcutAdvisor; import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.FactoryBean; import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.ListableBeanFactory; import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.PlatformTransactionManager;
@ -171,12 +170,7 @@ public class TransactionProxyFactoryBean extends AbstractSingletonProxyFactoryBe
* @see org.springframework.transaction.PlatformTransactionManager * @see org.springframework.transaction.PlatformTransactionManager
*/ */
public void setBeanFactory(BeanFactory beanFactory) { public void setBeanFactory(BeanFactory beanFactory) {
if (this.transactionInterceptor.getTransactionManager() == null && this.transactionInterceptor.setBeanFactory(beanFactory);
beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory lbf = (ListableBeanFactory) beanFactory;
PlatformTransactionManager ptm = BeanFactoryUtils.beanOfTypeIncludingAncestors(lbf, PlatformTransactionManager.class);
this.transactionInterceptor.setTransactionManager(ptm);
}
} }

View File

@ -22,6 +22,7 @@ import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.support.AopUtils; import org.springframework.aop.support.AopUtils;
import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.transaction.CallCountingTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.transaction.support.TransactionSynchronizationManager;
/** /**
@ -32,16 +33,34 @@ public class AnnotationDrivenTests extends TestCase {
public void testWithProxyTargetClass() throws Exception { public void testWithProxyTargetClass() throws Exception {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("annotationDrivenProxyTargetClassTests.xml", getClass()); ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("annotationDrivenProxyTargetClassTests.xml", getClass());
TransactionalService service = (TransactionalService) context.getBean("service"); CallCountingTransactionManager tm1 = context.getBean("transactionManager1", CallCountingTransactionManager.class);
CallCountingTransactionManager tm2 = context.getBean("transactionManager2", CallCountingTransactionManager.class);
TransactionalService service = context.getBean("service", TransactionalService.class);
assertTrue(AopUtils.isCglibProxy(service)); assertTrue(AopUtils.isCglibProxy(service));
service.setBeanName("someName"); service.setSomething("someName");
assertEquals(1, tm1.commits);
assertEquals(0, tm2.commits);
service.doSomething();
assertEquals(1, tm1.commits);
assertEquals(1, tm2.commits);
service.setSomething("someName");
assertEquals(2, tm1.commits);
assertEquals(1, tm2.commits);
service.doSomething();
assertEquals(2, tm1.commits);
assertEquals(2, tm2.commits);
} }
public static class TransactionCheckingInterceptor implements MethodInterceptor { public static class TransactionCheckingInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation methodInvocation) throws Throwable { public Object invoke(MethodInvocation methodInvocation) throws Throwable {
assertTrue(TransactionSynchronizationManager.isActualTransactionActive()); if (methodInvocation.getMethod().getName().equals("setSomething")) {
assertTrue(TransactionSynchronizationManager.isSynchronizationActive());
}
else {
assertFalse(TransactionSynchronizationManager.isSynchronizationActive());
}
return methodInvocation.proceed(); return methodInvocation.proceed();
} }
} }

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2007 the original author or authors. * Copyright 2002-2009 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -21,12 +21,16 @@ import org.springframework.transaction.annotation.Transactional;
/** /**
* @author Rob Harrop * @author Rob Harrop
* @author Juergen Hoeller
*/ */
@Transactional public class TransactionalService {
public class TransactionalService implements BeanNameAware {
public void setBeanName(String name) { @Transactional("synch")
// just for testing :) public void setSomething(String name) {
}
@Transactional("noSynch")
public void doSomething() {
} }
} }

View File

@ -2,7 +2,7 @@
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"> xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<tx:annotation-driven proxy-target-class="true" order="0"/> <tx:annotation-driven proxy-target-class="true" order="0"/>
@ -12,7 +12,14 @@
<bean id="txCheckingInterceptor" class="org.springframework.transaction.config.AnnotationDrivenTests$TransactionCheckingInterceptor"/> <bean id="txCheckingInterceptor" class="org.springframework.transaction.config.AnnotationDrivenTests$TransactionCheckingInterceptor"/>
<bean id="transactionManager" class="org.springframework.transaction.CallCountingTransactionManager"/> <bean id="transactionManager1" class="org.springframework.transaction.CallCountingTransactionManager">
<qualifier value="synch"/>
</bean>
<bean id="transactionManager2" class="org.springframework.transaction.CallCountingTransactionManager">
<property name="transactionSynchronizationName" value="SYNCHRONIZATION_NEVER"/>
<qualifier value="noSynch"/>
</bean>
<bean id="service" class="org.springframework.transaction.config.TransactionalService"/> <bean id="service" class="org.springframework.transaction.config.TransactionalService"/>