目录
DefaultBeanFactoryPointcutAdvisor实例结构
BeanFactoryTransactionAttributeSourceAdvisor 实例结构
2.1、findAdvisorsThatCanApply方法
2.3、AspectJExpressionPointcut的matches方法
2.4、TransactionAttributeSourcePointcut的matches方法
2.5、computeTransactionAttribute方法
3.3、内嵌事务处理handleExistingTransaction方法
前言:事务是我们日常开发过程中面对数据库的curd操作常接触的概念,这里不对事务的基础概念做介绍,如果读者想要了解事务相关的基础概念请参考博文的这两篇文章 1、mysql相关事务的介绍以及应用 2、spring事务的应用,spring提供了两种类型的事务使用方式,一种是编程式事务另一种则是声明式事务。在我们日常开发中因为声明式事务的使用简单而被大部分企业开发所采用,spring声明式事务让我们从复杂的事务处理中得以解脱,我们不需要关注诸如回滚,提交等事务细节(由spring帮忙处理),只需要配置相关的事务(xml配置或者注解形式)既可以使用spring的事务,让开发人员有更多的时间去关注业务逻辑。下面我们就来一步步分析下spring事务相关的原理。
话不多说,还是以一个基础示例开启对spring事务的分析,这里重点分析声明式事务处理。
一、spring事务的使用
1、xml配置使用spring的事务
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 扫描service包下所有使用注解的类型 -->
<context:component-scan base-package="com.soecode.lyf.service" />
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据库连接池 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置事务通知 会被spring转换为代理增强实力 -->
<tx:advice id="TestAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" read-only="false" propagation="REQUIRED" rollback-for="Throwable"/>
<tx:method name="del*" propagation="REQUIRED" rollback-for="Throwable"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Throwable"/>
<tx:method name="add*" propagation="REQUIRED" rollback-for="Throwable"/>
<tx:method name="find*" propagation="REQUIRED" rollback-for="Throwable"/>
<tx:method name="get*" propagation="REQUIRED" rollback-for="Throwable"/>
<tx:method name="apply*" propagation="REQUIRED" rollback-for="Throwable"/>
</tx:attributes>
</tx:advice>
<!-- 配置参与事务的类 -->
<aop:config>
<aop:pointcut id="allTestServiceMethod" expression="execution(* com.soecode.lyf.service.*.*(..))"/>
<aop:advisor pointcut-ref="allTestServiceMethod" advice-ref="TestAdvice" />
</aop:config>
</beans>
2、xml配置配合注解使用spring的事务
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 扫描service包下所有使用注解的类型 -->
<context:component-scan base-package="com.soecode.lyf.service" />
<!-- 配置事务管理器 -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据库连接池 -->
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 配置基于注解的声明式事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
3、服务实例类
//服务接口实例
@Service
public class BookServiceImpl implements BookService {
@Autowired
BookMapper bookMapper;
@Override
//对于只使用xml配置的事务 则这里不需要使用@Transactional注解
//只需要在xml配置<tx:advice>中的method 中的name能匹配到该方法即可使用spring的事务
//<tx:method name="save*" read-only="false" propagation="REQUIRED" rollback-for="Throwable"/>
//对于使用注解的方式 则需要使用@Transactional修饰方法、类或者类的接口
//rollbackFor属性显示的指明方法遇到什么异常后进行回滚操作 不写默认
//遇到RuntimeException异常后触发回滚
//@Transactional(rollbackFor = RuntimeException.class)
public void saveBook(Book book) {
book.setAuthor("梁晓声");
book.setName("知青");
book.setDescption("70年代人上山下乡");
book.setEvaluateNumber(9999);
book.setPrice("25.86");
book.setPublicationDate("1994-10-21");
book.setPublisHouse("北京石榴庄出版社");
book.setScore(9.5);
bookMapper.insertSelective(book);
//抛出对应的异常 从而触发spring事务的回滚操作
throw new RuntimeException("保存书籍失败");
}
}
4、结果
保存信息到数据库操作出现异常则数据库事务回滚 新的数据没有保存到数据库中这里验证了事务的存在。
二、事务标签解析
1、自定义标签解析
针对我们前面分析的spring 自定义标签的解析以及针对AOP自定义标签解析(spring自定义标签解析、aop原理分析)我们可以得出经验针对如上的<tx:advice/ > 和<aop:config/> 有对应的标签处理器 TxNamespaceHandler、AopNamespaceHandler
同时<tx:annotation-driven/> 也对应TxNamespaceHandler类 ,下面来分析两个标签处理器做了哪些操作。
2、纯xml使用spring事务 <tx:advice> 标签解析
2.0、<tx:advice/ > TxNamespaceHandler
在TxNamespaceHandler的init()方法中注册了一个标签解析类TxAdviceBeanDefinitionParser,具体的针对<tx:advice/> 标签的解析在该类的parser()方法中。
2.1、parser()方法
TxAdviceBeanDefinitionParser类是用来处理<tx:advice> 相关的自定义标签的,其中的parser()方法(该方法在父类中)主要是将自定义标签转换为对应的BeanDefintion实例注册到spring环境中,为了在以后获取bean实例做基础。
@Override
public final BeanDefinition parse(Element element, ParserContext parserContext) {
//解析advisor标签 并转换为BeanDefintion
AbstractBeanDefinition definition = parseInternal(element, parserContext);
if (definition != null && !parserContext.isNested()) {
try {
//获取beanDefintion的唯一标识
String id = resolveId(element, definition, parserContext);
if (!StringUtils.hasText(id)) {
parserContext.getReaderContext().error();
}
//获取beanDefintion的别名
String[] aliases = null;
if (shouldParseNameAsAliases()) {
String name = element.getAttribute(NAME_ATTRIBUTE);
if (StringUtils.hasLength(name)) {
aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));
}
}
//对BeanDefintion进行一次包装
BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);
//注册beanDefintionHolder
registerBeanDefinition(holder, parserContext.getRegistry());
//发布事件
if (shouldFireEvents()) {
BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);
postProcessComponentDefinition(componentDefinition);
parserContext.registerComponent(componentDefinition);
}
}
catch (BeanDefinitionStoreException ex) {
parserContext.getReaderContext().error(ex.getMessage(), element);
return null;
}
}
return definition;
}
上面的解析操作核心是parseInternal()具体的解析是在该方法中,由不同的子类进行自定义扩展,其他的获取id,获取别名,注册、发布操作都是创建BeanDefintion的通用操作。下面分析parseInternal()方法
2.2、parseInternal()方法
在该方法中主要将<tx:advice>整个标签解析成初始的GenericBeanDefinition,涉及到BeanDefintion的 parentName,scope
lazyinit class等属性其中好包括对其子类<tx:attributes> <tx:method/>的解析。其他属性设置简单这里不再叙述,我们主要关注class属性的设置 和doParser()方法,因为class属性是一个BeanDefintion的核心属性,明确了class属性,我们也就知道了spring最终获取到的是具体哪个类的实例或者代理对象,方便我们分析期实现的功能。doParser()方法对应主要对子标签进行解析,我们需要了解一下子标签对应的事务功能。
//具体创建<tx:advice>对应的BeanDefintion的方法
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
//创建一个GenericBeanDefinition实例 包装在BeanDefintionBUilder实例中
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
//空实现
String parentName = getParentName(element);
if (parentName != null) {
builder.getRawBeanDefinition().setParentName(parentName);
}
//获取并设置对应的Beandefintion的class属性
Class<?> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
}
//没有class 则尝试获取标签中的className,设置为Beandefintion的className属性
else {
String beanClassName = getBeanClassName(element);
if (beanClassName != null) {
builder.getRawBeanDefinition().setBeanClassName(beanClassName);
}
}
builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));
//设置scope
if (parserContext.isNested()) {
// Inner bean definition must receive same scope as containing bean.
builder.setScope(parserContext.getContainingBeanDefinition().getScope());
}
//设置beanDefintion的懒加载
if (parserContext.isDefaultLazyInit()) {
// Default-lazy-init applies to custom bean definitions as well.
builder.setLazyInit(true);
}
//对于<tx:advice>中的子标签进行解析
doParse(element, parserContext, builder);
//构建BeanDefintion成功并返回
return builder.getBeanDefinition();
}
2.3、获取事务BeanDefintion对应的class
@Override
protected Class<?> getBeanClass(Element element) {
return TransactionInterceptor.class;
}
代码很简单<tx:advice> 最终被转换为class属性为TransactionInterceptor的类,虽然代码简单但是这个类是我们业务使用事务的核心类,下面我们简单分析一下该类他实现了MethodInterceptor的invoke()方法,在这里类发挥作用的时候一定是调用其invoke()方法来实现事务的相关处理的。该类支撑着整个事务功能的架构,逻辑还是相对复杂的。我们先按下不表,在后面应用过程中在详述。
字段 | 描述 |
| ThreadLocal包装的TransactionInfo,该类是包含所有事务相关的全部属性
这些属性构成了TransactionInfo对象 |
| PlatformTransactionManager 事务管理器缓存列表 |
| 策略接口 提供了一个获取TransactionAttribute事务对象的 |
| spring工厂对象 |
2.4、doParse方法
//对<tx:attribute> 子标签进行解析
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
//设置事务transactionManager 到TransactionInterceptor对应的beanDefintion中
builder.addPropertyReference("transactionManager",
TxNamespaceHandler.getTransactionManagerName(element));
//获取tx:attribute 子标签
List<Element> txAttributes = DomUtils.getChildElementsByTagName(element, ATTRIBUTES_ELEMENT);
//tx:attribute 子标签个数大于1
if (txAttributes.size() > 1) {
//错误 元素attribute属性标签最多一个
}
//解析tx:attribute 子标签
else if (txAttributes.size() == 1) {
// Using attributes source.
Element attributeSourceElement = txAttributes.get(0);
RootBeanDefinition attributeSourceDefinition = parseAttributeSource(attributeSourceElement, parserContext);
builder.addPropertyValue("transactionAttributeSource", attributeSourceDefinition);
}
//如果没有 tx:attribute 子标签 则设置beanDefintion的transactionAttributeSource默认属性为
//org.springframework.transaction.annotation.AnnotationTransactionAttributeSource
//class对应的BeanDefintion 该类中包含对应的TransactionAttribute对象事务属性
else {
builder.addPropertyValue("transactionAttributeSource",
new RootBeanDefinition("org.springframework.transaction.annotation.AnnotationTransactionAttributeSource"));
}
}
//具体的解析<tx:attribute>
private RootBeanDefinition parseAttributeSource(Element attrEle, ParserContext parserContext) {
获取<tx:attribute>标签其中<tx:method>子标签
List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, METHOD_ELEMENT);
//创建一个集合用于存放<tx:method> 转换的RuleBasedTransactionAttribute 对象
ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap =
new ManagedMap<TypedStringValue, RuleBasedTransactionAttribute>(methods.size());
transactionAttributeMap.setSource(parserContext.extractSource(attrEle));
//遍历<tx:method>子标签
for (Element methodEle : methods) {
//解析<tx:method>中的name,传播行为 隔离级别,超时时间,是否只读
//异常回滚RollbackFor,异常不会滚noRollbackFor属性
//包装成RuleBasedTransactionAttribute类
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_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) {
//错误日志处理
}
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);
//将解析后转换成RuleBasedTransactionAttribute 放入map集合中
transactionAttributeMap.put(nameHolder, attribute);
}
//构造RootBeanDefinition 将transactionAttributeMap集合作为属性nameMap
//填充到attributeSourceDefinition中
RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchTransactionAttributeSource.class);
attributeSourceDefinition.setSource(parserContext.extractSource(attrEle));
attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);
return attributeSourceDefinition;
}
上面的方法很多但是逻辑简单主要来说就是解析<tx:method> 标签列表将其转换为RuleBasedTransactionAttribute事务属性对象,并将其作为属性nameMap包装在新创建class属性为NameMatchTransactionAttributeSource
的RootBeanDefinition中,同时这个创建的RootBeanDefinition 继续作为属性transactionAttributeSource存放在TransactionInterceptor对应的BeanDefintion中。总结给TransactionInterceptor对应的BeanDefintion设置RuleBasedTransactionAttribute事务列表,主要作用是判断业务方法是否满足RuleBasedTransactionAttribute事务列表,只有满足该RuleBasedTransactionAttribute事务列表中的其中一个对象则说明业务方法需要使用事务。
就拿我们上面的示例来说 解析<tx:method> 会转换七个事务类,例如save* 对应的事务类 del*对应的事务类,我的业务方法是saveBook() 则匹配save* 对应的事务类,说明该业务方法需要使用事务。
到现在为止<tx:advice> 解析已经完成。
最终生成一个两个主要的BeanDefintion :
TransactionInterceptor对应的beanDefintion类
NameMatchTransactionAttributeSource对应的beanDefintion类
3、纯xml使用spring事务 <aop:config> 标签解析
3.0、AopNamespaceHandler
与上面<tx:advice> 标签解析类似<aop:config> 标签解析使用的是AopNamespaceHadndler的init()方法注册的ConfigBeanDefinitionParser类来进行<aop:config>标签的解析。下面我们来分析ConfigBeanDefinitionParser的parser()方法的作用。
3.1、parser()方法
/**
*<aop:config> 标签解析
* <aop:config>
* <aop:pointcut/>
* <aop:advisor /> 一般用于事务处理
* <aop:aspect /> 用于单纯的aop处理
* </aop:config>
**/
public BeanDefinition parse(Element element, ParserContext parserContext) {
//根据标签名创建CompositeComponentDefinition 目前只分析到其用于
//解析标签时候的入栈出栈操作
CompositeComponentDefinition compositeDef =
new CompositeComponentDefinition(element.getTagName(), parserContext.extractSource(element));
parserContext.pushContainingComponent(compositeDef);
//该方法和分析springAOP类似这里注册一个class为
//AspectJAwareAdvisorAutoProxyCreator的beanDefintion
//该类是一个后置处理器类,用于在业务对象创建的时候根据情况进行创建代理操作
//创建代理的时候回将事务的功能织入到业务对象的匹配的方法中。这个是核心
configureAutoProxyCreator(parserContext, element);
//对AOP的子标签进行解析 子标签包括<aop:pointcut>、<aop:advisor> <aop:aspect>
List<Element> childElts = DomUtils.getChildElements(element);
for (Element elt: childElts) {
String localName = parserContext.getDelegate().getLocalName(elt);
//<aop:pointcut> 解析
//创建class属性为AspectJExpressionPointcut的BeanDefintion并注册
if (POINTCUT.equals(localName)) {
parsePointcut(elt, parserContext);
}
//解析<aop:advisor>
//创建class属性为DefaultBeanFactoryPointcutAdvisor的BeanDefintion并注册
else if (ADVISOR.equals(localName)) {
parseAdvisor(elt, parserContext);
}
//解析<aop:aspect> 原理和上面子标签解析方式一致 但是比较复杂
//因为该标签主要用于aop的使用,此处我们主要关注事务处理
else if (ASPECT.equals(localName)) {
parseAspect(elt, parserContext);
}
}
parserContext.popAndRegisterContainingComponent();
return null;
}
该方法中主要是针对<aop:config> 子标签进行解析
<tx:ponitcut> 创建并注册class属性为AspectJExpressionPointcut的BeanDefintion
<tx:advisor> 创建class并注册属性为DefaultBeanFactoryPointcutAdvisor的BeanDefintion
同时还注册了一个AspectJAwareAdvisorAutoProxyCreator bean后置处理器,用于创建代理对象
总结:对于纯xml配置主要注册了四个重要的BeanDefintion对象
1、AspectJAwareAdvisorAutoProxyCreator 后置处理器
2、AspectJExpressionPointcut 切入点表达式 用于匹配业务方式是否使用事务
3、TransactionInterceptor 事务拦截器
4、DefaultBeanFactoryPointcutAdvisor 代理增强器
这些类的主要作用是啥我们先留一个悬念,在事务调用过程中我们一一讲解。
4、注解形式使用spring事务
4.0、<tx:annotation-driven/ > AopNamespaceHandler
在AopNamespaceHandler的init()方法中注册了一个AnnotationDrivenBeanDefinitionParser class 用于 解析 <tx:annotation-driven /> 标签,下面我们来分析一下AnnotationDrivenBeanDefinitionParser的parse()方法
<!-- 配置基于注解的声明式事务 -->
<tx:annotation-driven transaction-manager="transactionManager" />
4.1、parse()方法
public BeanDefinition parse(Element element, ParserContext parserContext) {
//<tx:annotation-driven> 默认的mode属性为proxy
String mode = element.getAttribute("mode");
if ("aspectj".equals(mode)) {
// mode="aspectj"
registerTransactionAspect(element, parserContext);
}
else {
//事务相关的操作走该条件分支
AnnotationDrivenBeanDefinitionParser.AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
}
return null;
}
/**
* 这里注册三个事务相关的BeanDefintion
* AnnotationTransactionAttributeSource对应的BeanDefintion
* 获取对应的事务对象TransactionAttribute
* TransactionInterceptor对应的BeanDefintion
* 该实例对象的propertyValues中包含
* AnnotationTransactionAttributeSource对应的BeanDefintion
* transactionManager对应的事务管理器bean实例
* BeanFactoryTransactionAttributeSourceAdvisor对应的beanDefintion
* 该实例对象的propertyValues中包含
* AnnotationTransactionAttributeSource对应的BeanDefintion
* TransactionInterceptor对应的BeanDefintion
*
* 同时将如上的三个BeanDefintion 包装到CompositeComponentDefinition的内部ComponentDefinition列表中
*/
//注冊事务处理的相关BeanDefintion
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
//注册InfrastructureAdvisorAutoProxyCreator 对应的beanDefinition类 作用不详
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
Object eleSource = parserContext.extractSource(element);
//创建并注册AnnotationTransactionAttributeSource对应的BeanDefintion
// 创建TransactionAttributeSource对应的BeanDefintion
RootBeanDefinition sourceDef = new RootBeanDefinition(
"org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
sourceDef.setSource(eleSource);
//设置BeanDefintion的role (完全spring内部使用)
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//注册创建的BeanDefintion
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
//创建并注册TransactionInterceptor对应的BeanDefintion
// 创建TransactionInterceptor对应的BeanDefintion
RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
interceptorDef.setSource(eleSource);
//设置BeanDefintion的role (完全spring内部使用)
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//添加transactionManager事务管理器到interceptorDef的PropertyValues属性
//实际获取<tx:annotation-driven transaction-manager="" >中的transaction-manage属性,默认值:beanName = transactionManager
registerTransactionManager(element, interceptorDef);
//添加AnnotationTransactionAttributeSource对应的BeanDefintion到interceptorDef的PropertyValues属性
interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
//注册创建的BeanDefintion
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
//创建并注册BeanFactoryTransactionAttributeSourceAdvisor对应的BeanDefintion
//创建BeanFactoryTransactionAttributeSourceAdvisor对应的BeanDefintion
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
advisorDef.setSource(eleSource);
//设置BeanDefintion的role (完全spring内部使用)
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//添加 AnnotationTransactionAttributeSource对应的BeanDefintion 到advisorDef的PropertyValues属性
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
//添加 TransactionInterceptor对应的BeanDefintion 到advisorDef的PropertyValues属性
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
//如果有order排序 添加到advisorDef的PropertyValues属性
if (element.hasAttribute("order")) {
advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
}
//注册创建的BeanDefintion
parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);
//将如上的三个BeanDefintion组件注册包装成CompositeComponentDefinition到DefaultListableBeanFactory中
CompositeComponentDefinition compositeDef = new CompositeComponentDefinition(element.getTagName(), eleSource);
compositeDef.addNestedComponent(new BeanComponentDefinition(sourceDef, sourceName));
compositeDef.addNestedComponent(new BeanComponentDefinition(interceptorDef, interceptorName));
compositeDef.addNestedComponent(new BeanComponentDefinition(advisorDef, txAdvisorBeanName));
parserContext.registerComponent(compositeDef);
}
}
总结:对于通过注解形式使用事务主要注册了四个重要的BeanDefintion对象
1、InfrastructureAdvisorAutoProxyCreator 后置处理器
2、AnnotationTransactionAttributeSource 里面包含事务对象(注解形式的事务)
3、TransactionInterceptor 拦截器
该实例对象的propertyValues中包含
AnnotationTransactionAttributeSource对应的BeanDefintion
transactionManager对应的事务管理器bean实例
4、BeanFactoryTransactionAttributeSourceAdvisor 代理增强类
该实例对象的propertyValues中包含
AnnotationTransactionAttributeSource对应的BeanDefintion
TransactionInterceptor对应的BeanDefintion
这些类的主要作用是啥我们先留一个悬念,在事务调用过程中我们一一讲解。
xml形式使用事务 | 注解的形式使用事务 | 相同点 | 不同点 |
AspectJAwareAdvisorAutoProxyCreator | InfrastructureAdvisorAutoProxyCreator | | 一个用于处理xml配置的后置处理器,一个是应用于注解的后置处理器,核心方法相似。 |
NameMatchTransactionAttributeSource | AnnotationTransactionAttributeSource | 两者均是用来判断业务方法是否需要添加事务支持的 | 前置是使用名字通配符匹配的形式,判断业务方法是否匹配表达式 从而判断是否使用事务代理,后置是使用其中的SpringTransactionAnnotationParser事务注解解析业务接口、类、方法上@Transactional注解 根据业务方式是否有该对象来判断书是否使用事务代理 |
TransactionInterceptor | TransactionInterceptor | 进行事务开启,回滚,提交操作的核心类 | |
DefaultBeanFactoryPointcutAdvisor | BeanFactoryTransactionAttributeSourceAdvisor | 均继承AbstractBeanFactoryPointcutAdvisor 我们在第一个类后置处理器的应用中 查找使用该代理增强类来进行代理增强,这两个类均包含上面TransactionInterceptor实例 | 前者使用AspectJExpressionPointcut 判断是否使用事务 后者使AnnotationTransactionAttributeSource 来判断是否使用事务 |
三、匹配事务代理增强器
1、事务分析入口
根据我们对springAOP功能的分析,可以知道上面两种使用事务的方式分别注册了两个代理增器类型DefaultBeanFactoryPointcutAdvisor、BeanFactoryTransactionAttributeSourceAdvisor ,这两个类为后置处理器,这两个类都继承自AbstractAutoProxyCreator类,在spring生成业务bean实例的时候会调用其postProcessAfterInitialization()方法,分析到这里和我们相关的AOP的分析一致:
- 获取所有代理增强器
- 查找到与业务类实例匹配的代理增强器
- 根据jdk动态代理或者cglib代理创建代理对象
DefaultBeanFactoryPointcutAdvisor实例结构
BeanFactoryTransactionAttributeSourceAdvisor 实例结构
2、查找匹配的代理增强器
代理增强类可能有很多,如何判断当前业务bean实例需要使用某个代理增强类做代理增强呢?可能我们需要分析一下AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass)方法来看看其中的缘由。
2.1、findAdvisorsThatCanApply方法
/**
* 查询与对应的业务方法匹配的代理增强
* @param candidateAdvisors 所有候选的代理增强类
* @param clazz 业务bean class属性
* @return 与业务bean匹配的代理增强
*/
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
//候选代理增强为空 直接返回
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
//对于IntroductionAdvisor 这个是spring的新概念 叫做引入增强或者说叫做引介增强
//引入增强(Introduction Advice)的概念:一个Java类,没有实现A接口,在不修改Java类的情况下,
//使其具备A接口的功能。(非常强大有木有,A不需要动代码,就能有别的功能,吊炸天有木有)
//大家可以自行百度spring的引入增强
//添加引入增强类型的代理增强
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
//判断是否有引入增强
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
//继续对非引入增强其他类型的代理增强进行处理
//比如BeforeAdvide(前置增强)、AfterAdvice(后置增强)、ThrowsAdvice(异常增强)、RoundAdvice(环绕增强)
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
//普通代理增强的判断是否匹配
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
上面的方式针对所有的代理增强实例和目标class进行匹配,分成两种类型 一种是引入增强处理,一种是普通增强的处理。最终匹配的判断是在canApply()方法,所以话不多说我们来分析canApply()方法
2.2、canApply方法
/**
* 具体的匹配逻辑
* @param pc 注解形式的事务处理切入点对象 TransactionAttributeSourcePointcut
* xml形式的事务处理切入点对象 AspectJExpressionPointcut
* @param targetClass 业务class属性
* @param hasIntroductions 是否有引入增强
* @return
*/
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
Assert.notNull(pc, "Pointcut must not be null");
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
//从切入点获取方法匹配器 去匹配当前类 是否匹配切入点
// 普通增强使用该MethodMatcher
MethodMatcher methodMatcher = pc.getMethodMatcher();
// 引入增强使用该IntroductionAwareMethodMatcher 去进行匹配
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
//获取目标类的所有实现的接口class(java支持多接口 所以此处为列表)
Set<Class<?>> classes = new LinkedHashSet<Class<?>>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
//遍历所有接口类的所有方法
for (Class<?> clazz : classes) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//使用引入方法匹配器 或者普通方法匹配器去进行匹配操作 有任何一个方法匹配成功 则说明该代理增强适用于targetClass
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
该方法主要是从切入点对象中获取对应的方法匹配器,去对class中的所有方法迭代匹配,只要有一个方法匹配成功则说明该代理增强器是适用于该目标对象的。这里的匹配分成两种类型 一种是方法匹配器是MethodMatcher的接口实现,一种是方法匹配器是IntroductionAwareMethodMatcher的接口实现。这里补充说明一下我们开头的两种适用方式上 一种纯XML使用事务 使用的切入点是AspectJExpressionPointcut实例则调用其中的matches()方法,一种注解使用事务 使用的切入点是TransactionAttributeSourcePointcut实例则调用其中的matches()方法。下面我们对mathches()方法进行分析
2.3、AspectJExpressionPointcut的matches方法
//调用AspectJExpressionPointcut 的matches()方法
//AspectJExpressionPointcut 是IntroductionAwareMethodMatcher实现类
//走introductionAwareMethodMatcher.matches(method, targetClass)方式
public boolean matches(Method method, Class<?> targetClass, boolean beanHasIntroductions) {
//切入点校验
checkReadyToMatch();
//针对method 可能是接口方法 targetClass却是实现类的情况 获取实现类的目标方法
Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
//getShadowMatch方法的调用繁杂,有点类似Class的匹配,最终也会调用到Pointcut的match(Shadow shadow)方法
//匹配方法是否符合切入点表达式
ShadowMatch shadowMatch = getShadowMatch(targetMethod, method);
// 如果始终匹配则返回true
if (shadowMatch.alwaysMatches()) {
return true;
}
//不匹配返回false
else if (shadowMatch.neverMatches()) {
return false;
}
else {
// the maybe case
if (beanHasIntroductions) {
return true;
}
//如果上面的匹配结果不确定 则需要进行实际的验证
//比如args(...)则验证方法的参数是否有指定的类型,@args则验证方法的参数是否标识了指定的注解
RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
}
}
该方法只要使用我们在xml中声明的aop:pointcut 转换的切入点对象中的切入点表达式对目标方法进行匹配查找,如果匹配成功则返回true,到此为止有关xml的所匹配的代理增强的获取完成。
2.4、TransactionAttributeSourcePointcut的matches方法
//调用TransactionAttributeSourcePointcut 的matches()方法
//TransactionAttributeSourcePointcut 是MethodMatcher实现类
//走methodMatcher.matches(method, targetClass)方式
public boolean matches(Method method, Class<?> targetClass) {
if (TransactionalProxy.class.isAssignableFrom(targetClass)) {
return false;
}
//TransactionAttributeSource 获取TransactionAttribute事务属性对象源
//其getTransactionAttribute 获取TransactionAttribute事务属性对象
TransactionAttributeSource tas = getTransactionAttributeSource();
return (tas == null || tas.getTransactionAttribute(method, targetClass) != null);
}
这里TransactionAttributeSourcePointcut 将匹配操作委托给其属性TransactionAttribute实现类 这里具体调用AbstractFallbackTransactionAttributeSource的getTransactionAttribute方法来判断如果可以获取到事务对象则说明代理增强匹配目标对象。下面我们来看看AbstractFallbackTransactionAttributeSource的getTransactionAttribute方法。
/**
* 该方法主要是用来获取我们修饰在类或者方法上的@Transactional注解将其包装成TransactionAttribute
* 比如 @Transactional中的传播行为,隔离级别,是否只读,失败回滚等
*/
//TransactionAttributeSource 类的TransactionAttribute事务属性
public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
// 先从缓存中获取 有直接返回TransactionAttribute对象
Object cacheKey = getCacheKey(method, targetClass);
Object cached = this.attributeCache.get(cacheKey);
if (cached != null) {
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
return null;
}
else {
return (TransactionAttribute) cached;
}
}
else {
//没有缓存则需要去手动解析方法或者目标类上的@Transaction注解
TransactionAttribute txAtt = computeTransactionAttribute(method, targetClass);
// 如果没有放入缓存中为默认实现 默认实现是null对象
if (txAtt == null) {
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
}
else {
if (logger.isDebugEnabled()) {
//打印日志
}
//否则放入解析出来的TransactionAttribute到缓存中
this.attributeCache.put(cacheKey, txAtt);
}
return txAtt;
}
}
该方法只是对事务的获取进行了缓存的处理,没有实质的内容可言,主要的获取事务对象逻辑则由computeTransactionAttribute方法进行处理。
2.5、computeTransactionAttribute方法
/**
* 解析目标方法或者类上修饰的@Transactional属性
*/
private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
// 方法如果不允许访问 返回null
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
return null;
}
// 获取targetClass目标类的真实class因为targetClass可能是CGLIB代理类(需要获取其中的父class)
Class<?> userClass = ClassUtils.getUserClass(targetClass);
// The method may be on an interface, but we need attributes from the target class.
// If the target class is null, the method will be unchanged.
//如果目标方法是接口上的,这里会获取目标对象上的目标方法而非原始接口上的目标方法
Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
// If we are dealing with method with generic parameters, find the original method.
//对于泛型方法 需要查找其原始的方法
specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
//从上面获取的方法中获取@Transactional修饰的属性信息
TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
if (txAtt != null) {
return txAtt;
}
//方法获取不到注解信息则从方法对应的类上获取@Transactional修饰的属性信息
txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
if (txAtt != null) {
return txAtt;
}
//类上也获取不到注解信息 则看看其中的接口方法 或者接口上是否有@Transactional修饰的属性信息
if (specificMethod != method) {
// 从接口方法对应的类上获取@Transactional修饰的属性信息
txAtt = findTransactionAttribute(method);
if (txAtt != null) {
return txAtt;
}
//从接口上获取@Transactional修饰的属性信息
return findTransactionAttribute(method.getDeclaringClass());
}
return null;
}
该方法中主要针对目标对象的方法获取期中的@Transactional注解转换为事务对象,因为我们有的时候注解修饰在类上所有的方法自动应用事务,也可以修饰在方法上,更有可能是在其接口或者接口方法上,为了实现这种需求,上述代码主要先从目标方法上尝试获取事务,否则从该方法对应的类上,还没有从接口方法上获取,还是没有从接口上获取注解,最终都没有才任务该方法不需要使用事务功能,否则中间任何一个环境有事务则就返回事务,并认为该目标对象需要使用事务,上述具体的解析注解操作是在findTransactionAttribute方法上的我们来分析一下该方法。
2.6、解析@Transactional注解
//最终是由AnnotationTransactionAttributeSource类的determineTransactionAttribute()方法
//来具体解析@Transactional注解
protected TransactionAttribute findTransactionAttribute(Class<?> clazz) {
return determineTransactionAttribute(clazz);
}
//AnnotationTransactionAttributeSource解析@Transactional注解委托给了TransactionAnnotationParser
//AnnotationTransactionAttributeSource解析构造的时候默认使用了SpringTransactionAnnotationParser来进行处理
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
if (attr != null) {
return attr;
}
}
return null;
}
上面的代码最终表名AnnotationTransactionAttributeSource解析@Transactional注解委托给了TransactionAnnotationParser
默认使用了SpringTransactionAnnotationParser来进行处理。解析@@Transactional注解操作比较简单,这里不在详述
下面附上具体解析@Transactional注解的方法。至此注解形式使用事务中有关获取事务的代理增强到这里就完成了。
//调用SpringTransactionAnnotationParser进行解析@Transactional注解
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
//Transactional注解包装成AnnotationAttributes
AnnotationAttributes ann = AnnotatedElementUtils.getAnnotationAttributes(ae, Transactional.class.getName());
if (ann != null) {
//具体解析@Transactional注解
return parseTransactionAnnotation(ann);
}
else {
return null;
}
}
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
//事务传播行为
Propagation propagation = attributes.getEnum("propagation");
rbta.setPropagationBehavior(propagation.value());
//事务隔离级别
Isolation isolation = attributes.getEnum("isolation");
rbta.setIsolationLevel(isolation.value());
//事务超时时间
rbta.setTimeout(attributes.getNumber("timeout").intValue());
//是否只读
rbta.setReadOnly(attributes.getBoolean("readOnly"));
rbta.setQualifier(attributes.getString("value"));
//出现异常进行异常处理并进行回滚操作
ArrayList<RollbackRuleAttribute> rollBackRules = new ArrayList<RollbackRuleAttribute>();
Class<?>[] rbf = attributes.getClassArray("rollbackFor");
for (Class<?> rbRule : rbf) {
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
String[] rbfc = attributes.getStringArray("rollbackForClassName");
for (String rbRule : rbfc) {
RollbackRuleAttribute rule = new RollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
//出现异常只进行异常处理不进行回滚操作
Class<?>[] nrbf = attributes.getClassArray("noRollbackFor");
for (Class<?> rbRule : nrbf) {
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
String[] nrbfc = attributes.getStringArray("noRollbackForClassName");
for (String rbRule : nrbfc) {
NoRollbackRuleAttribute rule = new NoRollbackRuleAttribute(rbRule);
rollBackRules.add(rule);
}
rbta.getRollbackRules().addAll(rollBackRules);
return rbta;
}
上面讲述了很多,好像我们已经忘记最初的目标,这里总结一下:该小节主要是讲述如何xml和注解形式使用事务的过程中如何确定木目标对象需要使用事务的代理增强进行后续的创建代理对象的操作。
四、对象操作数据过程中的事务应用
通过上面的大量的分析,我们已经确定了 目标对象获取到事务的代理增强后会创建对应的代理对象,将事务的开启,回滚、提交,退回保存点灯事务相关操作和我们业务代码以AOP的形式结合起来,既然说都了AOP模式说明业务功能和事务功能是分开的,这里有关我们事务的功能是由哪一个类来提供呢?在二、中我们已经分析了spring应用事务的过程中创建了若干个BeanDefintion。上面的分析中大多都提到了。但是我们还想忘记了一个类TransactionInterceptor 不错该类就是我们可以使用事务的核心类,里面包含事务的相关功能,该类实现了MethodInterceptor 说明该类调用的过程中一定会调用其invoke(),我们就从该类的invoke()方法出来来揭开spring事务的神秘面纱。
1、invoke()方法
//TransactionInterceptor 事务的处理核心
//主要的事务处理是调用invoke()方法
public Object invoke(final MethodInvocation invocation) throws Throwable {
//获取MethodInvocation的目标类 即我们业务需要进行事务处理的方法
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
//所有的事务处理都在该方法中处理
return invokeWithinTransaction(invocation.getMethod(), targetClass, new TransactionAspectSupport.InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}
该方法主要获取了业务方法对应的拦截类MethodInvocation 获取其方法对应的目标类,具体的事务操作交由其父类TransactionAspectSupport的invokeWithinTransaction()下面我们来关注一下该方法。
2、invokeWithinTransaction方法
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final TransactionAspectSupport.InvocationCallback invocation)
throws Throwable {
// 获取事务属性信息
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
// 获取事务管理器 这里是我们配置的DataSourceTransactionManager
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
//拼接事务的切入点比如 com.soecode.lyf.service.impl.saveBook()
final String joinpointIdentification = methodIdentification(method, targetClass);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
//开启事务操作
TransactionAspectSupport.TransactionInfo txInfo = createTransactionIfNecessary
(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
//执行切入点对应的方法 即执行业务方法 saveBook()业务方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
//出现异常事务回滚操作
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
//程序正常执行的提交操作
commitTransactionAfterReturning(txInfo);
return retVal;
}
else {
//编程式事务处理方式
//该方式下的事务处理只是暴露出一个接口用于执行事物处理过程中有关业务方法的回调
//其他有关事物开启,提交回滚操作是编写在业务代码中的
try {
Object result = ((CallbackPreferringPlatformTransactionManager)
tm).execute(txAttr,
new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
TransactionAspectSupport.TransactionInfo txInfo =
prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}
catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new
TransactionAspectSupport.ThrowableHolderException(ex);
}
}
else {
// A normal return value: will lead to a commit.
return new TransactionAspectSupport.ThrowableHolder(ex);
}
}
finally {
cleanupTransactionInfo(txInfo);
}
}
});
Check result: It might indicate a Throwable to rethrow.
if (result instanceof TransactionAspectSupport.ThrowableHolder) {
throw ((TransactionAspectSupport.ThrowableHolder) result).getThrowable();
}
else {
return result;
}
}
catch (TransactionAspectSupport.ThrowableHolderException ex) {
throw ex.getCause();
}
}
}
这个方法包含了我们事务操作的核心方法,主要分成三个大部分(自己理解啊)
- 事务操作准备阶段
- 声明式事务处理(事务开启提交等操作由spring管理)
- 编程式事务管理(事务开启提交等操作需要程序显示编写)
2.1、进行事务操作的准备阶段
这里主要构造三个主要对象
- TransactionAttribute 匹配目标方法的事务属性对象
事务属性对象是指该对象是对事务隔离级别,传播行为,超时,是否只读,异常回滚,异常不回滚等事务属性进行抽象的事务对象。
所谓的匹配目标:我们需要确定该目标方法上有对应的事务,主要的方式实现是如下代码:
getTransactionAttributeSource().getTransactionAttribute(method, targetClass)
- 注解形式的获取事务主要是通过AnnotationTransactionAttributeSource类的getTransactionAttribute()方法获取目标对象的@Transactional注解将其转换为对应目标对象的事务对象
- xml形式使用事务则是通过NameMatchTransactionAttributeSource类的getTransactionAttribute()方法通过名字的模糊匹配获取对应的我们之前使用<tx:method name="del*" propagation="REQUIRED" rollback-for="Throwable"/> 修饰的事务信息
两者获取到的事务对象均为RuleBasedTransactionAttribute实例对象
- PlatformTransactionManager
spring事务的事务管理器,主要提供事务开启,回滚,提交三个核心功能,这里的事务管理器实例是我们配置的spring默认的DataSourceTransactionManager,所以后面事务操作我们会分析该实例 。
- String joinpointIdentification
提供目标方法的全限定方法名,主要用于日志记录等语义化操作。
-
TransactionStatus
事务的一些状态信息,如是否一个新的事务、是否已被标记为回滚,用于在事务开启,提交回滚时候的状态流转。
-
TransactionInfo
将如上对象包装到改类中,包含事务的所有相关属性对象
2.2、声明式事务管理
事务操作核心只有三个,数据库操作开始的时候开启事务,执行业务操作代码,没有异常则正常提交事务,有异常事务回滚
我们先使用列表的形式将代码片段罗列出来去分别分析。
开启事务 | TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification); |
执行业务代码 | retVal = invocation.proceedWithInvocation(); |
提交事务 | commitTransactionAfterReturning(txInfo); |
异常会馆 | completeTransactionAfterThrowing(txInfo, ex); |
2.3、编程式事务处理
因为编程式事务是需要代码显示编码处理所以spring对于编程式事务处理使用CallbackPreferringPlatformTransactionManager
执行对应的业务方法而已之所以篇幅比较长,主要是在异常处理环节。没啥说的不详述了。
3、开启事务
声明式事务处理中createTransactionIfNecessary()方法中开启事务
/**
* 开启事务 最终包装成TransactionInfo
* @param tm PlatformTransactionManager 事务管理
* @param txAttr 事务的一些基础信息 超时时间、隔离级别、传播属性
* @param joinpointIdentification 业务方法的切入点 需要添加事务处理的业务方法
* @return
*/
protected TransactionAspectSupport.TransactionInfo createTransactionIfNecessary(
PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIde
//将TransactionAttribute 包装成 DelegatingTransactionAttribute 里面包换事务相关属性信息
//比如事务的隔离级别,传播行为,是否只读,超时时间,异常回调方法等
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinpointIdentification;
}
};
}
//开启事务,返回TransactionStatus事务的一些状态信息,
//如是否一个新的事务、是否已被标记为回滚
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
status = tm.getTransaction(txAttr);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Skipping transactional joinpoint [" + joinpointIdentification +
"] because no transaction manager has been configured");
}
}
}
//将PlatformTransactionManager 事务管理器
// TransactionAttribute事务的一些基础信息
//joinpointIdentification 需要进行事务处理的业务方法
// TransactionStatus 事务的一些状态信息,如是否一个新的事务、是否已被标记为回滚
//将如上的对象包装成TransactionInfo对象
return prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
}
- 将TransactionAttribute 包装成DelegatingTransactionAttribute对象
- 开启事务 tm.getTransaction(txAttr)
- 将将PlatformTransactionManager 事务管理器 TransactionAttribute事务对象joinpointIdentification 业务方 TransactionStatus 事务状态对象包装成TransactionInfo对象 并绑定当前线程。
3.1、具体的开启事务
//开启事务的主要逻辑
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
//开启事务
Object transaction = doGetTransaction();
// Cache debug flag to avoid repeated checks.
boolean debugEnabled = logger.isDebugEnabled();
//如果没有事务属性信息 则创建一个默认TransactionAttribute对象
if (definition == null) {
definition = new DefaultTransactionDefinition();
}
//如果当前线程已经存在事务 使用单独的方法处理已经存在的事务
//判断依据DataSourceTransactionObject 的链接存在且链接的transactionActive标识为true
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}
//对于事务属性的超时检验 超时时间不能为负数
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
}
//没有存在的事务情况下,新的事务传播行为是PROPAGATION_MANDATORY 则抛出异常
//PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {
throw new IllegalTransactionStateException(
"No existing transaction found for transaction marked with propagation 'mandatory'");
}
//对于事务传播行为PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED
else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
//空挂起一个事务
AbstractPlatformTransactionManager.SuspendedResourcesHolder suspendedResources = suspend(null);
if (debugEnabled) {
logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
}
try {
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
//创建DefaultTransactionStatus 事务的一些状态信息对象
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
//构造transaction对象信息 比如数据库连接ConnectionHolder,隔离级别,timeOut等
//对于新链接绑定到当前线程
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException ex) {
resume(null, suspendedResources);
throw ex;
}
catch (Error err) {
resume(null, suspendedResources);
throw err;
}
}
else {
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
}
}
事务的开启涉及两种 :一种是嵌套事务处理,一种是无嵌套事务处理,根据事务不同的传播行为来具体分析
事务传播行为 | 事务传播行为说明 |
REQUIRED(默认属性) | 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。 被设置成这个级别时,会为每一个被调用的方法创建一个逻辑事务域。如果前面的方法已经创建了事务,那么后面的方法支持当前的事务,如果当前没有事务会重新建立事务。 |
MANDATORY | 支持当前事务,如果当前没有事务,就抛出异常。 |
NEVER | 以非事务方式执行,如果当前存在事务,则抛出异常。 |
NOT_SUPPORTED | 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
REQUIRES_NEW | 新建事务,如果当前存在事务,把当前事务挂起。 |
SUPPORTS | 支持当前事务,如果当前没有事务,就以非事务方式执行。 |
NESTED | 支持当前事务,新增Savepoint点,与当前事务同步提交或回滚。 嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。 |
PROPAGATION_NESTED | 内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA 事务管理器的支持。 |
PROPAGATION_REQUIRES_NEW | 外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。 |
如上代码主要实现逻辑如下
- 开启事务 doGetTransaction主要获取数据库的链接将其包装在DataSourceTransactionManager.DataSourceTransactionObject中,并返回
- 没有事务对象 则创建默认事务对象
- 对于嵌套事务处理 单独使用handleExistingTransaction方法处理
- 非嵌套事务处理针对不同的事务传播行为判断事务挂起、创建DefaultTransactionStatus 控制当前事务的状态(是否开启亦或回滚状态)
- doBegin()构造transaction对象信息 比如数据库连接ConnectionHolder,隔离级别,timeOut等、对于新链接绑定到当前线程
- prepareSynchronization方法 设置事务的同步属性
上面的相关功能主要关注点就是doBegin();
3.2、doBegin()方法
/填充transaction事务
protected void doBegin(Object transaction, TransactionDefinition definition) {
DataSourceTransactionManager.DataSourceTransactionObject txObject = (DataSourceTransactionManager.DataSourceTransactionObject)transaction;
Connection con = null;
try {
//设置事务的链接
if (txObject.getConnectionHolder() == null || txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
Connection newCon = this.dataSource.getConnection();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Acquired Connection [" + newCon + "] for JDBC transaction");
}
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
}
//将事务与资源设置为同步
txObject.getConnectionHolder().setSynchronizedWithTransaction(true);
con = txObject.getConnectionHolder().getConnection();
//设置事务的隔离级别
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
//取消数据库链接的自动提交,事务交由spring管理
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
//设置链接的事务为激活状态
txObject.getConnectionHolder().setTransactionActive(true);
//根据条件设置事务的超时时间
int timeout = this.determineTimeout(definition);
if (timeout != -1) {
txObject.getConnectionHolder().setTimeoutInSeconds(timeout);
}
if (txObject.isNewConnectionHolder()) {
//将对应的事务绑定到当前线程
TransactionSynchronizationManager.bindResource(this.getDataSource(), txObject.getConnectionHolder());
}
} catch (Throwable var7) {
if (txObject.isNewConnectionHolder()) {
DataSourceUtils.releaseConnection(con, this.dataSource);
txObject.setConnectionHolder((ConnectionHolder)null, false);
}
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", var7);
}
}
我们知道jdbc中 开启手动事务的关键是con.setAutoCommit(false),我们在doBegin()方法中发现了这里将我们获取到的数据库connection链接的自动提交关闭了,将事务操作交由spring管理
。
// 事务提交操作交由spring管理 txObject.setMustRestoreAutoCommit(true); //关闭事务自动提交、开启事务手动提交 con.setAutoCommit(false); |
除此之外doBegin()方法还设置了事务的隔离级别,超时时间,事务同步属性,事务激活等属性用于后续事务处理。
3.3、内嵌事务处理handleExistingTransaction方法
//内嵌事务处理
private TransactionStatus handleExistingTransaction(
TransactionDefinition definition, Object transaction, boolean debugEnabled)
throws TransactionException {
//对于传播行为PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NEVER) {
throw new IllegalTransactionStateException(
"Existing transaction found for transaction marked with propagation 'never'");
}
//对于传播行PROPAGATION_NOT_SUPPORTED为以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NOT_SUPPORTED) {
if (debugEnabled) {
logger.debug("Suspending current transaction");
}
//挂起事务 以非事务方式执行
Object suspendedResources = suspend(transaction);
boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);
return prepareTransactionStatus(
definition, null, false, newSynchronization, debugEnabled, suspendedResources);
}
//对于传播行为REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW) {
if (debugEnabled) {
logger.debug("Suspending current transaction, creating new transaction with name [" +
definition.getName() + "]");
}
//挂起事务
AbstractPlatformTransactionManager.SuspendedResourcesHolder suspendedResources = suspend(transaction);
try {
//创建新的事务去执行 新事务创建和普通事务创建一样
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
catch (RuntimeException beginEx) {
resumeAfterBeginException(transaction, suspendedResources, beginEx);
throw beginEx;
}
catch (Error beginErr) {
resumeAfterBeginException(transaction, suspendedResources, beginErr);
throw beginErr;
}
}
//对于PROPAGATION_NESTED 内层事务与外层事务就像两个独立的事务一样,
// 一旦内层事务进行了提交后,外层事务不不能对其进行回滚。两个事务互不影响。
// 两个事务不是一个真正的嵌套事务。同时它需要JTA 事务管理器的支持。
if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
if (!isNestedTransactionAllowed()) {
throw new NestedTransactionNotSupportedException(
"Transaction manager does not allow nested transactions by default - " +
"specify 'nestedTransactionAllowed' property with value 'true'");
}
if (debugEnabled) {
logger.debug("Creating nested transaction with name [" + definition.getName() + "]");
}
//因为在此传播行为下 内嵌事务和外部事务 不互相影响 这里需要为内部事务设置一个保存点
//从而使其内部事务和外部事务隔离
if (useSavepointForNestedTransaction()) {
DefaultTransactionStatus status =
prepareTransactionStatus(definition, transaction, false, false, debugEnabled, null);
status.createAndHoldSavepoint();
return status;
}
else {
//有的情况不能使用保存点操作 比如JTA 则这里需要使用新建事务来进行处理
boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
DefaultTransactionStatus status = newTransactionStatus(
definition, transaction, true, newSynchronization, debugEnabled, null);
doBegin(transaction, definition);
prepareSynchronization(status, definition);
return status;
}
}
// Assumably PROPAGATION_SUPPORTS or PROPAGATION_REQUIRED.
if (debugEnabled) {
logger.debug("Participating in existing transaction");
}
//事务校验 失败抛出异常 省略
}
针对支持内嵌事务的不同传播行为进行分别处理
- 对于传播行为PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
- 对于传播行PROPAGATION_NOT_SUPPORTED为以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
- 对于传播行PROPAGATION_REQUIRES_NEW 表示当前事务需要在一个新的事务中进行处理,会将原来的事务挂起,执行当前新的事务,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务.
- 对于传播行PROPAGATION_NESTED 内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA 事务管理器的支持。 这里spring针对嵌入式事务处理采取两种方式
- 一种是使用建立保存点的形式作为异常处理的回滚,内部提交后和外部事务互相独立。
- 对于无法使用保存点的比如JTA事务则使用创建新事务的方式进行事务处理,注意这里新事务创建不会挂起原事务 所有内部事务还外部事务互相独立。
4、回滚事务
声明式事务使用completeTransactionAfterThrowing(txInfo, ex) 做事务的异常回滚处理。
//spring事务异常回滚 异常不回滚 <tx:method name="save*" read-only="false" propagation="REQUIRED" rollback-for="RuntimeException" no-rollback-for="Exception"/> @Transactional(rollbackFor = RuntimeException.class,noRollbackFor = Exception.class) |
事务的处理有两种方式 一种是有异常回滚,一种是有异常但是不回滚,只有满足回滚条件才进行数据回滚
protected void completeTransactionAfterThrowing(TransactionAspectSupport.TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
//省略日志打印
//满足回滚条件的情况进行异常回滚
if (txInfo.transactionAttribute.rollbackOn(ex)) {
try {
//具体的异常回滚操作
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
//省略异常
}
}
else {
try {
//不满足回滚条件 有异常 也提交事务 不进行回滚操作
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
catch (TransactionSystemException ex2) {
//省略异常
}
}
}
}
4.1、回滚操作
private void processRollback(DefaultTransactionStatus status) {
try {
try {
//事务回滚前的调用
triggerBeforeCompletion(status);
//如果有保存点 则事务回滚到保存点
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Rolling back transaction to savepoint");
}
status.rollbackToHeldSavepoint();
}
//新事务 则直接回滚
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction rollback");
}
doRollback(status);
}
//对于分布式事务(非独立事务) 这里不做真正的回滚 只是做回滚标记
else if (status.hasTransaction()) {
if (status.isLocalRollbackOnly() || isGlobalRollbackOnParticipationFailure()) {
if (status.isDebug()) {
logger.debug("Participating transaction failed - marking existing transaction as rollback-only");
}
doSetRollbackOnly(status);
}
else {
if (status.isDebug()) {
logger.debug("Participating transaction failed - letting transaction originator decide on rollback");
}
}
}
else {
logger.debug("Should roll back transaction but cannot - no transaction available");
}
}
catch (RuntimeException ex) {
//异常处理
}
//事务回滚后的调用
triggerAfterCompletion(status, TransactionSynchronization.STATUS_ROLLED_BACK);
}
finally {
//事务回滚后的信息清除
cleanupAfterCompletion(status);
}
}
触发事务回滚前的调用
事务操作之前需要调用对应的触发器
有保存点回滚到保存点
对于有保存点的事务回滚到对应的保存点,常用语嵌入式事务,对于嵌入式的事务处理,内嵌的事务异常不会引起外部事物的回滚。保存点的回滚实现方式是根据数据库的底层链接进行的
新事务直接回滚
新事务的操作回滚操作也是使用底层数据库链接的底层API来操作。
回滚后的事务信息清除
设置事务状态为完成,如果当前事务是新的同步状态,则需要将绑定到当前线程的事务信息清除
结束之前事务的挂起状态
5、提交事务
//事务的提交操作 根据事务状态进行判断
public final void commit(TransactionStatus status) throws TransactionException {
//事务已经完成提交,不能重复提交
if (status.isCompleted()) {
throw new IllegalTransactionStateException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
//对于异常回滚过程中 没有新事务 也没有保存点的 分布式事务 回滚的时候只是保存回滚标识
//当某个嵌入事务发生回滚的时候 只是设置事务标识,而等到外部是事务提交的时候 判断出来该事务设置了
//回滚标识,由外部事务统一进行事务的提交
if (defStatus.isLocalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Transactional code has requested rollback");
}
processRollback(defStatus);
return;
}
if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) {
if (defStatus.isDebug()) {
logger.debug("Global transaction is marked as rollback-only but transactional code requested commit");
}
processRollback(defStatus);
// Throw UnexpectedRollbackException only at outermost transaction boundary
// or if explicitly asked to.
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
throw new UnexpectedRollbackException(
"Transaction rolled back because it has been marked as rollback-only");
}
return;
}
//没有回滚标识则进行提交操作
processCommit(defStatus);
}
事务提交主要对于非独立事务只是标记了回滚标记,这里在整个外部事务提交的时候,会根据回滚标识由外部事务统一进行事务回滚操作。只有没有回滚标识才执行真正的提交操作 processCommit()方法
5.1、processCommit()方法
//执行事务的提交操作者
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
beforeCompletionInvoked = true;
boolean globalRollbackOnly = false;
if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) {
globalRollbackOnly = status.isGlobalRollbackOnly();
}
//事务提交过程中 如果有保存点 提交操作时候清除保存点心
if (status.hasSavepoint()) {
if (status.isDebug()) {
logger.debug("Releasing transaction savepoint");
}
status.releaseHeldSavepoint();
}
//如果是新事务则调用数据库底层链接进行提交操作
else if (status.isNewTransaction()) {
if (status.isDebug()) {
logger.debug("Initiating transaction commit");
}
doCommit(status);
}
//省略异常处理
}
}
springAOP 的分析到此为止,感谢观看。