您好,登錄后才能下訂單哦!
本篇文章為大家展示了如何解決Controller層返回值的公共包裝類的問(wèn)題,內(nèi)容簡(jiǎn)明扼要并且容易理解,絕對(duì)能使你眼前一亮,通過(guò)這篇文章的詳細(xì)介紹希望你能有所收獲。
場(chǎng)景:在微服務(wù)中,一般返回?cái)?shù)據(jù)都會(huì)有個(gè)返回碼、返回信息和返回消息體,但是每次返回時(shí)候調(diào)用或者是封裝,太過(guò)麻煩,有沒有什么辦法不用每次都封裝呢?
答案是有的。
返回值對(duì)象 ResponseData
package com.study.auth.comm; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.annotation.JSONField; import com.alibaba.fastjson.serializer.SerializerFeature; import java.io.Serializable; /** * @Package: com.study.auth.comm * @Description: <返回?cái)?shù)據(jù)> * @Author: MILLA * @CreateDate: 2018/4/8 9:10 * @UpdateUser: MILLA * @UpdateDate: 2018/4/8 9:10 * @Version: 1.0 */ public final class ResponseData<T> implements Serializable { private static final long serialVersionUID = 7824278330465676943L; private static final String SUCCESS_CODE = "1000"; private static final String SUCCESS_MSG = "success"; /** * 響應(yīng)編碼 */ @JSONField(serialzeFeatures = {SerializerFeature.WriteMapNullValue}, ordinal = 1) private String code; /** * 響應(yīng)提示 */ @JSONField(serialzeFeatures = {SerializerFeature.WriteMapNullValue}, ordinal = 2) private String msg; /** * 返回的數(shù)據(jù) */ @JSONField(serialzeFeatures = {SerializerFeature.WriteMapNullValue}, ordinal = 10) private T data; public static ResponseData success() { return initData(SUCCESS_CODE, SUCCESS_MSG, null); } public static ResponseData error(String code) { String msg = PropertiesReaderUtil.getProperty(code, null); return initData(code, msg, null); } public static ResponseData error(String code, String msg) { return initData(code, msg, null); } public static <T> ResponseData success(T t) { return initData(SUCCESS_CODE, SUCCESS_MSG, t); } public static <T> ResponseData errorData(String code, T data) { String msg = PropertiesReaderUtil.getProperty(code, null); return initData(code, msg, data); } public static <T> ResponseData errorData(String code, String msg, T data) { return initData(code, msg, data); } private static <T> ResponseData initData(String code, String msg, T t) { ResponseData data = new ResponseData(SUCCESS_CODE); if (!isBlank(msg)) { data.setMsg(msg); } if (!isBlank(code)) { data.setCode(code); } if (t != null) { data.setData(t); } return data; } private static boolean isBlank(CharSequence cs) { int strLen; if (cs != null && (strLen = cs.length()) != 0) { for (int i = 0; i < strLen; ++i) { if (!Character.isWhitespace(cs.charAt(i))) { return false; } } return true; } else { return true; } } public ResponseData() { } public ResponseData(String code) { this.code = code; } public String getCode() { return code; } public void setCode(String code) { this.code = code; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public T getData() { return data; } public void setData(T data) { this.data = data; } @Override public String toString() { return JSON.toJSONString(this); } }
如上圖的包裝,還是太繁瑣了。
裝飾者模式使用-增強(qiáng)類InitializingAdviceDecorator通過(guò)實(shí)現(xiàn)InitializingBean和裝飾者模式對(duì)Controller層的返回值進(jìn)行包裝,大致思路:
通過(guò)RequestMappingHandlerAdapter獲取所有的返回值處理對(duì)象HandlerMethodReturnValueHandler創(chuàng)建一個(gè)新的集合存儲(chǔ)上一步獲取的集合(因?yàn)樯弦徊降慕Y(jié)果是unmodifiableList類型的)遍歷該集合找到HandlerMethodReturnValueHandler對(duì)象,將這個(gè)位置的handler替換程自定義的handler將新獲到的集合重新設(shè)置到RequestMappingHandlerAdapter的setReturnValueHandlers方法中
package com.study.auth.config; import com.study.auth.comm.ResponseData; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.core.MethodParameter; import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.ModelAndViewContainer; import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter; import org.springframework.web.servlet.mvc.method.annotation.RequestResponseBodyMethodProcessor; import java.util.ArrayList; import java.util.List; /** * @Package: com.study.auth.config.core * @Description: <增強(qiáng)controller層返回值> * @Author: milla * @CreateDate: 2020/09/04 14:42 * @UpdateUser: milla * @UpdateDate: 2020/09/04 14:42 * @UpdateRemark: <> * @Version: 1.0 */ @Configuration public class InitializingAdviceDecorator implements InitializingBean { @Autowired private RequestMappingHandlerAdapter adapter; @Override public void afterPropertiesSet() { //獲取所有的handler對(duì)象 List<HandlerMethodReturnValueHandler> returnValueHandlers = adapter.getReturnValueHandlers(); //因?yàn)樯厦娣祷氐氖莡nmodifiableList,所以需要新建list處理 List<HandlerMethodReturnValueHandler> handlers = new ArrayList(returnValueHandlers); this.decorateHandlers(handlers); //將增強(qiáng)的返回值回寫回去 adapter.setReturnValueHandlers(handlers); } /** * 使用自定義的返回值控制類 * * @param handlers */ private void decorateHandlers(List<HandlerMethodReturnValueHandler> handlers) { for (HandlerMethodReturnValueHandler handler : handlers) { if (handler instanceof RequestResponseBodyMethodProcessor) { //找到返回值的handler并將起包裝成自定義的handler ControllerReturnValueHandler decorator = new ControllerReturnValueHandler((RequestResponseBodyMethodProcessor) handler); int index = handlers.indexOf(handler); handlers.set(index, decorator); break; } } } /** * 自定義返回值的Handler * 采用裝飾者模式 */ private class ControllerReturnValueHandler implements HandlerMethodReturnValueHandler { //持有一個(gè)被裝飾者對(duì)象 private HandlerMethodReturnValueHandler handler; ControllerReturnValueHandler(RequestResponseBodyMethodProcessor handler) { this.handler = handler; } @Override public boolean supportsReturnType(MethodParameter returnType) { return true; } /** * 增強(qiáng)被裝飾者的功能 * * @param returnValue 返回值 * @param returnType 返回類型 * @param mavContainer view * @param webRequest 請(qǐng)求對(duì)象 * @throws Exception 拋出異常 */ @Override public void handleReturnValue(Object returnValue, MethodParameter returnType, ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception { //如果已經(jīng)封裝了結(jié)構(gòu)體就直接放行 if (returnValue instanceof ResponseData) { handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest); return; } //正常返回success ResponseData success = ResponseData.success(returnValue); handler.handleReturnValue(success, returnType, mavContainer, webRequest); } } }
配置文件讀取類PropertiesReaderUtil
package com.study.auth.comm; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.Properties; /** * @Package: com.milla.navicat.comm * @Description: <讀取配置properties工具類> * @Author: MILLA * @CreateDate: 2018/8/10 10:30 * @UpdateUser: MILLA * @UpdateDate: 2018/8/10 10:30 * @UpdateRemark: <> * @Version: 1.0 */ public final class PropertiesReaderUtil { private static final String ENCODING = "UTF-8"; private static final Logger logger = LoggerFactory.getLogger(PropertiesReaderUtil.class); private static Properties propsZH; private static Properties propsCN; private static String name = null; static { //加載英文 //loadProps(false); //加載中文 loadProps(true); } /** * 第一種,通過(guò)類加載器進(jìn)行獲取properties文件流 * 第二種,通過(guò)類進(jìn)行獲取properties文件流 * in = PropertyUtil.class.getResourceAsStream("/properties/message_ZH.properties"); * in = PropertiesReaderUtil.class.getClassLoader().getResourceAsStream("properties/message_ZH.properties"); */ synchronized static private void loadProps(boolean isZh) { logger.debug("start loading properties"); InputStream in = null; if (isZh) { propsZH = new Properties(); name = "properties/message_ZH.properties"; in = PropertiesReaderUtil.class.getClassLoader().getResourceAsStream(name); } else { propsCN = new Properties(); name = "properties/message_EN.properties"; in = PropertiesReaderUtil.class.getClassLoader().getResourceAsStream(name); } try { if (isZh) { propsZH.load(new InputStreamReader(in, ENCODING)); } else { propsCN.load(new InputStreamReader(in, ENCODING)); } } catch (Exception e) { logger.debug("loading properties error :{}", e); } finally { try { if (null != in) { in.close(); } } catch (IOException e) { logger.debug("closing properties io error :{}", e); } } } public static String getProperty(String key) { return getPropertyZH(key); } public static String getProperty(String key, String defaultValue) { return getPropertyZH(key, defaultValue); } public static String getPropertyZH(String key) { if (null == propsZH) { loadProps(true); } return propsZH.getProperty(key); } public static String getPropertyZH(String key, String defaultValue) { if (null == propsZH) { loadProps(true); } return propsZH.getProperty(key, defaultValue); } public static String getPropertyCN(String key) { if (null == propsCN) { loadProps(false); } return propsCN.getProperty(key); } public static String getPropertyCN(String key, String defaultValue) { if (null == propsCN) { loadProps(false); } return propsCN.getProperty(key, defaultValue); } }
配置文件message_ZH.properties路徑為:properties/message_ZH.properties
也可添加國(guó)家化英文或者是其他語(yǔ)言配置
1001=用戶未登錄
#====非業(yè)務(wù)返回碼=========
1100=服務(wù)器內(nèi)部錯(cuò)誤
1101=空指針異常
1102=數(shù)據(jù)類型轉(zhuǎn)換異常
1103=IO異常
1104=該方法找不到異常
1105=數(shù)組越界異常
1106=請(qǐng)求體缺失異常
1107=類型匹配異常
1108=請(qǐng)求參數(shù)缺失異常
1109=請(qǐng)求方法不支持異常
1110=請(qǐng)求頭類型不支持異常
1111=參數(shù)解析異常
1112=必要參數(shù)不能為空
#=======================
統(tǒng)一異常捕捉類RestfulExceptionHandler此時(shí),基本能保證增強(qiáng)Controller層的返回值了,如果有需要的話,可能通過(guò)@RestControllerAdvice注解,針對(duì)拋出的異常使用返回值對(duì)象進(jìn)行包裝
package com.study.auth.exception; import com.alibaba.fastjson.JSONException; import com.study.auth.comm.PropertiesReaderUtil; import com.study.auth.comm.ResponseData; import com.study.auth.constant.CommonConstant; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.TypeMismatchException; import org.springframework.boot.json.JsonParseException; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseStatus; import org.springframework.web.bind.annotation.RestControllerAdvice; import javax.security.auth.login.AccountException; import java.io.IOException; import java.sql.SQLException; /** * @Package: com.study.auth.exception * @Description: <所有異常攔截類> * @Author: milla * @CreateDate: 2020/09/04 15:35 * @UpdateUser: milla * @UpdateDate: 2020/09/04 15:35 * @UpdateRemark: <> * @Version: 1.0 */ @Slf4j @RestControllerAdvice public class RestfulExceptionHandler { private ResponseData responseData(String code, Exception e) { log.error("異常代碼:{},異常描述:{},異常堆棧:", code, PropertiesReaderUtil.getProperty(code), e); return ResponseData.error(code); } private ResponseData<String> responseData(String code, String message, Exception e) { log.error("異常代碼:{},異常描述:{},異常堆棧:", code, message, e); return ResponseData.error(code, message); } /** * 運(yùn)行時(shí)異常 * * @param e 異常 * @return */ @ExceptionHandler(Exception.class) public ResponseData runtimeExceptionHandler(Exception e) { return responseData(CommonConstant.EX_RUN_TIME_EXCEPTION, e); } /** * 處理SQLSyntaxErrorException * * @param e 異常 * @return */ @ExceptionHandler(SQLException.class) public ResponseData<String> sqlException(SQLException e) { return responseData(CommonConstant.EX_RUN_TIME_EXCEPTION, e.getMessage(), e); } /** * 處理CustomerMessageException * * @param e 異常 * @return */ @ExceptionHandler(CustomMessageException.class) public ResponseData<String> customerMessageException(CustomMessageException e) { return responseData(CommonConstant.EX_RUN_TIME_EXCEPTION, e.getMessage(), e); } /** * 處理AccountException * * @param e 異常 * @return */ @ExceptionHandler(AccountException.class) public ResponseData<String> accountException(AccountException e) { return responseData(e.getMessage(), e); } //---------------------------------------jdk/spring自帶的異常---------------------------------- /** * 處理IllegalArgumentException * * @param e 異常 * @return */ @ExceptionHandler(IllegalArgumentException.class) public ResponseData<String> illegalArgumentException(IllegalArgumentException e) { return responseData(CommonConstant.EX_RUN_TIME_EXCEPTION, e.getMessage(), e); } /** * 空指針異常 * * @param e 異常 * @return */ @ResponseStatus @ExceptionHandler(NullPointerException.class) public ResponseData nullPointerExceptionHandler(NullPointerException e) { return responseData(CommonConstant.EX_NULL_POINTER_EXCEPTION, e); } /** * 類型轉(zhuǎn)換異常 * * @param e 異常 * @return */ @ExceptionHandler(ClassCastException.class) public ResponseData classCastExceptionHandler(ClassCastException e) { return responseData(CommonConstant.EX_CLASS_CAST_EXCEPTION, e); } /** * IO異常 * * @param e 異常 * @return */ @ExceptionHandler(IOException.class) public ResponseData iOExceptionHandler(IOException e) { return responseData(CommonConstant.EX_IO_EXCEPTION, e); } /** * 未知方法異常 * * @param e 異常 * @return */ @ExceptionHandler(NoSuchMethodException.class) public ResponseData noSuchMethodExceptionHandler(NoSuchMethodException e) { return responseData(CommonConstant.EX_NO_SUCH_METHOD_EXCEPTION, e); } /** * 數(shù)組越界異常 * * @param e 異常 * @return */ @ExceptionHandler(IndexOutOfBoundsException.class) public ResponseData indexOutOfBoundsExceptionHandler(IndexOutOfBoundsException e) { return responseData(CommonConstant.EX_INDEX_OUT_OF_BOUNDS_EXCEPTION, e); } /** * 請(qǐng)求body缺失異常 * * @param e 異常 * @return */ @ExceptionHandler({HttpMessageNotReadableException.class}) public ResponseData requestNotReadable(HttpMessageNotReadableException e) { return responseData(CommonConstant.EX_HTTP_MESSAGE_NOT_READABLE_EXCEPTION, e); } /** * 類型匹配異常 * * @param e 異常 * @return */ @ExceptionHandler({TypeMismatchException.class}) public ResponseData requestTypeMismatch(TypeMismatchException e) { return responseData(CommonConstant.EX_HTTP_MESSAGE_NOT_READABLE_EXCEPTION, e); } /** * 方法不支持異常 * * @param e 異常 * @return */ @ExceptionHandler({HttpRequestMethodNotSupportedException.class}) public ResponseData methodNotSupported(HttpRequestMethodNotSupportedException e) { return responseData(CommonConstant.EX_HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION, e); } /** * 請(qǐng)求頭不支持異常 * * @param e 異常 * @return */ @ExceptionHandler({HttpMediaTypeNotSupportedException.class}) public ResponseData mediaTypeNotAcceptable(HttpMediaTypeNotSupportedException e) { return responseData(CommonConstant.EX_HTTP_MEDIA_TYPE_NOT_ACCEPTABLE_EXCEPTION, e); } /** * 參數(shù)解析異常 * * @param e 異常 * @return */ @ExceptionHandler(JSONException.class) public ResponseData runtimeExceptionHandler(JSONException e) { return responseData(CommonConstant.PARAMS_PARSE_EXCEPTION, e); } /** * 參數(shù)解析異常 * * @param e 異常 * @return */ @ExceptionHandler(JsonParseException.class) public ResponseData runtimeExceptionHandler(JsonParseException e) { return responseData(CommonConstant.PARAMS_PARSE_EXCEPTION, e); } /** * 請(qǐng)求參數(shù)缺失異常 * * @param e 異常 * @return */ @ExceptionHandler({MissingServletRequestParameterException.class}) public ResponseData requestMissingServletRequest(MissingServletRequestParameterException e) { return responseData(CommonConstant.EX_MISSING_SERVLET_REQUEST_PARAMETER_EXCEPTION, e); } /** * 參數(shù)不能為空 * * @param e 異常 * @return */ @ExceptionHandler(MethodArgumentNotValidException.class) public ResponseData exceptionHandler(MethodArgumentNotValidException e) { return responseData(CommonConstant.PARAMS_IS_NULL, e); } }
常量類 CommonConstant
package com.study.auth.constant; /** * @Package: com.study.auth.constant * @Description: <公共常量類> * @Author: milla * @CreateDate: 2020/09/04 15:37 * @UpdateUser: milla * @UpdateDate: 2020/09/04 15:37 * @UpdateRemark: <> * @Version: 1.0 */ public final class CommonConstant { /** * 當(dāng)前用戶名稱 */ public static final String C_CURRENT_ACCOUNT = "current_account"; /** * 用戶未登錄 */ public static final String EX_NO_TOKEN_EXCEPTION = "1001"; //--------------------------------非業(yè)務(wù)返回碼--------------------------------------- /** * 運(yùn)行時(shí)異常 */ public static final String EX_RUN_TIME_EXCEPTION = "1100"; /** * 空指針異常 */ public static final String EX_NULL_POINTER_EXCEPTION = "1101"; /** * 數(shù)據(jù)轉(zhuǎn)換異常 */ public static final String EX_CLASS_CAST_EXCEPTION = "1102"; /** * IO異常 */ public static final String EX_IO_EXCEPTION = "1103"; /** * 找不到該方法異常 */ public static final String EX_NO_SUCH_METHOD_EXCEPTION = "1104"; /** * 數(shù)組越界異常 */ public static final String EX_INDEX_OUT_OF_BOUNDS_EXCEPTION = "1105"; /** * 請(qǐng)求體缺失異常 */ public static final String EX_HTTP_MESSAGE_NOT_READABLE_EXCEPTION = "1106"; /** * TYPE匹配異常 */ public static final String EX_TYPE_MISMATCH_EXCEPTION = "1107"; /** * 請(qǐng)求參數(shù)丟失 */ public static final String EX_MISSING_SERVLET_REQUEST_PARAMETER_EXCEPTION = "1108"; /** * 請(qǐng)求方法類型不支持異常 */ public static final String EX_HTTP_REQUEST_METHOD_NOT_SUPPORTED_EXCEPTION = "1109"; /** * MEDIA 類型不支持異常 */ public static final String EX_HTTP_MEDIA_TYPE_NOT_ACCEPTABLE_EXCEPTION = "1110"; /** * 參數(shù)解析異常 */ public static final String PARAMS_PARSE_EXCEPTION = "1111"; /** * 參數(shù)不能為空 */ public static final String PARAMS_IS_NULL = "1112"; //----------------------------------------------------------------------------------- }
自定義異常類 CustomMessageException
package com.study.auth.exception; /** * @Package: com.study.auth.exception * @Description: <自定義異常類> * @Author: MILLA * @CreateDate: 2019/8/15 18:39 * @UpdateUser: MILLA * @UpdateDate: 2019/8/15 18:39 * @UpdateRemark: <> * @Version: 1.0 */ public class CustomMessageException extends RuntimeException { public CustomMessageException() { super(); } public CustomMessageException(String message) { super(message); } public CustomMessageException(String message, Throwable cause) { super(message, cause); } public CustomMessageException(Throwable cause) { super(cause); } protected CustomMessageException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { super(message, cause, enableSuppression, writableStackTrace); } }
所需依賴因?yàn)槭褂昧税⒗锏膄astJson工具類還需要進(jìn)入該類的依賴
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.58</version> </dependency>
至此,可以愉快的使用該返回值的增強(qiáng)類了,在為服務(wù)中,還以將該代碼重構(gòu)到comm中,供多個(gè)服務(wù)共同使用,避免重復(fù)早輪子
上述內(nèi)容就是如何解決Controller層返回值的公共包裝類的問(wèn)題,你們學(xué)到知識(shí)或技能了嗎?如果還想學(xué)到更多技能或者豐富自己的知識(shí)儲(chǔ)備,歡迎關(guān)注億速云行業(yè)資訊頻道。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。