Hello Coder


  • 首页

  • 归档

  • 标签

  • 搜索
close

Spring事务源码分析

发表于 2018-05-08

关于事务的具体概念和使用方式,Spring事务管理有详细介绍

基于注解的事务管理

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 数据源配置 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="#{jdbc['jdbc.driverClassName']}"/>
</bean>
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 开启注解方式配置事物 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

Spring事务调用过程

Spring事务使用AOP代理后的方法调用执行流程,如图所示:

2

从图中可以看出,调用事务时首先调用的是AOP代理对象而不是目标对象,首先执行事务切面,事务切面内部通过TransactionInterceptor环绕增强进行事务的增强。即进入目标方法之前开启事务,退出目标方法时提交/回滚事务。

源码分析

假设配置两个以上的<tx:annotation-driven/>标签:

1
2
3
4
5
6
7
8
9
10
<tx:annotation-driven transaction-manager="transactionManager1"/>
<bean id="transactionManager1"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource1"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager2"/>
<bean id="transactionManager2"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource2"/>
</bean>

结果只有第一个<tx:annotation-driven/>会生效,也就是使用@Transactional注解时不指定事务管理器,默认使用的事务管理器是transactionManager1。如果要使用多个事务管理器,需要在使用@Transactional时指定具体的TransactionManager

<tx:annotation-driven/>分析

这个标签是由TxNamespaceHandler解析的,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TxNamespaceHandler extends NamespaceHandlerSupport {
static final String TRANSACTION_MANAGER_ATTRIBUTE = "transaction-manager";
static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager";
public void init() {
// advice标签解析
registerBeanDefinitionParser("advice", new TxAdviceBeanDefinitionParser());
// annotation-driven标签解析
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenBeanDefinitionParser());
// jta-transaction-manager分布式事务标签解析
registerBeanDefinitionParser("jta-transaction-manager", new JtaTransactionManagerBeanDefinitionParser());
}
}

这里关注AnnotationDrivenBeanDefinitionParser解析器,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
public BeanDefinition parse(Element element, ParserContext parserContext) {
String mode = element.getAttribute("mode");
if ("aspectj".equals(mode)) {
// mode="aspectj"
// 提供对aspectj方式进行事务切入的支持
registerTransactionAspect(element, parserContext);
}
else {
// mode="proxy"
// 默认采用aop代理方式
AopAutoProxyConfigurer.configureAutoProxyCreator(element, parserContext);
}
return null;
}
}

AopAutoProxyConfigurer作为AnnotationDrivenBeanDefinitionParser静态内部类,关键代码如下:

重点代码被红框标记,最外层的if判断保证<tx:annotation-driven/>标签只能被解析一次,所以只有第一次被解析的标签会生效。分别注册了三个BeanDefinition,分别为AnnotationTransactionAttributeSource、TransactionInterceptor和BeanFactoryTransactionAttributeSourceAdvisor,并将前两个BeanDefinition添加到第三个BeanDefinition的属性当中,这三个bean支撑了整个事务功能

下面关注事务管理怎么注册的:

1
2
3
4
5
6
7
8
9
10
11
12
13
registerTransactionManager(element, interceptorDef);
// AnnotationDrivenBeanDefinitionParser.class
private static void registerTransactionManager(Element element, BeanDefinition def) {
def.getPropertyValues().add("transactionManagerBeanName",
TxNamespaceHandler.getTransactionManagerName(element));
}
// TxNamespaceHandler.class
static String getTransactionManagerName(Element element) {
return (element.hasAttribute(TRANSACTION_MANAGER_ATTRIBUTE) ?
element.getAttribute(TRANSACTION_MANAGER_ATTRIBUTE) : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME);
}

如果tx:annotation-driven没有指定transaction-manager,系统会默认注册transactionManager

1
2
3
4
5
6
7
8
9
10
11
12
13
14
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
// AopNamespaceUtils.class
public static void registerAutoProxyCreatorIfNecessary(
ParserContext parserContext, Element sourceElement) {
// 注册AutoProxyCreator,这里是InfrastructureAdvisorAutoProxyCreator
BeanDefinition beanDefinition = AopConfigUtils.registerAutoProxyCreatorIfNecessary(
parserContext.getRegistry(), parserContext.extractSource(sourceElement));
}
// AopConfigUtils.class
public static BeanDefinition registerAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) {
return registerOrEscalateApcAsRequired(InfrastructureAdvisorAutoProxyCreator.class, registry, source);
}

实际注册的是InfrastructureAdvisorAutoProxyCreator动态代理生成器

img

前面文章介绍使用Spring Aop做切面增强,使用的是AspectJAwareAdvisorAutoProxyCreator,可见普通aop代理生成器和InfrastructureAdvisorAutoProxyCreator都是继承于AbstractAdvisorAutoProxyCreator,具体的代理生成过程参考“Spring AOP源码分析”

参考

Spring源码解析之事务篇

Spring @Transactional原理及使用

Spring中AOP的配置从1.0到5.0的演进

Java中检查的异常与未检查的异常

发表于 2018-05-07

java异常

通常我们说的异常是包括exception和error:

  • Error(错误):是程序无法处理的错误,表示运行应用程序中较严重问题。大多数的错误与代码编写者执行的操作无关,而是表示代码运行时 JVM(Java 虚拟机)出现的问题。例如,当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止。
  • Exception(异常):是程序本身可以处理的异常。

除了RuntimeException与其子类,以及错误(Error),其他的都是检查异常

  • checked exception(检查异常) :编译器要求你必须处置的异常
  • unchecked exception(未检查异常):编译器不要求强制处置的异常

对于未检查异常也叫RuntimeException(运行时异常),对于运行时异常,java编译器不要求你一定要把它捕获或者一定要继续抛出,但是对checked exception(检查异常)要求你必须要在方法里面或者捕获或者继续抛出.

异常处理方法

  • 对未检查的异常(unchecked exception)的几种处理方式:
1
2
3
1、捕获
2、继续抛出
3、不处理
  • 对检查的异常(checked exception,除了RuntimeException,其他的异常都是checked exception)的几种处理方式:
1
2
1、继续抛出,消极的方法,一直可以抛到java虚拟机来处理
2、用try...catch捕获

注意:对于检查的异常必须处理,或者必须捕获或者必须抛出

​

mysql慢查询分析

发表于 2018-05-02

java字节码增强技术

发表于 2018-04-24

事务特性

发表于 2018-04-18

事务特性

  • 原子性:事务中的所有操作要么全部执行,要么都不执行;
  • 一致性:一个事务执行之前和执行之后都必须处于一致性状态;假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性
  • 隔离性:多个事务并发(同时)执行,每个事务都感觉不到系统中有其他的事务在执行;
  • 持久性:一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作;

隔离性

如果没有事务的隔离性,会发生的几种问题:

脏读

脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。

  当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。例如:用户A向用户B转账100元,对应SQL命令如下

1
2
update account set money=money+100 where name=’B’;
update account set money=money - 100 where name=’A’;

  当只执行第一条SQL时,A通知B查看账户,B发现确实钱已到账(此时即发生了脏读),而之后无论第二条SQL是否执行,只要该事务不提交,则所有操作都将回滚,那么当B以后再次查看账户时就会发现钱其实并没有转。

不可重复读

不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询之间,被另一个事务修改并提交了。

  例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发送了不可重复读。

  不可重复读和脏读的区别是:脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务已提交的数据。

幻读

幻读是指事务T1读取了满足某条件的一个数据集,事务T2插入了一行或者多行数据满足了T1的选择条件,导致事务T1再次使用同样的选择条件读取的时候,得到了比第一次读取更多的数据集

1
2
3
1、T1:select * from users where name = "A";
2、T2:insert into `users`(`id`, `name`) values (1, 'A');
3、T1:select * from users where name = "A";

  幻读和不可重复读都是读取了另一条已经提交的事务,所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体。

隔离级别

MySQL数据库提供了四种隔离级别:

隔离级别 脏读 不可重复读 幻读
未提交读(Read uncommitted) 可能 可能 可能
已提交读(Read committed) 不可能 可能 可能
可重复读(Repeatable read) 不可能 不可能 可能
可串行化(Serializable) 不可能 不可能 不可能

InnoDB默认级别:可重复读(Repeatable read),为什么能避免幻读,另一篇mysql锁有说明(间隙锁)

1…345…31
David

David

Develop Notes

155 日志
37 标签
GitHub Weibo
© 2016 - 2020 David
由 Hexo 强力驱动
主题 - NexT.Pisces