有如下代码场景,A
类的a1
方法没有标注@Transactional
注解,a2
方法标注了@Transactional
注解,那么在a1
方法里调用a2
方法,此时会开始事务吗?
不会开启事务。a1
方法是目标类A
的原生方法,调用a1
的时候即直接进入目标类A
进行调用,在目标类A
里面只有a2
的原生方法标注了@Transactional
注解,在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
)进行事务回滚操作。