中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringMVC中如何利用@ControllerAdvice和ResponseBodyAdvice接口統一處理返回值

發布時間:2021-10-19 17:27:32 來源:億速云 閱讀:122 作者:柒染 欄目:大數據

SpringMVC中如何利用@ControllerAdvice和ResponseBodyAdvice接口統一處理返回值,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

在我們進行Java的Web應用開發時,如何寫更少的代碼,做更多的事情。如何讓開發更容易上手,更專注于業務層面,不需要太關心底層的實現。這里就分享一些我平時在搭建基礎框架時候的一些心得體驗。

統一處理返回值

在web應用中,通常前后端會定義一個統一的對象來封裝返回值,一般除了業務數據之外,可能會包含一些請求相關的數據

例如以下這個對象

  • code來標識整個請求的結果

  • msg用于返回錯誤信息

  • data用于返回實際的業務數據。

{
	"code": 0,
	"msg": "success",
	"data": {}
}

統一封裝的好處就是前端可以使用統一的邏輯進行請求處理,能夠編寫通用代碼來處理返回值。

當然這也需要后端做一定的開發。通常我們都是直接寫在代碼里面,手動去創建一個封裝對象,然后將數據set進去,或者是封裝類添加一些靜態方法之類的。 在大部分情況下,這些工作都是重復的。

ResponseBodyAdvice 的執行流程

今天介紹的這個接口, ResponseBodyAdvice, 這是由SpringMvc提供的一個接口,在消息轉換前處理返回值,源碼如下:

public interface ResponseBodyAdvice<T>{
	boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType);
	T beforeBodyWrite(T body, MethodParameter returnType, MediaType selectedContentType,
			Class<? extends HttpMessageConverter<?>> selectedConverterType,
			ServerHttpRequest request, ServerHttpResponse response);
}

這個接口在返回值被消息轉換器寫回前端之前進行處理, 大致處理流程如下:

SpringMVC中如何利用@ControllerAdvice和ResponseBodyAdvice接口統一處理返回值

我們實現這個接口的代碼主要在這個方法里被調用 RequestResponseBodyAdviceChain.processBody, 可以看到這一段邏輯很簡單

先執行ResponseBodyAdvice.supports看當前切面類是否支持,如果支持再調用ResponseBodyAdvice.beforeBodyWrite方法并返回

返回值會被 HttpMessageConverter.write 接口在進行最終的轉換(例如轉JSON),然后寫回前端

private <T> Object processBody(@Nullable Object body, MethodParameter returnType, MediaType contentType,
		Class<? extends HttpMessageConverter<?>> converterType,
		ServerHttpRequest request, ServerHttpResponse response) {

	for (ResponseBodyAdvice<?> advice : getMatchingAdvice(returnType, ResponseBodyAdvice.class)) {
		if (advice.supports(returnType, converterType)) {
			body = ((ResponseBodyAdvice<T>) advice).beforeBodyWrite((T) body, returnType,
					contentType, converterType, request, response);
		}
	}
	return body;
}

ResponseBodyAdvice 的初始化

SpringMVC在初始化的時候, 會調用RequestMappingHandlerAdapter.initControllerAdviceCache,將ResponseBodyAdvice初始化到容器中

里面會調用ControllerAdviceBean.findAnnotatedBeans ,獲取所有帶有 @ControllerAdvice 注解的類

將所有實現了 ResponseBodyAdvice 接口的Bean放入requestResponseBodyAdviceBeans中, 在之前介紹到的 getAdvice() 方法取得就是該對象。

//代碼片段
public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext context) {
	return Arrays.stream(BeanFactoryUtils.beanNamesForTypeIncludingAncestors(context, Object.class))
			.filter(name -> context.findAnnotationOnBean(name, ControllerAdvice.class) != null)
			.map(name -> new ControllerAdviceBean(name, context))
			.collect(Collectors.toList());
}

// 代碼片段
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);
	}
	if (RequestBodyAdvice.class.isAssignableFrom(beanType)) {
		requestResponseBodyAdviceBeans.add(adviceBean);
	}
	if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
		requestResponseBodyAdviceBeans.add(adviceBean);
	}
}

了解到這些,我們實現一個通用的返回值處理就很簡單了, 只需要實現 ResponseBodyAdvice 接口,并且加上 @ControllerAdvice 注解就可以了

這是我實現的一個,統一封裝返回值的實現, 大家可以參考一下,根據自己的業務需求來進行修改

package com.diamondfsd.fast.mvc.advice;

import com.diamondfsd.fast.mvc.annotations.IgnoreAware;
import com.diamondfsd.fast.mvc.entity.FastResult;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.WeakHashMap;

/**
 * 統一返回數據封裝
 * @author Diamond
 */
@ControllerAdvice
public class FastMvcResponseBodyAwareAdvice implements ResponseBodyAdvice<Object> {

    private final Map<Method, Boolean> supportsCache = new WeakHashMap<>();

    private final String [] basePackages;
    private ObjectMapper objectMapper = new ObjectMapper();

    public FastMvcResponseBodyAwareAdvice(String [] basePackages) {
        this.basePackages = basePackages;
    }

    @Override
    public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
        if (supportsCache.containsKey(returnType.getMethod())) {
            return supportsCache.get(returnType.getMethod());
        }
        boolean isSupport = getIsSupport(returnType);
        supportsCache.put(returnType.getMethod(), isSupport);
        return isSupport;
    }

    private boolean getIsSupport(MethodParameter returnType) {
        Class<?> declaringClass = returnType.getMember().getDeclaringClass();

        IgnoreAware classIgnore = declaringClass.getAnnotation(IgnoreAware.class);
        IgnoreAware methodIgnore = returnType.getMethod().getAnnotation(IgnoreAware.class);
        if (classIgnore != null || methodIgnore != null || FastResult.class.equals(returnType.getGenericParameterType())) {
            return false;
        }
        for (int i = 0; i < basePackages.length; i++) {
            if (declaringClass.getPackage().getName().startsWith(basePackages[i])) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
                                  ServerHttpResponse response) {
        FastResult<Object> result = new FastResult<>();
        result.setData(body);
        if (returnType.getGenericParameterType().equals(String.class)) {
            try {
                response.getHeaders().set("Content-Type", "application/json;charset=utf-8");
                return objectMapper.writeValueAsString(result);
            } catch (JsonProcessingException e) {
                e.printStackTrace();
            }
        }
        return result;
    }

}

看完上述內容,你們掌握SpringMVC中如何利用@ControllerAdvice和ResponseBodyAdvice接口統一處理返回值的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

嘉黎县| 洞头县| 大足县| 卫辉市| 邹平县| 莲花县| 伊宁市| 同仁县| 澄城县| 防城港市| 望江县| 松原市| 子洲县| 漳平市| 东丽区| 广丰县| 兴仁县| 互助| 大悟县| 古浪县| 棋牌| 北安市| 沈阳市| 卢龙县| 承德市| 寻甸| 满城县| 五大连池市| 明星| 湾仔区| 犍为县| 庆安县| 新晃| 古蔺县| 金华市| 祁门县| 乐陵市| 崇州市| 靖州| 山阳县| 九龙城区|