/**PageBeginHtml Block Begin **/ /***自定义返回顶部小火箭***/ /*生成博客目录的JS 开始*/ /*生成博客目录的JS 结束*/

SpringBoot+Shiro引起事务失效、错误原因、解决方法

SpringBoot+Shiro引起事务失效、错误原因、解决方法

一、问题

今天发现用户注册的Service的事务并没有起到作用,再抛出一个RuntimeException后,并没有发生回滚,下面是调试步骤:

1、检查数据库的引擎是否是innoDB

2、启动类上是否加入@EnableTransactionManagement注解

3、是否在方法上加入@Transactional注解或Service的类上是否有@Transactional注解

4、方法是否为public

5、是否是因为抛出了Exception等checked异常

然而事务失效都不是这些原因引起的,并且发现其他Service的事务都可以正常使用。在查看打印的异常调用链的时候,发现这个Service是没有被AOP代理过的,所以推测可能是因为其他整合Spring的框架提前引用了这个Service。

为了验证,新建了一个Service,并且把代码copy到新建的类中,测试其事务,发现事务可以正常使用,下面是打印的异常信息:

从上面可以明显看到,Spring为这个service生成了代理类,证明事务是可以正常使用的,并且原service的失效应该是其他的整合Spring的框架提前引用造成的。

因为项目还使用了Shiro作为权限管理,并且在编写Shiro的自定义验证器Realm中引用了该UserService,后来把Realm中的Service换成了Mapper后,该Service的事务可以正常使用了。

 

错误原因:

Spring中事务是通过AOP创建代理对象来完成的,有BeanFactoryTransactionAttributeSourceAdvisor完成对需要事务的方法织入对事务的处理。完成创建AOP代理对象的功能由一个特殊的BeanPostProcessor完成--AnnotationAwareAspectJAutoProxyCreator。该类实现了BeanPostProcessor接口,在bean创建完成并将属性设置好之后,拦截bean,并创建代理对象,在原对象的方法功能上添加增强器中增强方法的处理。对于事务增强器BeanFactoryTransactionAttributeSourceAdvisor而言,也就是在原有方法上加入事务的功能。

但是,在ApplicationContext刷新上下文过程(refresh)中,上下文会调用registerBeanPostProcessors方法将BeanFactory中的所有BeanPostProcessor后处理器注册到BeanFactory中,使其后面流程中创建bean的时候生效。下面是其实现源码:


  1. public static void registerBeanPostProcessors(
  2. ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
  3. String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
  4. // Register BeanPostProcessorChecker that logs an info message when
  5. // a bean is created during BeanPostProcessor instantiation, i.e. when
  6. // a bean is not eligible for getting processed by all BeanPostProcessors.
  7. int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
  8. beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
  9. // Separate between BeanPostProcessors that implement PriorityOrdered,
  10. // Ordered, and the rest.
  11. List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
  12. List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
  13. List<String> orderedPostProcessorNames = new ArrayList<>();
  14. List<String> nonOrderedPostProcessorNames = new ArrayList<>();
  15. for (String ppName : postProcessorNames) {
  16. if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
  17. BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
  18. priorityOrderedPostProcessors.add(pp);
  19. if (pp instanceof MergedBeanDefinitionPostProcessor) {
  20. internalPostProcessors.add(pp);
  21. }
  22. }
  23. else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
  24. orderedPostProcessorNames.add(ppName);
  25. }
  26. else {
  27. nonOrderedPostProcessorNames.add(ppName);
  28. }
  29. }
  30. // First, register the BeanPostProcessors that implement PriorityOrdered.
  31. sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
  32. registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
  33. // AnnotationAwareAspectJAutoProxyCreator实现了Ordered接口,所以会在这里排序
  34. // 但是,实现Ordered接口的BeanPostProcessor中,有一个是MethodValidationPostProcessor
  35. List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
  36. for (String ppName : orderedPostProcessorNames) {
  37. BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
  38. orderedPostProcessors.add(pp);
  39. if (pp instanceof MergedBeanDefinitionPostProcessor) {
  40. internalPostProcessors.add(pp);
  41. }
  42. }
  43. sortPostProcessors(orderedPostProcessors, beanFactory);
  44. registerBeanPostProcessors(beanFactory, orderedPostProcessors);
  45. // Now, register all regular BeanPostProcessors.
  46. List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
  47. for (String ppName : nonOrderedPostProcessorNames) {
  48. BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
  49. nonOrderedPostProcessors.add(pp);
  50. if (pp instanceof MergedBeanDefinitionPostProcessor) {
  51. internalPostProcessors.add(pp);
  52. }
  53. }
  54. registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
  55. // Finally, re-register all internal BeanPostProcessors.
  56. sortPostProcessors(internalPostProcessors, beanFactory);
  57. registerBeanPostProcessors(beanFactory, internalPostProcessors);
  58. // Re-register post-processor for detecting inner beans as ApplicationListeners,
  59. // moving it to the end of the processor chain (for picking up proxies etc).
  60. beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
  61. }

上面流程为:

  1. 实例化所有实现了PriorityOrdered接口的BeanPostProcessor,排序后注册到BeanFactory
  2. 实例化所有实现了Ordered接口的BeanPostProcessor,排序后注册到BeanFactory
  3. 实例化剩余的BeanPostProcessor,注册到BeanFactory
  4. 注册内部使用的BeanPostProcessor
  5. 添加一个特殊的后处理器--ApplicationListenerDetector

由于AnnotationAwareAspectJAutoProxyCreator实现了Ordered接口,所以会在第2步中注册到BeanFactory,然后生效,可以拦截bean创建并生成代理对象。但是,在其注册前,有一个同样实现了Ordered接口的MethodValidationPostProcessor。在该类的实例化过程中,会由ValidationAutoConfiguration通过工厂方法来创建,创建过程中,需要传入Environment对象作为参数,然后Spring会从BeanFactory中查找所有符合Environment类型的bean,下面是查询过程:


  1. //type为需要的参数,类型为Environment
  2. private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
  3. List<String> result = new ArrayList<>();
  4. //遍历BeanFactory中保存的所有beanName
  5. for (String beanName : this.beanDefinitionNames) {
  6. if (!isAlias(beanName)) {
  7. try {
  8. RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
  9. // 检查是否是合格的bean
  10. if (!mbd.isAbstract() && (allowEagerInit ||
  11. (mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
  12. !requiresEagerInitForType(mbd.getFactoryBeanName()))) {
  13. // 是否是FactoryBean.
  14. boolean isFactoryBean = isFactoryBean(beanName, mbd);
  15. BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
  16. boolean matchFound =
  17. (allowEagerInit || !isFactoryBean ||
  18. (dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
  19. (includeNonSingletons ||
  20. (dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
  21. isTypeMatch(beanName, type);
  22. if (!matchFound && isFactoryBean) {
  23. beanName = FACTORY_BEAN_PREFIX + beanName;
  24. // 如果是FactoryBean,在比较类型是,会实例化FactoryBean对象,用作比对
  25. // isTypeMatch会比较FactoryBean对应的实际类型是否符合,所以会实例化FactoryBean
  26. matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
  27. }
  28. if (matchFound) {
  29. result.add(beanName);
  30. }
  31. }
  32. }
  33. //catch...略
  34. }
  35. }
  36. // Check manually registered singletons too.
  37. //省略直接注册的单例检查
  38. return StringUtils.toStringArray(result);
  39. }

如果存在FactoryBean的话,在比对过程中会实例化FactoryBean(isTypeMatch中实现)

在isTypeMatch中有这么一段代码:


  1. if (FactoryBean.class.isAssignableFrom(beanType)) {
  2. if (!BeanFactoryUtils.isFactoryDereference(name) && beanInstance == null) {
  3. // 如果是FactoryBean类型,需要对其实例化后才能知道到底它创建的bean是什么类型
  4. beanType = getTypeForFactoryBean(beanName, mbd);
  5. if (beanType == null) {
  6. return false;
  7. }
  8. }
  9. }

下面是getTypeForFactoryBean中的创建FactoryBean实例部分的代码:


  1. FactoryBean<?> fb = (mbd.isSingleton() ?
  2. getSingletonFactoryBeanForTypeCheck(beanName, mbd) :
  3. getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));

下面是getSingletonFactoryBeanForTypeCheck实现:


  1. private FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
  2. synchronized (getSingletonMutex()) {
  3. //先尝试从缓存中获取
  4. BeanWrapper bw = this.factoryBeanInstanceCache.get(beanName);
  5. if (bw != null) {
  6. return (FactoryBean<?>) bw.getWrappedInstance();
  7. }
  8. //从单例缓存中获取
  9. Object beanInstance = getSingleton(beanName, false);
  10. if (beanInstance instanceof FactoryBean) {
  11. return (FactoryBean<?>) beanInstance;
  12. }
  13. if (isSingletonCurrentlyInCreation(beanName) ||
  14. (mbd.getFactoryBeanName() != null && isSingletonCurrentlyInCreation(mbd.getFactoryBeanName()))) {
  15. return null;
  16. }
  17. //创建对象
  18. Object instance;
  19. try {
  20. beforeSingletonCreation(beanName);
  21. instance = resolveBeforeInstantiation(beanName, mbd);
  22. if (instance == null) {
  23. //实例化
  24. bw = createBeanInstance(beanName, mbd, null);
  25. instance = bw.getWrappedInstance();
  26. }
  27. }
  28. finally {
  29. afterSingletonCreation(beanName);
  30. }
  31. FactoryBean<?> fb = getFactoryBean(beanName, instance);
  32. if (bw != null) {
  33. this.factoryBeanInstanceCache.put(beanName, bw);
  34. }
  35. return fb;
  36. }
  37. }

从上面代码看以看到,Spring会实例化FactoryBean,以确定其创建的bean的类型

由于ShiroFilterFactoryBean实现了FactoryBean接口,所以它会提前被初始化。又因为SecurityManager,SecurityManager依赖于Realm实现类、Realm实现类又依赖于UserService,所以引发所有相关的bean提前初始化。

ShiroFilterFactoryBean -> SecurityManager -> Realm实现类 -> UserService

但是此时还只是ApplicationContext中registerBeanPostProcessors注册BeanPostProcessor处理器的阶段,此时AnnotationAwareAspectJAutoProxyCreator还没有注册到BeanFactory中,UserService无法享受到事务处理!

三、解决办法

  1. 在Realm实现中使用Mapper,而不是直接使用Service对象。缺点:直接和数据库交互,并且也没有Service中的逻辑交互以及缓存
  2. 在Realm中Service声明上加入@Lazy注解,延迟Realm实现中Service对象的初始化时间,这样就可以保证Service实际初始化的时候会被BeanPostProcessor拦截,创建具有事务功能的代理对象

 

 

posted @ 2020-08-25 17:17  一品堂.技术学习笔记  阅读(316)  评论(0编辑  收藏  举报