内容简介:公众号[
公众号[ 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被面试官问烂了,可为什么面试官还是喜欢继续问
点点" 在看 " 呗
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- RabbitMQ灵魂拷问
- 拷问传统企业CIO:微服务化值得吗?
- 灵魂拷问:Java 的 substring() 是如何工作的?
- 灵魂拷问:你为什么总有加不完的班
- 灵魂拷问 Go 语言:这个变量到底分配到哪里了?
- 中台灵魂拷问,计划经济模式还是市场经济模式
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
两周自制脚本语言
[日]千叶 滋 / 陈筱烟 / 人民邮电出版社 / 2014-6 / 59.00元
《两周自制脚本语言》是一本优秀的编译原理入门读物。全书穿插了大量轻松风趣的对话,读者可以随书中的人物一起从最简单的语言解释器开始,逐步添加新功能,最终完成一个支持函数、数组、对象等高级功能的语言编译器。本书与众不同的实现方式不仅大幅简化了语言处理器的复杂度,还有助于拓展读者的视野。 《两周自制脚本语言》适合对编译原理及语言处理器设计有兴趣的读者以及正在学习相关课程的大中专院校学生。同时,已经......一起来看看 《两周自制脚本语言》 这本书的介绍吧!