Spring中的@ControllerAdvice和ResponseBodyAdvice詳解
@ControllerAdvice
@ControllerAdvice作用于@Controller修飾的類(lèi)里面的所有方法。
它主要有兩種作用:
對(duì)Controller的入?yún)⑦M(jìn)行預(yù)處理
對(duì)Controller中的異常進(jìn)行全局統(tǒng)一處理
ResponseBodyAdvice
ResponseBodyAdvice作用于@ResponseBody注解修飾的方法,它可以對(duì)這些方法的返回值進(jìn)行修改。
它是一個(gè)接口,這個(gè)接口有兩個(gè)函數(shù):
/** * @param returnType 可以得到方法和參數(shù)的相關(guān)信息(注解呀,類(lèi)型呀) * @param converterType HttpMessageConverter的實(shí)現(xiàn)類(lèi) * @return 是否對(duì)某個(gè)接口(被@ResponseBody修飾)的返回值進(jìn)行修改。如果為true就會(huì)調(diào)用 * beforeBodyWrite方法 */ boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType); /** * @param body 被@ResponseBody修飾方法的返回值(區(qū)別于returnType) * @param returnType 可以得到方法和參數(shù)的相關(guān)信息(注解呀,類(lèi)型呀) * @param selectedContentType 選中的媒體類(lèi)型,即以什么格式寫(xiě)出數(shù)據(jù)(json、xml、text...) * @param selectedConverterType HttpMessageConverter的實(shí)現(xiàn)類(lèi)的具體類(lèi)型 * @param request 請(qǐng)求對(duì)象 * @param response 響應(yīng)對(duì)象 * @return */ T beforeBodyWrite(@Nullable T body, MethodParameter returnType, MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request, ServerHttpResponse response);
類(lèi)上注釋如下:
Allows customizing the response after the execution of an @ResponseBody or a ResponseEntity controller method but before the body is written with an HttpMessageConverter.
根據(jù)類(lèi)上的注釋可以得知如果滿足以下兩個(gè)條件,則返回值會(huì)被ResponseBodyAdvice的beforeBodyWrite方法修改,修改之后的值被HttpMessageConverter寫(xiě)出,返回給前端瀏覽器。
Controller里的方法被@ResponseBody修飾或者Controller里的方法返回ResponseEntityResponseBodyAdvice的supports方法返回true 注意兩者的執(zhí)行順序
如果針對(duì)異常情況和正常情況我們都做了統(tǒng)一處理,要留意不要重復(fù)處理。
示例
新建一個(gè)Advice類(lèi),它被@ControllerAdvice修飾,又實(shí)現(xiàn)了ResponseBodyAdvice接口。我們希望在這個(gè)類(lèi)里面既能實(shí)現(xiàn)全局異常處理,又能對(duì)后端返回的數(shù)據(jù)統(tǒng)一封裝。
返回結(jié)果封裝類(lèi)
在Controller里可以返回任意類(lèi)型,他們都會(huì)被封裝到Result的data屬性中。
public class Result { private int code; private String data; // getter and seter }
全局處理異常,全局封裝返回值
@ControllerAdvice public class Advice implements ResponseBodyAdvice { //因?yàn)檫@里也加了@ResponseBody注解,所以它的返回值也會(huì)被ResponseBodyAdvice 處理一遍 @ExceptionHandler(Exception.class) @ResponseBody public Result he(Exception e) { Result res = new Result(); res.setCode(500); res.setData(e.getMessage()); return res; } @Override public boolean supports(MethodParameter returnType, Class converterType) { //返回任意類(lèi)型都要封裝 return true; } ObjectMapper objectMapper = new ObjectMapper(); @Override public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) { Result res = new Result(); res.setCode(200); //如果返回值是String,直接放到Result里 if (body instanceof String) { res.setData((String) body); return res; } //如果返回值是標(biāo)準(zhǔn)返回格式,就不需要再次封裝了 //如果不加這個(gè)判斷,異常的結(jié)果會(huì)被封裝兩次 else if (body instanceof Result) { return body; } String dataStr = null; try { dataStr = objectMapper.writeValueAsString(body); res.setData(dataStr); } catch (JsonProcessingException e) { e.printStackTrace(); } return res; } }
注意:Advice類(lèi)加了@ControllerAdvice注解,并實(shí)現(xiàn)了ResponseBodyAdvice
接口
簡(jiǎn)單起見(jiàn),直接在啟動(dòng)類(lèi)里面寫(xiě)RESTful接口。一個(gè)拋出異常,一個(gè)返回一個(gè)實(shí)體類(lèi)。
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ResponseBody; import org.springframework.web.bind.annotation.RestController; @SpringBootApplication @Controller public class Demo1Application { public static void main(String[] args) { SpringApplication.run(Demo1Application.class, args); } @GetMapping("ex") @ResponseBody public String ex() throws Exception { throw new Exception("異常信息"); } @GetMapping("stu") @ResponseBody public Stu s() { final Stu stu = new Stu(); stu.setName("zcx"); stu.setAge(22); return stu; } } class Stu { private String name; private int age; //getter and setter }
原理
因?yàn)楸籃ResponseBody注解注釋的返回值都會(huì)被RequestResponseBodyMethodProcessor處理,它里面的 HttpMessageConverter在寫(xiě)出數(shù)據(jù)時(shí),會(huì)先拿到一個(gè)RequestResponseBodyAdviceChain,先用RequestResponseBodyAdviceChain里面的responseBodyAdvice對(duì)Controller返回值進(jìn)行處理,再寫(xiě)到瀏覽器。
拓展
與ResponseBodyAdvice類(lèi)似的有RequestBodyAdvice,它可以對(duì)@RequestBody注釋的參數(shù)進(jìn)行額外處理,在使用時(shí)注意不要在beforeBodyRead里面把inputMessage的body讀出來(lái),否則會(huì)有I/O異常??梢允褂胊fterBodyRead對(duì)參數(shù)進(jìn)行修改。
到此這篇關(guān)于Spring中的@ControllerAdvice和ResponseBodyAdvice詳解的文章就介紹到這了,更多相關(guān)@ControllerAdvice和ResponseBodyAdvice內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis之foreach標(biāo)簽的用法及多種循環(huán)問(wèn)題
這篇文章主要介紹了MyBatis之foreach標(biāo)簽的用法及多種循環(huán)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11Java集合排序規(guī)則接口Comparator用法解析
這篇文章主要介紹了Java集合排序規(guī)則接口Comparator用法解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09解決idea中maven新增的配置文件xx.xml沒(méi)生效問(wèn)題
這篇文章主要介紹了如何解決idea中maven新增的配置文件xx.xml沒(méi)生效問(wèn)題,公司項(xiàng)目有用自己的`私服,Maven正常去私服下載jar包是沒(méi)問(wèn)題的,但阿里云鏡像找不到相關(guān)的jar包報(bào)錯(cuò),文中通過(guò)圖文介紹的非常詳細(xì),需要的朋友可以參考下2024-06-06mybatis-plus自帶QueryWrapper自定義sql實(shí)現(xiàn)復(fù)雜查詢實(shí)例詳解
MyBatis-Plus是一個(gè)MyBatis(opens new window)的增強(qiáng)工具,在 MyBatis的基礎(chǔ)上只做增強(qiáng)不做改變,MyBatis可以無(wú)損升級(jí)為MyBatis-Plus,這篇文章主要給大家介紹了關(guān)于mybatis-plus自帶QueryWrapper自定義sql實(shí)現(xiàn)復(fù)雜查詢的相關(guān)資料,需要的朋友可以參考下2022-10-10Java 手動(dòng)解析不帶引號(hào)的JSON字符串的操作
這篇文章主要介紹了Java 手動(dòng)解析不帶引號(hào)的JSON字符串的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-10-10