使用AOP完成Spring事务管理
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" read-only="true" propagation="REQUIRED" />
<!-- rollback-for只对检查型异常适用 这里一般为自定义的Exception 继承Exception父类 因为RuntimeException和Error,Spring默认进行回顾,我们在源码分析中可以看到这一点 -->
<tx:method name="*" propagation="REQUIRED" rollback-for="Exception" />
</tx:attributes>
</tx:advice>
<!-- 适用切面来添加适用处理的功能 -->
<aop:config>
<aop:pointcut expression="execution(* wangcc.service.impl.*.*(..))"
id="serviceMethod" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethod" />
</aop:config>
当我们使用AOP完成Spring事务管理的时候,我们在处理业务逻辑的时候只需要关心业务逻辑的处理,而Spring事务会通过动态代理的方式来增强需要使用事务的方法来完成Spring事务。
AOP完成Spring事务管理源码分析
tx:advice节点的解析
我们观看上面的XML文件配置,很明显tx:advice这个节点生成的Bean是来完成Spring事务管理功能的,在AOP中充当增强方法处理的作用。而aop:pointcut节点就很明显是指定到底在哪些类的那些方法中执行这个方法增加,即Spring事务管理。
对于AOP,我们已经分析过其源码了,不过当时分析的是aop:pointcut与aop:aspect的组合。而这里aop:aspect换成了aop:advisor。我们知道在分析aop源码的时候aop:aspect节点下的aop:before,aop:after等节点都会传化为AspectJPointcutAdvisor类型的Bean,并且其中包含一个AbstractAspectJAdvice的子类类型的Bean,而且这些子类除了AspectJMethodBeforeAdvice(之后进行了一层封装后实现接口),都直接实现了MethodInterceptor接口,而最后我们需要的就是实现了MethodInterceptor接口的类。
那么我们在分析tx:advice节点的时候,显然可以想象得到他的最终产物也对应着一个Interceptor。
事实也是如此,我们找到解析该节点的类TxAdviceBeanDefinitionParser。找到getBeanClass方法,你会发现BeanClass对应着是TransactionInterceptor,而他也是实现了MethodInterceptor接口的一个类。
@Override
protected Class<?> getBeanClass(Element element) {
return TransactionInterceptor.class;
}
我们来看下TxAdviceBeanDefinitionParser的声明
class TxAdviceBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
public abstract class AbstractSingleBeanDefinitionParser extends AbstractBeanDefinitionParser {
public abstract class AbstractBeanDefinitionParser implements BeanDefinitionParser {
我们可以看到直接实现BeanDefinitionParser接口的是他祖先类AbstractBeanDefinitionParser类。
- parse
@Override
public final BeanDefinition parse(Element element, ParserContext parserContext) {
AbstractBeanDefinition definition = parseInternal(element, parserContext);
//...省略了一坨代码,主要是将得到的Bean定义注册到Spring容器中,并做一些处理。不是关注的重点
return definition;
}
在parse方法中,重点是parseInternal方法的实现,也就是如何得到Bean定义。而这个方法在AbstractBeanDefinitionParser中是个抽象方法。具体的实现在AbstractSingleBeanDefinitionParser中。
- parseInternal
@Override
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
//看是否有parent
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
//getBeanClass留给子类实现
//得到该Bean的Class,这里就是之前说的TransactionInterceptor
Class<?> beanClass = getBeanClass(element);
//将Class注册到Bean定义中
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
}
else {
//getBeanClassName留给子类实现,但是这里TxAdviceBeanDefinitionParser并没实现
String beanClassName = getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
if (parserContext.isNested()) {
// Inner bean definition must receive same scope as containing bean.
builder.setScope(parserContext.getContainingBeanDefinition().getScope());
}
if (parserContext.isDefaultLazyInit()) {
// Default-lazy-init applies to custom bean definitions as well.
builder.setLazyInit(true);
}
//对Bean定义进行一系列的参数设置之后,终于来到解析节点,这个方法也是给子类实现的。
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
在对Bean定义进行了一系列参数的设置之后,调用doParse对节点进行解析。
- doParse
@Override
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
//注入transcationManager属性,如果在配置中没有配置transaction-manager属性,那么这时Spring配置文件中,事务管理器的Bean的Id必须是transcationManager。
builder.addPropertyReference("transactionManager", TxNamespaceHandler.getTransactionManagerName(element));
//得到tx: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 = txAttributes.get(0);
RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);
//用得到的attributeSourceDefinition注入transactionAttributeSource属性
builder.addPropertyValue("transactionAttributeSource", attributeSourceDefinition);
}
//如果没有显式的配置这个节点,还是需要注入transactionAttributeSource属性,这里给出一个默认的AnnotationTransactionAttributeSource
else {
// Assume annotations source.
builder.addPropertyValue("transactionAttributeSource",
new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"));
}
}
我们可以看到doParse方法主要就是为TransactionInterceptor这个类注册了两个属性,transactionAttributeSource和transactionManager。
我们再看看当有显式配置tx:attributes节点时对transactionAttributeSource属性的配置。
RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);
- parseAttributeSource
private RootBeanDefinition parseAttributeSource(Element attrEle, ParserContext parserContext) {
//得到所有的tx:method节点
List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, METHOD_ELEMENT);
//创建一个Map,用来存放TypedStringValue(name属性值的包装类)为key,RuleBasedTransactionAttribute为Value的键值对
//即每一个method name都对应着相应TransactionAttribute
ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap =
new ManagedMap<TypedStringValue, RuleBasedTransactionAttribute>(methods.size());
transactionAttributeMap.setSource(parserContext.extractSource(attrEle));
for (Element methodEle : methods) {
//得到tx:method中name属性的属性值
String name = methodEle.getAttribute(METHOD_NAME_ATTRIBUTE);
//将属性值封装成TypedStringValue类型
TypedStringValue nameHolder = new TypedStringValue(name);
nameHolder.setSource(parserContext.extractSource(methodEle));
RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();
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);
}
if (StringUtils.hasText(isolation)) {
attribute.setIsolationLevelName(RuleBasedTransactionAttribute.PREFIX_ISOLATION + isolation);
}
if (StringUtils.hasText(timeout)) {
try {
attribute.setTimeout(Integer.parseInt(timeout));
}
catch (NumberFormatException ex) {
parserContext.getReaderContext().error("Timeout must be an integer value: [" + timeout + "]", methodEle);
}
}
if (StringUtils.hasText(readOnly)) {
attribute.setReadOnly(Boolean.valueOf(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));
}
List<RollbackRuleAttribute> rollbackRules = new LinkedList<RollbackRuleAttribute>();
if (methodEle.hasAttribute(ROLLBACK_FOR_ATTRIBUTE)) {
String rollbackForValue = methodEle.getAttribute(ROLLBACK_FOR_ATTRIBUTE);
addRollbackRuleAttributesTo(rollbackRules,rollbackForValue);
}
if (methodEle.hasAttribute(NO_ROLLBACK_FOR_ATTRIBUTE)) {
String noRollbackForValue = methodEle.getAttribute(NO_ROLLBACK_FOR_ATTRIBUTE);
addNoRollbackRuleAttributesTo(rollbackRules,noRollbackForValue);
}
attribute.setRollbackRules(rollbackRules);
transactionAttributeMap.put(nameHolder, attribute);
}
RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchTransactionAttributeSource.class);
attributeSourceDefinition.setSource(parserContext.extractSource(attrEle));
attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);
return attributeSourceDefinition;
}
这个方法很长,但是并不难理解。
就是如果在tx:method节点中有对Methodname显式的指定事务传播属性,事务级别,只读,超时等属性,就把他加入到RuleBasedTransactionAttribute中。没有显式指定的话就使用默认的。然后将所有的method节点的信息都存入到NameMatchTransactionAttributeSource中。
那么到这里,我们就分析完了tx:advice节点了。
他主要就是创建了类型为TransactionInterceptor的Bean,并且注入了这个Bean的两个属性,transactionAttributeSource和transactionManager。而当有tx:method节点的时候,transactionAttributeSource属性对应的类型是NameMatchTransactionAttributeSource,如果没有,则对应AnnotationTransactionAttributeSource。
aop:config节点的解析
关于aop:config节点的解析,我们在讲解Spring AOP实现的时候已经说过了,但是当时讲的是aop:aspect和aop:pointcut的组合,现在我们要讲解aop:pointcut和aop:advisor的组合。
我们直接看到ConfigBeanDefinitionParser中解析节点的方法。
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
configureAutoProxyCreator(parserContext, element);
List<Element> childElts = DomUtils.getChildElements(element);
for (Element elt: childElts) {
String localName = parserContext.getDelegate().getLocalName(elt);
if (POINTCUT.equals(localName)) {
parsePointcut(elt, parserContext);
}
else if (ADVISOR.equals(localName)) {
parseAdvisor(elt, parserContext);
}
else if (ASPECT.equals(localName)) {
parseAspect(elt, parserContext);
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
- parseAdvisor
private void parseAdvisor(Element advisorElement, ParserContext parserContext) {
AbstractBeanDefinition advisorDef = createAdvisorBeanDefinition(advisorElement, parserContext);
String id = advisorElement.getAttribute(ID);
try {
this.parseState.push(new AdvisorEntry(id));
String advisorBeanName = id;
if (StringUtils.hasText(advisorBeanName)) {
parserContext.getRegistry().registerBeanDefinition(advisorBeanName, advisorDef);
}
else {
advisorBeanName = parserContext.getReaderContext().registerWithGeneratedName(advisorDef);
}
Object pointcut = parsePointcutProperty(advisorElement, parserContext);
if (pointcut instanceof BeanDefinition) {
advisorDef.getPropertyValues().add(POINTCUT, pointcut);
parserContext.registerComponent(
new AdvisorComponentDefinition(advisorBeanName, advisorDef, (BeanDefinition) pointcut));
}
else if (pointcut instanceof String) {
advisorDef.getPropertyValues().add(POINTCUT, new RuntimeBeanReference((String) pointcut));
parserContext.registerComponent(
new AdvisorComponentDefinition(advisorBeanName, advisorDef));
}
}
finally {
this.parseState.pop();
}
}
方法不难理解,就是将pointcut和advice-ref,也就是之前我们分析的那个节点对应的Bean,TranscationInterceptor关联起来得到DefaultBeanFactoryPointcutAdvisor,然后注入到Spring容器中。
到这里,我们的切面就完成了,然而最重要的是怎么调用我们这个切面。
运用AOP实现的事务管理的调用过程
具体怎么注册拦截器,怎么创建代理类的过程请看AOP源码解析部分。
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
// Work out the target class: may be {@code null}.
// The TransactionAttributeSource should be passed the target class
// as well as the method, which may be from an interface.
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}
invokeWithinTransaction
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
final String joinpointIdentification = methodIdentification(method, targetClass);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
//开启事务,设置事务隔离级别,传播属性等
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// This is an around advice: Invoke the next interceptor in the chain.
// This will normally result in a target object being invoked.
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
//如果抛出异常,处理他
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
//省略了CallbackPreferringPlatformTransactionManager这一块,这一块基本就是编程式事务实现事务管理的逻辑
}