1.什么是事务
保证业务操作完整性的一种
数据库机制
2.事务的ACID
- A:原子性,多次操作要不一起成功,腰部一起失败(部分失败为savepoint)
- C:一致性,事务开始时数据状态,事务结束时状态数据一致
- I:隔离性,多个事务不能互相影响,做到隔离
- D:持久性,事务操作的结果,永久持久化到数据库或者其他存储介质当中
3.事务处理
单机版事务处理(Spring支持)
1. Connection (JDBC,Hiberate,Mybatis):数据库连接对象
Hiberate(JPA):Transcation(Connection)
Mybatis: SqlSession(Connection)
2. DataSource -> Connection
connection.setAutoCommit(false)
connection.commit()
connection.rollback();
以上是单机版开发的核心步骤,主要是核心是 数据库连接对象(Connection)
问题:在单机版系统中开发传统的MVC分层架构中如何保证 Service DAO层共用同一个连接对象呢
答: 使用ThreadLocal
来实现共用一个Connection
对象
4.Spring控制事务
核心要点:通过AOP方式来创建事务
控制事务编程
1. 编码 TrancationTemplate
2. 声明式事务:AOP Proxy代理设计模式,通过配置文件或者注解的方式来控制事务
4.1 事务开发的伪代码
- 通过配置文件的方式,如下
<!-- 配置事务管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dynamicDataSource"/>
</bean>
<!-- 配置事务传播特性 -->
<tx:advice id="transactionAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="insert*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="recycle*" propagation="REQUIRED"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="load*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="query*" read-only="false"/>
</tx:attributes>
</tx:advice>
<!-- 配置参与事务的方法 -->
<aop:config>
<aop:advisor pointcut="execution(*.*(..))" advice-ref="transactionAdvice"/>
</aop:config>
其主要核心是AOP的编程
- 原始对象
- 额外功能(增强)
- 控制事务的代码
PlatformTransactionManager
:接口,控制事务
- 子类有:
DataSourceTrancationManager
(JDBC,Mybatis)、HibernateTransctionManager
(Hibernate JPA)- 开启事务
- 提交事务
- 回滚事务
- 事务属性
- 隔离属性:isolation
- 传播属性:propagation
- 只读属性:readonly
- 超时属性:timeout
- 异常属性:exception
- 组转切面
5.事务属性
隔离属性(isolation):隔离属性事务的并发问题
- ISOLATION_DEFAULT: 数据库默认配置,
MYSQL REPEATABLE_READ
- ISOLATION_READ_UNCOMMITTED:读未提交,读取到另一个事务未提交的数据
- ISOLATION_READ_COMMITTED:读已提交
- ISOLATION_REPEATABLE_READ:可重复读
- ISOLATION_SERIALIZABLE:串行化
TransactionDefinition类中定义
int ISOLATION_DEFAULT = -1;
int ISOLATION_READ_UNCOMMITTED = Connection.TRANSACTION_READ_UNCOMMITTED;
int ISOLATION_READ_COMMITTED = Connection.TRANSACTION_READ_COMMITTED;
int ISOLATION_REPEATABLE_READ = Connection.TRANSACTION_REPEATABLE_READ;
int ISOLATION_SERIALIZABLE = Connection.TRANSACTION_SERIALIZABLE;
传播属性:propagation
- REQUIRED:外部没有事务,开启事务;外部存在事务,则融合
- REQUIRED_NEW:外部没有事务,开启事务;外部存在事务则挂起事务,开启新的事务执行,完成后外部事务继续执行
- MANDATORY:外部必须存在事务
- SUPPPORTS:外部没有事务,则不开启事务,外部存在事务,则融合
- NOT_SUPPORTED:外部没有事务则不开事务,外部有事务,抛出异常
- NESTED:内嵌事务SAVEPOINT
只读属性:readonly
设置事务为只读,提高事务运行的效率
超时属性:timeout
通过超时属性设置本事务最长等待时间 -1:代表由数据库底层决定等待的时间
异常属性:exception
- RuntimeException及其子类:默认回滚事务
- Exception及其子类:默认提交事务
注意⚠️:使用事务属性的目的就是更好的描述事务的特点
5.1 Spring实现了事务属性中的哪几种呢?
Spring底层没有实现ISOLATION
READONLY
TIMEOUT
,其实现主要由JDBC规范来完成即数据库驱动完成
conn.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
conn.setReadOnly(true);
conn.setNetworkTimeout();
Spring底层实现了:传播属性:PROPAGATION
、异常属性:EXCEPTION
6.Spring事务实现的源码浅析
Spring事务实现主要是由AOP来实现,在Spring当中AOP的实现主要由代理实现(JDK动态代理、CGLIB动态代理)
AOP的大致原理如下:
- 声明切入点:Pointcut-> AspectJExpressionPointcut
- 声明增强逻辑:Advice -> MethodInterceptor
- 组装增强和切入点:Advisor-> DefaultPointcutAdvisor
伪代码如下:
public static void main(String[] args) throws NoSuchMethodException {
// 声明切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* *())");
// 声明增强逻辑
MethodInterceptor methodInterceptor = methodInvocation -> {
System.out.println("before....");
methodInvocation.proceed();
System.out.println("after....");
return null;
};
// 组装切点、增强逻辑
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut,methodInterceptor);
// 通过代理工厂创建目标的代理对象
final Target1 target1 = new Target1();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target1);
factory.addAdvisor(advisor);
final I1 proxy = (I1) factory.getProxy();
proxy.bar();
proxy.foo();
}
Spring 选择代理条件
- proxyTargetClass = false : 目标
实现
了接口
,采用JDK
实现 - proxyTargetClass = false : 目标
没
有实现接口
,采用CGLIB
实现 - proxyTargetClass = true : 采用
CGLIB
实现
DefaultPointcutAdvisor#setTargetClass(true | false);
注意⚠️:Spring穿件动态代理的时机主要体现在两处地方依赖注入之前(populate)
和初始化(initialize)之后
- 依赖注入之前创建代理主要是解决循环依赖的问题
- 初始化之后创建代理是为了增强Bean
Spring 事务逻辑
1. Spring事务的底层依附 (DataSource -> Connection -> ThreadLocal -> commit | rollback )
2. 增强额外功能
DataSourceTransactionMannager(PlatformTransactionManager) : JDBC Mybatis
TransactionAttributes
3. BeanPostProcesser创建代理
4. 调用业务方法的过程当中,动态的将 原始功能 + 事务额外功能整合在一起
1.Spring 激活事务功能注解 EnableTransactionManagement 分析
其为一个组合注解,内部使用@Import
注解导入了一个TransactionManagementConfigurationSelector
类,Import注解的主要作用也是导入一个Bean到容器中,其解析主要是由ConfigurationClassPostProcessor
这个Bean工厂后置处理器来解析执行最终会调用到ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()
方法中的ConfigurationClassParser#doProcessConfigurationClass()
中解析,在此不多赘述了,详细可以查阅Spring IOC篇章深入学习
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
2.TransactionManagementConfigurationSelector类解析
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(),
ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {determineTransactionAspectClass()};
default:
return null;
}
}
private String determineTransactionAspectClass() {
return (ClassUtils.isPresent("javax.transaction.Transactional", getClass().getClassLoader()) ?
TransactionManagementConfigUtils.JTA_TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME :
TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME);
}
}
adviceMode默认为AdviceMode.PROXY
AutoProxyRegistrar
:主要用于解析@EnableTransactionManagement
注解属性和注册一个用于事务控制创建代理的BeanPostProcesser
,下文详细分析
ProxyTransactionManagementConfiguration
:主要是解析事务属性(TranscationAttrbiutes)
6.1 AutoProxyRegistrar类解析
6.1.1 AopConfigUtils.registerAutoProxyCreatorIfNecessary()方法分析
6.1.2 InfrastructureAdvisorAutoProxyCreator类分析
类图如下
6.1.3 AbstractAutoProxyCreator#wrapIfNecessary:方法解析
6.1.4 AbstractAutoProxyCreator#createProxy:方法解析
6.2 ProxyTransactionManagementConfiguration 类解析
Aop创建了动态代理对象,动态字节码技术在运行的过程中,动态加入额外的功能
注意⚠️:代理是在运行的过程中,才会把原始对象功能与额外功能进行整合。动态代理的运行效率要相低于静态代理
上文分析是由JDK创建的动态代理,调用我们自己的业务方法时,会到相应的代理
CglibAopProxy``JdkDynamicAopProxy
中的invoke()方法详细逻辑如下图
invocation.proceed()方法最终会执行到下图所框位置
由于我们已经声明一个事务拦截器,如下图,所以此处会进入TransactionInterceptor#invoke();
方法
6.2.1 TransactionInterceptor#invoke()方法解析
protected Object invokeWithinTransaction(Method method, @Nullable Class<?> targetClass,
final InvocationCallback invocation) throws Throwable {
// TransactionAttributeSource 工具类,获取对应方法上事务属性的工具类
TransactionAttributeSource tas = getTransactionAttributeSource();
// TransactionAttribute 封装了事务属性
final TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
// 事务管理器,DataSourceTrancationManager,在配置Bean中或者配置文件当中,是自己配置的内容
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
// 需要控制事务的方法名字
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
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 {
// 原始方法的执行
retVal = invocation.proceedWithInvocation();
}
catch (Throwable ex) {
// target invocation exception
completeTransactionAfterThrowing(txInfo, ex);
throw ex;
}
finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
TransactionAttributeSource 工具类,获取对应方法上事务属性的工具类
TransactionAttribute 封装了事务属性
PlatformTransactionManager:事务管理器,DataSourceTrancationManager,在配置Bean中或者配置文件当中,是自己配置的内容
joinpointIdentification:需要控制事务的方法名字
大家看到这里就应该可以发现,事务应该就是在这里被创建的,
retVal = invocation.proceedWithInvocation();这一行是目标方法即我们的业务方法
事务应该在此方法
之前
进行创建事务
,在此方法发生异常之后
进行回滚事务
,正常执行成功之后
进行提交事务
我们下面逐步开始分析
6.2.2 createTransactionIfNecessary(tm, txAttr, joinpointIdentification);方法分析
status = tm.getTransaction(txAttr); 方法含义
1. 事务的开启 -> Connection.setAutoCommit(false)
2. 获取事务的状态 -> TransactionStatus
3. 事务同步相关的状态信息
TransactionSynchronizationManager,通过ThreadLocal存储了很多线程同步标志
Spring处理 传播属性相关内容和控制事务
最重要的一个工具类
目的 把与事务相关的一些重要信息,绑定ThrealLocal