欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

ResponseBodyAdvice的使用原理源碼解析

 更新時(shí)間:2023年03月13日 14:31:19   作者:半夏之沫  
這篇文章主要為大家介紹了ResponseBodyAdvice的使用原理源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

ResponseBodyAdvice接口可以在將handler方法的返回值寫入response前對(duì)返回值進(jìn)行處理,例如將返回值封裝成一個(gè)與客戶端約定好的對(duì)象以便于客戶端處理響應(yīng)數(shù)據(jù)。本篇文章將學(xué)習(xí)如何使用ResponseBodyAdvice以及其實(shí)現(xiàn)原理。

SpringBoot版本:2.4.1

正文

一. ResponseBodyAdvice的使用

假如已經(jīng)存在一個(gè)Controller,如下所示。

@RestController
public class DemoController {
    @RequestMapping(value = "/api/v1/demo1/getdefault", method = RequestMethod.GET)
    public ResponseEntity<Demo1> getDefaultDemo1() {
        return new ResponseEntity<>(Demo1.defaultDemo1, HttpStatus.INTERNAL_SERVER_ERROR);
    }
    @RequestMapping(value = "/api/v1/demo2/getdefault", method = RequestMethod.GET)
    public Demo2 getDefaultDemo2() {
        return Demo2.defaultDemo2;
    }
}
public class Demo1 {
    private int id;
    private String name;
    public static Demo1 defaultDemo1 = new Demo1(1, "Admin");
    public Demo1() {}
    public Demo1(int id, String name) {		this.id = id;
		this.name = name;
    }
    // 省略getter和setter
}
public class Demo2 {
    private int id;
    private String desc;
    public static Demo2 defaultDemo2 = new Demo2(1, "Root");
    public Demo2() {}
    public Demo2(int id, String desc) {
		this.id = id;
        this.desc = desc;
    }
    // 省略getter和setter
}

上述Controller中有兩個(gè)方法,并且返回值分別為ResponseEntity<Demo1>和Demo2。此時(shí)客戶端收到響應(yīng)之后,針對(duì)響應(yīng)體的處理變得十分不方便,如果增加更多的方法,并且返回值都不相同,那么客戶端將需要根據(jù)不同的請(qǐng)求來(lái)特定的處理響應(yīng)體。因此為了方便客戶端處理響應(yīng)數(shù)據(jù),服務(wù)器端專門創(chuàng)建了一個(gè)返回結(jié)果類ReturnResult,并且規(guī)定服務(wù)器端的所有handler方法執(zhí)行后往response中寫入的響應(yīng)體都必須為ReturnResult。在這種情況下,使用ResponseBodyAdvice可以在不修改已有業(yè)務(wù)代碼的情況下輕松實(shí)現(xiàn)上述需求。假設(shè)自定義的返回結(jié)果類ReturnResult如下所示。

public class ReturnResult<T> {
    private int statusCode;
    private T body;
    public ReturnResult() {}
    public ReturnResult(T body) {
        this.body = body;
    }
    // 省略getter和setter
}

ReturnResult的body就是原本需要寫入response的響應(yīng)內(nèi)容,現(xiàn)在整個(gè)ReturnResult為需要寫入response的響應(yīng)內(nèi)容,相當(dāng)于ReturnResult對(duì)handler方法的返回值進(jìn)行了一層封裝。

現(xiàn)在創(chuàng)建一個(gè)ReturnResultAdvice類并實(shí)現(xiàn)ResponseBodyAdvice接口,如下所示。

@ControllerAdvice
public class ReturnResultAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(@Nullable MethodParameter returnType, @Nullable Class converterType) {
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object body, @Nullable MethodParameter returnType,
                                  @Nullable MediaType selectedContentType, @Nullable Class selectedConverterType,
                                  @Nullable ServerHttpRequest request, @Nullable ServerHttpResponse response) {
        if (body == null) {
            return null;
        }
        if (body instanceof ReturnResult) {
            return body;
        }
        return new ReturnResult<>(body);
    }
}

ReturnResultAdvice的beforeBodyWrite() 方法會(huì)在handler方法返回值寫入response前被調(diào)用。

此時(shí)調(diào)用DemoController的接口,會(huì)發(fā)現(xiàn)響應(yīng)數(shù)據(jù)結(jié)構(gòu)統(tǒng)一為ReturnResult。

小節(jié):由@ControllerAdvice注解修飾并實(shí)現(xiàn)ResponseBodyAdvice接口的類所實(shí)現(xiàn)的beforeBodyWrite()方法會(huì)在handler方法返回值寫入response前被調(diào)用,并且handler方法返回值會(huì)作為入?yún)魅?code>beforeBodyWrite(),從而可以在返回值寫入response前對(duì)返回值進(jìn)行一些定制操作,例如對(duì)返回值進(jìn)行一層封裝。

二. ResponseBodyAdvice的原理

首先說明一下為什么第一小節(jié)中DemoController的getDefaultDemo1() 方法的返回值類型為ResponseEntity<Demo1>,但是實(shí)際往response寫的響應(yīng)體內(nèi)容為ResponseEntity中的body。首先所有ResponseBodyAdvice接口的調(diào)用是發(fā)生在AbstractMessageConverterMethodProcessor的writeWithMessageConverters() 方法中,這個(gè)方法的聲明如下所示。

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
            ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
            throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException;

其中value就是需要寫入響應(yīng)體的值,同時(shí)也是ResponseBodyAdvice要處理的值。然后如果handler方法的返回值是非ResponseEntity對(duì)象且handler方法由@ResponseBody注解修飾,那么writeWithMessageConverters() 方法的調(diào)用發(fā)生在RequestResponseBodyMethodProcessor#handleReturnValue方法中;

如果handler方法的返回值是ResponseEntity對(duì)象,那么writeWithMessageConverters() 方法的調(diào)用發(fā)生在HttpEntityMethodProcessor#handleReturnValue中,分別看一下在這兩個(gè)方法中調(diào)用writeWithMessageConverters() 時(shí)傳入的參數(shù),就可以解釋之前的疑問了。

RequestResponseBodyMethodProcessor#handleReturnValue

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    ......
    writeWithMessageConverters(returnValue, returnType, inputMessage, outputMessage);
}

HttpEntityMethodProcessor#handleReturnValue()

public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
        ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
    ......
    HttpEntity<?> responseEntity = (HttpEntity<?>) returnValue;
    ......
    writeWithMessageConverters(responseEntity.getBody(), returnType, inputMessage, outputMessage);
    ......
}

現(xiàn)在正式開始對(duì)ResponseBodyAdvice的原理進(jìn)行分析。

已知所有ResponseBodyAdvice接口的調(diào)用是發(fā)生在AbstractMessageConverterMethodProcessor的writeWithMessageConverters() 方法中,其部分源碼如下所示。

protected <T> void writeWithMessageConverters(@Nullable T value, MethodParameter returnType,
        ServletServerHttpRequest inputMessage, ServletServerHttpResponse outputMessage)
        throws IOException, HttpMediaTypeNotAcceptableException, HttpMessageNotWritableException {
    ......
    if (selectedMediaType != null) {
        selectedMediaType = selectedMediaType.removeQualityValue();
        for (HttpMessageConverter<?> converter : this.messageConverters) {
            GenericHttpMessageConverter genericConverter = (converter instanceof GenericHttpMessageConverter ?
                    (GenericHttpMessageConverter<?>) converter : null);
            if (genericConverter != null ?
                    ((GenericHttpMessageConverter) converter).canWrite(targetType, valueType, selectedMediaType) :
                    converter.canWrite(valueType, selectedMediaType)) {
                // ResponseBodyAdvice的調(diào)用發(fā)生在這里
                body = getAdvice().beforeBodyWrite(body, returnType, selectedMediaType,
                        (Class<? extends HttpMessageConverter<?>>) converter.getClass(),
                        inputMessage, outputMessage);
                if (body != null) {
                    Object theBody = body;
                    LogFormatUtils.traceDebug(logger, traceOn ->
                            "Writing [" + LogFormatUtils.formatValue(theBody, !traceOn) + "]");
                    addContentDispositionHeader(inputMessage, outputMessage);
                    if (genericConverter != null) {
                        genericConverter.write(body, targetType, selectedMediaType, outputMessage);
                    }
                    else {
                        ((HttpMessageConverter) converter).write(body, selectedMediaType, outputMessage);
                    }
                }
                else {
                    if (logger.isDebugEnabled()) {
                        logger.debug("Nothing to write: null body");
                    }
                }
                return;
            }
        }
    }
    ......
}

AbstractMessageConverterMethodProcessor的getAdvice() 方法會(huì)返回其在構(gòu)造函數(shù)中加載好的RequestResponseBodyAdviceChain對(duì)象,下面看一下RequestResponseBodyAdviceChain的beforeBodyWrite() 方法。

public Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType contentType,
        Class<? extends HttpMessageConverter<?>> converterType,
        ServerHttpRequest request, ServerHttpResponse response) {
    return processBody(body, returnType, contentType, converterType, request, response);
}
private <T> Object processBody(@Nullable Object body, MethodParameter returnType, MediaType contentType,
        Class<? extends HttpMessageConverter<?>> converterType,
        ServerHttpRequest request, ServerHttpResponse response) {
    // 從加載好的ResponseBodyAdvice中獲取適用于當(dāng)前handler的ResponseBodyAdvice
    for (ResponseBodyAdvice<?> advice : getMatchingAdvice(returnType, ResponseBodyAdvice.class)) {
        if (advice.supports(returnType, converterType)) {
            // 執(zhí)行ResponseBodyAdvice的beforeBodyWrite()方法以處理handler方法返回值
            body = ((ResponseBodyAdvice<T>) advice).beforeBodyWrite((T) body, returnType,
                    contentType, converterType, request, response);
        }
    }
    return body;
}
private <A> List<A> getMatchingAdvice(MethodParameter parameter, Class<? extends A> adviceType) {
    // 獲取ResponseBodyAdvice集合
    List<Object> availableAdvice = getAdvice(adviceType);
    if (CollectionUtils.isEmpty(availableAdvice)) {
        return Collections.emptyList();
    }
    List<A> result = new ArrayList<>(availableAdvice.size());
    for (Object advice : availableAdvice) {
        // 判斷ResponseBodyAdvice是否由@ControllerAdvice注解修飾
        if (advice instanceof ControllerAdviceBean) {
            ControllerAdviceBean adviceBean = (ControllerAdviceBean) advice;
            // 判斷ResponseBodyAdvice是否適用于當(dāng)前handler
            if (!adviceBean.isApplicableToBeanType(parameter.getContainingClass())) {
                continue;
            }
            advice = adviceBean.resolveBean();
        }
        if (adviceType.isAssignableFrom(advice.getClass())) {
            result.add((A) advice);
        }
    }
    return result;
}

在RequestResponseBodyAdviceChain中,beforeBodyWrite() 方法調(diào)用了processBody() 方法,processBody() 方法會(huì)遍歷所有加載好并且適用于當(dāng)前handler的ResponseBodyAdvice并執(zhí)行,至此,所有由@ControllerAdvice注解修飾的ResponseBodyAdvice接口會(huì)在這里執(zhí)行。

小節(jié):由@ControllerAdvice注解修飾的ResponseBodyAdvice接口會(huì)被SpringMVC框架加載到RequestResponseBodyMethodProcessorHttpEntityMethodProcessor這兩個(gè)返回值處理器中,當(dāng)這兩個(gè)返回值處理器將返回值寫入response前,適用于當(dāng)前handler的ResponseBodyAdvice接口會(huì)被調(diào)用,從而可以完成對(duì)返回值的定制化改造。

三. ResponseBodyAdvice的加載

由第二小節(jié)可知,正是因?yàn)镽equestResponseBodyMethodProcessor和HttpEntityMethodProcessor這兩個(gè)返回值處理器會(huì)將由@ControllerAdvice注解修飾的ResponseBodyAdvice接口加載,才能夠?qū)崿F(xiàn)將返回值寫入response前調(diào)用這些ResponseBodyAdvice接口對(duì)返回值進(jìn)行一些操作。那么本小節(jié)將對(duì)esponseBodyAdvice接口的加載進(jìn)行學(xué)習(xí)。

首先給出結(jié)論:ResponseBodyAdvice的加載發(fā)生在RequestMappingHandlerAdapterafterPropertiesSet()方法中。

已知,RequestMappingHandlerAdapter實(shí)現(xiàn)了InitializingBean接口,因此RequestMappingHandlerAdapter實(shí)現(xiàn)了afterPropertiesSet() 方法。該方法實(shí)現(xiàn)如下。

public void afterPropertiesSet() {
    // 加載ControllerAdviceBean相關(guān)內(nèi)容(同時(shí)就會(huì)將由@ControllerAdvice注解修飾的ResponseBodyAdvice接口加載)
    initControllerAdviceCache();
    if (this.argumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
        this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.initBinderArgumentResolvers == null) {
        List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
        this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
    }
    if (this.returnValueHandlers == null) {
        // 獲取返回值處理器,在這里就會(huì)完成RequestResponseBodyMethodProcessor和HttpEntityMethodProcessor的初始化,初始化的同時(shí)就會(huì)完成ResponseBodyAdvice接口的加載
        List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
        this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
    }
}

上述實(shí)現(xiàn)中,initControllerAdviceCache() 方法會(huì)加載ControllerAdviceBean相關(guān)內(nèi)容到RequestMappingHandlerAdapter中,這其中就包含由@ControllerAdvice注解修飾的ResponseBodyAdvice接口。然后在getDefaultReturnValueHandlers() 方法中會(huì)創(chuàng)建返回值處理器,在創(chuàng)建RequestResponseBodyMethodProcessor和HttpEntityMethodProcessor時(shí)會(huì)使用加載好的ResponseBodyAdvice接口完成這兩個(gè)返回值處理器的初始化。上述兩個(gè)方法的部分源碼如下所示。

initControllerAdviceCache()

private void initControllerAdviceCache() {
    if (getApplicationContext() == null) {
        return;
    }
    // 獲取由@ControllerAdvice注解修飾的bean
    List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
    List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
    for (ControllerAdviceBean adviceBean : adviceBeans) {
        Class<?> beanType = adviceBean.getBeanType();
        if (beanType == null) {
            throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
        }
        Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
        if (!attrMethods.isEmpty()) {
            this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
        }
        Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
        if (!binderMethods.isEmpty()) {
            this.initBinderAdviceCache.put(adviceBean, binderMethods);
        }
        // 如果ControllerAdviceBean實(shí)現(xiàn)了ResponseBodyAdvice接口,那么這個(gè)ControllerAdviceBean需要加載到requestResponseBodyAdvice中
        if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
            requestResponseBodyAdviceBeans.add(adviceBean);
        }
    }
    if (!requestResponseBodyAdviceBeans.isEmpty()) {
        this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
    }
    ......
}

getDefaultReturnValueHandlers()

private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
    List<HandlerMethodReturnValueHandler> handlers = new ArrayList<>(20);
    ......
    // 創(chuàng)建并加載HttpEntityMethodProcessor
    handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
            this.contentNegotiationManager, this.requestResponseBodyAdvice));
    ...
    // 創(chuàng)建并加載RequestResponseBodyMethodProcessor
    handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
            this.contentNegotiationManager, this.requestResponseBodyAdvice));
    ......
    return handlers;
}

根據(jù)getDefaultReturnValueHandlers() 方法可知,在創(chuàng)建HttpEntityMethodProcessor或者RequestResponseBodyMethodProcessor時(shí),會(huì)將RequestMappingHandlerAdapter加載好的ResponseBodyAdvice傳入構(gòu)造函數(shù),并且,無(wú)論是HttpEntityMethodProcessor還是RequestResponseBodyMethodProcessor,其構(gòu)造函數(shù)最終都會(huì)調(diào)用到父類AbstractMessageConverterMethodArgumentResolver的構(gòu)造函數(shù),并在其中初始化一個(gè)RequestResponseBodyAdviceChain以完成ResponseBodyAdvice的加載。構(gòu)造函數(shù)源碼如下所示。

HttpEntityMethodProcessor#HttpEntityMethodProcessor

public HttpEntityMethodProcessor(List<HttpMessageConverter<?>> converters,
        @Nullable ContentNegotiationManager manager, List<Object> requestResponseBodyAdvice) {
    super(converters, manager, requestResponseBodyAdvice);
}

AbstractMessageConverterMethodProcessor#AbstractMessageConverterMethodProcessor

protected AbstractMessageConverterMethodProcessor(List&lt;HttpMessageConverter&lt;?&gt;&gt; converters,
        @Nullable ContentNegotiationManager manager, @Nullable List&lt;Object&gt; requestResponseBodyAdvice) {
    super(converters, requestResponseBodyAdvice);
    this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
    this.safeExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
    this.safeExtensions.addAll(SAFE_EXTENSIONS);
}

AbstractMessageConverterMethodArgumentResolver#AbstractMessageConverterMethodArgumentResolver

public AbstractMessageConverterMethodArgumentResolver(List<HttpMessageConverter<?>> converters,
        @Nullable List<Object> requestResponseBodyAdvice) {
    Assert.notEmpty(converters, "'messageConverters' must not be empty");
    this.messageConverters = converters;
    this.allSupportedMediaTypes = getAllSupportedMediaTypes(converters);
    this.advice = new RequestResponseBodyAdviceChain(requestResponseBodyAdvice);
}

小節(jié):RequestMappingHandlerAdapter會(huì)在其實(shí)現(xiàn)的afterPropertiesSet()方法中加載由@ControllerAdvice注解修飾的ResponseBodyAdvice接口,然后會(huì)創(chuàng)建并加載返回值處理器,在創(chuàng)建RequestResponseBodyMethodProcessorHttpEntityMethodProcessor這兩個(gè)返回值處理器時(shí)會(huì)傳入加載好的ResponseBodyAdvice,從而完成了ResponseBodyAdvice的加載。

總結(jié)

如果需要使用ResponseBodyAdvice來(lái)對(duì)handler方法的返回值做處理,則需要?jiǎng)?chuàng)建一個(gè)類并實(shí)現(xiàn)ResponseBodyAdvice接口,同時(shí)該類還需要被@ControllerAdvice注解修飾,這樣的一個(gè)ResponseBodyAdvice接口會(huì)被RequestMappingHandlerAdapter加載,以及在初始化RequestResponseBodyMethodProcessor和HttpEntityMethodProcessor這兩個(gè)返回值處理器時(shí)被加載。在通過這兩個(gè)返回值處理器將返回值寫入response前,加載好的ResponseBodyAdvice接口的beforeBodyWrite() 方法會(huì)被返回值處理器調(diào)用,完成對(duì)返回值的定制化處理。

以上就是ResponseBodyAdvice的使用原理源碼解析的詳細(xì)內(nèi)容,更多關(guān)于ResponseBodyAdvice原理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

最新評(píng)論