

Spring 声明式事务处理的实现原理,来自面试官的穷追拷问
source link: https://mp.weixin.qq.com/s/Q-Q4-NDCMPmlurtVM-0XMw
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

公众号[ JavaQ ]原创,专注分享Java基础原理分析、实战技术、微服务架构、分布式系统构建,诚邀点赞关注!
面试官 :有如下代码场景,A类的a1方法没有标注@Transactional注解,a2方法标注了@Transactional注解,那么在a1方法里调用a2方法,此时会开始事务吗?
小小白 :不会开启事务。
面试官 :解释一下为什么?
小小白 :a1方法是目标类A的原生方法,调用a1的时候即直接进入目标类A进行调用,在目标类A里面只有a2的原生方法,在a1里调用a2,即直接执行a2的原生方法,并不通过创建代理对象进行调用,所以并不会进入TransactionInterceptor的invoke方法,不会开启事务。
面试官 :那此时如果在a1方法上标注@Transactional注解,a2方法不标注@Transactional注解,但是a1方法的访问修饰符是protected,在a1方法里调用a2方法会开始事务吗?
小小白 :也不会开启事务。
面试官 :根据你上面的回答改了代码,为什么还是不会开启事务?
小小白 :@Transactional的工作机制是基于AOP实现的,而AOP是使用动态代理实现的,动态代理要么是JDK方式、要么是Cglib方式。如果是JDK动态代理的方式,根据上面的分析可以知道,目标类的目标方法是在接口中定义的,也就是必须是public修饰的方法才可以被代理。如果是Cglib方式,代理类是目标类的子类,理论上可以代理public和protected方法,但是Spring在进行事务增强是否能够应用到当前目标类判断的时候,遍历的是目标类的public方法,所以Cglib方式也只对public方法有效。
面试官 :Spring框架中声明式事务处理是如何实现的?
小小白 :Spring容器在初始化每个单例bean的时候,会遍历容器中的所有BeanPostProcessor实现类,并执行其postProcessAfterInitialization方法,在执行AbstractAutoProxyCreator类的postProcessAfterInitialization方法时会遍历容器中所有的切面,查找与当前实例化bean匹配的切面,这里会获取事务属性切面,查找@Transactional注解及其属性值,然后根据得到的切面创建一个代理对象,默认是使用JDK动态代理创建代理,如果目标类是接口,则使用JDK动态代理,否则使用Cglib。在创建代理的过程中会获取当前目标方法对应的拦截器,此时会得到TransactionInterceptor实例,在它的invoke方法中实现事务的开启和回滚,在需要进行事务操作的时候,Spring会在调用目标类的目标方法之前进行开启事务、调用异常回滚事务、调用完成会提交事务。是否需要开启新事务,是根据@Transactional注解上配置的参数值来判断的。如果需要开启新事务,获取Connection连接,然后将连接的自动提交事务改为false,改为手动提交。当对目标类的目标方法进行调用的时候,若发生异常将会进入completeTransactionAfterThrowing方法。
面试官 :能否通俗的讲述一下它的实现原理?
小小白 :如果在类A上标注Transactional注解,Spring容器会在启动的时候,为类A创建一个代理类B,类A的所有public方法都会在代理类B中有一个对应的代理方法,调用类A的某个public方法会进入对应的代理方法中进行处理;如果只在类A的b方法(使用public修饰)上标注Transactional注解,Spring容器会在启动的时候,为类A创建一个代理类B,但只会为类A的b方法创建一个代理方法,调用类A的b方法会进入对应的代理方法中进行处理,调用类A的其它public方法,则还是进入类A的方法中处理。在进入代理类的某个方法之前,会先执行TransactionInterceptor类中的invoke方法,完成整个事务处理的逻辑,如是否开启新事务、在目标方法执行期间监测是否需要回滚事务、目标方法执行完成后提交事务等。
面试官 :Spring框架对事务回滚的实现,是不是对所有类型的异常都会进行事务回滚操作?
小小白 :Spring并不会对所有类型异常都进行事务回滚操作,默认是只对Unchecked Exception(Error和RuntimeException)进行事务回滚操作。
面试官 :有没有遇到过Transactional注解的不合理用法?
小小白 :当下对数据库连接的使用基本上都用连接池技术,每个应用会根据环境和自身需求设置一些合适的连接池配置,如果每个连接都一直被长时间占用,会导致数据库连接数不够用、系统各项压力指标不断攀升、系统缓慢等问题,所以说连接池中的每一个连接都是很昂贵的。那么问题就来了,只要需要事务就需要占用一个数据库连接,如果在需要开启事务的方法里进行一些IO操作、网络通讯等需要长时间处理的操作,这个数据库连接就一直被占用着,直到方法执行结束后自动提交事务或执行过程中发生异常回滚事务,这个数据库连接才会被释放掉。这个过程中还有一个很可怕的问题,如果在需要开启事务的方法里进行了网络通讯操作,而这个操作没有设置网络超时时间,那这个数据库连接就会被一直占用着。上述问题,在流量很大的情况下简直就是灾难,会直接导致应用系统挂掉。
面试官 :如何解决这些问题?
小小白 :正确的使用Transactional注解需要做到如下四点:
-
不要在类上标注Transactional注解,要在需要的方法上标注。即使类的每个方法都需要事务也不要在类上标注,因为有可能你或别人新添加的方法根本不需要事务;
-
标注了Transactional注解的方法体中不要涉及耗时很久的操作,如IO操作、网络通信等;
-
根据业务需要设置合适的事务参数,如是否需要新事务、超时时间等;
-
控制事务影响的范围,代码中减少事务影响的代码。
往期推荐 :
面试官一步一步的套路你,为什么SimpleDateFormat不是线程安全的
都说ThreadLocal被面试官问烂了,可为什么面试官还是喜欢继续问
点点" 在看 " 呗
Recommend
-
44
-
45
云湖湖导读: 在当今大数据时代,随着存储数据量的增长,对数据库插入与读取性能的要求越来越严苛。为了提升访问数据库的性能,已经有越来越多成熟的方案来并发访问数据库,Spark JDBC Datasource就是其中之一。而另...
-
31
查询优化:顾名思义就是创建索引、视图等方式使数据库快速查找到需要的东西索引分为:唯一索引、主键索引、聚集索引、非聚集索引、复合索引、全文索引。在SQLServer中,一个表只能创建一个聚集索引,但可以创建多个非聚集索引。设置某列为主键,该列默认就为聚集索...
-
14
数据服务的高可用是所有企业都想拥有的,但是要想让数据有高可用性,就需要冗余数据写多份。写多份的问题会带来一致性的问题,而一致性的问题又会带来性能问题,这就会陷入一个无解的死循环!这里所谓数据一致性,就是当多个用户试图同时访问一个数据库时,如果它们...
-
23
博文目录一、索引二、视图三、存储过程四、系统存储过程五、触发器六、事务七、锁一、索引索引提供指针以指向存储在表中指定列的数据值,然后根据指定的次序排列这些指针,再跟随指针到达包含该值的列。1、什么是索引数据库中的索引与书籍中的目录相似。在一本书中...
-
17
前言 本专题大纲: 我重新整理了大纲,思考了很久,决定单独将MySQL的事务实现原理跟Spring中的事务示例分为两篇文章,因为...
-
16
.NET简谈事务、分布式事务处理 在本人的 “
-
11
分布式系统的事务处理 分布式系统的事务处理 当我们在生产线上用一台服务器来提供数据服务的时候,我会遇到如下的两个问题: 1)一台服务器的性能不足以提供足够的能力服务于所有的网络请求。 2)我们总是害怕...
-
12
-
4
Spring中使用@Async与@Transactional协调异步与事务处理 本文旨在阐明 Spring@Transacti...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK