Spring MVC 接口 ResponseBodyAdvice 及其應(yīng)用最佳實(shí)踐記錄
ResponseBodyAdvice
是 Spring MVC 提供的一個(gè)強(qiáng)大接口,允許你在響應(yīng)體被寫(xiě)入 HTTP 響應(yīng)之前對(duì)其進(jìn)行全局處理。
下面我將全面介紹它的工作原理、使用場(chǎng)景和最佳實(shí)踐。
基本概念
接口定義
public interface ResponseBodyAdvice<T> { boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType); @Nullable T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response); }
核心方法
supports()
- 決定是否對(duì)該方法的返回值應(yīng)用 advice
- 參數(shù):
returnType
: 控制器方法的返回類(lèi)型信息converterType
: 將用于序列化響應(yīng)體的消息轉(zhuǎn)換器類(lèi)型
beforeBodyWrite()
- 在消息轉(zhuǎn)換器寫(xiě)入響應(yīng)體之前對(duì)其進(jìn)行處理
- 參數(shù):
body
: 控制器返回的原始響應(yīng)體- 其他參數(shù)與
supports()
相同 request
/response
: 當(dāng)前請(qǐng)求和響應(yīng)對(duì)象
典型應(yīng)用場(chǎng)景
1. 統(tǒng)一響應(yīng)封裝
最常見(jiàn)的用途是將所有控制器的返回值包裝成統(tǒng)一格式:
@RestControllerAdvice public class UnifiedResponseAdvice 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) { if (body instanceof ApiResponse) { return body; } return ApiResponse.success(body); } }
2. 響應(yīng)數(shù)據(jù)脫敏
對(duì)敏感數(shù)據(jù)進(jìn)行自動(dòng)處理:
@Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if (body instanceof UserInfo) { UserInfo user = (UserInfo) body; user.setIdCard(desensitize(user.getIdCard())); } return body; }
3. 響應(yīng)數(shù)據(jù)緩存
緩存特定響應(yīng):
@Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if (request.getURI().getPath().contains("/api/cacheable")) { cacheManager.put(generateCacheKey(request), body); } return body; }
高級(jí)用法與最佳實(shí)踐
1. 精確控制應(yīng)用范圍
通過(guò) supports()
方法精確控制哪些方法需要處理:
@Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { // 只處理標(biāo)注了@ResponseWrap注解的方法 return returnType.hasMethodAnnotation(ResponseWrap.class); // 或者排除特定包下的控制器 // return !returnType.getDeclaringClass().getPackage().getName().startsWith("org.springdoc"); }
2. 處理特殊情況 處理String類(lèi)型返回值
@Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if (body instanceof String) { response.getHeaders().setContentType(MediaType.APPLICATION_JSON); return objectMapper.writeValueAsString(ApiResponse.success(body)); } // 其他處理... }
處理文件下載等非JSON響應(yīng)
@Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { // 排除文件下載等場(chǎng)景 return !ResourceHttpMessageConverter.class.isAssignableFrom(converterType); }
3. 性能優(yōu)化
@RestControllerAdvice public class CustomResponseAdvice implements ResponseBodyAdvice<Object> { // 重用ObjectMapper實(shí)例 private static final ObjectMapper objectMapper = new ObjectMapper(); // 預(yù)定義的成功響應(yīng) private static final ApiResponse<?> EMPTY_SUCCESS = ApiResponse.success(null); @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { if (body == null) { return EMPTY_SUCCESS; } // 其他處理... } }
常見(jiàn)問(wèn)題解決方案
1. 與Swagger的兼容性問(wèn)題
@Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { // 排除Swagger相關(guān)的控制器 return !returnType.getDeclaringClass().getPackage().getName().startsWith("springfox.documentation"); }
2. 循環(huán)引用問(wèn)題
當(dāng)包裝的對(duì)象存在循環(huán)引用時(shí),需要在ObjectMapper中配置:
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); objectMapper.configure(SerializationFeature.FAIL_ON_SELF_REFERENCES, false);
3. 異常處理
雖然ResponseBodyAdvice
不處理異常,但可以與@ExceptionHandler
配合使用:
@ExceptionHandler(Exception.class) public ApiResponse<?> handleException(Exception e) { return ApiResponse.failure(e.getMessage()); }
完整示例
@RestControllerAdvice public class GlobalResponseAdvice implements ResponseBodyAdvice<Object> { private final ObjectMapper objectMapper; public GlobalResponseAdvice(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } @Override public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) { // 排除Swagger和Actuator端點(diǎn) return !(returnType.getDeclaringClass().getName().contains("springfox") || returnType.getDeclaringClass().getName().contains("org.springframework.boot.actuate")); } @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { // 非JSON響應(yīng)不處理 if (!selectedContentType.includes(MediaType.APPLICATION_JSON)) { return body; } // 已經(jīng)是包裝類(lèi)型不處理 if (body instanceof ApiResponse) { return body; } // 處理String類(lèi)型返回值 if (body instanceof String) { try { response.getHeaders().setContentType(MediaType.APPLICATION_JSON); return objectMapper.writeValueAsString(ApiResponse.success(body)); } catch (JsonProcessingException e) { throw new RuntimeException("JSON序列化失敗", e); } } // 空值處理 if (body == null) { return ApiResponse.success(); } // 默認(rèn)包裝 return ApiResponse.success(body); } @Data @NoArgsConstructor @AllArgsConstructor public static class ApiResponse<T> { private int code; private String message; private T data; private long timestamp = System.currentTimeMillis(); public static <T> ApiResponse<T> success() { return new ApiResponse<>(200, "success", null); } public static <T> ApiResponse<T> success(T data) { return new ApiResponse<>(200, "success", data); } } }
通過(guò)合理使用ResponseBodyAdvice
,你可以實(shí)現(xiàn)響應(yīng)處理的集中管理,使代碼更加整潔和一致。
到此這篇關(guān)于Spring MVC 接口 ResponseBodyAdvice 及其應(yīng)用最佳實(shí)踐記錄的文章就介紹到這了,更多相關(guān)Spring MVC ResponseBodyAdvice 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中的HashMap內(nèi)存泄漏問(wèn)題詳解
這篇文章主要介紹了Java中的HashMap內(nèi)存泄漏問(wèn)題詳解,WeakHashMap中的key是弱引用,如果再使用之后沒(méi)有及時(shí)remove掉這個(gè)key,那么當(dāng)GC時(shí)key就可能會(huì)被回收,導(dǎo)致key對(duì)應(yīng)的value對(duì)象占用的內(nèi)存無(wú)法回收進(jìn)而導(dǎo)致內(nèi)存泄漏,需要的朋友可以參考下2023-09-09基于Java編寫(xiě)簡(jiǎn)易的算式測(cè)試程序
本文將利用Java語(yǔ)言編寫(xiě)一個(gè)簡(jiǎn)易的算式測(cè)試程序,這個(gè)程序可以自動(dòng)生成指定數(shù)量的加減乘三則運(yùn)算題目,感興趣的小伙伴可以了解一下2022-05-05解決idea中yml文件圖標(biāo)問(wèn)題及自動(dòng)提示失效的情況
這篇文章主要介紹了解決idea中yml文件圖標(biāo)問(wèn)題及自動(dòng)提示失效的情況,具有很好的價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-01-01解決因jdk版本引起的TypeNotPresentExceptionProxy異常
這篇文章介紹了解決因jdk版本引起的TypeNotPresentExceptionProxy異常的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-12-12