由hsf框架異常拋出引發的思考


先交待背景,項目使用了HSF框架,Pandora容器,此項目我方作為生產者提供服務給其他第三方調用,在測試過程中,發現接口有不盡人意的地方,以下為具體描述。

假定A作為服務生產者,B作為服務消費者,A提供有接口方法addEntityFile,定義如下

ResultJsonEO<List<EntityResultEO>> addEntityFileJson(EntityFileInputDTO inputDTO);

其中,ResultJsonEO結構如下
public class ResultJsonEO<T>{

public int code;
private String msg;

/**
* 實體對象
*/
private T data;

public String getMsg() {
return msg;
}

public ResultJsonEO<T> setMsg(String msg) {
this.msg = msg;
return this;
}

public T getData() {
return data;
}

public ResultJsonEO<T> setData(T data) {
this.data = data;
return this;
}

public ResultJsonEO<T> setCode(ResultCode resultCode) {
this.code = resultCode.code;
return this;
}

public int getCode() {
return code;
}

public ResultJsonEO<T> setCode(int code) {
this.code = code;
return this;
}

/**
* 調用成功
* @return
*/
public boolean isSuccess() {
return ResultCode.SUCCESS.code == this.code;
}

}

對於調用方來說,根據返回的json對象(泛化)可以得知,不論提供方內部如何異常,能拿到的總是ResultJsonEO對象,並能通過isSuccess()方法得知返回結果是否正常。 實際上,在測試時,我們發現服務內部發生異常后,對方得不到結果,只得到如下異常提示,非常不友好。

java.util.HashMap cannot be cast to xx.common.api.domain.eo.ResultJsonEO

剛開始看到這個報錯的時候,很疑惑,因為找遍了代碼也沒有找到哪里有HashMap轉json對象的這種操作,最后才發現這壓根就是hsf框架將異常泛化后拋出的,終究還是因為服務內部沒有針對異常進行捕獲或拋出。

 

這里補充說明一下,如果不考慮統一返回json對象,其實可以做一個全局異常處理,一旦遇到了異常,就統一拋出異常信息,只不過這時對於消費者來說,調用此接口方法時,就需要額外的異常處理。

 

找到了問題原因所在,接下來就是針對性處理。假如是一個傳統的ssh項目,或者是一個單純的springCloud項目(沒有用到這種rpc框架),大體結構如下

controller

service

dao

domain

通常都是在service層處理業務邏輯,這一層也負責事務處理,而且在service層是不會進行異常捕獲的,避免中斷spring事務,異常捕獲都是放在controller層里處理,通常都有統一的全局異常捕獲處理。此項目中,報上述錯誤時的大體結構如下

apiservice      接口定義層

apiserviceImpl    接口實現層,主要的業務邏輯均在里面,包括事務處理

dao

domain

首先,想到了一種解決方案,即將apiserviceImpl層拆分,拆分為apiserviceImpl層和service層,主要的邏輯和事務管理均在service層中完成,apiserviceImpl充當一個類似controller層的角色(注意,這里如果把apiserviceImpl充當controller層來看時,service層是一定要拆分出去的,假如不拆分,直接針對apiserviceImpl進行異常捕獲,碰到事務時,一旦發生異常,事務就會被中斷)。

apiservice

apiserviceImpl

service      專門處理業務邏輯,事務

dao

domain

這樣做的好處在於層次清晰,也可以使用全局異常處理,切面面向impl層,唯一的弊端在於所有的impl代碼都得調整,進行代碼拆分和service注入,改動稍微有點大。

 

最后,采用了Filter的方式,只需要增加一個Filter,繼承hsf原有的Filter的onResponse方法。

import com.taobao.hsf.invocation.filter.ServerFilter;

public class HsfServerFilter implements ServerFilter {
  /**
  * @param invocation
  * @param rpcResult
  */
  @Override
  public void onResponse(Invocation invocation, RPCResult rpcResult) {
  Class returnType = invocation.getReturnClass();
  Object appResponse = rpcResult.getAppResponse();
  if(appResponse instanceof Exception) {
  Object re = rpcResult.getAppResponse();
  log.error("methodName:"+invocation.getMethodName()
+"\tmethodArgs:"+invocation.getMethodArgs().toString()
+"\tmethodException:"+re.toString());
  if(returnType.getName().indexOf("ResultDataEO") > -1){
  rpcResult.setAppResponse(ResultDataEO.fail("服務異常,請聯系管理員......"));
  }else{
  rpcResult.setAppResponse(ResultJsonResponse.responseErr("服務異常,請聯系管理員......"));
  }
  }
  }
}

onResponse方法,無論接口實現類中是否執行成功,此方法總是會執行的,只不過執行成功的情況下,getAppResponse()得到的是正確的返回類,比如ResultJsonEO,失敗的情況下則是異常。上面的解決方式則是異常情況下,強制將返回的信息賦值進去。

 

這樣一來,代碼改動量不大,而且也不會影響事務(onResponse方法區別於前面提到的aop切面實現的方式,切面方式相當於是把實現類里的代碼try catch了,而filter中的onResponse方法是在實現類中的方法執行完之后才會進入)。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM