1.概述
对声明式事务管理,Spring提供了基于Transactional注解的实现方式,使用简单,减少了很多复杂的配置。但是,正因为它的简单,很多开发人员在使用的时候,随手就是一个@Transactional,以为这样就把事务的问题解决了,何不知这样的使用方式很可能留下了很大的性能隐患。
2.原理
Spring很多的特性是基于动态代理实现的,Transactional注解的实现就是其中之一。TxNamespaceHandler类是深入理解Transactional注解实现方式的入口类。从源码分析得知,如果在类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方法,完成整个事务处理的逻辑,如是否开启新事务、在目标方法执行期间监测是否需要回滚事务、目标方法执行完成后提交事务等。
3.问题
当下对数据库连接的使用基本上都用连接池技术,每个应用会根据环境和自身需求设置一些合适的连接池配置,如果每个连接都一直被长时间占用,会导致数据库连接数不够用、系统各项压力指标不断攀升、系统缓慢等问题,所以说连接池中的每一个连接都是很昂贵的。那么问题就来了,只要需要事务就需要占用一个数据库连接,如果在需要开启事务的方法里进行一些IO操作、网络通讯等需要长时间处理的操作,这个数据库连接就一直被占用着,直到方法执行结束后自动提交事务或执行过程中发生异常回滚事务,这个数据库连接才会被释放掉。这个过程中还有一个很可怕的问题,如果在需要开启事务的方法里进行了网络通讯操作,而这个操作没有设置网络超时时间,那这个数据库连接就会被一直占用着。上述问题,在流量很大的情况下简直就是灾难,会直接导致应用系统挂掉。
4.正确姿势
正确的使用Transactional注解要做到如下三点:
1) 不要在类上标注Transactional注解,要在需要的方法上标注。即使类的每个方法都需要事务也不要在类上标注,因为有可能你或别人新添加的方法根本不需要事务。
2) 标注了Transactional注解的方法体中不要涉及耗时很久的操作,如IO操作、网络通信等。
3) 根据业务需要设置合适的事务参数,如是否需要新事务、超时时间等。
5.小结
对Spring框架的使用,多思考为什么。