Spring基于注解管理事物需要两个注解:
- @EnableTransactionManagement 开启基于注解事务管理功能;
- @Transactional 表示当前方法是一个事务方法;
@EnableTransactionManagement用于配置类,用于开启Spring基于注解管理事物的功能。@Transactional注解用于方法上,表示当前方法是一个事物方法。
@EnableTransactionManagement开启基于注解管理事物:
@EnableTransactionManagement
@ComponentScan("cn.zsm.transaction")
@Configuration
public class TxConfig {
//数据源
@Bean
public DataSource dataSource() throws Exception{
ComboPooledDataSource dataSource = new ComboPooledDataSource();
dataSource.setUser("root");
dataSource.setPassword("123456");
dataSource.setDriverClass("com.mysql.jdbc.Driver");
dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
return dataSource;
}
//JdbcTemplate 模板类,用于简化增删改查操作
@Bean
public JdbcTemplate jdbcTemplate() throws Exception{
//Spring对@Configuration类会特殊处理;给容器中加组件的方法,多次调用都只是从容器中找组件
JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource());
return jdbcTemplate;
}
//注册事务管理器在容器中
@Bean
public PlatformTransactionManager transactionManager() throws Exception{
return new DataSourceTransactionManager(dataSource());
}
}
一个简单的数据库链接配置和Spring的事物管理器配置完成。可以看到配置类中上有三个注解:@configuration、@Component、@EnableTransactionManagement,分别用于标识当前类为配置类、扫描指定路径下的包、开启Spring基于注解管理事物功能。下面来看一下@EnableTransactionManagement注解都做了什么:
@EnableTransactionManagement
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class) //导入组件
public @interface EnableTransactionManagement {
//true创建基于子类(CGLIB)的代理,false创建基于Java接口的代理
boolean proxyTargetClass() default false;
// 指示应如何应用事务通知。默认{@link AdviceMode#PROXY}.
AdviceMode mode() default AdviceMode.PROXY;
//在特定连接点应用多个通知时,指示事务顾问的执行顺序。默认Ordered#LOWEST_PRECEDENCE
int order() default Ordered.LOWEST_PRECEDENCE;
}
@EnableTransactionManagement给容器中导入了TransactionManagementConfigurationSelector实例,
TransactionManagementConfigurationSelector:
public class TransactionManagementConfigurationSelector extends AdviceModeImportSelector<EnableTransactionManagement> {
//根据adviceMode类型,选择给容器导入的组件
@Override
protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()};
case ASPECTJ:
return new String[] {TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
}
TransactionManagementConfigurationSelector类中只有一个方法,返回一个String数组,而数组中定义了两个类信息。且返回数组的值根据adviceMode的值来确定。回到@EnableTransactionManagement注解中,定义了一个属性:AdviceMode mode() default AdviceMode.PROXY ,默认是PROXY,所以这里默认给容器注入了两个组件:
- AutoProxyRegistrar
- ProxyTransactionManagementConfiguration
下面就来看看这两个组件都做了什么:
AutoProxyRegistrar:
public class AutoProxyRegistrar implements ImportBeanDefinitionRegistrar {
private final Log logger = LogFactory.getLog(getClass());
//注册、升级和配置标准自动代理创建器
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
boolean candidateFound = false;
Set<String> annoTypes = importingClassMetadata.getAnnotationTypes();
for (String annoType : annoTypes) {
AnnotationAttributes candidate = AnnotationConfigUtils.attributesFor(importingClassMetadata, annoType);
if (candidate == null) {
continue;
}
Object mode = candidate.get("mode");
Object proxyTargetClass = candidate.get("proxyTargetClass");
if (mode != null && proxyTargetClass != null && AdviceMode.class == mode.getClass() &&
Boolean.class == proxyTargetClass.getClass()) {
candidateFound = true;
if (mode == AdviceMode.PROXY) { //注册自动代理创建器
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
if ((Boolean) proxyTargetClass) {
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
return;
}
}
}
}
if (!candidateFound) {
String name = getClass().getSimpleName();
logger.warn(String.format("%s was imported but no annotations were found " +
"having both 'mode' and 'proxyTargetClass' attributes of type " +
"AdviceMode and boolean respectively. This means that auto proxy " +
"creator registration and configuration may not have occurred as " +
"intended, and components may not be proxied as expected. Check to " +
"ensure that %s has been @Import'ed on the same class where these " +
"annotations are declared; otherwise remove the import of %s " +
"altogether.", name, name, name));
}
}
}
AutoProxyRegistrar类的作用是注册一个自动代理创建器:AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry),而该方法默认情况下最终会给容器中注册一个:InfrastructureAdvisorAutoProxyCreator。InfrastructureAdvisorAutoProxyCreator是一个后置处理器,他的功能类似于Spring注解开启AOP功能中的AnnotationAwareAspectJAutoProxyCreator,有兴趣的朋友可以点进去看看,这里不再详细描述。
ProxyTransactionManagementConfiguration:
@Configuration
public class ProxyTransactionManagementConfiguration extends AbstractTransactionManagementConfiguration {
//给容器注入BeanFactoryTransactionAttributeSourceAdvisor事物增强器
@Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource());//设置事物属性信息
advisor.setAdvice(transactionInterceptor()); //设置事物拦截器
advisor.setOrder(this.enableTx.<Integer>getNumber("order")); //排序
return advisor;
}
//注册事物的属性信息AnnotationTransactionAttributeSource
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
//注册事物拦截器信息TransactionInterceptor
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionInterceptor transactionInterceptor() {
TransactionInterceptor interceptor = new TransactionInterceptor();
interceptor.setTransactionAttributeSource(transactionAttributeSource()); //设置事物属性信息
if (this.txManager != null) {
interceptor.setTransactionManager(this.txManager); //设置事物管理器
}
return interceptor;
}
}
ProxyTransactionManagementConfiguration类被@Configuration标注,说明它是一个配置类,它给容器中注入一个事物增强器BeanFactoryTransactionAttributeSourceAdvisor,事物增强器中设置了事物的属性信息、事物拦截器和排序规则。其中事物的属性信息和事物的拦截器是注解管理事物的关键。
1、注册事物的注解信息:AnnotationTransactionAttributeSource
@Bean
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource();
}
AnnotationTransactionAttributeSource:
public AnnotationTransactionAttributeSource() {
this(true);
}
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {
this.publicMethodsOnly = publicMethodsOnly;
this.annotationParsers = new LinkedHashSet<TransactionAnnotationParser>(2); //保存注解的解析器
this.annotationParsers.add(new SpringTransactionAnnotationParser());
if (jta12Present) {
this.annotationParsers.add(new JtaTransactionAnnotationParser());
}
if (ejb3Present) {
this.annotationParsers.add(new Ejb3TransactionAnnotationParser());
}
}
AnnotationTransactionAttributeSource中可以注册三个注解的解析器:SpringTransactionAnnotationParser、JtaTransactionAnnotationParser、Ejb3TransactionAnnotationParser,每个注解解析器也就是利用反射实现注解的功能,可以解析注解中各个属性的值,并执行属性所表示的含义。因此这里可以获取到事物的注解信息。
2、注册事物的拦截器:TransactionInterceptor
public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
public TransactionInterceptor() {
}
public TransactionInterceptor(PlatformTransactionManager ptm, Properties attributes) {
setTransactionManager(ptm);
setTransactionAttributes(attributes);
}
public TransactionInterceptor(PlatformTransactionManager ptm, TransactionAttributeSource tas) {
setTransactionManager(ptm);
setTransactionAttributeSource(tas);
}
@Override
public Object invoke(final MethodInvocation invocation) throws Throwable {
//获取目标类
Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);
//基于事物,执行目标方法
return invokeWithinTransaction(invocation.getMethod(), targetClass, new InvocationCallback() {
@Override
public Object proceedWithInvocation() throws Throwable {
return invocation.proceed();
}
});
}
//---------------------------------------------------------------------
// 序列化支持
//---------------------------------------------------------------------
private void writeObject(ObjectOutputStream oos) throws IOException {
// 依赖于默认的序列化,
oos.defaultWriteObject();
// 超类字段进行反序列化。
oos.writeObject(getTransactionManagerBeanName());
oos.writeObject(getTransactionManager());
oos.writeObject(getTransactionAttributeSource());
oos.writeObject(getBeanFactory());
}
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
ois.defaultReadObject();
setTransactionManagerBeanName((String) ois.readObject());
setTransactionManager((PlatformTransactionManager) ois.readObject());
setTransactionAttributeSource((TransactionAttributeSource) ois.readObject());
setBeanFactory((BeanFactory) ois.readObject());
}
}
从TransactionInterceptor类的源码中可以看出,该类实现了MethodIntercept接口。所以在目标方法执行的时候,他会执行拦截器链中的TransactionInterceptor,进行事物拦截。而TransactionInterceptor类中处理三个构造方法和两个私有的序列化方法,只有一个对外暴露的公共方法invoke()。而invoke方法的执行逻辑:先获取目标代理类信息,然后调用invokeWithinTransaction执行目标方法。
invokeWithinTransaction:
protected Object invokeWithinTransaction(Method method, Class<?> targetClass, final InvocationCallback invocation)
throws Throwable {
//先获取事物属性
final TransactionAttribute txAttr = getTransactionAttributeSource().getTransactionAttribute(method, targetClass);
//获取PlatformTransactionManager,默认按照类型获取一个PlatformTransactionManager
final PlatformTransactionManager tm = determineTransactionManager(txAttr);
//获取事物方法
final String joinpointIdentification = methodIdentification(method, targetClass, txAttr);
if (txAttr == null || !(tm instanceof CallbackPreferringPlatformTransactionManager)) {
//如果需要,根据给定的TransactionAttribute创建事务。
TransactionInfo txInfo = createTransactionIfNecessary(tm, txAttr, joinpointIdentification);
Object retVal = null;
try {
//执行事物方法
retVal = invocation.proceedWithInvocation();
}catch (Throwable ex) {
completeTransactionAfterThrowing(txInfo, ex);//异常回滚事物
throw ex;
}finally {
cleanupTransactionInfo(txInfo);
}
commitTransactionAfterReturning(txInfo); //正常结束事物方法,提交
return retVal;
}
else {//CallbackPreferringPlatformTransactionManager:传入一个TransactionCallback。
try {
Object result = ((CallbackPreferringPlatformTransactionManager) tm).execute(txAttr,
new TransactionCallback<Object>() {
@Override
public Object doInTransaction(TransactionStatus status) {
TransactionInfo txInfo = prepareTransactionInfo(tm, txAttr, joinpointIdentification, status);
try {
return invocation.proceedWithInvocation();
}catch (Throwable ex) {
if (txAttr.rollbackOn(ex)) {
if (ex instanceof RuntimeException) {
throw (RuntimeException) ex;
}
else {
throw new ThrowableHolderException(ex);
}
}else {
return new ThrowableHolder(ex);
}
}finally {
cleanupTransactionInfo(txInfo);
}
}
});
// Check result: It might indicate a Throwable to rethrow.
if (result instanceof ThrowableHolder) {
throw ((ThrowableHolder) result).getThrowable();
}else {
return result;
}
}catch (ThrowableHolderException ex) {
throw ex.getCause();
}
}
}
invokeWithinTransaction方法的工作流程:
- 先获取事物属性
- 获取事物管理器PlatformTransactionManager ,如果没有指定则按照类型工容器总获取一个。
- 获取事物方法
- 执行事物方法,出现异常就回滚,正常结束就提交
至此,Spring完成了事物管理的功能。下面看一下Spring使用事物管理的注解@transactional:
@transactional:只是用于标注该方法将会被事物管理
@Transactional定义了多种属性,这里看一下它有哪些属性,作用是什么:
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {
@AliasFor("transactionManager")
String value() default "";
//指定使用哪个事物管理器管理事物方法、类
@AliasFor("value")
String transactionManager() default "";
//事务传播类型。共有7种
Propagation propagation() default Propagation.REQUIRED;
//事物隔离级别
Isolation isolation() default Isolation.DEFAULT;
//事务的超时时间
int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;
//事物是否只读
boolean readOnly() default false;
//Spring 默认在出现RuntimeException和Error时回滚事物,这里可以自定义异常类型回滚事物
Class<? extends Throwable>[] rollbackFor() default {};
//类似于rollbackFor属性,只不过这里的属性值是类名字符串,且不支持通配符如:
//{@code "ServletException"}将匹配{@code javax.servlet。及其子类。
String[] rollbackForClassName() default {};
//定义哪些异常不会滚事物
Class<? extends Throwable>[] noRollbackFor() default {};
//定义哪些异常不会滚事物
String[] noRollbackForClassName() default {};
}
@Transactional的属性中有两个属性propagation、isolation分别用来定义Spring事物的传播类型和事物的隔离级别。下面就看一下Spring事物的这两个属性的属性值信息:
事物传播类型:
事务的第一个方面是传播行为。传播行为定义关于客户端和被调用方法的事务边界。Spring定义了7中传播行为。
传播行为 | 意义 |
---|---|
PROPAGATION_MANDATORY | 表示该方法必须运行在一个事务中。如果当前没有事务正在发生,将抛出一个异常 |
PROPAGATION_NESTED | 表示如果当前正有一个事务在进行中,则该方法应当运行在一个嵌套式事务中。被嵌套的事务可以独立于封装事务进行提交或回滚。如果封装事务不存在,行为就像PROPAGATION_REQUIRES一样。 |
PROPAGATION_NEVER | 表示当前的方法不应该在一个事务中运行。如果一个事务正在进行,则会抛出一个异常。 |
PROPAGATION_NOT_SUPPORTED | 表示该方法不应该在一个事务中运行。如果一个现有事务正在进行中,它将在该方法的运行期间被挂起。 |
PROPAGATION_SUPPORTS | 表示当前方法不需要事务性上下文,但是如果有一个事务已经在运行的话,它也可以在这个事务里运行。 |
PROPAGATION_REQUIRES_NEW | 表示当前方法必须在它自己的事务里运行。一个新的事务将被启动,而且如果有一个现有事务在运行的话,则将在这个方法运行期间被挂起。 |
PROPAGATION_REQUIRED | (默认传播行为)表示当前方法必须在一个事务中运行。如果一个现有事务正在进行中,该方法将在那个事务中运行,否则就要开始一个新事务。 |
传播规则回答了这样一个问题,就是一个新的事务应该被启动还是被挂起,或者是一个方法是否应该在事务性上下文中运行。
事物隔离级别:
隔离级别 | 含义 |
---|---|
ISOLATION_DEFAULT | 使用后端数据库默认的隔离级别。 |
ISOLATION_READ_UNCOMMITTED | 允许读取尚未提交的更改。可能导致脏读、幻影读或不可重复读。 |
ISOLATION_READ_COMMITTED | 允许从已经提交的并发事务读取。可防止脏读,但幻影读和不可重复读仍可能会发生。 |
ISOLATION_REPEATABLE_READ | 对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,但幻影读仍可能发生。 |
ISOLATION_SERIALIZABLE | 完全服从ACID的隔离级别,确保不发生脏读、不可重复读和幻影读。这在所有隔离级别中也是最慢的,因为它通常是通过完全锁定当前事务所涉及的数据表来完成的。 |
- 脏读(Dirty read)-- 脏读发生在一个事务读取了被另一个事务改写但尚未提交的数据时。如果这些改变在稍后被回滚了,那么第一个事务读取的数据就会是无效的。
- 不可重复读(Nonrepeatable read)-- 不可重复读发生在一个事务执行相同的查询两次或两次以上,但每次查询结果都不相同时。这通常是由于另一个并发事务在两次查询之间更新了数据。
- 幻影读(Phantom reads)-- 幻影读和不可重复读相似。当一个事务(T1)读取几行记录后,另一个并发事务(T2)插入了一些记录时,幻影读就发生了。在后来的查询中,第一个事务(T1)就会发现一些原来没有的额外记录。