Transactional 事物

声明范围

可以选择在以下几处地方声明 @Transactional

  • 在实现类上面声明,类里面定义的所有方法都被事物管理
  • 在实现方法上面声明,仅该方法被事物管理

注意:在接口(接口类、方法)上面声明 Transactional 时,若代理对象是基于子类的代理,那么该注解将失效。
最佳实践就是在实现类的具体方法上面增加 Transactional 注解。

传播行为

事务传播行为用来描述由某一个事务传播行为修饰的方法被嵌套进另一个方法的时事务如何传播。

  • REQUIRED(0)

默认选择,必须在事物环境中运行;如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。

  • SUPPORTS(1)

支持,若当前存在事物,加入该事物;如果当前没有事务,就以非事务方式执行。

  • MANDATORY(2)

字面意思,强制性的,即当前存在事物,加入事物;否则抛出异常。
throw new IllegalTransactionStateException(“Transaction propagation ‘mandatory’ but no existing transaction found”);

  • REQUIRES_NEW(3)

新建事务,如果当前存在事务,把当前事务挂起。

  • NOT_SUPPORTED(4)

以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

  • NEVER(5)

以非事务方式执行,如果当前存在事务,则抛出异常。

  • NESTED(6)

中文翻译:嵌套,如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,新建一个事物
注意 REQUIRED 是加入事物,和父事物是同一个事物环境;NESTED 是嵌套事物,他有自己的事物环境,回滚不会影响父事物。

Isolation 隔离级别

  • DEFAULT(-1)

使用底层数据库的隔离级别

  • READ_UNCOMMITTED(1)

读未提交,可以读取其它事物已修改但未提交的数据,造成脏读

  • READ_COMMITTED(2)

读已提交,只能读取到其它事物已提交的数据,但是会造成不可重复读

  • REPEATABLE_READ(4)

可重复读,事物执行期间读取到的数据是一致的,但是会出现幻读情况

  • SERIALIZABLE(8)

串行操作

setTransactionIsolation、从源码可知,当 Spring 设置隔离级别与数据库不一致时,将以 Spring 设置的隔离级别为准,且仅对当前 SESSION 生效

rollbackFor 指定异常回滚

默认情况下,事物只会回滚 RuntimeException 和 Error

timeout 事物超时

事物执行的超时时间。

如果用这个注解描述一个方法的话,线程已经跑到方法里面,如果已经过去60秒了还没跑完这个方法并且线程在这个方法中的后面还有涉及到对数据库的增删改查操作时会报事务超时错误(会回滚)。

如果已经过去 60 秒了还没跑完但是后面已经没有涉及到对数据库的增删改查操作,那么这时不会报事务超时错误(不会回滚)。
https://www.jianshu.com/p/eac776d763c0

是否只读

当前只读事务就不能进行写的操作,否则报错 Cause: java.sql.SQLException: Connection is read-only. Queries leading to data modification are not allowed;

失效场景

  • 数据库引擎不支持事物,例如 MyISAM
  • Bean 没有被 Spring 管理
  • 方法非 public
  • 方法自调用
  • 数据源没有配置事务管理器(单数据源默认配置了,多数据源要手动配置)
  • 异常被 catch 没有抛出
  • 抛出的异常非 RuntimeException or Error

    被代理的方法为什么要 public? JDK 实现接口生成代理,接口一定是 public 地;若用 cglib,那么非 public 的方法子类获取不到。

动态代理

Spring默认使用JDK的动态代理实现AOP,类如果实现了接口,Spring就会使用这种方式实现动态代理;否则使用 CGLIB 实现。
JDK 动态代理是通过实现 InvocationHandler 接口,重写 invoke,然后通过 Proxy.newProxyInstance 返回代理对象;CGLib直接操作字节码,生成类的子类,重写类的方法完成代理。注意,CGLIB 不能代理被 final 修饰的方法,因为 final 是不可变不可继承。

若使用 Spring Boot 2.5.2 版本,application.properties 配置文件需开启一下配置spring.aop.proxy-target-class=false,否则会默认使用 CGLIB。

CGLIB 动态代理

  1. // 没有实现接口
  2. @Component
  3. public class TestB {
  4. public void sayHello(){
  5. System.out.println("Hello B");
  6. }
  7. }
  8. @Aspect
  9. @Component
  10. public class AspectB {
  11. @Before(value = "execution(* com.example.springaop.cblib.TestB.*(..))")
  12. public void before(){
  13. System.out.println("befor B Hello");
  14. }
  15. }
  16. @SpringBootTest
  17. class SpringAopApplicationTests {
  18. @Test
  19. void testB(){
  20. testB.sayHello();
  21. System.out.println(testB.getClass());
  22. }
  23. }
  24. 输出
  25. befor B Hello
  26. Hello B
  27. class com.example.springaop.cblib.TestB$$EnhancerBySpringCGLIB$$3914f3c1

源码分析

https://juejin.cn/post/6844903982721138696
从 Bean 的生命周期可以知道,Bean 在初始化阶段会执行 BeanPostProcessor 扩展点实现,直接从这里开始学习。
1. BeanPostProcessor.postProcessAfterInitialization 执行 AOP 增强

  1. public abstract class AbstractAutowireCapableBeanFactory {
  2. protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
  3. Object wrappedBean = bean;
  4. if (mbd == null || !mbd.isSynthetic()) {
  5. wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
  6. }
  7. if (mbd == null || !mbd.isSynthetic()) {
  8. // 执行后置拓展点
  9. wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
  10. }
  11. return wrappedBean;
  12. }
  13. // 执行 BeanPostProcessor 拓展点后置方法
  14. public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
  15. for(Iterator var4 = this.getBeanPostProcessors().iterator(); var4.hasNext(); result = current) {
  16. BeanPostProcessor processor = (BeanPostProcessor)var4.next();
  17. current = processor.postProcessAfterInitialization(result, beanName);
  18. }
  19. return result;
  20. }
  21. }
  1. AbstractAutoProxyCreator 重写 postProcessAfterInitialization 方法,通过 ProxyFactory.getProxy 方法得到代理对象。

    1. public abstract class AbstractAutoProxyCreator {
    2. public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
    3. if (bean != null) {
    4. Object cacheKey = this.getCacheKey(bean.getClass(), beanName);
    5. if (this.earlyProxyReferences.remove(cacheKey) != bean) {
    6. // 返回代理
    7. return this.wrapIfNecessary(bean, beanName, cacheKey);
    8. }
    9. }
    10. return bean;
    11. }
    12. protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    13. Object proxy = this.createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    14. }
    15. protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {
    16. return proxyFactory.getProxy(classLoader);
    17. }
    18. }
  2. ProxyFactory.getProxy 会先调用 ProxyCreatorSupport.getAopProxyFactory 创建工厂,然后再调用 createAopProxy 方法创建代理。 ```java public class ProxyFactory extends ProxyCreatorSupport { public Object getProxy(@Nullable ClassLoader classLoader) {
    1. return this.createAopProxy().getProxy(classLoader);
    } }

public class ProxyCreatorSupport extends AdvisedSupport { protected final synchronized AopProxy createAopProxy() { if (!this.active) { this.activate(); } return this.getAopProxyFactory().createAopProxy(this); } }

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {

  1. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  2. //
  3. if (NativeDetector.inNativeImage() || !config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
  4. return new JdkDynamicAopProxy(config);
  5. } else {
  6. Class<?> targetClass = config.getTargetClass();
  7. if (targetClass == null) {
  8. throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
  9. } else {
  10. // 如果不是接口 && 不是代理类,那么走 ObjenesisCglibAopProxy,否则走 JdkDynamicAopProxy
  11. return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
  12. }
  13. }
  14. }
  15. private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
  16. Class<?>[] ifcs = config.getProxiedInterfaces();
  17. return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]);
  18. }

} ```

SpringBoot

image.png