在Spring Boot中,@Transactional注解用于声明一个事务方法,在方法执行期间,如果出现异常或者抛出指定的异常,就会回滚事务。但是,有几种情况下@Transactional注解可能会失效:
注解放在private、final或static方法上
@Transactional注解不能放在private、final或static方法上,否则会被忽略。
事务方法不是public的
默认情况下,只有public修饰的方法才能被Spring的事务管理器拦截,非public方法会被忽略。
在使用@Transactional注解时,需要注意如果标注了注解的方法修饰符不是public,则该注解将不会起作用。这是因为@Transactional是基于动态代理实现的,当创建bean实例时,Spring框架会扫描带有@Transactional注解的方法,并通过AOP技术生成代理对象并对事务进行管理。
在判断是否对某个bean进行代理时,Spring会创建一个BeanFactoryTransactionAttributeSourceAdvisor实例,并遍历该bean中的方法对象来判断是否包含@Transactional注解。当任何一个方法带有该注解时,就认为该bean适配该切点,需要创建代理对象并代理该方法的事务开闭逻辑。因此,如果标注了该注解的方法的修饰符不是public,则寻找到的注解信息为空,无法确定该切点是否适配该bean,从而导致事务不生效。
综上所述,标注@Transactional注解的方法必须使用public修饰,保证其正常被AOP代理,确保事务得到正确的开启和关闭。
异常被捕获并处理了
如果在@Transactional注解标记的方法中,出现了异常但是该异常被捕获并处理了,并没有抛出异常,那么这个事务就会正常提交,而不是回滚。
Spring AOP与AspectJ混用
如果项目引入了AspectJ,并且使用了@Aspect注解,则需要将事务配置成AspectJ模式(mode=AspectJ),否则事务不起作用。
没有开启事务注解支持
在Spring Boot项目中,需要在配置类上添加@EnableTransactionManagement注解来启用事务注解支持。
!!!类内部调用@Transactional标注的方法
@Transactional的实现原理在于以该注解为切面坐标,在扫描动态生成的代理类中以此坐标来在其前后分别设置事务开启和事务提交操作的增强,那么这些的前提是在于,你的操作环境是在于Spring动态代理生成的代理类中,而不是this对应的本类
具体来说,当一个类内部的方法(假设它也在同一个类中)调用被@Transactional注解标记的方法时,如果该类被Spring框架管理,则调用到该方法的语句行将在运行时被替换为通过代理类进行的调用,从而正确地开启和关闭事务。但如果要调用的方法是通过反射等方式访问(例如调用私有方法或调用非public方法),或者直接调用而不经过代理类,则由于未能通过代理类执行,将无法触发事务管理
既然事务管理是基于动态代理对象的代理逻辑实现的,那么如果在类内部调用类内部的事务方法,这个调用事务方法的过程并不是通过代理对象来调用的,而是直接通过this对象来调用方法,绕过的代理对象,肯定就是没有代理逻辑了。
那就是要内部调用该咋办才好?!
@Autowired注解通常用于自动装配Bean,其中包括普通Bean、集合类型的Bean和自定义的复杂对象Bean等。在Spring容器中,所有由容器管理的Bean都是被代理的对象,就是说在java中通过@Autowired注入的对象都是代理对象
所以说,我们可以这样玩儿
通过以上三步就可以实现。通过代理对象来调用该方法,这样子就能确保该事物控制注解是生效的状态