1. Spring Tx 编程
整个Spring事务可以通过编程与声明两种方式进行操作:
1. AbstractPlatformTransactionManager: 通过其子类来自己控制事务开始, 提交, 回滚等操作
2. TransactionTemplate: 通过传入一个回调函数来控制程序的执行, 其内部还是通过 AbstractPlatformTransactionManager的子类
3. TransactionProxyFactoryBean: 通过在配置文件中配置 transactionAttributes 来决定对哪些方法进行事务的作用, 此时的 TransactionAttributes获取器是 NameMatchTransactionAttributeSource <-- 这站方式使用得比较少了
4. 通过 Tx namespace 的方式来声明事务
5. 通过在方法上标注 @Transactional 注解的方式来实现声明式事务
2. Spring Tx 编程式事务 AbstractPlatformTransactionManager
AbstractPlatformTransactionManager 是事务操作的基础类, 它具有 begin(事务开启), suspend(挂起事务, 比如 PROPAGATION_REQUIRES_NEW 级别), resume (将挂起的事务重用, 往往在上个事务提交会回滚后), commit(事务的提交), rollback(事务的回滚, 默认我们遇到 RuntimeException | Error 时回滚), 当然这些方法都是模版方法, 都是留给对应ORM的子类去实现的, 比如我们最常用的 DataSourceTransactionManager, 下面的 Demo 就是基于 DataSourceTransactionManager :
private static final String CREATE_TABLE_SQL = "create table test" +
"(id int(10) AUTO_INCREMENT PRIMARY KEY, " +
"name varchar(100))";
private static final String DROP_TABLE_SQL = "drop table test";
private static final String INSERT_SQL = "insert into test(name) values(?)";
private static final String COUNT_SQL = "select count(*) from test";
public static void main(String[] args) {
// 定义连接池
DataSource dataSource = getDataSource();
// 先建 PlatformTransactionManager
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
// 定义 TransactionDefinition
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
// 获取 TransactionStatus
TransactionStatus transactionStatus = transactionManager.getTransaction(definition);
// 从连接池中获取 <- 这里其实是将 DataSource <--> Connection 放到 ThreadLocal 里面
Connection con = DataSourceUtils.getConnection(dataSource);
try {
// 执行 SQL
con.prepareStatement(CREATE_TABLE_SQL).execute();
PreparedStatement pstmt = con.prepareStatement(INSERT_SQL);
pstmt.setString(1, "test");
pstmt.execute();
ResultSet resultSet = con.prepareStatement(COUNT_SQL).executeQuery();
// 打印查询结果
while(resultSet.next()){ System.out.println(resultSet.getString(1));}
con.prepareStatement(DROP_TABLE_SQL).execute();
transactionManager.commit(transactionStatus);
} catch (Exception e) { // 遇到异常就直接回滚
transactionManager.rollback(transactionStatus);
}
}
// 设置数据库连接池
public static DataSource getDataSource(){
org.apache.commons.dbcp.BasicDataSource basicDataSource = new org.apache.commons.dbcp.BasicDataSource();
basicDataSource.setDriverClassName("com.mysql.jdbc.Driver");
basicDataSource.setUrl("jdbc:mysql://localhost:3306/tuomatuo?useUnicode=true&characterEncoding=UTF8");
basicDataSource.setUsername("root");
basicDataSource.setPassword("123456");
return basicDataSource;
}
在上面的代码中出现了一个新的角色 DataSourceUtils, 这个类主要是通过 TransactionSynchronizationManager 对 ThreadLocal 中存储的 DataSource 的操作; 以上代码主要是如下几步:
1. 新建数据库连接 DataSource
2. 新建 DataSourceTransactionManager, 将 DataSource 赋值到其中
3. 构建默认的事务配置器 TransactionDefinition
4. 通过 DataSourceTransactionManager 获取事务状态器 TransactionStatus (此时已经将 Connection 等事务配置信息存储到 ThreadLocal 中)
5. 通过 DataSourceUtils.getConnection(dataSource) 获取存储在 ThreadLocal 中的 Connection, 接着就是直接对 数据库的操作
6. 直接成功提交事务, 失败的话就会直接回滚事务
PS: 在运行 Demo 时数据库的连接替换成本地环境的参数
3. Spring Tx 编程式事务 TransactionTemplate
TransactionTemplate其实只是在 AbstractPlatformTransactionManager上面做了一个简单的封装, 通过传递一个回调函数 TransactionCallback 来执行数据库的操作, 其他部分的操作与直接使用 AbstractPlatformTransactionManager 是一样的
<!-- 设置目标 -->
<bean id="testBeanTarget" class="org.springframework.tests.sample.beans.TestBean">
<property name="name"><value>dependency</value></property>
</bean>
<!-- 配置 dataSource -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="validationQuery">
<value>select 1 from dual</value>
</property>
</bean>
<!-- 配置 DataSourceTransactionManager -->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 配置 TransactionProxyFactoryBean -->
<bean id="transactionProxyFactoryBean" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!-- 代理的目标类 -->
<property name="target"><ref local="testBeanTarget"/></property>
<!-- 配置 transactionManager -->
<property name="transactionManager"><ref local="transactionManager"/></property>
<!-- 设置 transactionAttributes, 这里使用 NameMatchTransactionAttributeSource 来获取 transactionAttribute 信息 -->
<!-- 在父类 TransactionAspectSupport.invokeWithinTransaction 中会用到 -->
<property name="transactionAttributes">
<props>
<prop key="s*">PROPAGATION_MANDATORY</prop>
<prop key="setAg*"> PROPAGATION_REQUIRED,readOnly</prop>
<prop key="set*">PROPAGATION_SUPPORTS</prop>
</props>
</property>
</bean>
从上面的 Demo 中发现, 其实整个配置信息不复杂, 缺点是 TransactionProxyFactoryBean 是针对单个 Target, 若项目中出现非常多需要事务控制的方法, 则需要配置非常多的 TransactionProxyFactoryBean, 所以出现了基于 Tx NameSpace 与 基于注解 @Transactional 的事务
4. Spring Tx 基于 Tx Namespace 的事务配置
基于 Tx nameSpace 的事务配置, 其实就是在 BeanFactory 中注入 TransactionInterceptor, NameMatchTransactionAttributeSource , 其中 TransactionInterceptor 是个 MethodInterceptor, 通过对目标对象代理, 而拦截器 TransactionInterceptor 中就是完成事务的操作, 先看一个对应的事务配置信息:
<!-- 配置拦截器 -->
<tx:advice id="txAdvice"> <!-- TxAdviceBeanDefinitionParser 解析标签成 org.springframework.transaction.interceptor.TransactionInterceptor -->
<tx:attributes> <!-- TxAdviceBeanDefinitionParser 解析标签成 NameMatchTransactionAttributeSource -> String, TransactionAttribute -->
<tx:method name="get*" read-only="true"/>
<tx:method name="set*"/>
<tx:method name="exceptional"/>
</tx:attributes>
</tx:advice>
<!-- 配置 AspectJAwareAdvisorAutoProxyCreator 与 DefaultBeanFactoryPointcutAdvisor -->
<aop:config> <!-- 解析成 DefaultBeanFactoryPointcutAdvisor 与 AspectJExpressionPointcut <- 对应的表达式是 "execution (* com.lami.mhao.service.*.add*(..))" -->
<aop:advisor pointcut="execution (* com.lami.mhao.service.*.add*(..))" advice-ref="txAdvice"/>
</aop:config>
从上面的配置我们可以看到, 原来 事务命名空间这种配置其实就是在 BeanFactory 中注入 MethodInterceptor <--TransactionInterceptor, 在 TransactionInterceptor 中通过 PlatformTransactionManager 来进行事务操作, 在 TransactionInterceptor 中的操作如下:
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
// If the transaction attribute is null, the method is non-transactional.
// 这里读取事务的属性和设置, 通过 TransactionAttributeSource 对象取得
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
// 获取 beanFactory 中的 transactionManager
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
// 构造方法唯一标识(类, 方法, 如 service.UserServiceImpl.save)
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
/**
* 这里区分不同类型的 PlatformTransactionManager 因为它们的调用方式不同
* 对 CallbackPreferringPlatformTransactionManager 来说, 需要回调函数来
* 实现事务的创建和提交
* 对于非 CallbackPreferringPlatformTransactionManager 来说, 不需要通过
* 回调函数来实现事务的创建和提交
* 像 DataSourceTransactionManager 就不是 CallbackPreferringPlatformTransactionManager
* 不需要通过回调的方式来使用
*/
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
// Standard transaction demarcation with getTransaction and commit/rollback calls.
// 这里创建事务, 同时把创建事务过程中得到的信息放到 TransactionInfo 中去 (创建事务的起点)
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 {
// 这里把与线程绑定的 TransactionInfo 设置为 oldTransactionInfo
cleanupTransactionInfo(txInfo);
}
// 这里通过事务处理器来对事务进行提交
commitTransactionAfterReturning(txInfo);
return retVal;
}
}
其中非常重要的 TransactionAttribute 是从 NameMatchTransactionAttributeSource(这个类也是通过解析 xml 时注入的) 中获取的, 主流程如下:
// 对调用的方法进行判断, 判断它是否是事务方法, 如果是事务方法, 那么取出相应的事务配置属性
@Override
public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
// 判断当前目标调用的方法与配置的事务方法是否直接匹配
if (!ClassUtils.isUserLevelMethod(method)) {
return null;
}
// Look for direct name match.
String methodName = method.getName();
// 通过方法名从 nameMap 中获取 attr
TransactionAttribute attr = this.nameMap.get(methodName);
// 如果不能直接匹配, 就通过 PatternMatchUtils 的 simpleMatch 方法来进行匹配判断
if (attr == null) {
// Look for most specific name match.
String bestNameMatch = null;
for (String mappedName : this.nameMap.keySet()) {
if (isMatch(methodName, mappedName) && // 方法名称匹配 <- 正则匹配
(bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
attr = this.nameMap.get(mappedName); // 匹配成功, 则直接获取 TransactionAttribute <- 这里的 TransactionAttribute 是在解析 xml 时注入 BeanFactory 的
bestNameMatch = mappedName;
}
}
}
return attr; // 返回通过 methodName 获取的 attr
}
PS: 对应的 Tx Namespace 解析器是 TxAdviceBeanDefinitionParser <- 代码量不多
5. Spring Tx 基于注解 @Transactional 的事务
在使用这种形式的事务时, 只需要在配置文件中加上 <tx:annotation-driven />, 接着就可以在方法上加上 @Transactional 就能通过对某个方法内的 SQL 操作进行事务操作, 其中主要的参与者是:
1. AbstractAdvisorAutoProxyCreator: 自动Advisor收集, 自动代理创建器
2. AnnotationTransactionAttributeSource: 通过获取&解析方法上@Transactional 来获取 TransactionAttribute 的 TransactionAttributeSource
3. TransactionInterceptor: 一个 MethodInterceptor, 正真的事务操作其实就是在这个类中, 本质上也是通过 PlatformTransactionManager 来操作的
4. BeanFactoryTransactionAttributeSourceAdvisor: 一个基于TransactionAttributeSourcePointcut 的 Advisor, 这种 Pointcut 是通过 AnnotationTransactionAttributeSource, SpringTransactionAnnotationParser 解析方法上的 @Transactional 注解获取 TransactionAttribute 来确定是否匹配成功的 Pointcut
<tx:annotation-driven /> 的解析器则是: org.springframework.transaction.config.AnnotationDrivenBeanDefinitionParser, 主要流程如下:
public static void configureAutoProxyCreator(Element element, ParserContext parserContext) {
// 若 BeanFactory 中没有注入 自动代理创建器, 则将注入 InfrastructureAdvisorAutoProxyCreator
AopNamespaceUtils.registerAutoProxyCreatorIfNecessary(parserContext, element);
String txAdvisorBeanName = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME;
// 没有注入过 internalTransactionAdvisor
if (!parserContext.getRegistry().containsBeanDefinition(txAdvisorBeanName)) {
Object eleSource = parserContext.extractSource(element);
// Create the TransactionAttributeSource definition.
RootBeanDefinition sourceDef = new RootBeanDefinition( // 注入 AnnotationTransactionAttributeSource (TransactionAttribute 提取器, 其主要是通过注释在方法上的 @Transactional 获取 TransactionAttribute)
"org.springframework.transaction.annotation.AnnotationTransactionAttributeSource");
sourceDef.setSource(eleSource);
sourceDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 这里标志 AnnotationTransactionAttributeSource 是 IOC 的基础类 <- 针对基础类不会进行 AOP 等操作
String sourceName = parserContext.getReaderContext().registerWithGeneratedName(sourceDef);
// Create the TransactionInterceptor definition. // 注入 TransactionInterceptor, 正真的事务操作就是在这里拦截器里面 <- 主要还是通过 PlatformTransactionManager
RootBeanDefinition interceptorDef = new RootBeanDefinition(TransactionInterceptor.class);
interceptorDef.setSource(eleSource);
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 标志这是个基础类 <- 基础类不会被 AOP 等特性操作
registerTransactionManager(element, interceptorDef); // 设置 transactionManager 的 beanName
interceptorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
// 注册 TransactionInterceptor, 并使用 Spring 中的定义规则生成 beanName
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
// 注入 BeanFactoryTransactionAttributeSourceAdvisor, 这个类中的 Pointcut 是 TransactionAttributeSourcePointcut, 而
// 只要 TransactionAttributeSource 通 Method, Class 中获取 TransactionAttribute 就表示这个方法将被事务操作
// Create the TransactionAttributeSourceAdvisor definition.
RootBeanDefinition advisorDef = new RootBeanDefinition(BeanFactoryTransactionAttributeSourceAdvisor.class);
advisorDef.setSource(eleSource);
advisorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 标志这是个基础类 <- 基础类不会被 AOP 等特性操作
// 将 sourceName 的 bean 注入 advisorDef 的 transactionAttributeSource 属性中
advisorDef.getPropertyValues().add("transactionAttributeSource", new RuntimeBeanReference(sourceName));
advisorDef.getPropertyValues().add("adviceBeanName", interceptorName);
// 如果配置了 order 属性, 则加入到 bean 中
if (element.hasAttribute("order")) {
advisorDef.getPropertyValues().add("order", element.getAttribute("order"));
}
// 注入 BeanFactoryTransactionAttributeSourceAdvisor
parserContext.getRegistry().registerBeanDefinition(txAdvisorBeanName, advisorDef);
// 创建 CompositeComponentDefinition <- 其中包含上面的需要注入的 BeanDefinition <-- 组合模式
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);
}
}
6. 总结
在 Spring 中整个事务的操作是围绕 PlatformTransactionManager 来进行操作的, PlatformTransactionManager 中封装了 begin(开启事务), suspend(将先前的事务挂起来 传播级别可能就是PROPAGATION_REQUIRES_NEW), resume(重用先前的事务属性), commit(提交事务), 这些方法就是模版方法, 在 AbstractPlatformTransactionManager中有着整个操作的逻辑, 对应的 ORM 只需要实现对应模版方法就可以(比如 我们最常用的 DataSourceTransactionManager) ; 而需要理解我们最常用的, 基于 @Transactional 的事务, 需要先理解以下 Aop 这样就是非常好理解, 对应的 Pointcut 其实就是通过在方法上获取 @Transactional 的信息来决定!
7. 参考:
Spring Aop核心源码分析
Spring技术内幕
Spring 揭秘
Spring 源码深度分析
开涛 Spring 杂谈
伤神 Spring 源码分析
Spring源码情操陶冶
网友评论