OriSpirit1.png

前言:

有关事务的特性 ACID ,想必大家已经熟记于心了。(如果还有不了解事务特性的朋友,建议先花点时间进行了解,以便文中下面的内容不会对您造成困扰。)

本文我们主要以 Spring 的事务管理为主,MySQL 的事务相关内容可参考 MySQL 系列文章:MySQL 学习总结(一) —— 认识 MySQL 以及简单应用

1. Spring 事务说明

虽然说 Spring 提供了对事务的支持,但并不代表在程序中使用了 Spring 的事务管理就能保证我们的项目一定支持事务。

为什么这么说呢?

因为 Spring 的事务管理本质上使用的是项目中所使用的数据库的事务。简单点说,如果项目中使用的数据库本身就不支持事务,那么即使我们开启了 Spring 的事务管理也是徒劳。

下面这张表中可以看出在 MySQL 5.7 版本中仅有 InnoDB 引擎是支持事务的

+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                        | Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| InnoDB             | DEFAULT | Supports transactions, row-level locking, and foreign keys     | YES          | YES  | YES        |
| MRG_MYISAM         | YES     | Collection of identical MyISAM tables                          | NO           | NO   | NO         |
| MEMORY             | YES     | Hash based, stored in memory, useful for temporary tables      | NO           | NO   | NO         |
| BLACKHOLE          | YES     | /dev/null storage engine (anything you write to it disappears) | NO           | NO   | NO         |
| MyISAM             | YES     | MyISAM storage engine                                          | NO           | NO   | NO         |
| CSV                | YES     | CSV storage engine                                             | NO           | NO   | NO         |
| ARCHIVE            | YES     | Archive storage engine                                         | NO           | NO   | NO         |
| PERFORMANCE_SCHEMA | YES     | Performance Schema                                             | NO           | NO   | NO         |
| FEDERATED          | NO      | Federated MySQL storage engine                                 | NULL         | NULL | NULL       |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+

查看您自身的 MySQL 所支持的存储引擎执行如下语句:

mysql> show engines;

1.1. 数据回滚

事务存在的意义是 保证数据正确且可靠。基于这个条件,MySQL 中的 redo logundo log 起到了关键作用。

重做日志(redo log)

redo log 的功能是保证事务的持久性。 当我们往数据中插入或更新一条数据时,MySQL 会先在内存中将对应的数据更新,但是更新之后不会马上持久化至磁盘中,而是把执行的操作记录至 redo log 中。最后再通过 redo log 中记录的操作日志将数据持久化到磁盘上。

可能有人觉得这样多此一举,其实不然。也许有朋友跟笔者遇到过相同的情况,数据库突然断电或者是崩溃,在重启数据库后经常会遇到一些莫名其妙的问题,这在使用 Oracle 11g 时感触颇深(好像扯远了)。而 redo log 的存在就使得 MySQL 在发生故障重启服务后,根据 redo log 进行数据恢复,从而保证了事务的持久性。

回滚日志(undo log)

顾名思义,该日志的功能相当于是记录一个保存点,当执行的数据库操作遇到异常时,通过 undo log 将数据库中的信息回退到修改前的样子。也就是说,undo log 保证了事务的原子性

undo log 同时也会将操作记录至 redo log 中,从而保证了 undo log 的数据不会轻易地丢失。

1.2. Spring 事务管理

众所周知,Spring 提供了两种方式的事务管理:

  • 编程式事务
  • 声明式事务

Spring 中定义了几个事务相关的重要接口和类:

  • TransactionDefinition:定义事务的接口,用于定义 事务隔离级别、传播行为,超时设置等信息。
  • PlatformTransactionManager:平台事务管理器,Spring 传统事务管理的核心接口。
  • ReactiveTransactionManager:响应式事务管理器,Spring 响应式事务管理的核心接口。
  • TransactionTemplate:处理事务的模板类。

其中 ReactiveTransactionManager 是在 Spring 5.2 之后引入的,其源码部分基于响应式编程进行编写。

2. 事务相关接口源码分析

现在就对上面提到的部分接口和类进行源码分析,以便更清晰地认识 Spring 事务管理。

2.1. TransactionDefinition 事务定义

TransactionDefinition 源码:

public interface TransactionDefinition {

	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 = 1;  // same as java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;

	int ISOLATION_READ_COMMITTED = 2;  // same as java.sql.Connection.TRANSACTION_READ_COMMITTED;

	int ISOLATION_REPEATABLE_READ = 4;  // same as java.sql.Connection.TRANSACTION_REPEATABLE_READ;

	int ISOLATION_SERIALIZABLE = 8;  // same as java.sql.Connection.TRANSACTION_SERIALIZABLE;

	int TIMEOUT_DEFAULT = -1;

	// 获取事务的传播行为,默认为 REQUIRED
	default int getPropagationBehavior() {
		return PROPAGATION_REQUIRED;
	}

	// 获取事务的隔离级别,默认为 DEFAULT
	default int getIsolationLevel() {
		return ISOLATION_DEFAULT;
	}

	// 获取事务的超时时间,默认为 -1,表示没有超时时间
	default int getTimeout() {
		return TIMEOUT_DEFAULT;
	}

	// 判断是否为只读事务,默认为 false
	default boolean isReadOnly() {
		return false;
	}

	// 获取事务名称,默认为 null
	@Nullable
	default String getName() {
		return null;
	}


	// Static builder methods
	static TransactionDefinition withDefaults() {
		return StaticTransactionDefinition.INSTANCE;
	}

}

可以看到 TransactionDefinition 的功能都是用来定义和获取事务中的属性值,具体分为以下几种:

  • 事务传播行为
  • 事务隔离级别
  • 事务超时时间
  • 是否只读事务
  • 事务名称

2.2. PlatformTransactionManager 平台事务管理器

PlatformTransactionManager 继承自 TransactionManager 接口。 TransactionManagerCloneableRandomAcess类似 —— 都是空接口,只是用来表名该接口的主要功能。

PlatformTransactionManager 源码:

public interface PlatformTransactionManager extends TransactionManager {

	// 获取事务
	TransactionStatus getTransaction(@Nullable TransactionDefinition definition)
			throws TransactionException;
	// 提交事务
	void commit(TransactionStatus status) throws TransactionException;
	// 回滚事务
	void rollback(TransactionStatus status) throws TransactionException;

}

该接口提供了三个方法:

  • getTransaction:以 TransactionDefinition 作为入参,返回一个 TransactionStatus 对象(下面讲)。
  • commit:传入 TransactionStatus 对象,提交该事务。
  • rollback:传入 TransactionStatus 对象,回滚该事务。

2.3. TransactionStatus 事务状态

TransactionStatus 的继承结构:

image.png

Flushable 接口只提供了一个 flush 方法。

SavepointManager 源码很容易理解:

public interface SavepointManager {

	// 创建保存点
	Object createSavepoint() throws TransactionException;

	// 回滚至指定保存点
	void rollbackToSavepoint(Object savepoint) throws TransactionException;

	// 删除指定保存点
	void releaseSavepoint(Object savepoint) throws TransactionException;

}

TransactionExecution 源码:

public interface TransactionExecution {

	// 判断当前事务是否为新事务
	boolean isNewTransaction();

	// 设置当前事务为只回滚事务
	void setRollbackOnly();

	// 判断当前事务是否为只回滚事务
	boolean isRollbackOnly();

	// 判断当前事务是否已经被提交或者回滚
	boolean isCompleted();
}

TransactionStatus 在这些父类接口的基础上,额外添加了一个方法:

public interface TransactionStatus extends TransactionExecution, SavepointManager, Flushable {

	// 是否有保存点
	boolean hasSavepoint();

	@Override
	void flush();
}

3. 事务属性

上面也提到了 TransactionDefinition 接口中定义了一些事务的属性,下面结合源码中注释给出的信息逐一分析这些事务的属性所指代的意思。

3.1. 传播行为

所谓事务的传播行为,是指在事务间的方法调用时事务应该如何传递。

举个例子:

public class PropagationDemo {
  
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodA() {
        methodB();
        // doSomething
    }
  
    @Transactional(propagation = Propagation.REQUIRED)
    public void methodB() {
        // doSomething
    }
}

上面的示例中用到了 @Transactional 注解,也就是基于声明式事务编写的伪代码,关于这一块下面再讲。示例中 methodA方法调用了 methodB 方法,而 methodB 方法的事务的传播行为由方法上的 @Transactional 注解决定。

先说结果:上述伪代码,当 methodB 执行异常时,methodAmethodB 均要回滚事务,同样的 methodA 异常时,两个方法均要回滚事务,至于为什么下面讲到。

Spring 中提供了 7 中事务的传播行为,为了方便还提供了一个 Propagation 枚举类来指定事务的传播行为:

public enum Propagation {

	REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),

	SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),

	MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),

	REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),

	NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),

	NEVER(TransactionDefinition.PROPAGATION_NEVER),

	NESTED(TransactionDefinition.PROPAGATION_NESTED);

	private final int value;

	Propagation(int value) {
		this.value = value;
	}

	public int value() {
		return this.value;
	}
}

传播行为图表:

传播行为类型说明
REQUIRED最常用的传播行为,@Transactional 注解中默认使用的如果当前没有事务就新建一个事务,如果当前已经存在事务就加入到这个事务中。
SUPPORTS支持事务,如果当前没有事务,就以非事务方式执行。
MANDATORY必须使用事务,如果当前没有事务,抛出异常。
REQUIRES_NEW新建一个事务,如果当前已有事务,将当前事务挂起。
NOT_SUPPORTED不支持事务,如果当前存在事务,将当前事务挂起,最终均以非事务方式执行。
NEVER不支持事务,如果当前存在事务,排除异常。
NESTED嵌套事务,如果当前没有事务就新建一个事务,如果当前已有事务,则在嵌套事务内执行。

🌰 有关其他事务传播行为的示例可参照:Spring 事务传播行为详解

3.2. 隔离级别

为了方便起见,Spring 中也定义了一个 Isolation 枚举类用于指定事务的隔离级别:

public enum Isolation {

	DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),

	READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),

	READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),

	REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),

	SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);

	private final int value;

	Isolation(int value) {
		this.value = value;
	}

	public int value() {
		return this.value;
	}
}
隔离级别脏读不可重复读幻读第一类丢失修改第二类丢失修改
不设置任何隔离级别YYYYY
READ-UNCOMMITEDYYYNY
READ-COMMITEDNYYNY
REPEATABLE-READNNYNN
SERIALIZABLENNNNN

🌰 有关 MySQL 隔离级别的说明可参考:MySQL 学习总结(一) —— 认识 MySQL 以及简单应用

3.3. 超时时间

事务的超时属性,是指处理该事务能用的最长时间,如果超过该时间事务还没有处理完成,Spring 会默认该事务处理失败,从而执行回滚操作。

设置事务的超时时间,单位为秒,-1 即表示不设置超时属性。

3.4. 只读事务

当一个事务中执行的所有 SQL 语句均为查询语句时,我们就可以将该事务定义为只读属性。

可能有人会问,执行查询语句也要牵扯到事务吗?

其实 MySQL 官方已经做出了这方面的说明:

In InnoDB, all user activity occurs inside a transaction. If autocommit mode is enabled, each SQL statement forms a single transaction on its own. By default, MySQL starts the session for each new connection with autocommit enabled, so MySQL does a commit after each SQL statement if that statement did not return an error. If a statement returns an error, the commit or rollback behavior depends on the error.

简单点说,MySQL 会给每一个新的连接会话都开启 autocommit 模式,所以在 MySQL 中每一条 SQL 语句都会在一个单独的事务中进行处理,执行成功后自动提交事务,执行失败则回滚事务。

所以当在 Spring 中声明了事务的只读属性时,在该事务中所有的 SQL 语句会被放在同一个事务中,MySQL 就会去它们进行优化。

3.5. 回滚策略

回滚策略没有在 TransactionDefinition 进行定义,在使用 @Transactional 注解时可以通过 rollbackFor 属性指定事务的回滚策略,如:

@Transactional(rollbackFor = Exception.class)
public void excuteService(){
}

上面的代码指定 excuteService 方法在遇到异常时进行回滚。

默认的回滚策略为在遇到 RuntimeExceptionError 时进行回滚,附上源码注释:

/**
 * <p>By default, a transaction will be rolling back on {@link RuntimeException}
 * and {@link Error} but not on checked exceptions (business exceptions). See
 * {@link org.springframework.transaction.interceptor.DefaultTransactionAttribute#rollbackOn(Throwable)}
 * for a detailed explanation.
 */

4. 编程式事务管理

在了解了事务接口的相关功能后,开始使用这些接口编写伪代码。

4.1. PlatformTransactionManager 事务管理

示例伪代码:

public class TransactionManagerDemo {
    @Autowired
    private PlatformTransactionManager tansactionManager;

    public void exucuteTransaction() {
	// DefaultTransactionDefinition 是 TransactionDefinition 的默认实现
	TransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
	// 根据 transactionDefinition 获取一个 TransactionStatus 对象,用于后续的事务操作
	TransactionStatus transactionStatus = trasactionManager.getTransaction(transactionDefinition);
	try {
	    // 处理事务逻辑的代码
	    ......  
            // 执行完毕后,提交事务
	    transactionManager.commit(transactionStatus);
	} catch(Exception e) {
	    // 出现异常时,回滚事务
	    transactionManager.rollBack(transactionStatus);
	}
    }
}

上面使用到了 DefaultTransactionDefinition ,该类为 TransactionDefinition 的默认实现,一起来看看它的源码:

public class DefaultTransactionDefinition implements TransactionDefinition, Serializable {

	public static final String PREFIX_PROPAGATION = "PROPAGATION_";

	public static final String PREFIX_ISOLATION = "ISOLATION_";

	public static final String PREFIX_TIMEOUT = "timeout_";

	public static final String READ_ONLY_MARKER = "readOnly";


	/** Constants instance for TransactionDefinition. */
	static final Constants constants = new Constants(TransactionDefinition.class);

	private int propagationBehavior = PROPAGATION_REQUIRED;

	private int isolationLevel = ISOLATION_DEFAULT;

	private int timeout = TIMEOUT_DEFAULT;

	private boolean readOnly = false;

	@Nullable
	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 final void setPropagationBehaviorName(String constantName) throws IllegalArgumentException {
		if (!constantName.startsWith(PREFIX_PROPAGATION)) {
			throw new IllegalArgumentException("Only propagation constants allowed");
		}
		setPropagationBehavior(constants.asNumber(constantName).intValue());
	}

	// 省略其他单独设置属性值的方法
}

从源码中可以看出,DefaultTransactionDefinition 的属性为:

  • 传播行为:REQUORED
  • 隔离级别:DEFAULT,即为没有隔离级别
  • 超时时间:DEFAULT,即为没有超时时间
  • 是否只读事务:false

还可以通过它的有参构造传入一个 TransactionDefinition 对象创建一个事务定义的对象。

除此之外,还可以通过它的 setXX 方法,同时传入一个 Constants 对象,对它的各个属性进行单独设置。

4.2. TransactionTemplate 事务模板

TransactionTemplate 继承图:

TransactionTemplate 继承图.png

TransactionTemplate 源码:

public class TransactionTemplate extends DefaultTransactionDefinition
		implements TransactionOperations, InitializingBean {

	protected final Log logger = LogFactory.getLog(getClass());

	@Nullable
	private PlatformTransactionManager transactionManager;

	public TransactionTemplate() {
	}

	public TransactionTemplate(PlatformTransactionManager transactionManager) {
		this.transactionManager = transactionManager;
	}

	public TransactionTemplate(PlatformTransactionManager transactionManager, TransactionDefinition transactionDefinition) {
		super(transactionDefinition);
		this.transactionManager = transactionManager;
	}

	// 省略 setter、getter

	// 执行事务管理的核心方法
	@Override
	@Nullable
	public <T> T execute(TransactionCallback<T> action) throws TransactionException {
		Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");

		if (this.transactionManager instanceof CallbackPreferringPlatformTransactionManager) {
			return ((CallbackPreferringPlatformTransactionManager) this.transactionManager).execute(this, action);
		}
		else {
			TransactionStatus status = this.transactionManager.getTransaction(this);
			T result;
			try {
				result = action.doInTransaction(status);
			}
			catch (RuntimeException | Error ex) {
				// Transactional code threw application exception -> rollback
				rollbackOnException(status, ex);
				throw ex;
			}
			catch (Throwable ex) {
				// 回滚事务
				rollbackOnException(status, ex);
				throw new UndeclaredThrowableException(ex, "TransactionCallback threw undeclared checked exception");
			}
			this.transactionManager.commit(status);
			return result;
		}
	}

	// 异常时回滚事务的方法
	private void rollbackOnException(TransactionStatus status, Throwable ex) throws TransactionException {
		Assert.state(this.transactionManager != null, "No PlatformTransactionManager set");

		logger.debug("Initiating transaction rollback on application exception", ex);
		try {
			this.transactionManager.rollback(status);
		}
		catch (TransactionSystemException ex2) {
			logger.error("Application exception overridden by rollback exception", ex);
			ex2.initApplicationException(ex);
			throw ex2;
		}
		catch (RuntimeException | Error ex2) {
			logger.error("Application exception overridden by rollback exception", ex);
			throw ex2;
		}
	}

}

可见在 TransactionTemplate 中存在一个 PlatformTransactionManager 属性,也就是说它对直接使用 PlatformTransactionManager 的事务管理方式进行了封装,使我们在开发过程中无需注意更多地细节,直接使用创建好的 TransactionTemplate 对象调用其 excute 方法即可。

在调用 excute 方法时需要传入 TransactionCallback 对象,TransactionCallback 接口源码如下:

@FunctionalInterface
public interface TransactionCallback<T> {

	@Nullable
	T doInTransaction(TransactionStatus status);

}
  • @FunctionalInterface 注解是 Java 8 引入的新注解,加上该注解的接口表示该接口满足函数式编程风格

  • TransactionCallback 接口只有一个方法 doInTransaction ,该方法需要传入 TransactionStatus 对象,用于返回一个结果对象,如返回 TransactionStatus 中的域对象。

  • 通常对于该接口的使用方法,注释中也有说明,附上注释说明如下:

    /* 
     * Callback interface for transactional code. Used with {@link TransactionTemplate}'s
     * {@code execute} method, often as anonymous class within a method implementation.
     */
    

查看 TransactionCallback 的子类:

image.png

Spring-Kafka 中均是以内部类的形式使用,Spring 中提供了 TransactionCallbackWithoutResult 抽象类对 TransactionCallback 进行简单实现 doInTransaction 方法实现直接返回 null,并提供一个抽象方法 doInTransactionWithoutResult 让其子类实现具体的 doInTransaction 逻辑。源码如下:

public abstract class TransactionCallbackWithoutResult implements TransactionCallback<Object> {

	@Override
	@Nullable
	public final Object doInTransaction(TransactionStatus status) {
		doInTransactionWithoutResult(status);
		return null;
	}

	protected abstract void doInTransactionWithoutResult(TransactionStatus status);

}

了解了以上内容之后,开始编写使用 TransactionTemplate 管理事务的示例伪代码:

public class TransactionManagerDemo {

    @Autowired
    private TransactionTemplate transactionTemplate;

    @Autowired
    private PlatformTransactionManager transactionManager;
  
    public void executeTransaction() {
        transactionTemplate.setTransactionManager(transactionManager);
        transactionTemplate.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                try {
                    // 处理事务的逻辑代码
		    ......
                } catch (Exception e) {
                    // 事务处理异常,将 TransactionStatus 设为只回滚事务
                    status.setRollbackOnly();
                }
            }
        });
    }
}

5. 声明式事务

在日常开发过程中推荐使用声明式事务,即使用 @Transactional 注解来标明需要进行事务管理的方法、类或接口。

@Transactional 源码:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Transactional {

	@AliasFor("transactionManager")
	String value() default "";

	@AliasFor("value")
	String transactionManager() default "";

	Propagation propagation() default Propagation.REQUIRED;

	Isolation isolation() default Isolation.DEFAULT;

	int timeout() default TransactionDefinition.TIMEOUT_DEFAULT;

	boolean readOnly() default false;

	Class<? extends Throwable>[] rollbackFor() default {};

	String[] rollbackForClassName() default {};

	Class<? extends Throwable>[] noRollbackFor() default {};

	String[] noRollbackForClassName() default {};
}

5.1. 属性说明

属性名说明是否常用
transactionManager指定事务管理器
propagation指定事务传播行为,默认为 Propagation.REQUIRED
isolation指定事务隔离级别,默认为 Isolation.DEFAULT
timeout指定事务超时时间,默认为 -1
readOnly指定事务是否为只读事务,默认为 false
rollbackFor指定触发事务回滚的异常类型,可以传多个
rollbackForClassName指定触发事务回滚的异常名称,可以传多个
noRollbackFor指定不触发事务回滚的异常类型,可以传多个
noRollbackForClassName指定不触发事务回滚的异常名称,可以传多个

5.2. @Transactional 工作原理

首先 @Transactional 是基于 Spring AOP 实现的,而这其中实现切面功能的具体类为 TransactionInterceptor ,其中执行增强的方法为 invoke ,源码如下:

public class TransactionInterceptor extends TransactionAspectSupport implements MethodInterceptor, Serializable {
    // ......

	@Override
	@Nullable
	public Object invoke(MethodInvocation invocation) throws Throwable {

		Class<?> targetClass = (invocation.getThis() != null ? AopUtils.getTargetClass(invocation.getThis()) : null);

		return invokeWithinTransaction(invocation.getMethod(), targetClass, invocation::proceed);
	}

    // ......
}

invoke 方法调用了其继承自 TransactionAspectSupportinvokeWithinTransaction 方法:

image.png

从声明式事务管理的处理过程中可以看到,被 @Transactional 注解标注的方法会执行如下过程:

  1. 在执行实际方法前,创建一个 TransactionInfo 对象。
  2. 执行方法,如果发生异常,则抛出异常并回滚事务。
  3. 方法执行成功后,返回结果并提交事务。

这里记录一下有关 AOP 动态代理的问题:

关于使用的是 JDK 动态代理还是 cglib 动态代理,取决于 DefaultAopProxyFactory 中的 createAopProxy 方法:

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) {
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			}
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

	// ......
}

可以看到,如果目标类实现了接口,就使用 JDK 动态代理,否则使用 cglib 动态代理。

5.3. @Transactional 注解失效问题

在开发过程中往往会遇到 @Transactional 注解不生效的问题,造成该问题的原因常见于:

  • @Transactional 标注在非 public 访问修饰符的方法上,导致注解不生效。

  • 在同一个类内部,没有被 @Transactional 标注的方法调用了 @Transactional 标注的方法,导致注解不生效。举个栗子:

    public class transactionDemo {
    
    	public void methodA() {
    	    methodB();
    	}
    
    	@Transactional
    	public void methodB(){
    	    // doSomething
    	}
    } 
    

    此时 methodA 会导致 methodB@Transactional 注解失效。原因是事务管理是基于动态代理来实现的,那么没有添加 @Transactional 注解的方法就不会走动态代理的流程,所以调用其他的方法也就不会走代理类,也就不会实现增强,从而导致注解失效。

  • 在方法内将捕获到的异常处理掉了但没有抛出新的异常,导致事务操作不会进行回滚操作,从而注解不生效。

6. Reference

  1. Spring 事务传播行为详解
  2. MySQL 学习总结(一) —— 认识 MySQL 以及简单应用
  3. MySQL 官方文档
  4. 一口气说出 6 种 @Transactional 注解的失效场景

关于作者:NekoChips
本文地址:https://chenyangjie.com.cn/articles/2020/05/13/1589347028274.html
版权声明:本篇所有文章仅用于学习和技术交流,本作品采用 BY-NC-SA 4.0 许可协议,如需转载请注明出处!
许可协议:知识共享许可协议