Introduce FeatureSpecification support
Introduce FeatureSpecification interface and implementations
FeatureSpecification objects decouple the configuration of
spring container features from the concern of parsing XML
namespaces, allowing for reuse in code-based configuration
(see @Feature* annotations below).
* ComponentScanSpec
* TxAnnotationDriven
* MvcAnnotationDriven
* MvcDefaultServletHandler
* MvcResources
* MvcViewControllers
Refactor associated BeanDefinitionParsers to delegate to new impls above
The following BeanDefinitionParser implementations now deal only
with the concern of XML parsing. Validation is handled by their
corresponding FeatureSpecification object. Bean definition creation
and registration is handled by their corresponding
FeatureSpecificationExecutor type.
* ComponentScanBeanDefinitionParser
* AnnotationDrivenBeanDefinitionParser (tx)
* AnnotationDrivenBeanDefinitionParser (mvc)
* DefaultServletHandlerBeanDefinitionParser
* ResourcesBeanDefinitionParser
* ViewControllerBeanDefinitionParser
Update AopNamespaceUtils to decouple from XML (DOM API)
Methods necessary for executing TxAnnotationDriven specification
(and eventually, the AspectJAutoProxy specification) have been
added that accept boolean arguments for whether to proxy
target classes and whether to expose the proxy via threadlocal.
Methods that accepted and introspected DOM Element objects still
exist but have been deprecated.
Introduce @FeatureConfiguration classes and @Feature methods
Allow for creation and configuration of FeatureSpecification objects
at the user level. A companion for @Configuration classes allowing
for completely code-driven configuration of the Spring container.
See changes in ConfigurationClassPostProcessor for implementation
details.
See Feature*Tests for usage examples.
FeatureTestSuite in .integration-tests is a JUnit test suite designed
to aggregate all BDP and Feature* related tests for a convenient way
to confirm that Feature-related changes don't break anything.
Uncomment this test and execute from Eclipse / IDEA. Due to classpath
issues, this cannot be compiled by Ant/Ivy at the command line.
Introduce @FeatureAnnotation meta-annotation and @ComponentScan impl
@FeatureAnnotation provides an alternate mechanism for creating
and executing FeatureSpecification objects. See @ComponentScan
and its corresponding ComponentScanAnnotationParser implementation
for details. See ComponentScanAnnotationIntegrationTests for usage
examples
Introduce Default[Formatting]ConversionService implementations
Allows for convenient instantiation of ConversionService objects
containing defaults appropriate for most environments. Replaces
similar support originally in ConversionServiceFactory (which is now
deprecated). This change was justified by the need to avoid use
of FactoryBeans in @Configuration classes (such as
FormattingConversionServiceFactoryBean). It is strongly preferred
that users simply instantiate and configure the objects that underlie
our FactoryBeans. In the case of the ConversionService types, the
easiest way to do this is to create Default* subtypes. This also
follows convention with the rest of the framework.
Minor updates to util classes
All in service of changes above. See diffs for self-explanatory
details.
* BeanUtils
* ObjectUtils
* ReflectionUtils
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,24 +16,18 @@
|
||||
|
||||
package org.springframework.transaction.config;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
import org.springframework.aop.config.AopNamespaceUtils;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.BeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
|
||||
import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
import org.springframework.context.config.ExecutorContext;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
|
||||
* implementation that allows users to easily configure all the infrastructure
|
||||
* beans required to enable annotation-driven transaction demarcation.
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser
|
||||
* BeanDefinitionParser} implementation that allows users to easily configure
|
||||
* all the infrastructure beans required to enable annotation-driven transaction
|
||||
* demarcation.
|
||||
*
|
||||
* <p>By default, all proxies are created as JDK proxies. This may cause some
|
||||
* problems if you are injecting objects as concrete classes rather than
|
||||
@@ -43,7 +37,9 @@ import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rob Harrop
|
||||
* @author Chris Beams
|
||||
* @since 2.0
|
||||
* @see TxAnnotationDriven
|
||||
*/
|
||||
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
@@ -51,95 +47,43 @@ class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
|
||||
* The bean name of the internally managed transaction advisor (mode="proxy").
|
||||
*/
|
||||
public static final String TRANSACTION_ADVISOR_BEAN_NAME =
|
||||
"org.springframework.transaction.config.internalTransactionAdvisor";
|
||||
TxAnnotationDrivenExecutor.TRANSACTION_ADVISOR_BEAN_NAME;
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed transaction aspect (mode="aspectj").
|
||||
*/
|
||||
public static final String TRANSACTION_ASPECT_BEAN_NAME =
|
||||
"org.springframework.transaction.config.internalTransactionAspect";
|
||||
|
||||
private static final String TRANSACTION_ASPECT_CLASS_NAME =
|
||||
"org.springframework.transaction.aspectj.AnnotationTransactionAspect";
|
||||
TxAnnotationDrivenExecutor.TRANSACTION_ASPECT_BEAN_NAME;
|
||||
|
||||
|
||||
/**
|
||||
* Parses the '<code><tx:annotation-driven/></code>' tag. Will
|
||||
* Parses the {@code <tx:annotation-driven/>} tag. Will
|
||||
* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}
|
||||
* with the container as necessary.
|
||||
*/
|
||||
public BeanDefinition parse(Element element, ParserContext parserContext) {
|
||||
String mode = element.getAttribute("mode");
|
||||
if ("aspectj".equals(mode)) {
|
||||
// mode="aspectj"
|
||||
registerTransactionAspect(element, parserContext);
|
||||
}
|
||||
else {
|
||||
// mode="proxy"
|
||||
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
|
||||
}
|
||||
new TxAnnotationDriven(element.getAttribute("transaction-manager"))
|
||||
.order(element.getAttribute("order"))
|
||||
.mode(element.getAttribute("mode"))
|
||||
.proxyTargetClass(Boolean.valueOf(element.getAttribute("proxy-target-class")))
|
||||
.source(parserContext.extractSource(element))
|
||||
.sourceName(element.getTagName())
|
||||
.execute(createExecutorContext(parserContext));
|
||||
return null;
|
||||
}
|
||||
|
||||
private void registerTransactionAspect(Element element, ParserContext parserContext) {
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(TRANSACTION_ASPECT_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition();
|
||||
def.setBeanClassName(TRANSACTION_ASPECT_CLASS_NAME);
|
||||
def.setFactoryMethodName("aspectOf");
|
||||
registerTransactionManager(element, def);
|
||||
parserContext.registerBeanComponent(new BeanComponentDefinition(def, TRANSACTION_ASPECT_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerTransactionManager(Element element, BeanDefinition def) {
|
||||
def.getPropertyValues().add("transactionManagerBeanName",
|
||||
TxNamespaceHandler.getTransactionManagerName(element));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Inner class to just introduce an AOP framework dependency when actually in proxy mode.
|
||||
* Adapt the given ParserContext instance into an ExecutorContext.
|
||||
*
|
||||
* TODO SPR-7420: consider unifying the two through a superinterface.
|
||||
* TODO SPR-7420: create a common ParserContext-to-ExecutorContext adapter util
|
||||
*/
|
||||
private static class AopAutoProxyConfigurer {
|
||||
|
||||
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
|
||||
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
|
||||
|
||||
if (!parserContext.getRegistry().containsBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME)) {
|
||||
Object eleSource = parserContext.extractSource(element);
|
||||
|
||||
// Create the TransactionAttributeSource definition.
|
||||
RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationTransactionAttributeSource.class);
|
||||
sourceDef.setSource(eleSource);
|
||||
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
|
||||
|
||||
// Create the TransactionInterceptor definition.
|
||||
RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
|
||||
interceptorDef.setSource(eleSource);
|
||||
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registerTransactionManager(element, interceptorDef);
|
||||
interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
|
||||
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
|
||||
|
||||
// Create the TransactionAttributeSourceAdvisor definition.
|
||||
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
|
||||
advisorDef.setSource(eleSource);
|
||||
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
|
||||
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
|
||||
if (element.hasAttribute("order")) {
|
||||
advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
|
||||
}
|
||||
parserContext.getRegistry().registerBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME, advisorDef);
|
||||
|
||||
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, TRANSACTION_ADVISOR_BEAN_NAME));
|
||||
parserContext.registerComponent(compositeDef);
|
||||
}
|
||||
}
|
||||
private ExecutorContext createExecutorContext(ParserContext parserContext) {
|
||||
ExecutorContext executorContext = new ExecutorContext();
|
||||
executorContext.setRegistry(parserContext.getRegistry());
|
||||
executorContext.setRegistrar(parserContext);
|
||||
executorContext.setProblemReporter(parserContext.getReaderContext().getProblemReporter());
|
||||
return executorContext;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -74,7 +74,7 @@ public class JtaTransactionManagerBeanDefinitionParser extends AbstractSingleBea
|
||||
|
||||
@Override
|
||||
protected String resolveId(Element element, AbstractBeanDefinition definition, ParserContext parserContext) {
|
||||
return TxNamespaceHandler.DEFAULT_TRANSACTION_MANAGER_BEAN_NAME;
|
||||
return TxAnnotationDriven.DEFAULT_TRANSACTION_MANAGER_BEAN_NAME;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -37,50 +37,55 @@ import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser}
|
||||
* for the <code><tx:advice></code> tag.
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser
|
||||
* BeanDefinitionParser} for the {@code <tx:advice/>} tag.
|
||||
*
|
||||
* @author Rob Harrop
|
||||
* @author Juergen Hoeller
|
||||
* @author Adrian Colyer
|
||||
* @author Chris Beams
|
||||
* @since 2.0
|
||||
*/
|
||||
class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
private static final String ATTRIBUTES = "attributes";
|
||||
private static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";
|
||||
|
||||
private static final String TIMEOUT = "timeout";
|
||||
private static final String METHOD_ELEMENT = "method";
|
||||
|
||||
private static final String READ_ONLY = "read-only";
|
||||
private static final String METHOD_NAME_ATTRIBUTE = "name";
|
||||
|
||||
private static final String NAME_MAP = "nameMap";
|
||||
private static final String ATTRIBUTES_ELEMENT = "attributes";
|
||||
|
||||
private static final String PROPAGATION = "propagation";
|
||||
private static final String TIMEOUT_ATTRIBUTE = "timeout";
|
||||
|
||||
private static final String ISOLATION = "isolation";
|
||||
private static final String READ_ONLY_ATTRIBUTE = "read-only";
|
||||
|
||||
private static final String ROLLBACK_FOR = "rollback-for";
|
||||
private static final String PROPAGATION_ATTRIBUTE = "propagation";
|
||||
|
||||
private static final String NO_ROLLBACK_FOR = "no-rollback-for";
|
||||
private static final String ISOLATION_ATTRIBUTE = "isolation";
|
||||
|
||||
private static final String ROLLBACK_FOR_ATTRIBUTE = "rollback-for";
|
||||
|
||||
private static final String NO_ROLLBACK_FOR_ATTRIBUTE = "no-rollback-for";
|
||||
|
||||
|
||||
@Override
|
||||
protected Class getBeanClass(Element element) {
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return TransactionInterceptor.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
builder.addPropertyReference("transactionManager", TxNamespaceHandler.getTransactionManagerName(element));
|
||||
builder.addPropertyReference("transactionManager", element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE));
|
||||
|
||||
List txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES);
|
||||
List<Element> txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES_ELEMENT);
|
||||
if (txAttributes.size() > 1) {
|
||||
parserContext.getReaderContext().error(
|
||||
"Element <attributes> is allowed at most once inside element <advice>", element);
|
||||
}
|
||||
else if (txAttributes.size() == 1) {
|
||||
// Using attributes source.
|
||||
Element attributeSourceElement = (Element) txAttributes.get(0);
|
||||
Element attributeSourceElement = txAttributes.get(0);
|
||||
RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);
|
||||
builder.addPropertyValue("transactionAttributeSource", attributeSourceDefinition);
|
||||
}
|
||||
@@ -92,20 +97,21 @@ class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
}
|
||||
|
||||
private RootBeanDefinition parseAttributeSource(Element attrEle, ParserContext parserContext) {
|
||||
List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, "method");
|
||||
ManagedMap transactionAttributeMap = new ManagedMap(methods.size());
|
||||
List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, METHOD_ELEMENT);
|
||||
ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap =
|
||||
new ManagedMap<TypedStringValue, RuleBasedTransactionAttribute>(methods.size());
|
||||
transactionAttributeMap.setSource(parserContext.extractSource(attrEle));
|
||||
|
||||
for (Element methodEle : methods) {
|
||||
String name = methodEle.getAttribute("name");
|
||||
String name = methodEle.getAttribute(METHOD_NAME_ATTRIBUTE);
|
||||
TypedStringValue nameHolder = new TypedStringValue(name);
|
||||
nameHolder.setSource(parserContext.extractSource(methodEle));
|
||||
|
||||
RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();
|
||||
String propagation = methodEle.getAttribute(PROPAGATION);
|
||||
String isolation = methodEle.getAttribute(ISOLATION);
|
||||
String timeout = methodEle.getAttribute(TIMEOUT);
|
||||
String readOnly = methodEle.getAttribute(READ_ONLY);
|
||||
String propagation = methodEle.getAttribute(PROPAGATION_ATTRIBUTE);
|
||||
String isolation = methodEle.getAttribute(ISOLATION_ATTRIBUTE);
|
||||
String timeout = methodEle.getAttribute(TIMEOUT_ATTRIBUTE);
|
||||
String readOnly = methodEle.getAttribute(READ_ONLY_ATTRIBUTE);
|
||||
if (StringUtils.hasText(propagation)) {
|
||||
attribute.setPropagationBehaviorName(RuleBasedTransactionAttribute.PREFIX_PROPAGATION + propagation);
|
||||
}
|
||||
@@ -121,16 +127,16 @@ class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
}
|
||||
}
|
||||
if (StringUtils.hasText(readOnly)) {
|
||||
attribute.setReadOnly(Boolean.valueOf(methodEle.getAttribute(READ_ONLY)));
|
||||
attribute.setReadOnly(Boolean.valueOf(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));
|
||||
}
|
||||
|
||||
List<RollbackRuleAttribute> rollbackRules = new LinkedList<RollbackRuleAttribute>();
|
||||
if (methodEle.hasAttribute(ROLLBACK_FOR)) {
|
||||
String rollbackForValue = methodEle.getAttribute(ROLLBACK_FOR);
|
||||
if (methodEle.hasAttribute(ROLLBACK_FOR_ATTRIBUTE)) {
|
||||
String rollbackForValue = methodEle.getAttribute(ROLLBACK_FOR_ATTRIBUTE);
|
||||
addRollbackRuleAttributesTo(rollbackRules,rollbackForValue);
|
||||
}
|
||||
if (methodEle.hasAttribute(NO_ROLLBACK_FOR)) {
|
||||
String noRollbackForValue = methodEle.getAttribute(NO_ROLLBACK_FOR);
|
||||
if (methodEle.hasAttribute(NO_ROLLBACK_FOR_ATTRIBUTE)) {
|
||||
String noRollbackForValue = methodEle.getAttribute(NO_ROLLBACK_FOR_ATTRIBUTE);
|
||||
addNoRollbackRuleAttributesTo(rollbackRules,noRollbackForValue);
|
||||
}
|
||||
attribute.setRollbackRules(rollbackRules);
|
||||
@@ -140,7 +146,7 @@ class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchTransactionAttributeSource.class);
|
||||
attributeSourceDefinition.setSource(parserContext.extractSource(attrEle));
|
||||
attributeSourceDefinition.getPropertyValues().add(NAME_MAP, transactionAttributeMap);
|
||||
attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);
|
||||
return attributeSourceDefinition;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.transaction.config;
|
||||
|
||||
import org.springframework.beans.factory.parsing.SimpleProblemCollector;
|
||||
import org.springframework.context.config.AbstractFeatureSpecification;
|
||||
import org.springframework.context.config.AdviceMode;
|
||||
import org.springframework.context.config.FeatureSpecificationExecutor;
|
||||
import org.springframework.transaction.PlatformTransactionManager;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
public final class TxAnnotationDriven extends AbstractFeatureSpecification {
|
||||
|
||||
static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
|
||||
|
||||
private static final Class<? extends FeatureSpecificationExecutor> EXECUTOR_TYPE = TxAnnotationDrivenExecutor.class;
|
||||
|
||||
private Object txManager = null;
|
||||
|
||||
private Object order = null;
|
||||
|
||||
private Boolean proxyTargetClass = false;
|
||||
|
||||
private Object mode = AdviceMode.PROXY;
|
||||
|
||||
/**
|
||||
* Create a {@code TxAnnotationDriven} specification assumes the presence of a
|
||||
* {@link PlatformTransactionManager} bean named {@value #DEFAULT_TRANSACTION_MANAGER_BEAN_NAME}.
|
||||
*
|
||||
* <p>See the alternate constructors defined here if your transaction manager does
|
||||
* not follow this default naming or you wish to refer to it by bean instance rather
|
||||
* than by bean name.
|
||||
* @see #TxAnnotationDriven(String)
|
||||
* @see #TxAnnotationDriven(PlatformTransactionManager)
|
||||
*/
|
||||
public TxAnnotationDriven() {
|
||||
this(DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@code TxAnnotationDriven} specification that will use the specified
|
||||
* transaction manager bean name.
|
||||
*
|
||||
* @param txManagerBeanName name of {@link PlatformTransactionManager} bean or a
|
||||
* ${placeholder} or SpEL #{expression} resolving to bean name. If {@code null},
|
||||
* falls back to default value of {@value #DEFAULT_TRANSACTION_MANAGER_BEAN_NAME}.
|
||||
*/
|
||||
public TxAnnotationDriven(String txManagerBeanName) {
|
||||
super(EXECUTOR_TYPE);
|
||||
this.txManager = txManagerBeanName != null ?
|
||||
txManagerBeanName :
|
||||
DEFAULT_TRANSACTION_MANAGER_BEAN_NAME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new TxAnnotationDriven specification that will use the specified transaction
|
||||
* manager.
|
||||
*
|
||||
* @param txManager the {@link PlatformTransactionManager} bean to use. Must not be {@code null}.
|
||||
*/
|
||||
public TxAnnotationDriven(PlatformTransactionManager txManager) {
|
||||
super(EXECUTOR_TYPE);
|
||||
Assert.notNull(txManager, "transaction manager must not be null");
|
||||
this.txManager = txManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the transaction manager to use. May be a {@link PlatformTransactionManager}
|
||||
* instance or a String representing the bean name or a placeholder or SpEL expression
|
||||
* that resolves to the bean name.
|
||||
*/
|
||||
Object transactionManager() {
|
||||
return this.txManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate how transactional advice should be applied.
|
||||
* @see AdviceMode
|
||||
*/
|
||||
public TxAnnotationDriven mode(AdviceMode mode) {
|
||||
this.mode = mode;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate how transactional advice should be applied.
|
||||
* @param name matching one of the labels in the AdviceMode enum;
|
||||
* placeholder and SpEL expressions are not allowed.
|
||||
* @see AdviceMode
|
||||
*/
|
||||
TxAnnotationDriven mode(String mode) {
|
||||
if (StringUtils.hasText(mode)) {
|
||||
this.mode = mode;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return how transactional advice should be applied.
|
||||
*/
|
||||
AdviceMode mode() {
|
||||
if (this.mode instanceof AdviceMode) {
|
||||
return (AdviceMode)this.mode;
|
||||
}
|
||||
if (this.mode instanceof String) {
|
||||
return ObjectUtils.caseInsensitiveValueOf(AdviceMode.values(), (String)this.mode);
|
||||
}
|
||||
// TODO SPR-7420: deal with in validate & raise problem
|
||||
throw new IllegalStateException(
|
||||
"invalid type for field 'mode' (must be of type AdviceMode or String): "
|
||||
+ this.mode.getClass().getName());
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether class-based (CGLIB) proxies are to be created as opposed
|
||||
* to standard Java interface-based proxies.
|
||||
*
|
||||
* <p>Note: Class-based proxies require the {@link Transactional @Transactional}
|
||||
* annotation to be defined on the concrete class. Annotations in interfaces will
|
||||
* not work in that case (they will rather only work with interface-based proxies)!
|
||||
*/
|
||||
public TxAnnotationDriven proxyTargetClass(Boolean proxyTargetClass) {
|
||||
this.proxyTargetClass = proxyTargetClass;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether class-based (CGLIB) proxies are to be created as opposed
|
||||
* to standard Java interface-based proxies.
|
||||
*/
|
||||
Boolean proxyTargetClass() {
|
||||
return this.proxyTargetClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate the ordering of the execution of the transaction advisor
|
||||
* when multiple advice executes at a specific joinpoint. The default is
|
||||
* {@code null}, indicating that default ordering should be used.
|
||||
*/
|
||||
public TxAnnotationDriven order(int order) {
|
||||
this.order = order;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate the ordering of the execution of the transaction advisor
|
||||
* when multiple advice executes at a specific joinpoint. The default is
|
||||
* {@code null}, indicating that default ordering should be used.
|
||||
*/
|
||||
public TxAnnotationDriven order(String order) {
|
||||
if (StringUtils.hasText(order)) {
|
||||
this.order = order;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the ordering of the execution of the transaction advisor
|
||||
* when multiple advice executes at a specific joinpoint. May return
|
||||
* {@code null}, indicating that default ordering should be used.
|
||||
*/
|
||||
Object order() {
|
||||
return this.order;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doValidate(SimpleProblemCollector problems) {
|
||||
if (this.mode instanceof String) {
|
||||
if (!ObjectUtils.containsConstant(AdviceMode.values(), (String)this.mode)) {
|
||||
problems.error("no such mode name: " + this.mode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.transaction.config;
|
||||
|
||||
import org.springframework.aop.config.AopNamespaceUtils;
|
||||
import org.springframework.beans.factory.config.BeanDefinition;
|
||||
import org.springframework.beans.factory.config.RuntimeBeanReference;
|
||||
import org.springframework.beans.factory.parsing.BeanComponentDefinition;
|
||||
import org.springframework.beans.factory.parsing.ComponentRegistrar;
|
||||
import org.springframework.beans.factory.parsing.CompositeComponentDefinition;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.context.config.AbstractSpecificationExecutor;
|
||||
import org.springframework.context.config.ExecutorContext;
|
||||
import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource;
|
||||
import org.springframework.transaction.interceptor.BeanFactoryTransactionAttributeSourceAdvisor;
|
||||
import org.springframework.transaction.interceptor.TransactionInterceptor;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* TODO SPR-7420: document
|
||||
*
|
||||
* @author Chris Beams
|
||||
* @since 3.1
|
||||
*/
|
||||
final class TxAnnotationDrivenExecutor extends AbstractSpecificationExecutor<TxAnnotationDriven> {
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed transaction advisor (used when mode == PROXY).
|
||||
*/
|
||||
public static final String TRANSACTION_ADVISOR_BEAN_NAME =
|
||||
"org.springframework.transaction.config.internalTransactionAdvisor";
|
||||
|
||||
/**
|
||||
* The bean name of the internally managed transaction aspect (used when mode == ASPECTJ).
|
||||
*/
|
||||
public static final String TRANSACTION_ASPECT_BEAN_NAME =
|
||||
"org.springframework.transaction.config.internalTransactionAspect";
|
||||
|
||||
private static final String TRANSACTION_ASPECT_CLASS_NAME =
|
||||
"org.springframework.transaction.aspectj.AnnotationTransactionAspect";
|
||||
|
||||
|
||||
@Override
|
||||
protected void doExecute(TxAnnotationDriven txSpec, ExecutorContext executorContext) {
|
||||
BeanDefinitionRegistry registry = executorContext.getRegistry();
|
||||
ComponentRegistrar registrar = executorContext.getRegistrar();
|
||||
switch (txSpec.mode()) {
|
||||
case ASPECTJ:
|
||||
registerTransactionAspect(txSpec, registry, registrar);
|
||||
break;
|
||||
case PROXY:
|
||||
AopAutoProxyConfigurer.configureAutoProxyCreator(txSpec, registry, registrar);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
String.format("AdviceMode %s is not supported", txSpec.mode()));
|
||||
}
|
||||
}
|
||||
|
||||
private void registerTransactionAspect(TxAnnotationDriven spec, BeanDefinitionRegistry registry, ComponentRegistrar registrar) {
|
||||
if (!registry.containsBeanDefinition(TRANSACTION_ASPECT_BEAN_NAME)) {
|
||||
RootBeanDefinition def = new RootBeanDefinition();
|
||||
def.setBeanClassName(TRANSACTION_ASPECT_CLASS_NAME);
|
||||
def.setFactoryMethodName("aspectOf");
|
||||
registerTransactionManager(spec, def);
|
||||
registrar.registerBeanComponent(new BeanComponentDefinition(def, TRANSACTION_ASPECT_BEAN_NAME));
|
||||
}
|
||||
}
|
||||
|
||||
private static void registerTransactionManager(TxAnnotationDriven spec, BeanDefinition def) {
|
||||
Object txManager = spec.transactionManager();
|
||||
Assert.notNull(txManager, "transactionManager must be specified");
|
||||
if (txManager instanceof String) {
|
||||
def.getPropertyValues().add("transactionManagerBeanName", txManager);
|
||||
} else {
|
||||
def.getPropertyValues().add("transactionManager", txManager);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inner class to just introduce an AOP framework dependency when actually in proxy mode.
|
||||
*/
|
||||
private static class AopAutoProxyConfigurer {
|
||||
|
||||
public static void configureAutoProxyCreator(TxAnnotationDriven txSpec, BeanDefinitionRegistry registry, ComponentRegistrar registrar) {
|
||||
Object source = txSpec.source();
|
||||
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(registry, registrar, source, txSpec.proxyTargetClass());
|
||||
|
||||
if (!registry.containsBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME)) {
|
||||
|
||||
// Create the TransactionAttributeSource definition.
|
||||
RootBeanDefinition sourceDef = new RootBeanDefinition(AnnotationTransactionAttributeSource.class);
|
||||
sourceDef.setSource(source);
|
||||
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
String sourceName = registrar.registerWithGeneratedName(sourceDef);
|
||||
|
||||
// Create the TransactionInterceptor definition.
|
||||
RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
|
||||
interceptorDef.setSource(source);
|
||||
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registerTransactionManager(txSpec, interceptorDef);
|
||||
interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
|
||||
String interceptorName = registrar.registerWithGeneratedName(interceptorDef);
|
||||
|
||||
// Create the TransactionAttributeSourceAdvisor definition.
|
||||
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
|
||||
advisorDef.setSource(source);
|
||||
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
|
||||
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
|
||||
if (txSpec.order() != null) {
|
||||
advisorDef.getPropertyValues().add("order", txSpec.order());
|
||||
}
|
||||
registry.registerBeanDefinition(TRANSACTION_ADVISOR_BEAN_NAME, advisorDef);
|
||||
|
||||
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(txSpec.sourceName(), source);
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
|
||||
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, TRANSACTION_ADVISOR_BEAN_NAME));
|
||||
registrar.registerComponent(compositeDef);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,17 +39,6 @@ import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
*/
|
||||
public class TxNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";
|
||||
|
||||
static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
|
||||
|
||||
|
||||
static String getTransactionManagerName(Element element) {
|
||||
return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?
|
||||
element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);
|
||||
}
|
||||
|
||||
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
|
||||
|
||||
Reference in New Issue
Block a user