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的時候生效。下面是其實現源碼:

	public static void registerBeanPostProcessors(
			ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

		String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

		// Register BeanPostProcessorChecker that logs an info message when
		// a bean is created during BeanPostProcessor instantiation, i.e. when
		// a bean is not eligible for getting processed by all BeanPostProcessors.
		int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
		beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

		// Separate between BeanPostProcessors that implement PriorityOrdered,
		// Ordered, and the rest.
		List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
		List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
		List<String> orderedPostProcessorNames = new ArrayList<>();
		List<String> nonOrderedPostProcessorNames = new ArrayList<>();
		for (String ppName : postProcessorNames) {
			if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
				BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
				priorityOrderedPostProcessors.add(pp);
				if (pp instanceof MergedBeanDefinitionPostProcessor) {
					internalPostProcessors.add(pp);
				}
			}
			else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
				orderedPostProcessorNames.add(ppName);
			}
			else {
				nonOrderedPostProcessorNames.add(ppName);
			}
		}

		// First, register the BeanPostProcessors that implement PriorityOrdered.
		sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

		// AnnotationAwareAspectJAutoProxyCreator實現了Ordered接口,所以會在這裏排序
		// 但是,實現Ordered接口的BeanPostProcessor中,有一個是MethodValidationPostProcessor
		List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
		for (String ppName : orderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			orderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		sortPostProcessors(orderedPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, orderedPostProcessors);

		// Now, register all regular BeanPostProcessors.
		List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
		for (String ppName : nonOrderedPostProcessorNames) {
			BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
			nonOrderedPostProcessors.add(pp);
			if (pp instanceof MergedBeanDefinitionPostProcessor) {
				internalPostProcessors.add(pp);
			}
		}
		registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

		// Finally, re-register all internal BeanPostProcessors.
		sortPostProcessors(internalPostProcessors, beanFactory);
		registerBeanPostProcessors(beanFactory, internalPostProcessors);

		// Re-register post-processor for detecting inner beans as ApplicationListeners,
		// moving it to the end of the processor chain (for picking up proxies etc).
		beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
	}

上面流程爲:

  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,下面是查詢過程:

	//type爲需要的參數,類型爲Environment
	private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {
		List<String> result = new ArrayList<>();
		
		//遍歷BeanFactory中保存的所有beanName
		for (String beanName : this.beanDefinitionNames) {
			if (!isAlias(beanName)) {
				try {
					RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
					// 檢查是否是合格的bean
					if (!mbd.isAbstract() && (allowEagerInit ||
							(mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&
									!requiresEagerInitForType(mbd.getFactoryBeanName()))) {
						// 是否是FactoryBean.
						boolean isFactoryBean = isFactoryBean(beanName, mbd);
						BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();
						boolean matchFound =
								(allowEagerInit || !isFactoryBean ||
										(dbd != null && !mbd.isLazyInit()) || containsSingleton(beanName)) &&
								(includeNonSingletons ||
										(dbd != null ? mbd.isSingleton() : isSingleton(beanName))) &&
								isTypeMatch(beanName, type);
						if (!matchFound && isFactoryBean) {
							beanName = FACTORY_BEAN_PREFIX + beanName;
							// 如果是FactoryBean,在比較類型是,會實例化FactoryBean對象,用作比對
							// isTypeMatch會比較FactoryBean對應的實際類型是否符合,所以會實例化FactoryBean
							matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);
						}
						if (matchFound) {
							result.add(beanName);
						}
					}
				}
				//catch...略
			}
		}

		// Check manually registered singletons too.
		//省略直接註冊的單例檢查

		return StringUtils.toStringArray(result);
	}

如果存在FactoryBean的話,在比對過程中會實例化FactoryBean(isTypeMatch中實現)

在isTypeMatch中有這麼一段代碼:

		if (FactoryBean.class.isAssignableFrom(beanType)) {
			if (!BeanFactoryUtils.isFactoryDereference(name) && beanInstance == null) {
				// 如果是FactoryBean類型,需要對其實例化後才能知道到底它創建的bean是什麼類型
				beanType = getTypeForFactoryBean(beanName, mbd);
				if (beanType == null) {
					return false;
				}
			}
		}

下面是getTypeForFactoryBean中的創建FactoryBean實例部分的代碼:

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

下面是getSingletonFactoryBeanForTypeCheck實現:

	private FactoryBean<?> getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {
		synchronized (getSingletonMutex()) {
			//先嚐試從緩存中獲取
			BeanWrapper bw = this.factoryBeanInstanceCache.get(beanName);
			if (bw != null) {
				return (FactoryBean<?>) bw.getWrappedInstance();
			}
			//從單例緩存中獲取
			Object beanInstance = getSingleton(beanName, false);
			if (beanInstance instanceof FactoryBean) {
				return (FactoryBean<?>) beanInstance;
			}
			if (isSingletonCurrentlyInCreation(beanName) ||
					(mbd.getFactoryBeanName() != null && isSingletonCurrentlyInCreation(mbd.getFactoryBeanName()))) {
				return null;
			}

			//創建對象
			Object instance;
			try {
				beforeSingletonCreation(beanName);
				instance = resolveBeforeInstantiation(beanName, mbd);
				if (instance == null) {
					//實例化
					bw = createBeanInstance(beanName, mbd, null);
					instance = bw.getWrappedInstance();
				}
			}
			finally {
				afterSingletonCreation(beanName);
			}

			FactoryBean<?> fb = getFactoryBean(beanName, instance);
			if (bw != null) {
				this.factoryBeanInstanceCache.put(beanName, bw);
			}
			return fb;
		}
	}

從上面代碼看以看到,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攔截,創建具有事務功能的代理對象

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章