面试题:Spring声明式事务处理

有如下代码场景,A类的a1方法没有标注@Transactional注解,a2方法标注了@Transactional注解,那么在a1方法里调用a2方法,此时会开始事务吗?

不会开启事务。a1方法是目标类A的原生方法,调用a1的时候即直接进入目标类A进行调用,在目标类A里面只有a2的原生方法标注了@Transactional注解,在a1里调用a2,即直接执行a2的原生方法,并不通过创建代理对象进行调用,所以并不会进入TransactionInterceptorinvoke方法,不会开启事务。

那此时如果在a1方法上标注@Transactional注解,a2方法不标注@Transactional注解,但是a1方法的访问修饰符是protected,在a1方法里调用a2方法会开始事务吗?

也不会开启事务。@Transactional的工作机制是基于AOP实现的,而AOP是使用动态代理实现的,动态代理要么是JDK方式、要么是Cglib方式。如果是JDK动态代理的方式,根据上面的分析可以知道,目标类的目标方法是在接口中定义的,也就是必须是public修饰的方法才可以被代理。如果是Cglib方式,代理类是目标类的子类,理论上可以代理publicprotected方法,但是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方法会进入对应的代理方法中进行处理;

如果只在类Ab方法(使用public修饰)上标注@Transactional注解,Spring容器会在启动的时候,为类A创建一个代理类B,但只会为类Ab方法创建一个代理方法,调用类Ab方法会进入对应的代理方法中进行处理,调用类A的其它public方法,则还是进入类A的方法中处理。在进入代理类的某个方法之前,会先执行TransactionInterceptor类中的invoke方法,完成整个事务处理的逻辑,如是否开启新事务、在目标方法执行期间监测是否需要回滚事务、目标方法执行完成后提交事务等。

Spring框架对事务回滚的实现,是不是对所有类型的异常都会进行事务回滚操作?

Spring并不会对所有类型异常都进行事务回滚操作,默认是只对Unchecked ExceptionErrorRuntimeException)进行事务回滚操作。

发布了94 篇原创文章 · 获赞 0 · 访问量 722

猜你喜欢

转载自blog.csdn.net/qq_46578181/article/details/105458054