文章目录
- 一、目标:事务处理
- 二、设计:事务处理
- 2.1 事务单元测试
- 2.2 事务设计
- 三、实现:事务处理
- 3.1 工程结构
- 3.2 事务管理的核心类图
- 3.3 定义事务注解
- 3.3.1 定义事务注解
- 3.3.2 定义事务接口
- 3.3.3 默认事务定义实现类
- 3.3.4 委托事务定义实现类
- 3.4 事务属性
- 3.4.1 事务属性接口
- 3.4.2 默认事务属性实现类
- 3.4.3 委托事务属性实现类
- 3.4.4 回滚规则属性
- 3.4.5 基于规则的事务属性实现类
- 3.5 注解工具包
- 3.5.1 注解属性
- 3.5.2 注解类型工具类
- 3.6 解析事务注解
- 3.6.1 用于解析已知事务注释类型的策略接口
- 3.6.2 Spring的事务注释类型解析实现类
- 3.7 事务状态
- 3.7.1 事务执行接口
- 3.7.2 存档管理接口
- 3.7.3 事务状态接口
- 3.7.4 事务状态抽象类
- 3.7.5 默认事务状态
- 3.8 事务属性来源
- 3.8.1 事务属性来源接口
- 3.8.2 事务属性来源抽象类
- 3.8.3 注解事务属性来源实现类
- 3.9 事务拦截处理
- 3.9.1 事务管理器平台接口
- 3.9.2 事务管理器平台抽象类
- 3.9.3 事务处理
- 3.9.4 事务拦截器
- 3.9.5 事务同步管理器
- 3.10 获取数据库连接
- 3.10.1 数据库连接处理器接口
- 3.10.2 简单数据库连接处理器
- 3.10.3 数据库连接处理器
- 3.10.4 JDBC事务支撑对象
- 3.10.5 数据源事务管理器
- 3.10.6 数据源操作抽象类
- 3.10.7 JDBC操作模板
- 四、测试:事务处理
- 4.1 添加测试配置
- 4.1.1 事务测试类
- 4.1.2 Spring属性配置文件
- 4.1.3 初始化测试
- 4.2 单元测试
- 4.2.1 有事务测试
- 4.2.2 无事务测试
- 五、总结:事务处理
一、目标:事务处理
💡 如何运用 @Transaction 注解标识、AOP 切面包装、将 JDBC 中的事务操作功能整合到 Spring 的框架结构中?
- 事务: 数据必须保证完整性和一致性。这也是事务的4个特性:
ACID
- 原子性(
Atomicity
):事务是一个原子操作,由一系列的动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用。 - 一致性(
Consistency
):一旦事务完成(无论成功还是失败),系统必须确保它所建模的业务处于一致的状态,而不是部分完成、部分失败。现实中的数据不应该被破坏。 - 隔离性(
Isolation
):许多事务可能会同时处理相同的数据,因此每个事务都应该与其他事务隔离,防止数据被损坏。 - 持久性(
Durability
):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响,这样就能从任何系统崩溃中恢复过来。在通常情况下,事务的结果会被写到持久化存储器中。
- 原子性(
二、设计:事务处理
2.1 事务单元测试
JdbcTest.java
package com.lino.springframework.test;
import com.alibaba.druid.pool.DruidDataSource;
import com.mysql.cj.jdbc.Driver;
import org.junit.Before;
import org.junit.Test;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @description: JDBC测试类
*/
public class JdbcTest {
private DruidDataSource dataSource;
private Connection connection;
private Statement statement;
@Before
public void init() throws SQLException {
dataSource = new DruidDataSource();
dataSource.setDriver(new Driver());
dataSource.setUrl("jdbc:mysql://localhost:3306/spring?useSSL=false&serverTimezone=Asia/Shanghai");
dataSource.setUsername("root");
dataSource.setPassword("123456");
}
@Test
public void test_translation() throws SQLException {
connection = dataSource.getConnection().getConnection();
statement = connection.createStatement();
connection.setAutoCommit(false);
try {
statement.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
statement.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
} catch (Exception e) {
e.printStackTrace();
connection.rollback();
}
connection.commit();
}
}
- 这就是使用 JDBC 直接操作提交事务的方式,首先设置自动提交
connection.setAutoCommit(false)
来关闭事务,然后在程序处理过程中进行手动提交事务和回滚事务的操作。
测试结果
信息: {dataSource-1} inited
java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'PRIMARY'
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:115)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:790)
at com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:675)
at com.lino.springframework.test.JdbcTest.test_translation(JdbcTest.java:40)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
2.2 事务设计
💡 设计:事务的核心逻辑处理该如何完成?
- 简单来说,我们最终目的就是把数据库的事务操作用自定义注解和 AOP 切面进行管理,并扩充相应的事务特性。
- 有了这个结构,就可以把这部分操作数据库事务的功能迁移到 Spring 中,使用注解标记和 AOP 切面进行拦截处理。
- 如果想要提交事务,则需要保证是一个链接下进行处理的。这是需要引入事务管理器进行处理,使用
ThreadLocal
进行保存。 - 只有定义了事务注解,才能对需要处理的事务进行方法拦截。在拦截到事务之后,把这种操作方法交给代理类,也就是把用户对数据库的操作包装到事务的代码库中,方便对操作方法进行事务提交和回滚等操作。
三、实现:事务处理
3.1 工程结构
spring-step-19
|-src
|-main
| |-java
| |-com.lino.springframework
| |-aop
| | |-aspectj
| | | |-AspectJExpressionPointcut.java
| | | |-AspectJExpressionPointcutAdvisor.java
| | |-framework
| | | |-adapter
| | | | |-MethodBeforeAdviceInterceptor.java
| | | |-autoproxy
| | | | |-DefaultAdvisorAutoProxyCreator.java
| | | |-AopProxy.java
| | | |-Cglib2AopProxy.java
| | | |-JdkDynamicAopProxy.java
| | | |-ProxyFactory.java
| | | |-ReflectiveMethodInvocation.java
| | |-AdvisedSupport.java
| | |-Advisor.java
| | |-BeforeAdvice.java
| | |-ClassFilter.java
| | |-MethodBeforeAdvice.java
| | |-MethodMatcher.java
| | |-Pointcut.java
| | |-PointcutAdvisor.java
| | |-TargetSource.java
| |-beans
| | |-factory
| | | |-annotation
| | | | |-Autowired.java
| | | | |-AutowiredAnnotationBeanPostProcessor.java
| | | | |-Qualifier.java
| | | | |-Value.java
| | | |-config
| | | | |-AutowireCapableBeanFactory.java
| | | | |-BeanDefinition.java
| | | | |-BeanFactoryPostProcessor.java
| | | | |-BeanPostProcessor.java
| | | | |-BeanReference.java
| | | | |-ConfigurableBeanFactory.java
| | | | |-InstantiationAwareBeanPostProcessor.java
| | | | |-SingletonBeanRegistry.java
| | | |-support
| | | | |-AbstractAutowireCapableBeanFactory.java
| | | | |-AbstractBeabDefinitionReader.java
| | | | |-AbstractBeabFactory.java
| | | | |-BeabDefinitionReader.java
| | | | |-BeanDefinitionRegistry.java
| | | | |-CglibSubclassingInstantiationStrategy.java
| | | | |-DefaultListableBeanFactory.java
| | | | |-DefaultSingletonBeanRegistry.java
| | | | |-DisposableBeanAdapter.java
| | | | |-FactoryBeanRegistrySupport.java
| | | | |-InstantiationStrategy.java
| | | | |-SimpleInstantiationStrategy.java
| | | |-xml
| | | | |-XmlBeanDefinitionReader.java
| | | |-Aware.java
| | | |-BeanClassLoaderAware.java
| | | |-BeanFactory.java
| | | |-BeanFactoryAware.java
| | | |-BeanNameAware.java
| | | |-ConfigurableListableBeanFactory.java
| | | |-DisposableBean.java
| | | |-FactoryBean.java
| | | |-HierarcgicalBeanFactory.java
| | | |-InitializingBean.java
| | | |-ListableBeanFactory.java
| | | |-ObjectFactory.java
| | | |-PropertyPlaceholderConfigurer.java
| | |-BeansException.java
| | |-PropertyValue.java
| | |-PropertyValues.java
| |-context
| | |-annotation
| | | |-ClassPathBeanDefinitionScanner.java
| | | |-ClassPathScanningCandidateComponentProvider.java
| | | |-Scope.java
| | |-event
| | | |-AbstractApplicationEventMulticaster.java
| | | |-ApplicationContextEvent.java
| | | |-ApplicationEventMulticaster.java
| | | |-ContextclosedEvent.java
| | | |-ContextRefreshedEvent.java
| | | |-SimpleApplicationEventMulticaster.java
| | |-support
| | | |-AbstractApplicationContext.java
| | | |-AbstractRefreshableApplicationContext.java
| | | |-AbstractXmlApplicationContext.java
| | | |-ApplicationContextAwareProcessor.java
| | | |-ClassPathXmlApplicationContext.java
| | | |-ConversionServiceFactoryBean.java
| | |-ApplicationContext.java
| | |-ApplicationContextAware.java
| | |-ApplicationEvent.java
| | |-ApplicationEventPublisher.java
| | |-ApplicationListener.java
| | |-ConfigurableApplicationContext.java
| |-core
| | |-annotation
| | | |-AbstractAliasAwareAnnotationAttributeExtractor.java
| | | |-AliasFor.java
| | | |-AnnotatedElementUtils.java
| | | |-AnnotationAttributeExtractor.java
| | | |-AnnotationAttributes.java
| | | |-AnnotationConfigurationException.java
| | | |-AnnotationUtils.java
| | | |-DefaultAnnotationAttributeExtractor.java
| | | |-SynthesizedAnnotation.java
| | | |-SynthesizedAnnotationInvocationHandler.java
| | |-convert
| | | |-converter
| | | | |-Converter.java
| | | | |-ConverterFactory.java
| | | | |-ConverterRegistry.java
| | | | |-GenericConverter.java
| | | |-support
| | | | |-DefaultConversionService.java
| | | | |-GenericConversionService.java
| | | | |-StringToNumberConverterFactory.java
| | | |-ConversionService
| | |-io
| | | |-ClassPathResource.java
| | | |-DefaultResourceLoader.java
| | | |-FileSystemResource.java
| | | |-Resource.java
| | | |-ResourceLoader.java
| | | |-UrlResource.java
| | |-util
| | | |-ConcurrentReferenceHashMap.java
| | | |-ObjectUtils.java
| | | |-StringUtils.java
| | |-BridgeMethodResolver.java
| | |-GraalDetector.java
| | |-MethodClassKey.java
| | |-MethodParameter.java
| | |-ResolvableType.java
| | |-SerializableTypeWrapper.java
| |-jdbc
| | |-core
| | | |-ColumnMapRowMapper.java
| | | |-JdbcOperations.java
| | | |-JdbcTemplate.java
| | | |-ResultSetExtractor.java
| | | |-RowMapper.java
| | | |-RowMapperResultSetExtractor.java
| | | |-SqlProvider.java
| | | |-StatementCallback.java
| | |-datasource
| | | |-ConnectionHandle.java
| | | |-ConnectionHolder.java
| | | |-DataSourceTransactionManager.java
| | | |-DataSourceUtils.java
| | | |-JdbcTransactionObjectSupport.java
| | | |-SimpleConnectionHandle.java
| | |-support
| | | |-JdbcAccessor.java
| | | |-JdbcUtils.java
| | |-CannotGetJdbcConnectionException.java
| | |-UncategorizedSQLException.java
| |-stereotype
| | |-Component.java
| |-tx.transaction
| | |-annotation
| | | |-AnnotationTransactionAttributeSource.java
| | | |-SpringTransactionAnnotationParser.java
| | | |-Transactional.java
| | | |-TransactionAnnotationParser.java
| | |-interceptor
| | | |-AbstractFallbackTransactionAttributeSource.java
| | | |-DefaultTransactionAttribute.java
| | | |-DelegatingTransactionAttribute.java
| | | |-RollbackRuleAttribute.java
| | | |-RuleBasedTransactionAttribute.java
| | | |-TransactionAspectSupport.java
| | | |-TransactionAttribute.java
| | | |-TransactionAttributeSource.java
| | | |-TransactionInterceptor.java
| | |-support
| | | |-AbstractPlatformTransactionManager.java
| | | |-AbstractTransactionStatus.java
| | | |-DefaultTransactionDefinition.java
| | | |-DefaultTransactionStatus.java
| | | |-DelegatingTransactionDefinition.java
| | | |-TransactionSynchronizationManager.java
| | |-CannotCreateTransactionException.java
| | |-NestedTransactionNotSupportedException.java
| | |-PlatformTransactionManager.java
| | |-SavepointManager.java
| | |-TransactionDefinition.java
| | |-TransactionException.java
| | |-TransactionExecution.java
| | |-TransactionStatus.java
| |-util
| | |-ClassUtils.java
| | |-NumberUtils.java
| | |-ReflectionUtils.java
| | |-StringValueResolver.java
|-test
|-java
|-com.lino.springframework.test
|-bean
|-JdbcService.java
|-ApiTest.java
|-JdbcTest.java
|-resources
|-spring.xml
3.2 事务管理的核心类图
- 以自定义事务注解
@Transactional
进行方法标记,用于事务管理扫描。把需要被事务管理的方法拦截 AOP 切面。 - 因为事务需要在同一个链接下操作,所以需要使用
TransactionSynchronizationManager
类提供的ThreadLocal
方法记录当前线程下的链接信息。 - 事务的控制都需要被切面事务支撑类
TransactionAspectSupport
进行包装和处理,以便提交和回滚事务。
3.3 定义事务注解
3.3.1 定义事务注解
Transactional.java
package com.lino.springframework.tx.transaction.annotation;
import java.lang.annotation.*;
/**
* @description: 事务注解
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
/**
* 回滚
*/
Class<? extends Throwable>[] rollbackFor() default {};
}
- 只有作用到方法上的事务标记性注解,才能被
SpringTransactionAnnotationParser
类扫描并管理事务。
3.3.2 定义事务接口
TransactionDefinition.java
package com.lino.springframework.tx.transaction;
import java.sql.Connection;
/**
* @description: 事务定义接口
*/
public interface TransactionDefinition {
/**
* 这个是Spring默认的事务传播行为
* 如果正处于一个事务中,则加入,否则创建一个新的事务
*/
int PROPAGATION_REQUIRED = 0;
/**
* 如果正处于一个事务中,则加入,否则不使用事务
*/
int PROPAGATION_SUPPORTS = 1;
/**
* 如果正处于一个事务中,则加入,否则抛出事务
*/
int PROPAGATION_MANDATORY = 2;
/**
* 无论如何都会创建一个新事务
* 如果正处于一个事务中,则先挂起当前事务,然后创建
*/
int PROPAGATION_REQUIRES_NEW = 3;
/**
* 不使用事务
* 如果正处于一个事务中,则挂起当前事务,不使用
*/
int PROPAGATION_NOT_SUPPORTED = 4;
/**
* 不使用事务
* 如果正处于一个事务中,则抛出异常
*/
int PROPAGATION_NEVER = 5;
/**
* 嵌套事务
* 如果正处于一个事务中,则创建一个事务嵌套其中,否则创建一个新事务
*/
int PROPAGATION_NESTED = 6;
/**
* 使用默认的隔离级别
*/
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;
/**
* 默认超时时间
*/
int TIMEOUT_DEFAULT = -1;
/**
* 获取传播行为
*/
int getPropagationBehavior();
/**
* 获取事务隔离级别
*/
int getIsolationLevel();
/**
* 获取事务超时时间
*/
int getTimeout();
/**
* 是否总是读
*/
boolean isReadOnly();
/**
* 获取事务名称
*/
String getName();
}
3.3.3 默认事务定义实现类
DefaultTransactionDefinition.java
package com.lino.springframework.tx.transaction.support;
import com.lino.springframework.tx.transaction.TransactionDefinition;
import java.io.Serializable;
/**
* @description: 默认事务定义
*/
public class DefaultTransactionDefinition implements TransactionDefinition, Serializable {
private int propagationBehavior = PROPAGATION_REQUIRED;
private int isolationLevel = ISOLATION_DEFAULT;
private int timeout = TIMEOUT_DEFAULT;
private boolean readOnly = false;
private String name;
public DefaultTransactionDefinition() {
}
public DefaultTransactionDefinition(TransactionDefinition other) {
this.propagationBehavior = other.getPropagationBehavior();
this.isolationLevel = other.getIsolationLevel();
this.timeout = other.getTimeout();
this.readOnly = other.isReadOnly();
this.name = other.getName();
}
public DefaultTransactionDefinition(int propagationBehavior) {
this.propagationBehavior = propagationBehavior;
}
public void setPropagationBehavior(int propagationBehavior) {
this.propagationBehavior = propagationBehavior;
}
@Override
public int getPropagationBehavior() {
return this.propagationBehavior;
}
public void setIsolationLevel(int isolationLevel) {
this.isolationLevel = isolationLevel;
}
@Override
public int getIsolationLevel() {
return this.isolationLevel;
}
public void setTimeout(int timeout) {
if (timeout < TIMEOUT_DEFAULT) {
throw new IllegalArgumentException("Timeout must be a positive integer or TIMEOUT_DEFAULT");
}
this.timeout = timeout;
}
@Override
public int getTimeout() {
return this.timeout;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
@Override
public boolean isReadOnly() {
return this.readOnly;
}
public void setName(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
}
3.3.4 委托事务定义实现类
DelegatingTransactionDefinition.java
package com.lino.springframework.tx.transaction.support;
import com.lino.springframework.tx.transaction.TransactionDefinition;
import java.io.Serializable;
import java.util.Objects;
/**
* @description: 委托事务定义
*/
public class DelegatingTransactionDefinition implements TransactionDefinition, Serializable {
private final TransactionDefinition targetDefinition;
public DelegatingTransactionDefinition(TransactionDefinition targetDefinition) {
this.targetDefinition = targetDefinition;
}
@Override
public int getPropagationBehavior() {
return this.targetDefinition.getPropagationBehavior();
}
@Override
public int getIsolationLevel() {
return this.targetDefinition.getIsolationLevel();
}
@Override
public int getTimeout() {
return this.targetDefinition.getTimeout();
}
@Override
public boolean isReadOnly() {
return this.targetDefinition.isReadOnly();
}
@Override
public String getName() {
return this.targetDefinition.getName();
}
@Override
public boolean equals(Object other) {
return this.targetDefinition.equals(other);
}
@Override
public int hashCode() {
return this.targetDefinition.hashCode();
}
@Override
public String toString() {
return this.targetDefinition.toString();
}
}
3.4 事务属性
3.4.1 事务属性接口
TransactionAttribute.java
package com.lino.springframework.tx.transaction.interceptor;
import com.lino.springframework.tx.transaction.TransactionDefinition;
/**
* @description: 事务属性
*/
public interface TransactionAttribute extends TransactionDefinition {
/**
* 回滚
*
* @param ex 异常
* @return 回滚结果
*/
boolean rollbackOn(Throwable ex);
}
3.4.2 默认事务属性实现类
DefaultTransactionAttribute.java
package com.lino.springframework.tx.transaction.interceptor;
import com.lino.springframework.tx.transaction.support.DefaultTransactionDefinition;
/**
* @description: 默认事务属性
*/
public class DefaultTransactionAttribute extends DefaultTransactionDefinition implements TransactionAttribute {
public DefaultTransactionAttribute() {
super();
}
@Override
public boolean rollbackOn(Throwable ex) {
return (ex instanceof RuntimeException || ex instanceof Error);
}
@Override
public String toString() {
return "DefaultTransactionAttribute{}";
}
}
3.4.3 委托事务属性实现类
DelegatingTransactionAttribute.java
package com.lino.springframework.tx.transaction.interceptor;
import com.lino.springframework.tx.transaction.support.DelegatingTransactionDefinition;
import java.io.Serializable;
/**
* @description: 委托事务属性
*/
public class DelegatingTransactionAttribute extends DelegatingTransactionDefinition implements TransactionAttribute, Serializable {
private final TransactionAttribute targetAttribute;
public DelegatingTransactionAttribute(TransactionAttribute targetAttribute) {
super(targetAttribute);
this.targetAttribute = targetAttribute;
}
@Override
public boolean rollbackOn(Throwable ex) {
return this.targetAttribute.rollbackOn(ex);
}
}
3.4.4 回滚规则属性
RollbackRuleAttribute.java
package com.lino.springframework.tx.transaction.interceptor;
import java.io.Serializable;
/**
* @description: 回滚规则属性
*/
public class RollbackRuleAttribute implements Serializable {
private final String exceptionName;
public RollbackRuleAttribute(Class<?> clazz) {
this.exceptionName = clazz.getName();
}
}
3.4.5 基于规则的事务属性实现类
RuleBasedTransactionAttribute.java
package com.lino.springframework.tx.transaction.interceptor;
import java.io.Serializable;
import java.util.List;
/**
* @description: 基于规则的事务属性
*/
public class RuleBasedTransactionAttribute extends DefaultTransactionAttribute implements Serializable {
private List<RollbackRuleAttribute> rollbackRules;
public RuleBasedTransactionAttribute() {
super();
}
public void setRollbackRules(List<RollbackRuleAttribute> rollbackRules) {
this.rollbackRules = rollbackRules;
}
}
3.5 注解工具包
3.5.1 注解属性
AnnotationAttributes.java
package com.lino.springframework.core.annotation;
import cn.hutool.core.lang.Assert;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.util.LinkedHashMap;
/**
* @description: 注解属性
*/
public class AnnotationAttributes extends LinkedHashMap<String, Object> {
private final Class<? extends Annotation> annotationType;
boolean validated = false;
final String displayName;
public AnnotationAttributes(Class<? extends Annotation> annotationType) {
Assert.notNull(annotationType, "'annotationType' must not be null");
this.annotationType = annotationType;
this.displayName = annotationType.getName();
}
public Class<?>[] getClassArray(String attributeName) {
return getRequiredAttribute(attributeName, Class[].class);
}
private <T> T getRequiredAttribute(String attributeName, Class<T> expectedType) {
Object value = get(attributeName);
if (!expectedType.isInstance(value) && expectedType.isArray() && expectedType.getComponentType().isInstance(value)) {
Object array = Array.newInstance(expectedType.getComponentType(), 1);
Array.set(array, 0, value);
value = array;
}
return (T) value;
}
public Class<? extends Annotation> annotationType() {
return this.annotationType;
}
}
3.5.2 注解类型工具类
AnnotatedElementUtils.java
package com.lino.springframework.core.annotation;
import cn.hutool.core.collection.CollUtil;
import com.lino.springframework.core.BridgeMethodResolver;
import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.*;
/**
* @description: 注解类型工具类
*/
public class AnnotatedElementUtils {
private static final Annotation[] EMPTY_ANNOTATION_ARRAY = new Annotation[0];
public static AnnotationAttributes findMergedAnnotationAttributes(AnnotatedElement element,
Class<? extends Annotation> annotationType, boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
return searchWithFindSemantics(element, annotationType, null, new MergedAnnotationAttributesProcessor(classValuesAsString, nestedAnnotationsAsMap));
}
private static <T> T searchWithFindSemantics(AnnotatedElement element,
Class<? extends Annotation> annotationType,
String annotationName, Processor<T> processor) {
return searchWithFindSemantics(element, (null != annotationType ? Collections.singleton(annotationType) : Collections.emptySet()),
annotationName, null, processor);
}
private static <T> T searchWithFindSemantics(AnnotatedElement element,
Set<Class<? extends Annotation>> annotationType,
String annotationName,
Class<? extends Annotation> containerType,
Processor<T> processor) {
if (containerType != null && !processor.aggregates()) {
throw new IllegalArgumentException("Search for repeatable annotations must supply an aggregating Processor");
}
return searchWithFindSemantics(element, annotationType, annotationName, containerType, processor, new HashSet<>(), 0);
}
private static <T> T searchWithFindSemantics(AnnotatedElement element,
Set<Class<? extends Annotation>> annotationTypes, String annotationName,
Class<? extends Annotation> containerType, Processor<T> processor,
Set<AnnotatedElement> visited, int metaDepth) {
if (visited.add(element)) {
try {
// Locally declared annotations (ignoring @Inherited)
Annotation[] annotations = AnnotationUtils.getDeclaredAnnotations(element);
if (annotations.length > 0) {
List<T> aggregatedResults = (processor.aggregates() ? new ArrayList<>() : null);
// Search in local annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (!AnnotationUtils.isInJavaLangAnnotationPackage(currentAnnotationType)) {
if (annotationTypes.contains(currentAnnotationType) ||
currentAnnotationType.getName().equals(annotationName) ||
processor.alwaysProcesses()) {
T result = processor.process(element, annotation, metaDepth);
if (result != null) {
if (aggregatedResults != null && metaDepth == 0) {
aggregatedResults.add(result);
} else {
return result;
}
}
}
// Repeatable annotations in container?
else if (currentAnnotationType == containerType) {
for (Annotation contained : getRawAnnotationsFromContainer(element, annotation)) {
T result = processor.process(element, contained, metaDepth);
if (aggregatedResults != null && result != null) {
// No need to post-process since repeatable annotations within a
// container cannot be composed annotations.
aggregatedResults.add(result);
}
}
}
}
}
// Recursively search in meta-annotations
for (Annotation annotation : annotations) {
Class<? extends Annotation> currentAnnotationType = annotation.annotationType();
if (!AnnotationUtils.hasPlainJavaAnnotationsOnly(currentAnnotationType)) {
T result = searchWithFindSemantics(currentAnnotationType, annotationTypes, annotationName,
containerType, processor, visited, metaDepth + 1);
if (result != null) {
processor.postProcess(currentAnnotationType, annotation, result);
if (aggregatedResults != null && metaDepth == 0) {
aggregatedResults.add(result);
} else {
return result;
}
}
}
}
if (!CollUtil.isEmpty(aggregatedResults)) {
// Prepend to support top-down ordering within class hierarchies
processor.getAggregatedResults().addAll(0, aggregatedResults);
}
}
if (element instanceof Method) {
Method method = (Method) element;
T result;
// Search on possibly bridged method
Method resolvedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (resolvedMethod != method) {
result = searchWithFindSemantics(resolvedMethod, annotationTypes, annotationName,
containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
}
// Search on methods in interfaces declared locally
Class<?>[] ifcs = method.getDeclaringClass().getInterfaces();
if (ifcs.length > 0) {
result = searchOnInterfaces(method, annotationTypes, annotationName,
containerType, processor, visited, metaDepth, ifcs);
if (result != null) {
return result;
}
}
// Search on methods in class hierarchy and interface hierarchy
Class<?> clazz = method.getDeclaringClass();
while (true) {
clazz = clazz.getSuperclass();
if (clazz == null || clazz == Object.class) {
break;
}
Set<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(clazz);
if (!annotatedMethods.isEmpty()) {
for (Method annotatedMethod : annotatedMethods) {
if (AnnotationUtils.isOverride(method, annotatedMethod)) {
Method resolvedSuperMethod = BridgeMethodResolver.findBridgedMethod(annotatedMethod);
result = searchWithFindSemantics(resolvedSuperMethod, annotationTypes,
annotationName, containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
}
}
}
// Search on interfaces declared on superclass
result = searchOnInterfaces(method, annotationTypes, annotationName,
containerType, processor, visited, metaDepth, clazz.getInterfaces());
if (result != null) {
return result;
}
}
} else if (element instanceof Class) {
Class<?> clazz = (Class<?>) element;
if (!Annotation.class.isAssignableFrom(clazz)) {
// Search on interfaces
for (Class<?> ifc : clazz.getInterfaces()) {
T result = searchWithFindSemantics(ifc, annotationTypes, annotationName,
containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
}
// Search on superclass
Class<?> superclass = clazz.getSuperclass();
if (superclass != null && superclass != Object.class) {
T result = searchWithFindSemantics(superclass, annotationTypes, annotationName,
containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
}
}
}
} catch (Throwable ex) {
AnnotationUtils.handleIntrospectionFailure(element, ex);
}
}
return null;
}
private static <T> T searchOnInterfaces(Method method, Set<Class<? extends Annotation>> annotationTypes,
String annotationName, Class<? extends Annotation> containerType,
Processor<T> processor, Set<AnnotatedElement> visited, int metaDepth, Class<?>[] ifcs) {
for (Class<?> ifc : ifcs) {
Set<Method> annotatedMethods = AnnotationUtils.getAnnotatedMethodsInBaseType(ifc);
if (!annotatedMethods.isEmpty()) {
for (Method annotatedMethod : annotatedMethods) {
if (AnnotationUtils.isOverride(method, annotatedMethod)) {
T result = searchWithFindSemantics(annotatedMethod, annotationTypes, annotationName,
containerType, processor, visited, metaDepth);
if (result != null) {
return result;
}
}
}
}
}
return null;
}
private static <A extends Annotation> A[] getRawAnnotationsFromContainer(AnnotatedElement element, Annotation container) {
try {
A[] value = (A[]) AnnotationUtils.getValue(container);
if (value != null) {
return value;
}
} catch (Throwable ex) {
AnnotationUtils.handleIntrospectionFailure(element, ex);
}
// Unable to read value from repeating annotation container -> ignore it.
return (A[]) EMPTY_ANNOTATION_ARRAY;
}
public static AnnotatedElement forAnnotations(final Annotation... annotations) {
return new AnnotatedElement() {
@Override
public <T extends Annotation> T getAnnotation(Class<T> annotationClass) {
for (Annotation annotation : annotations) {
if (annotation.annotationType() == annotationClass) {
return (T) annotation;
}
}
return null;
}
@Override
public Annotation[] getAnnotations() {
return annotations;
}
@Override
public Annotation[] getDeclaredAnnotations() {
return annotations;
}
};
}
private static class MergedAnnotationAttributesProcessor implements Processor<AnnotationAttributes> {
private final boolean classValuesAsString;
private final boolean nestedAnnotationsAsMap;
private final boolean aggregates;
private final List<AnnotationAttributes> aggregatedResults;
MergedAnnotationAttributesProcessor() {
this(false, false, false);
}
MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap) {
this(classValuesAsString, nestedAnnotationsAsMap, false);
}
MergedAnnotationAttributesProcessor(boolean classValuesAsString, boolean nestedAnnotationsAsMap,
boolean aggregates) {
this.classValuesAsString = classValuesAsString;
this.nestedAnnotationsAsMap = nestedAnnotationsAsMap;
this.aggregates = aggregates;
this.aggregatedResults = (aggregates ? new ArrayList<>() : Collections.emptyList());
}
@Override
public boolean alwaysProcesses() {
return false;
}
@Override
public boolean aggregates() {
return this.aggregates;
}
@Override
public List<AnnotationAttributes> getAggregatedResults() {
return this.aggregatedResults;
}
@Override
public AnnotationAttributes process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth) {
return AnnotationUtils.retrieveAnnotationAttributes(annotatedElement, annotation,
this.classValuesAsString, this.nestedAnnotationsAsMap);
}
@Override
public void postProcess(AnnotatedElement element, Annotation annotation, AnnotationAttributes attributes) {
annotation = AnnotationUtils.synthesizeAnnotation(annotation, element);
Class<? extends Annotation> targetAnnotationType = attributes.annotationType();
// Track which attribute values have already been replaced so that we can short
// circuit the search algorithms.
Set<String> valuesAlreadyReplaced = new HashSet<>();
for (Method attributeMethod : AnnotationUtils.getAttributeMethods(annotation.annotationType())) {
String attributeName = attributeMethod.getName();
String attributeOverrideName = AnnotationUtils.getAttributeOverrideName(attributeMethod, targetAnnotationType);
// Explicit annotation attribute override declared via @AliasFor
if (attributeOverrideName != null) {
if (valuesAlreadyReplaced.contains(attributeOverrideName)) {
continue;
}
List<String> targetAttributeNames = new ArrayList<>();
targetAttributeNames.add(attributeOverrideName);
valuesAlreadyReplaced.add(attributeOverrideName);
// Ensure all aliased attributes in the target annotation are overridden. (SPR-14069)
List<String> aliases = AnnotationUtils.getAttributeAliasMap(targetAnnotationType).get(attributeOverrideName);
if (aliases != null) {
for (String alias : aliases) {
if (!valuesAlreadyReplaced.contains(alias)) {
targetAttributeNames.add(alias);
valuesAlreadyReplaced.add(alias);
}
}
}
overrideAttributes(element, annotation, attributes, attributeName, targetAttributeNames);
}
// Implicit annotation attribute override based on convention
else if (!AnnotationUtils.VALUE.equals(attributeName) && attributes.containsKey(attributeName)) {
overrideAttribute(element, annotation, attributes, attributeName, attributeName);
}
}
}
private void overrideAttributes(AnnotatedElement element, Annotation annotation,
AnnotationAttributes attributes, String sourceAttributeName, List<String> targetAttributeNames) {
Object adaptedValue = getAdaptedValue(element, annotation, sourceAttributeName);
for (String targetAttributeName : targetAttributeNames) {
attributes.put(targetAttributeName, adaptedValue);
}
}
private void overrideAttribute(AnnotatedElement element, Annotation annotation,
AnnotationAttributes attributes, String sourceAttributeName, String targetAttributeName) {
attributes.put(targetAttributeName, getAdaptedValue(element, annotation, sourceAttributeName));
}
private Object getAdaptedValue(
AnnotatedElement element, Annotation annotation, String sourceAttributeName) {
Object value = AnnotationUtils.getValue(annotation, sourceAttributeName);
return AnnotationUtils.adaptValue(element, value, this.classValuesAsString, this.nestedAnnotationsAsMap);
}
}
private interface Processor<T> {
T process(AnnotatedElement annotatedElement, Annotation annotation, int metaDepth);
void postProcess(AnnotatedElement annotatedElement, Annotation annotation, T result);
boolean alwaysProcesses();
boolean aggregates();
List<T> getAggregatedResults();
}
}
AnnotatedElementUtils
工具包源码包在springframework.core
包下面,具体代码请查看源码。
3.6 解析事务注解
3.6.1 用于解析已知事务注释类型的策略接口
TransactionAnnotationParser.java
package com.lino.springframework.tx.transaction.annotation;
import com.lino.springframework.tx.transaction.interceptor.TransactionAttribute;
import java.lang.reflect.AnnotatedElement;
/**
* @description: 用于解析已知事务注释类型的策略接口
*/
public interface TransactionAnnotationParser {
/**
* 基于该解析器理解的注释类型,解析给定方法或类的事务属性
*
* @param element 注释类型
* @return 事务属性
*/
TransactionAttribute parseTransactionAnnotation(AnnotatedElement element);
}
3.6.2 Spring的事务注释类型解析实现类
SpringTransactionAnnotationParser.java
package com.lino.springframework.tx.transaction.annotation;
import com.lino.springframework.core.annotation.AnnotatedElementUtils;
import com.lino.springframework.core.annotation.AnnotationAttributes;
import com.lino.springframework.tx.transaction.interceptor.RollbackRuleAttribute;
import com.lino.springframework.tx.transaction.interceptor.RuleBasedTransactionAttribute;
import com.lino.springframework.tx.transaction.interceptor.TransactionAttribute;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.util.ArrayList;
import java.util.List;
/**
* @description: Spring的事务注释类型解析实现类
*/
public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
@Override
public TransactionAttribute parseTransactionAnnotation(AnnotatedElement element) {
AnnotationAttributes attributes = AnnotatedElementUtils.findMergedAnnotationAttributes(
element, Transactional.class, false, false);
if (null != attributes) {
return parseTransactionAnnotation(attributes);
} else {
return null;
}
}
protected TransactionAttribute parseTransactionAnnotation(AnnotationAttributes attributes) {
RuleBasedTransactionAttribute rbta = new RuleBasedTransactionAttribute();
List<RollbackRuleAttribute> rollbackRules = new ArrayList<>();
for (Class<?> rbRule : attributes.getClassArray("rollbackFor")) {
rollbackRules.add(new RollbackRuleAttribute(rbRule));
}
rbta.setRollbackRules(rollbackRules);
return rbta;
}
}
SpringTransactionAnnotationParser#parseTransactionAnnotation
方法用于提取配置注解@Transactional
的方法。- 这里的提取操作使用了
AnnotatedElementUtils.findMergedAnnotationAttributes
方法,此方法来自Spring Core
核心包提供的功能。
- 这里的提取操作使用了
- 接下来调用内部方法
parseTransactionAnnotation(AnnotationAttributes attributes)
,用于解析@Transactional
,并设置相关的事务属性。
3.7 事务状态
3.7.1 事务执行接口
TransactionExecution.java
package com.lino.springframework.tx.transaction;
/**
* @description: 事务执行接口
*/
public interface TransactionExecution {
/**
* Return whether the present transaction is new; otherwise participating
* in an existing transaction, or potentially not running in an actual
* transaction in the first place.
*/
boolean isNewTransaction();
/**
* Set the transaction rollback-only. This instructs the transaction manager
* that the only possible outcome of the transaction may be a rollback, as
* alternative to throwing an exception which would in turn trigger a rollback.
*/
void setRollbackOnly();
/**
* Return whether the transaction has been marked as rollback-only
* (either by the application or by the transaction infrastructure).
*/
boolean isRollbackOnly();
/**
* Return whether this transaction is completed, that is,
* whether it has already been committed or rolled back.
*/
boolean isCompleted();
}
3.7.2 存档管理接口
SavepointManager.java
package com.lino.springframework.tx.transaction;
/**
* @description: 存档管理接口
*/
public interface SavepointManager {
/**
* 保存存档
*/
Object createSavepoint() throws TransactionException;
/**
* 回滚存档
*/
void rollbackToSavepoint(Object savepoint) throws TransactionException;
/**
* 释放存档
*/
void releaseSavepoint(Object savepoint) throws TransactionException;
}
3.7.3 事务状态接口
TransactionStatus.java
package com.lino.springframework.tx.transaction;
import java.io.Flushable;
import java.io.IOException;
/**
* @description: 事务状态接口
*/
public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {
/**
* 判断是否有保存点
*
* @return 判断是否有保存点
*/
boolean hasSavepoint();
/**
* 刷新
*
* @throws IOException IO异常
*/
@Override
void flush() throws IOException;
}
3.7.4 事务状态抽象类
AbstractTransactionStatus.java
package com.lino.springframework.tx.transaction.support;
import com.lino.springframework.tx.transaction.NestedTransactionNotSupportedException;
import com.lino.springframework.tx.transaction.SavepointManager;
import com.lino.springframework.tx.transaction.TransactionException;
import com.lino.springframework.tx.transaction.TransactionStatus;
import java.io.IOException;
/**
* @description: 事务状态抽象类
*/
public abstract class AbstractTransactionStatus implements TransactionStatus {
private boolean rollbackOnly = false;
private boolean completed = false;
private Object savepoint;
@Override
public boolean isNewTransaction() {
return false;
}
@Override
public void setRollbackOnly() {
this.rollbackOnly = true;
}
@Override
public boolean isRollbackOnly() {
return isLocalRollbackOnly() || isGlobalRollbackOnly();
}
public boolean isLocalRollbackOnly() {
return this.rollbackOnly;
}
public boolean isGlobalRollbackOnly() {
return false;
}
@Override
public void flush() throws IOException {
}
public void setCompleted(boolean completed) {
this.completed = completed;
}
@Override
public boolean isCompleted() {
return completed;
}
public Object getSavepoint() {
return savepoint;
}
public void setSavepoint(Object savepoint) {
this.savepoint = savepoint;
}
@Override
public boolean hasSavepoint() {
return this.savepoint != null;
}
@Override
public Object createSavepoint() throws TransactionException {
return getSavepointManager().createSavepoint();
}
@Override
public void rollbackToSavepoint(Object savepoint) throws TransactionException {
getSavepointManager().rollbackToSavepoint(savepoint);
}
@Override
public void releaseSavepoint(Object savepoint) throws TransactionException {
getSavepointManager().releaseSavepoint(savepoint);
}
protected SavepointManager getSavepointManager() {
throw new NestedTransactionNotSupportedException("This transaction does not support savepoints");
}
}
3.7.5 默认事务状态
DefaultTransactionStatus.java
package com.lino.springframework.tx.transaction.support;
/**
* @description: 默认事务状态
*/
public class DefaultTransactionStatus extends AbstractTransactionStatus {
private final Object transaction;
private final boolean newTransaction;
public DefaultTransactionStatus(Object transaction, boolean newTransaction) {
this.transaction = transaction;
this.newTransaction = newTransaction;
}
public Object getTransaction() {
return transaction;
}
public boolean hasTransaction() {
return this.transaction != null;
}
@Override
public boolean isNewTransaction() {
return hasTransaction() && this.newTransaction;
}
}
3.8 事务属性来源
3.8.1 事务属性来源接口
TransactionAttributeSource.java
package com.lino.springframework.tx.transaction.interceptor;
import java.lang.reflect.Method;
/**
* @description: 事务属性来源接口
*/
public interface TransactionAttributeSource {
/**
* 获取事务属性
*
* @param method 方法
* @param targetClass 类类型
* @return 事务属性
*/
TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass);
}
3.8.2 事务属性来源抽象类
AbstractFallbackTransactionAttributeSource.java
package com.lino.springframework.tx.transaction.interceptor;
import com.lino.springframework.core.MethodClassKey;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @description: 事务属性来源接口抽象类
*/
public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {
private final Map<Object, TransactionAttribute> attributeCache = new ConcurrentHashMap<>(1024);
private static final TransactionAttribute NULL_TRANSACTION_ATTRIBUTE = new DefaultTransactionAttribute() {
@Override
public String toString() {
return "null";
}
};
@Override
public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
if (method.getDeclaringClass() == Object.class) {
return null;
}
Object cacheKey = getCacheKey(method, targetClass);
TransactionAttribute cached = this.attributeCache.get(cacheKey);
if (null != cached) {
if (cached == NULL_TRANSACTION_ATTRIBUTE) {
return null;
} else {
return cached;
}
} else {
TransactionAttribute txAttr = computeTransactionAttribute(method, targetClass);
if (null == txAttr) {
this.attributeCache.put(cacheKey, NULL_TRANSACTION_ATTRIBUTE);
} else {
this.attributeCache.put(cacheKey, txAttr);
}
return txAttr;
}
}
protected Object getCacheKey(Method method, Class<?> targetClass) {
return new MethodClassKey(method, targetClass);
}
protected TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
if (!Modifier.isPublic(method.getModifiers())) {
return null;
}
TransactionAttribute txAttr = findTransactionAttribute(method);
if (null != txAttr) {
return txAttr;
}
return findTransactionAttribute(method.getDeclaringClass());
}
/**
* 在方法上查找事务的相关属性
*
* @param method 方法
* @return 事务属性
*/
protected abstract TransactionAttribute findTransactionAttribute(Method method);
/**
* 在类上查找事务的相关属性
*
* @param clazz 类
* @return 事务属性
*/
protected abstract TransactionAttribute findTransactionAttribute(Class<?> clazz);
}
3.8.3 注解事务属性来源实现类
AnnotationTransactionAttributeSource.java
package com.lino.springframework.tx.transaction.annotation;
import com.lino.springframework.tx.transaction.interceptor.AbstractFallbackTransactionAttributeSource;
import com.lino.springframework.tx.transaction.interceptor.TransactionAttribute;
import java.io.Serializable;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Set;
/**
* @description: 注解事务属性来源接口抽象类
*/
public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable {
private final boolean publicMethodsOnly;
private final Set<TransactionAnnotationParser> annotationParsers;
public AnnotationTransactionAttributeSource() {
this(true);
}
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
this.publicMethodsOnly = publicMethodsOnly;
this.annotationParsers = Collections.singleton(new SpringTransactionAnnotationParser());
}
@Override
protected TransactionAttribute findTransactionAttribute(Method method) {
return determineTransactionAttribute(method);
}
@Override
public TransactionAttribute findTransactionAttribute(Class<?> clazz) {
return determineTransactionAttribute(clazz);
}
protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
for (TransactionAnnotationParser annotationParser : annotationParsers) {
TransactionAttribute attr = annotationParser.parseTransactionAnnotation(element);
if (attr != null) {
return attr;
}
}
return null;
}
}
3.9 事务拦截处理
3.9.1 事务管理器平台接口
PlatformTransactionManager.java
package com.lino.springframework.tx.transaction;
/**
* @description: 事务管理器平台
*/
public interface PlatformTransactionManager {
/**
* 获取事务
*
* @param definition 事务定义
* @return 事务状态
* @throws TransactionException 事务异常
*/
TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException;
/**
* 提交事务
*
* @param status 事务状态
* @throws TransactionException 事务异常
*/
void commit(TransactionStatus status) throws TransactionException;
/**
* 回滚事务
*
* @param status 事务状态
* @throws TransactionException 事务异常
*/
void rollback(TransactionStatus status) throws TransactionException;
}
3.9.2 事务管理器平台抽象类
AbstractPlatformTransactionManager.java
package com.lino.springframework.tx.transaction.support;
import com.lino.springframework.tx.transaction.PlatformTransactionManager;
import com.lino.springframework.tx.transaction.TransactionDefinition;
import com.lino.springframework.tx.transaction.TransactionException;
import com.lino.springframework.tx.transaction.TransactionStatus;
import java.io.Serializable;
/**
* @description: 抽象事务管理器平台
*/
public abstract class AbstractPlatformTransactionManager implements PlatformTransactionManager, Serializable {
@Override
public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
Object transaction = doGetTransaction();
if (null == definition) {
definition = new DefaultTransactionDefinition();
}
if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {
throw new TransactionException("Invalid transaction timeout " + definition.getTimeout());
}
// 暂定事务传播为默认的行为
DefaultTransactionStatus status = newTransactionStatus(definition, transaction, true);
// 开始事务
doBegin(transaction, definition);
return status;
}
private DefaultTransactionStatus newTransactionStatus(TransactionDefinition definition, Object transaction, boolean newTransaction) {
return new DefaultTransactionStatus(transaction, newTransaction);
}
@Override
public void commit(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalArgumentException(
"Transaction is already completed - do not call or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
processCommit(defStatus);
}
private void processCommit(DefaultTransactionStatus status) throws TransactionException {
doCommit(status);
}
@Override
public void rollback(TransactionStatus status) throws TransactionException {
if (status.isCompleted()) {
throw new IllegalArgumentException(
"Transaction is already completed - do not call commit or rollback more than once per transaction");
}
DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status;
processRollback(defStatus, false);
}
private void processRollback(DefaultTransactionStatus status, boolean unexpected) {
doRollback(status);
}
/**
* 获取事务
*
* @return 事务
* @throws TransactionException 事务异常
*/
protected abstract Object doGetTransaction() throws TransactionException;
/**
* 开始事务
*
* @param transaction 事务对象
* @param definition 事务定义
* @throws TransactionException 事务异常
*/
protected abstract void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException;
/**
* 提交事务
*
* @param status 事务状态
* @throws TransactionException 事务异常
*/
protected abstract void doCommit(DefaultTransactionStatus status) throws TransactionException;
/**
* 回滚事务
*
* @param status 事务状态
* @throws TransactionException 事务异常
*/
protected abstract void doRollback(DefaultTransactionStatus status) throws TransactionException;
}
- 在事务的拦截处理中看到,我们使用
TransactionAspectSupport#invokeWithinTransaction
对事务进行了获取、提交和回滚操作。 - 这些功能都是来自
AbstractPlatformTransactionManager
抽象事务管理器平台,在这个类中可以设置事务的传播行为、事务的操作。- 事务的获取、提交、回滚操作也来自数据库连接池提供的功能。
3.9.3 事务处理
TransactionAspectSupport.java
package com.lino.springframework.tx.transaction.interceptor;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.threadlocal.NamedThreadLocal;
import com.lino.springframework.beans.BeansException;
import com.lino.springframework.beans.factory.BeanFactory;
import com.lino.springframework.beans.factory.BeanFactoryAware;
import com.lino.springframework.beans.factory.InitializingBean;
import com.lino.springframework.tx.transaction.PlatformTransactionManager;
import com.lino.springframework.tx.transaction.TransactionStatus;
import com.lino.springframework.util.ClassUtils;
import java.lang.reflect.Method;
/**
* @description: 事务切面
*/
public abstract class TransactionAspectSupport implements BeanFactoryAware, InitializingBean {
private static final ThreadLocal<TransactionInfo> transactionInfoHolder =
new NamedThreadLocal<>("Current aspect-driven transaction");
private BeanFactory beanFactory;
private TransactionAttributeSource transactionAttributeSource;
private PlatformTransactionManager transactionManager;
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, InvocationCallback invocation) throws Throwable {
TransactionAttributeSource tas = getTransactionAttributeSource();
// 查找事务注解 Transactional
TransactionAttribute txAttr = (tas != null ? tas.getTransactionAttribute(method, targetClass) : null);
PlatformTransactionManager manager = determineTransactionManager();
String joinPointIdentification = methodIdentification(method, targetClass);
TransactionInfo txInfo = createTransactionIfNecessary(manager, txAttr, joinPointIdentification);
Object retVal = null;
try {
retVal = invocation.proceedWithInvocation();
} catch (Throwable e) {
completeTransactionAfterThrowing(txInfo, e);
throw e;
} finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo);
return retVal;
}
public TransactionAttributeSource getTransactionAttributeSource() {
return transactionAttributeSource;
}
public void setTransactionAttributeSource(TransactionAttributeSource transactionAttributeSource) {
this.transactionAttributeSource = transactionAttributeSource;
}
public PlatformTransactionManager getTransactionManager() {
return transactionManager;
}
public void setTransactionManager(PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
/**
* 当前使用DataSourceTransactionManager
*/
protected PlatformTransactionManager determineTransactionManager() {
return getTransactionManager();
}
/**
* 获取目标方法的唯一标识
*/
private String methodIdentification(Method method, Class<?> targetClass) {
return ClassUtils.getQualifiedMethodName(method, targetClass);
}
protected TransactionInfo createTransactionIfNecessary(PlatformTransactionManager tm, TransactionAttribute txAttr, String joinPointIdentification) {
if (txAttr != null && txAttr.getName() == null) {
txAttr = new DelegatingTransactionAttribute(txAttr) {
@Override
public String getName() {
return joinPointIdentification;
}
};
}
TransactionStatus status = null;
if (txAttr != null) {
if (tm != null) {
status = tm.getTransaction(txAttr);
}
}
return prepareTransactionInfo(tm, txAttr, joinPointIdentification, status);
}
protected TransactionInfo prepareTransactionInfo(PlatformTransactionManager tm, TransactionAttribute txAttr, String joinPointIdentification, TransactionStatus status) {
TransactionInfo txInfo = new TransactionInfo(tm, txAttr, joinPointIdentification);
if (txAttr != null) {
txInfo.newTransactionStatus(status);
}
txInfo.bindToThread();
return txInfo;
}
protected void completeTransactionAfterThrowing(TransactionInfo txInfo, Throwable ex) {
if (null != txInfo && null != txInfo.getTransactionStatus()) {
if (txInfo.transactionAttribute != null && txInfo.transactionAttribute.rollbackOn(ex)) {
txInfo.getTransactionManager().rollback(txInfo.getTransactionStatus());
} else {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
}
protected void cleanupTransactionInfo(TransactionInfo txInfo) {
if (null != txInfo) {
txInfo.restoreThreadLocalStatus();
}
}
protected void commitTransactionAfterReturning(TransactionInfo txInfo) {
if (null != txInfo && null != txInfo.getTransactionStatus()) {
txInfo.getTransactionManager().commit(txInfo.getTransactionStatus());
}
}
protected interface InvocationCallback {
Object proceedWithInvocation() throws Throwable;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void afterPropertiesSet() throws Exception {
}
protected final class TransactionInfo {
private final PlatformTransactionManager transactionManager;
private final TransactionAttribute transactionAttribute;
private final String joinPointIdentification;
private TransactionStatus transactionStatus;
private TransactionInfo oldTransactionInfo;
public TransactionInfo(PlatformTransactionManager transactionManager,
TransactionAttribute transactionAttribute, String joinpointIdentification) {
this.transactionManager = transactionManager;
this.transactionAttribute = transactionAttribute;
this.joinPointIdentification = joinpointIdentification;
}
public PlatformTransactionManager getTransactionManager() {
Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");
return transactionManager;
}
public String getJoinPointIdentification() {
return joinPointIdentification;
}
public TransactionAttribute getTransactionAttribute() {
return transactionAttribute;
}
public void newTransactionStatus(TransactionStatus status) {
this.transactionStatus = status;
}
public TransactionStatus getTransactionStatus() {
return transactionStatus;
}
public boolean hasTransaction() {
return null != this.transactionStatus;
}
private void bindToThread() {
this.oldTransactionInfo = transactionInfoHolder.get();
transactionInfoHolder.set(this);
}
private void restoreThreadLocalStatus() {
transactionInfoHolder.set(this.oldTransactionInfo);
}
}
}
- 方法拦截器中的
invokeWithinTransaction
方法,就是TransactionAspectSupport#invokeWithinTransaction
提供的具体拦截操作。invokeWithinTransaction
方法中的操作包括:提取注解方法、开启事务的提交和回滚,以及把方法包装到invocation.proceedWithInvocation
中并提交。
- 到这里,已经完成了最基本的事务注解查找到提交的链路。
3.9.4 事务拦截器
TransactionInterceptor.java
package com.lino.springframework.tx.transaction.interceptor;
import com.lino.springframework.tx.transaction.PlatformTransactionManager;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import java.io.Serializable;
/**
* @description: 事务拦截器
*/
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource transactionAttributeSource) {
setTransactionManager(ptm);
setTransactionAttributeSource(transactionAttributeSource);
}
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
return invokeWithinTransaction(invocation.getMethod(), invocation.getThis().getClass(), invocation::proceed);
}
}
- 因为需要使用一个被 AOP 拦截操作处理事务的类,所以需要实现一个事务拦截功能。
3.9.5 事务同步管理器
TransactionSynchronizationManager.java
package com.lino.springframework.tx.transaction.support;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.thread.threadlocal.NamedThreadLocal;
import java.util.HashMap;
import java.util.Map;
/**
* @description: 事务同步管理器
*/
public abstract class TransactionSynchronizationManager {
/**
* 当前线程的连接存储
*/
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");
/**
* 事务的名称
*/
private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal<>("Current transaction name");
/**
* 事务是否是只读
*/
private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal<>("Current transaction read-only status");
/**
* 事务的隔离级别
*/
private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal<>("Current transaction isolation level");
/**
* 事务是否开启
*/
private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal<>("Actual transaction active");
public static Object getResource(Object key) {
return doGetResource(key);
}
private static Object doGetResource(Object actualKey) {
Map<Object, Object> map = resources.get();
if (null == map) {
return null;
}
return map.get(actualKey);
}
public static void bindResource(Object key, Object value) throws IllegalStateException {
Assert.notNull(value, "Value must not be null");
Map<Object, Object> map = resources.get();
if (null == map) {
map = new HashMap<>(16);
resources.set(map);
}
map.put(key, value);
}
}
TransactionSynchronizationManager
类主要使用ThreadLocal
来记录本地线程的特性,保存一次线程下的 DB 链接,便于用户操作数据,以及保证 Spring 管理事务时获取的是同一个链接。否则在不同链接下,就不能再提交事务了。
3.10 获取数据库连接
3.10.1 数据库连接处理器接口
ConnectionHandle.java
package com.lino.springframework.jdbc.datasource;
import java.sql.Connection;
/**
* @description: 数据库连接处理器接口
*/
public interface ConnectionHandle {
/**
* 获取连接
*
* @return 连接
*/
Connection getConnection();
/**
* 释放连接
*
* @param con 连接
*/
default void releaseConnection(Connection con) {}
}
3.10.2 简单数据库连接处理器
SimpleConnectionHandle.java
package com.lino.springframework.jdbc.datasource;
import cn.hutool.core.lang.Assert;
import java.sql.Connection;
/**
* @description: 简单数据库连接处理器
*/
public class SimpleConnectionHandle implements ConnectionHandle {
private final Connection connection;
public SimpleConnectionHandle(Connection connection) {
Assert.notNull(connection, "Connection must not be null");
this.connection = connection;
}
@Override
public Connection getConnection() {
return this.connection;
}
}
3.10.3 数据库连接处理器
ConnectionHolder.java
package com.lino.springframework.jdbc.datasource;
import cn.hutool.core.lang.Assert;
import java.sql.Connection;
/**
* @description: 数据库连接处理器
*/
public class ConnectionHolder {
private ConnectionHandle connectionHandle;
private Connection currentConnection;
public ConnectionHolder(ConnectionHandle connectionHandle) {
this.connectionHandle = connectionHandle;
}
public ConnectionHolder(Connection connection) {
this.connectionHandle = new SimpleConnectionHandle(connection);
}
public ConnectionHandle getConnectionHandle() {
return connectionHandle;
}
protected boolean hasConnection() {
return this.connectionHandle != null;
}
protected void setConnection(Connection connection) {
if (null != this.currentConnection) {
if (null != this.connectionHandle) {
this.connectionHandle.releaseConnection(this.currentConnection);
}
this.currentConnection = null;
}
if (null != connection) {
this.connectionHandle = new SimpleConnectionHandle(connection);
} else {
this.connectionHandle = null;
}
}
protected Connection getConnection() {
Assert.notNull(this.connectionHandle, "Active connection is required.");
if (null == this.currentConnection) {
this.currentConnection = this.connectionHandle.getConnection();
}
return this.currentConnection;
}
}
3.10.4 JDBC事务支撑对象
JdbcTransactionObjectSupport.java
package com.lino.springframework.jdbc.datasource;
/**
* @description: JDBC事务对象
*/
public abstract class JdbcTransactionObjectSupport {
private ConnectionHolder connectionHolder;
public void setConnectionHolder(ConnectionHolder connectionHolder) {
this.connectionHolder = connectionHolder;
}
public ConnectionHolder getConnectionHolder() {
return connectionHolder;
}
public boolean hasConnectionHolder() {
return null != this.connectionHolder;
}
}
3.10.5 数据源事务管理器
DataSourceTransactionManager.java
package com.lino.springframework.jdbc.datasource;
import cn.hutool.core.lang.Assert;
import com.lino.springframework.beans.factory.InitializingBean;
import com.lino.springframework.tx.transaction.CannotCreateTransactionException;
import com.lino.springframework.tx.transaction.TransactionDefinition;
import com.lino.springframework.tx.transaction.TransactionException;
import com.lino.springframework.tx.transaction.support.AbstractPlatformTransactionManager;
import com.lino.springframework.tx.transaction.support.DefaultTransactionStatus;
import com.lino.springframework.tx.transaction.support.TransactionSynchronizationManager;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
/**
* @description: 数据源事务管理器
*/
public class DataSourceTransactionManager extends AbstractPlatformTransactionManager implements InitializingBean {
private DataSource dataSource;
public DataSourceTransactionManager() {
}
public DataSourceTransactionManager(DataSource dataSource) {
setDataSource(dataSource);
afterPropertiesSet();
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public DataSource getDataSource() {
return dataSource;
}
protected DataSource obtainDataSource() {
DataSource dataSource = getDataSource();
Assert.notNull(dataSource, "No DataSource set");
return dataSource;
}
@Override
protected Object doGetTransaction() throws TransactionException {
DataSourceTransactionObject txObject = new DataSourceTransactionObject();
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(obtainDataSource());
txObject.setConnectionHolder(conHolder, false);
return txObject;
}
@Override
protected void doCommit(DefaultTransactionStatus status) throws TransactionException {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
try {
con.commit();
} catch (SQLException e) {
throw new TransactionException("Could not commit JDBC transaction", e);
}
}
@Override
protected void doRollback(DefaultTransactionStatus status) throws TransactionException {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();
Connection con = txObject.getConnectionHolder().getConnection();
try {
con.rollback();
} catch (SQLException e) {
throw new TransactionException("Could not roll back JDBC transaction", e);
}
}
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) throws TransactionException {
DataSourceTransactionObject txObject = (DataSourceTransactionObject) transaction;
Connection con = null;
try {
Connection newCon = obtainDataSource().getConnection();
txObject.setConnectionHolder(new ConnectionHolder(newCon), true);
con = txObject.getConnectionHolder().getConnection();
if (con.getAutoCommit()) {
con.setAutoCommit(false);
}
prepareTransactionalConnection(con, definition);
TransactionSynchronizationManager.bindResource(obtainDataSource(), txObject.getConnectionHolder());
} catch (SQLException e) {
try {
assert con != null;
con.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
txObject.setConnectionHolder(null, false);
throw new CannotCreateTransactionException("Could not open JDBC Connection for transaction", e);
}
}
@Override
public void afterPropertiesSet() {
if (null == getDataSource()) {
throw new IllegalArgumentException("Property 'datasource' is required");
}
}
protected void prepareTransactionalConnection(Connection con, TransactionDefinition definition) throws SQLException {
if (definition.isReadOnly()) {
try (Statement stmt = con.createStatement()) {
stmt.execute("set transaction read only");
}
}
}
public static class DataSourceTransactionObject extends JdbcTransactionObjectSupport {
private boolean newConnectionHolder;
private boolean mustRestoreAutoCommit;
public void setConnectionHolder(ConnectionHolder connectionHolder, boolean newConnectionHolder) {
super.setConnectionHolder(connectionHolder);
this.newConnectionHolder = newConnectionHolder;
}
}
}
3.10.6 数据源操作抽象类
DataSourceUtils.java
package com.lino.springframework.jdbc.datasource;
import com.lino.springframework.jdbc.CannotGetJdbcConnectionException;
import com.lino.springframework.tx.transaction.support.TransactionSynchronizationManager;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* @description: 数据源操作抽象类
*/
public abstract class DataSourceUtils {
/**
* 获取数据库连接
*
* @param dataSource 数据库对象
* @return 数据库连接
*/
public static Connection getConnection(DataSource dataSource) {
try {
return doGetConnection(dataSource);
} catch (SQLException e) {
throw new CannotGetJdbcConnectionException("Failed to obtain JDBC Connection", e);
}
}
/**
* Actually obtain a JDBC Connection from the given DataSource.
* Same as {@link #getConnection}, but throwing the original SQLException.
* <p>Is aware of a corresponding Connection bound to the current thread, for example
* when using {@link DataSourceTransactionManager}. Will bind a Connection to the thread
* if transaction synchronization is active (e.g. if in a JTA transaction).
*
* @param dataSource the DataSource to obtain Connections from
* @return a JDBC Connection from the given DataSource
* @throws SQLException if thrown by JDBC methods
* @see #doReleaseConnection
*/
public static Connection doGetConnection(DataSource dataSource) throws SQLException {
ConnectionHolder conHolder = (ConnectionHolder) TransactionSynchronizationManager.getResource(dataSource);
if (null != conHolder && conHolder.hasConnection()) {
return conHolder.getConnection();
}
return fetchConnection(dataSource);
}
private static Connection fetchConnection(DataSource dataSource) throws SQLException {
Connection conn = dataSource.getConnection();
if (null == conn) {
throw new IllegalArgumentException("DataSource return null from getConnection():" + dataSource);
}
return conn;
}
public static void releaseConnection(Connection con, DataSource dataSource) {
try {
doReleaseConnection(con, dataSource);
} catch (Exception ignore) {
}
}
public static void doReleaseConnection(Connection con, DataSource dataSource) throws SQLException {
if (con == null) {
return;
}
doCloseConnection(con, dataSource);
}
public static void doCloseConnection(Connection con, DataSource dataSource) throws SQLException {
con.close();
}
}
- 在
DataSourceUtils
数据源工具操作类中,最初只是用dataSource.getConnection
方法获取数据源链接。 - 为了扩展事务的功能,需要使用
TransactionSynchronizationManager.getResource(dataSource)
存储数据源链接,便于操作事务时获取同一个链接。
3.10.7 JDBC操作模板
JdbcTemplate.java
package com.lino.springframework.jdbc.core;
import cn.hutool.core.lang.Assert;
import com.lino.springframework.jdbc.UncategorizedSQLException;
import com.lino.springframework.jdbc.datasource.DataSourceUtils;
import com.lino.springframework.jdbc.support.JdbcAccessor;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;
import java.util.Map;
/**
* @description: JDBC 操作模板
*/
public class JdbcTemplate extends JdbcAccessor implements JdbcOperations {
/**
* 查询大小
*/
private int fetchSize = -1;
/**
* 最大行数
*/
private int maxRows = -1;
/**
* 查询时间
*/
private int queryTimeout = -1;
...
@Override
public <T> T execute(StatementCallback<T> action) {
Connection con = DataSourceUtils.getConnection(obtainDataSource());
try {
Statement stmt = con.createStatement();
applyStatementSettings(stmt);
return action.doInStatement(stmt);
} catch (SQLException ex) {
throw new UncategorizedSQLException("ConnectionCallback", getSql(action), ex);
}
}
private static String getSql(Object sqlProvider) {
if (sqlProvider instanceof SqlProvider) {
return ((SqlProvider) sqlProvider).getSql();
} else {
return null;
}
}
...
}
- 修改了
execute
方法中,添加了getSql
方法。
四、测试:事务处理
4.1 添加测试配置
4.1.1 事务测试类
JdbcService.java
package com.lino.springframework.test.bean;
import com.lino.springframework.jdbc.core.JdbcTemplate;
import com.lino.springframework.tx.transaction.annotation.Transactional;
/**
* @description: JDBC服务类
*/
public class JdbcService {
@Transactional(rollbackFor = Exception.class)
public void saveData(JdbcTemplate jdbcTemplate) {
jdbcTemplate.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
jdbcTemplate.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
}
public void saveDataNoTransaction(JdbcTemplate jdbcTemplate) {
jdbcTemplate.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
jdbcTemplate.execute("insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())");
}
}
- 模拟一个用户操作事务的 Bean 对象有两种方法,一种是使用
@Transactional
注解管理事务,另一种是不使用@Transactional
注解管理事务。 - 按照预期,使用事务注解提交两条一样的语句,数据库不会有任何记录,因为存在主键冲突。没有使用事务注解的语句会成功提交一条记录。
4.1.2 Spring属性配置文件
spring.xml
<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?useSSL=false&serverTimezone=Asia/Shanghai"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<bean id="jdbcTemplate" class="com.lino.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="jdbcService" class="com.lino.springframework.test.bean.JdbcService"/>
</beans>
- 在
spring.xml
配置文件中,先配置数据库的链接信息及库表。- 再将
dataSource
注入JdbcTemplate
中,由JdbcTemplate
完成数据库的操作。
- 再将
- 配置
jdbcService Bean
对象是一个用于操作验证数据库事务的方法。
4.1.3 初始化测试
ApiTest.java
package com.lino.springframework.test;
import com.alibaba.druid.pool.DruidDataSource;
import com.lino.springframework.aop.AdvisedSupport;
import com.lino.springframework.aop.TargetSource;
import com.lino.springframework.aop.aspectj.AspectJExpressionPointcut;
import com.lino.springframework.aop.framework.Cglib2AopProxy;
import com.lino.springframework.context.support.ClassPathXmlApplicationContext;
import com.lino.springframework.jdbc.core.JdbcTemplate;
import com.lino.springframework.jdbc.datasource.DataSourceTransactionManager;
import com.lino.springframework.test.bean.JdbcService;
import com.lino.springframework.tx.transaction.annotation.AnnotationTransactionAttributeSource;
import com.lino.springframework.tx.transaction.interceptor.TransactionInterceptor;
import org.junit.Before;
import org.junit.Test;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
/**
* @description: 测试类
*/
public class ApiTest {
private JdbcTemplate jdbcTemplate;
private JdbcService jdbcService;
private DataSource dataSource;
@Before
public void init() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
jdbcTemplate = applicationContext.getBean(JdbcTemplate.class);
dataSource = applicationContext.getBean(DruidDataSource.class);
jdbcService = applicationContext.getBean(JdbcService.class);
}
}
init
方法是一个初始化操作,用于获取需要的 Bean 对象。
4.2 单元测试
4.2.1 有事务测试
ApiTest.java
@Test
public void test_Transaction() throws SQLException {
AnnotationTransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource();
transactionAttributeSource.findTransactionAttribute(jdbcService.getClass());
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
TransactionInterceptor interceptor = new TransactionInterceptor(transactionManager, transactionAttributeSource);
// 组装代理对象
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTargetSource(new TargetSource(jdbcService));
advisedSupport.setMethodInterceptor(interceptor);
advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* com.lino.springframework.test.bean.JdbcService.*(..))"));
// 代理对象(Cglib2AopProxy)
JdbcService proxy_cglib = (JdbcService) new Cglib2AopProxy(advisedSupport).getProxy();
// 测试调用,有事务【不能同时提交2条有主键冲突的数据】
proxy_cglib.saveData(jdbcTemplate);
}
测试结果
信息: {dataSource-1} inited
com.lino.springframework.jdbc.UncategorizedSQLException: insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())
at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:75)
at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:102)
at com.lino.springframework.test.bean.JdbcService.saveData(JdbcService.java:16)
at com.lino.springframework.test.bean.JdbcService$$FastClassByCGLIB$$d07274b8.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at com.lino.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.proceed(Cglib2AopProxy.java:61)
at com.lino.springframework.tx.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:42)
at com.lino.springframework.tx.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:23)
at com.lino.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:44)
at com.lino.springframework.test.bean.JdbcService$$EnhancerByCGLIB$$aac0f4c.saveData(<generated>)
at com.lino.springframework.test.ApiTest.test_Transaction(ApiTest.java:59)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'PRIMARY'
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:115)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:790)
at com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:675)
at com.alibaba.druid.pool.DruidPooledStatement.execute(DruidPooledStatement.java:644)
at com.lino.springframework.jdbc.core.JdbcTemplate$1ExecuteStatementCallback.doInStatement(JdbcTemplate.java:98)
at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:73)
... 35 more
- 使用事务注解的测试结果为报错主键冲突,数据库不会有任何记录。
4.2.2 无事务测试
ApiTest.java
@Test
public void test_Transaction() throws SQLException {
AnnotationTransactionAttributeSource transactionAttributeSource = new AnnotationTransactionAttributeSource();
transactionAttributeSource.findTransactionAttribute(jdbcService.getClass());
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource);
TransactionInterceptor interceptor = new TransactionInterceptor(transactionManager, transactionAttributeSource);
// 组装代理对象
AdvisedSupport advisedSupport = new AdvisedSupport();
advisedSupport.setTargetSource(new TargetSource(jdbcService));
advisedSupport.setMethodInterceptor(interceptor);
advisedSupport.setMethodMatcher(new AspectJExpressionPointcut("execution(* com.lino.springframework.test.bean.JdbcService.*(..))"));
// 代理对象(Cglib2AopProxy)
JdbcService proxy_cglib = (JdbcService) new Cglib2AopProxy(advisedSupport).getProxy();
// 测试调用,无事务【提交2条有主键冲突的数据,成功一条】
proxy_cglib.saveDataNoTransaction(jdbcTemplate);
}
测试结果
信息: {dataSource-1} inited
com.lino.springframework.jdbc.UncategorizedSQLException: insert into user (id, userId, userHead, createTime, updateTime) values (1, '184172133','01_50', now(), now())
at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:75)
at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:102)
at com.lino.springframework.test.bean.JdbcService.saveDataNoTransaction(JdbcService.java:21)
at com.lino.springframework.test.bean.JdbcService$$FastClassByCGLIB$$d07274b8.invoke(<generated>)
at net.sf.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at com.lino.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation.proceed(Cglib2AopProxy.java:61)
at com.lino.springframework.tx.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:42)
at com.lino.springframework.tx.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:23)
at com.lino.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor.intercept(Cglib2AopProxy.java:44)
at com.lino.springframework.test.bean.JdbcService$$EnhancerByCGLIB$$aac0f4c.saveDataNoTransaction(<generated>)
at com.lino.springframework.test.ApiTest.test_Transaction(ApiTest.java:62)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69)
at com.intellij.rt.junit.IdeaTestRunner$Repeater$1.execute(IdeaTestRunner.java:38)
at com.intellij.rt.execution.junit.TestsRepeater.repeat(TestsRepeater.java:11)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:35)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54)
Caused by: java.sql.SQLIntegrityConstraintViolationException: Duplicate entry '1' for key 'PRIMARY'
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:115)
at com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:95)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
at com.mysql.cj.jdbc.StatementImpl.executeInternal(StatementImpl.java:790)
at com.mysql.cj.jdbc.StatementImpl.execute(StatementImpl.java:675)
at com.alibaba.druid.pool.DruidPooledStatement.execute(DruidPooledStatement.java:644)
at com.lino.springframework.jdbc.core.JdbcTemplate$1ExecuteStatementCallback.doInStatement(JdbcTemplate.java:98)
at com.lino.springframework.jdbc.core.JdbcTemplate.execute(JdbcTemplate.java:73)
... 35 more
- 没有使用事务注解同样会出现主键冲突的错误,但是数据库中会添加一条新插入的记录。
五、总结:事务处理
- 先了解事务的特性及其最基本的使用流程,再思考如何把事务拆解到 AOP 中进行包装。这个过程也是对一个复杂问题的细化分析和处理。
- 关于事务的操作,定义了事务注解,并在
invokeWithinTransaction
方法中使用和提取。- 并用此方法控制事务,包括获取事务、提交事务、回滚事务等。
ThreadLocal
是一个非常重要的知识点。如果没有使用统一的数据库链接,则无法做到用户的使用链接和切面链接保持为同一个,也就不能提交事务。