归纳来说,事务是一个由有限操做集合组成的逻辑单元。事务操做包含两个目的,数据一致以及操做隔离。数据一致是指事务提交时保证事务内的全部操做都成功完成,而且更改永久生效;事务回滚时,保证可以恢复到事务执行以前的状态。操做隔离则是指多个同时执行的事务之间应该相互独立,互不影响。html
事务是一个比较普遍的概念,事务管理资源除了咱们熟知的数据库外,还能够包含消息队列、文件系统等。固然,通常来讲,咱们说的事务单指“数据库事务”。接下来咱们会以MySQL数据库、Spring声明式事务为主要研究对象,可是不少事务概念、接口抽象和实现方式同时适用于其余状况。java
想要阅读更多技术干货、行业洞察,欢迎关注网易云信博客。spring
了解网易云信,来自网易核心架构的通讯与视频云服务。数据库
提到事务,不可避免须要涉及到事务的ACID属性:编程
咱们将严格遵循ACID属性的事务称为刚性事务。与之相对,指望最终一致性,在事务执行的中间状态容许暂时不遵循ACID属性的事务称为柔性事务,可参考传统事务与柔性事务,柔性事务的使用涉及到分布式事务方案,能够后续扩展,这里咱们先将注意集中在事务实现原理上。架构
根据SQL92标准,MySQL的InnoDB引擎提供四种隔离级别(即ACID中的I):读未提交(READ UNCOMMITTED)、读已提交(READ COMMITTED)、可重复读(REPEATABLE READ)和串行化(SERIALIZABLE),InnoDB默认的隔离级别是REPEATABLE READ
,其可避免脏读和不可重复读,但不能避免幻读,须要指出的是,InnoDB引擎的多版本并发控制机制(MVCC)并无彻底避免幻读,关于该问题以及隔离级别说明,可参考MySQL的InnoDB的幻读问题。并发
Spring针对方法嵌套调用时事务的建立行为定义了七种事务传播机制,分别是PROPAGATION_REQUIRED、PROPAGATION_SUPPORT、PROPAGATION_MANDATORY、PROPAGATION_REQUIRES_NEW、PROPAGATION_NOT_SUPPORTED、PROPAGATION_NEVER以及PROPAGATION_NESTED,基本上从字面意思就能知道每种传播机制各自的行为表现,Spring默认的事务传播机制是PROPAGATION_REQUIRED
,即若是当前存在事务,则使用当前事务,不然建立新的事务。详情可参考Spring事务传播行为。框架
事务的行为包括事务开启、事务提交和事务回滚。InnoDB全部的用户SQL执行都在事务控制以内,在默认状况下,autocommit设置为true
,单条SQL执行成功后,MySQL会自动提交事务,或者若是SQL执行出错,则根据异常类型执行事务提交或者回滚。可使用START TRANSACTION
(SQL标准)或者BEGIN
开启事务,使用COMMIT
和ROLLBACK
提交和回滚事务;也能够经过设置autocommit属性来控制事务行为,当设置autocommit为false
时,其后执行的多条SQL语句将在一个事务内,直到执行COMMIT
或者ROLLBACK
事务才会提交或者回滚。分布式
Spring使用AOP(面向切面编程)来实现声明式事务,后续在讲Spring事务具体实现的时候会详细说明,关于AOP的概念可参考Spring AOP概念理解(通俗易懂),这里再也不细说。说下动态代理和AOP加强。ide
动态代理是Spring实现AOP的默认方式,分为两种:JDK动态代理和CGLIB动态代理。JDK动态代理面向接口,经过反射生成目标代理接口的匿名实现类;CGLIB动态代理则经过继承,使用字节码加强技术(或者objenesis
类库)为目标代理类生成代理子类。Spring默认对接口实现使用JDK动态代理,对具体类使用CGLIB,同时也支持配置全局使用CGLIB来生成代理对象。
咱们在切面配置中会使用到@Aspect
注解,这里用到了Aspectj的切面表达式。Aspectj是java语言实现的一个AOP框架,使用静态代理模式,拥有完善的AOP功能,与Spring AOP互为补充。Spring采用了Aspectj强大的切面表达式定义方式,可是默认状况下仍然使用动态代理方式,并未使用Aspectj的编译器和织入器,固然也支持配置使用Aspectj静态代理替代动态代理方式。Aspectj功能更强大,比方说它支持对字段、POJO类进行加强,与之相对,Spring只支持对Bean方法级别进行加强。
Spring对方法的加强有五种方式:
org.springframework.aop.BeforeAdvice
):在目标方法执行以前进行加强;org.springframework.aop.AfterReturningAdvice
):在目标方法执行以后进行加强;org.aopalliance.intercept.MethodInterceptor
):在目标方法执行先后都执行加强;org.springframework.aop.ThrowsAdvice
):在目标方法抛出异常后执行加强;org.springframework.aop.IntroductionInterceptor
):为目标类添加新的方法和属性。声明式事务的实现就是经过环绕加强的方式,在目标方法执行以前开启事务,在目标方法执行以后提交或者回滚事务,事务拦截器的继承关系图能够体现这一点:
统一一致的事务抽象是Spring框架的一大优点,不管是全局事务仍是本地事务,JTA、JDBC、Hibernate仍是JPA,Spring都使用统一的编程模型,使得应用程序能够很容易地在全局事务与本地事务,或者不一样的事务框架之间进行切换。下图是Spring事务抽象的核心类图:
接口PlatformTransactionManager
定义了事务操做的行为,其依赖TransactionDefinition
和TransactionStatus
接口,其实大部分的事务属性和行为咱们以MySQL数据库为例已经有过了解,这里再对应介绍下。
PlatformTransactionManager
:事务管理器
getTransaction
方法:事务获取操做,根据事务属性定义,获取当前事务或者建立新事物;commit
方法:事务提交操做,注意这里所说的提交并不是直接提交事务,而是根据当前事务状态执行提交或者回滚操做;rollback
方法:事务回滚操做,一样,也并不是必定直接回滚事务,也有可能只是标记事务为只读,等待其余调用方执行回滚。TransactionDefinition
:事务属性定义
getPropagationBehavior
方法:返回事务的传播属性,默认是PROPAGATION_REQUIRED
;getIsolationLevel
方法:返回事务隔离级别,事务隔离级别只有在建立新事务时才有效,也就是说只对应传播属性PROPAGATION_REQUIRED
和PROPAGATION_REQUIRES_NEW
;getTimeout
方法:返回事务超时时间,以秒为单位,一样只有在建立新事务时才有效;isReadOnly
方法:是否优化为只读事务,支持这项属性的事务管理器会将事务标记为只读,只读事务不容许有写操做,不支持只读属性的事务管理器须要忽略这项设置,这一点跟其余事务属性定义不一样,针对其余不支持的属性设置,事务管理器应该抛出异常。getName
方法:返回事务名称,声明式事务中默认值为“类的彻底限定名.方法名”。TransactionStatus
:当前事务状态
isNewTransaction
方法:当前方法是否建立了新事务(区别于使用现有事务以及没有事务);hasSavepoint
方法:在嵌套事务场景中,判断当前事务是否包含保存点;setRollbackOnly
和isRollbackOnly
方法:只读属性设置(主要用于标记事务,等待回滚)和查询;flush
方法:刷新底层会话中的修改到数据库,通常用于刷新如Hibernate/JPA的会话,是否生效由具体事务资源实现决定;isCompleted
方法:判断当前事务是否已完成(已提交或者已回滚)。部分Spring包含的对PlatformTransactionManager
的实现类以下图所示:
AbstractPlatformTransactionManager
抽象类实现了Spring事务的标准流程,其子类DataSourceTransactionManager
是咱们使用较多的JDBC单数据源事务管理器,而JtaTransactionManager
是JTA(Java Transaction API)规范的实现类,另外两个则分别是JavaEE容器WebLogic和WebSphere的JTA事务管理器的具体实现。
以前提到,Spring采用AOP来实现声明式事务,那么事务的AOP切面是如何织入的呢?这一点涉及到AOP动态代理对象的生成过程。
代理对象生成的核心类是AbstractAutoProxyCreator
,实现了BeanPostProcessor
接口,会在Bean初始化完成以后,经过postProcessAfterInitialization
方法生成代理对象,关于BeanPostProcessor
在Bean生命周期中的做用,可参考一些经常使用的Spring扩展接口。
看一下AbstractAutoProxyCreator
类的核心代码,主要关注三个方法:postProcessAfterInitialization、wrapIfNecessary和createProxy,为了突出核心流程,以注释代替了部分代码的具体实现,后续的源码分析也采用相同的处理。
// AbstractAutoProxyCreator.class
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
if (!this.earlyProxyReferences.contains(cacheKey)) {
// 建立代理对象
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
// 参数检查,跳过已经执行过代理对象生成,或者已知的不须要生成代理对象的Bean
...
// Create proxy if we have advice.
// 查询当前Bean全部的AOP加强配置,最终是经过AOPUtils工具类实现
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
if (specificInterceptors != DO_NOT_PROXY) {
this.advisedBeans.put(cacheKey, Boolean.TRUE);
// 执行AOP织入,建立代理对象
Object proxy = createProxy(
bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return bean;
}
protected Object createProxy(Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
}
// 实例化代理工厂类
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.copyFrom(this);
// 当全局使用动态代理时,设置是否须要对目标Bean强制使用CGLIB动态代理
...
// 构建AOP加强顾问,包含框架公共加强和应用程序自定义加强
// 设置proxyFactory属性,如加强、目标类、是否容许变动等
...
// 建立代理对象
return proxyFactory.getProxy(getProxyClassLoader());
}
复制代码
最后是经过调用ProxyFactory#getProxy(java.lang.ClassLoader)
方法来建立代理对象:
// ProxyFactory.class
public Object getProxy(ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
// ProxyFactory父类ProxyCreatorSupport.class
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
return getAopProxyFactory().createAopProxy(this);
}
public ProxyCreatorSupport() {
this.aopProxyFactory = new DefaultAopProxyFactory();
}
复制代码
ProxyFactory
的父类构造器实例化了DefaultAopProxyFactory
类,从其源代码咱们能够看到Spring动态代理方式选择策略的实现:若是目标类optimize,proxyTargetClass属性设置为true
或者未指定须要代理的接口,则使用CGLIB生成代理对象,不然使用JDK动态代理。
public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 若是optimize,proxyTargetClass属性设置为true或者未指定代理接口,则使用CGLIB生成代理对象
if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
// 参数检查,targetClass为空抛出异常
...
// 目标类自己是接口或者代理对象,仍然使用JDK动态代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// Objenesis是一个能够不经过构造器建立子类的java工具类库
// 做为Spring 4.0后CGLIB的默认实现
return new ObjenesisCglibAopProxy(config);
}
else {
// 不然使用JDK动态代理
return new JdkDynamicAopProxy(config);
}
}
...
}
复制代码
咱们已经了解了AOP切面织入生成代理对象的过程,当Bean方法经过代理对象调用时,会触发对应的AOP加强拦截器,前面提到声明式事务是一种环绕加强,对应接口为MethodInterceptor
,事务加强对该接口的实现为TransactionInterceptor
,类图以下:
事务拦截器TransactionInterceptor
在invoke
方法中,经过调用父类TransactionAspectSupport
的invokeWithinTransaction
方法进行事务处理,该方法支持声明式事务和编程式事务。
// TransactionInterceptor.class
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
// 获取targetClass
...
// Adapt to TransactionAspectSupport's invokeWithinTransaction...
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
// 实际执行目标方法
return invocation.proceed();
}
});
}
// TransactionInterceptor父类TransactionAspectSupport.class
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, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// 事务获取
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
// 经过回调执行目标方法
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// 目标方法执行抛出异常,根据异常类型执行事务提交或者回滚操做
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
// 清理当前线程事务信息
cleanupTransactionInfo(txInfo);
}
// 目标方法执行成功,提交事务
commitTransactionAfterReturning(txInfo);
return retVal;
} else {
// 带回调的事务执行处理,通常用于编程式事务
...
}
}
复制代码
在讲Spring事务抽象时,有提到事务抽象的核心接口为PlatformTransactionManager
,它负责管理事务行为,包括事务的获取、提交和回滚。在invokeWithinTransaction
方法中,咱们能够看到createTransactionIfNecessary
、commitTransactionAfterReturning
和completeTransactionAfterThrowing
都是针对该接口编程,并不依赖于特定事务管理器,这里是对Spring事务抽象的实现。
//TransactionAspectSupport.class
protected TransactionInfo createTransactionIfNecessary( PlatformTransactionManager tm, TransactionAttribute txAttr, final String joinpointIdentification) {
...
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
// 获取事务
status = tm.getTransaction(txAttr);
...
}
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
if (txInfo != null && txInfo.hasTransaction()) {
...
// 提交事务
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
if (txInfo != null && txInfo.hasTransaction()) {
...
if (txInfo.transactionAttribute.rollbackOn(ex)) {
try {
// 异常类型为回滚异常,执行事务回滚
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
}
...
} else {
try {
// 异常类型为非回滚异常,仍然执行事务提交
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
...
}
protected final class TransactionInfo {
private final PlatformTransactionManager transactionManager;
...
复制代码
另外,在获取事务时,AbstractPlatformTransactionManager#doBegin
方法负责开启新事务,在DataSourceTransactionManager
有以下代码:
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
// 获取数据库链接con
...
if (con.getAutoCommit()) {
txObject.setMustRestoreAutoCommit(true);
if (logger.isDebugEnabled()) {
logger.debug("Switching JDBC Connection [" + con + "] to manual commit");
}
con.setAutoCommit(false);
}
...
}
复制代码
这里才真正开启了数据库事务。
提到事务传播机制时,咱们常常提到一个条件“若是当前已有事务”,那么Spring是如何知道当前是否已经开启了事务呢?在AbstractPlatformTransactionManager
中是这样作的:
// AbstractPlatformTransactionManager.class
@Override
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
// 参数为null时构造默认值
...
if (isExistingTransaction(transaction)) {
// Existing transaction found -> check propagation behavior to find out how to behave.
return handleExistingTransaction(definition, transaction, debugEnabled);
}
...
// 获取当前事务对象
protected abstract Object doGetTransaction() throws TransactionException;
// 判断当前事务对象是否包含活跃事务
protected boolean isExistingTransaction(Object transaction) throws TransactionException {
return false;
}
复制代码
注意getTransaction
方法是final
的,没法被子类覆盖,保证了获取事务流程的一致和稳定。抽象方法doGetTransaction
获取当前事务对象,方法isExistingTransaction
判断当前事务对象是否存在活跃事务,具体逻辑由特定事务管理器实现,看下咱们使用最多的DataSourceTransactionManager
对应的实现:
// DataSourceTransactionManager.class
@Override
protected Object doGetTransaction() {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
ConnectionHolder conHolder =
(ConnectionHolder) TransactionSynchronizationManager.getResource(this.dataSource);
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
@Override
protected boolean isExistingTransaction(Object transaction) {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
return (txObject.hasConnectionHolder() && txObject.getConnectionHolder().isTransactionActive());
}
复制代码
能够看到,获取当前事务对象时,使用了TransactionSynchronizationManager#getResource
方法,类图以下:
TransactionSynchronizationManager
经过ThreadLocal
对象在当前线程记录了resources
和synchronizations
属性。resources
是一个HashMap,用于记录当前参与事务的事务资源,方便进行事务同步,在DataSourceTransactionManager
的例子中就是以dataSource
做为key,保存了数据库链接,这样在同一个线程中,不一样的方法调用就能够经过dataSource
获取相同的数据库链接,从而保证全部操做在一个事务中进行。synchronizations
属性是一个TransactionSynchronization
对象的集合,AbstractPlatformTransactionManager
类中定义了事务操做各个阶段的调用流程,以事务提交为例:
// AbstractPlatformTransactionManager.class
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
try {
boolean beforeCompletionInvoked = false;
try {
prepareForCommit(status);
triggerBeforeCommit(status);
triggerBeforeCompletion(status);
....
else if (status.isNewTransaction()) {
// 记录日志
...
doCommit(status);
}
...
// 事务调用异常处理
...
try {
triggerAfterCommit(status);
}
finally {
triggerAfterCompletion(status, TransactionSynchronization.STATUS_COMMITTED);
}
}
}
复制代码
咱们能够看到,有不少trigger前缀的方法,这些方法用于在事务操做的各个阶段触发回调,从而能够精确控制在事务执行的不一样阶段所要执行的操做,这些回调实际上都经过TransactionSynchronizationUtils
来实现,它会遍历TransactionSynchronizationManager#synchronizations
集合中的TransactionSynchronization
对象,而后分别触发集合中各元素对应方法的调用。例如:
TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
@Override
public void afterCommit() {
// do something after commit
}
});
复制代码
这段代码就在当前线程的事务synchronizations
属性中,添加了一个自定义同步类,若是当前存在事务,那么在事务管理器执行事务提交以后,就会触发afterCommit
方法,能够经过这种方式在事务执行的不一样阶段自定义一些操做。
到这里,咱们已经对Spring事务的实现原理和处理流程有了必定的了解。
想要阅读更多技术干货、行业洞察,欢迎关注网易云信博客。
了解网易云信,来自网易核心架构的通讯与视频云服务。