剛剛接到一個上家公司同事的一個電話,問我為什么service方法事務不會滾了,日志打印了,調用webservice報錯。
我讓他把這個調用執行webservice的方法截圖發給我,如下:
public Object[] send(String operationName,Object[] params,String endPoint) throws Exception { Object[] results = null; Client client = null; try { // 獲取外部服務注冊信息 Map<String, ExtWebserviceInfo> extMap = (Map<String, ExtWebserviceInfo>)SimpleCacheUtils.get(ExtWebService.WEBSERVICE); // 獲取操作名稱 if (!ToolUtil.isNullOrEmpty(extMap)) { // 獲取服務方法名 String methodName = extMap.get(operationName).getServiceValue(); LOGGER.info("獲取到方法標識為*"+operationName+"的操作名"+methodName); // 創建client client = new Client(new URL(endPoint)); LOGGER.info("監測到請求"+endPoint+"服務"+methodName+"操作方法的動作..."); // 執行調用 results = client.invoke(methodName, params); LOGGER.info("向"+endPoint+"服務"+methodName+"操作方法發出的請求成功..."); } } catch (Exception e) { LOGGER.info("向"+endPoint+"服務標識為"+operationName+"的操作方法發出的請求出現異常..."+e.getLocalizedMessage()); throw(Exception)new Exception().initCause(e); } finally{ if(client != null){ client.close(); } } return results; }
這個方法是一個調用執行webservice的方法,並返回執行結果給調用者,就是上面這個方法報錯。我一看到這個代碼,中有try catch,我想一定是這個方法中把異常給攔截了,service中捕獲不到異常,導致aop攔截不到異常信息,導致事務不回滾。
但是仔細一看發現這個方法雖然捕獲了異常,但是捕獲后有重新拋出異常。
} catch (Exception e) { LOGGER.info("向"+endPoint+"服務標識為"+operationName+"的操作方法發出的請求出現異常..."+e.getLocalizedMessage()); throw(Exception)new Exception().initCause(e); } finally{
這不是又拋出異常了嗎?應該沒有問題了啊。
仔細分一下,就發現問題就出在這里,它拋出的是一個Exception,而spring在事務回滾時,是只對runtimeException或者其子類的異常才會回滾。
而且這里拋出的是一個Exception,別人調用的時候肯定提示要trycatch,如果一不小心,catch中沒有重新拋出runtimeException,那就會出現事務不回滾的問題,而且此問題一般不易被發現。
總結一下:
1、spring做aop事務攔截的一般都會配置在service層;
2、service層一般情況下不能trycatch,有時候有檢查行異常,那么就往上拋,如果又不想拋,那么就trycatch中在throw new RuntimeException("出錯了!!!");
3、service層拋出的異常必須是runtimeException或者其子類的異常。