一、@ResponseBodyAdvice的理解
1、ResponseBodyAdvice接口類 —— 其描述大致解釋下:
允許在執行{@code@ResponseBody}或{@code ResponseEntity}控制器方法之后,但在使用{@code HttpMessageConverter}編寫主體之前自定義響應。
實現可以直接用{@code RequestMappingHandlerAdapter}和{@code exceptionhandleexceptionresolver}注冊,或者更可能用{@code@ControllerAdvice}注釋,在這種情況下,它們將被兩者自動檢測。
/** * Allows customizing the response after the execution of an {@code @ResponseBody} * or a {@code ResponseEntity} controller method but before the body is written * with an {@code HttpMessageConverter}. * * Implementations may be registered directly with * {@code RequestMappingHandlerAdapter} and {@code ExceptionHandlerExceptionResolver} * or more likely annotated with {@code @ControllerAdvice} in which case they * will be auto-detected by both.*/
public interface ResponseBodyAdvice<T> { /** * Whether this component supports the given controller method return type * and the selected {@code HttpMessageConverter} type. * @param returnType the return type * @param converterType the selected converter type * @return {@code true} if {@link #beforeBodyWrite} should be invoked; * {@code false} otherwise */
boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType); /** * Invoked after an {@code HttpMessageConverter} is selected and just before * its write method is invoked. * @param body the body to be written * @param returnType the return type of the controller method * @param selectedContentType the content type selected through content negotiation * @param selectedConverterType the converter type selected to write to the response * @param request the current request * @param response the current response * @return the body that was passed in or a modified (possibly new) instance */ T beforeBodyWrite(T body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response); }
2、個人理解:
ResponseBodyAdvice 接口是在 Controller 執行 return 之后,在 response 返回給客戶端之前,執行的對 response 的一些處理,可以實現對 response 數據的一些統一封裝或者加密等操作。
該接口一共有兩個方法:
(1)supports —— 判斷是否要執行beforeBodyWrite方法,true為執行,false不執行 —— 通過supports方法,我們可以選擇哪些類或哪些方法要對response進行處理,其余的則不處理。
(2)beforeBodyWrite —— 對 response 處理的具體執行方法。
二、實例
有一個Controller類,返回參數為OutputObject,我們通過ResponseBodyAdvice,對該類的所有方法返回的OutputObject中的部分數據進行統一加密處理。
// 對響應報文統一處理,對bean內容進行加密 @Component //聲明該類要處理的包路徑
@ControllerAdvice("com.cmos.edcreg.web.controller") public class ResponseAdvice implements ResponseBodyAdvice { private final Logger logger = LoggerFactory.getLogger(ResponseAdvice.class); // 對response處理的具體方法 @Override public Object beforeBodyWrite(Object arg0, MethodParameter arg1, MediaType arg2, Class arg3, ServerHttpRequest arg4, ServerHttpResponse arg5) { OutputObject out = new OutputObject(); try { //arg0轉換為OutputObject類型
ObjectMapper objectMapper=new ObjectMapper(); out = objectMapper.readValue(org.json.JSONObject.valueToString(arg0), OutputObject.class); //獲取加密密鑰
String oldEncryptKey = out.getBean().get("oldEncryptKey"); //獲取加密字符串
DesSpecial des = new DesSpecial(); String encryptData = des.strEnc(JSON.toJSONString(out.getBean()), oldEncryptKey, null, null); //封裝數據(清除原來數據,放入加密數據)
out.getBean().clear(); out.getBean().put("data", encryptData); return out; } catch (Exception e) { logger.error("返回報文處理出錯", e); out.setReturnCode(ReturnInfoEnums.PROCESS_ERROR.getCode()); out.setReturnMessage(ReturnInfoEnums.PROCESS_ERROR.getMessage()); return out; } } /* 選擇哪些類,或哪些方法需要走beforeBodyWrite * 從arg0中可以獲取方法名和類名 * arg0.getMethod().getDeclaringClass().getName()為獲取方法名 */ @Override public boolean supports(MethodParameter arg0, Class arg1) { return "com.cmos.edcreg.web.controller.GdH5AppointmentActiveVideoNewController".equals(arg0.getMethod().getDeclaringClass().getName()); } }
三、在spring項目開發過程中的應用場景
對controller層返回值進行修改增強處理。比如返回值5,需要封裝成:{"code":"0","data":5,,"msg":"success"} 格式返回前端
1、controller層業務代碼
@RestController //此注解包含@ResponseBody注解
@RequestMapping("/mp") public class ResponseBodyAdviceController { @RequestMapping(value = "/hello", method = RequestMethod.GET) public int hello() {
return 5; } }
2、實現ResponseBodyAdvice接口的切面類
/** *此注解針對controller層的類做增強功能,即對加了@RestController注解的類進行處理 */ @ControllerAdvice(annotations = RestController.class) public class RestResultWrapper implements ResponseBodyAdvice<Object> { @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { return true; } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { //定義一個統一的返回類
RestResult responseResult = new RestResult( "0", body, "success"); //如果handler處理類的返回類型是String(即控制層的返回值類型),為了保證一致性,這里需要將ResponseResult轉回去
if(body instanceof String) { return JSON.toJSONString(responseResult); } //封裝后的數據返回到前端頁面
return JSONObject.toJSON(responseResult); } }
3、返回公共類的創建
// 統一返回Rest風格的數據結構
public class RestResult<T> implements Serializable { private String code = "2000"; // 成功時返回的數據,失敗時返回具體的異常信息
private T data; // 請求失敗返回的提示信息,給前端進行頁面展示的信息
private String message ; public RestResult() { } @Override public String toString() { return "RestResult{" +
"code='" + code + '\'' +
", data=" + data +
", message=" + message +
'}'; } public RestResult(String code, T data, String message) { this.code = code; this.data = data; this.message = message; } }