1. 程式人生 > >使用ControllerAdvice獲取controller介面@ResponseBody返回值

使用ControllerAdvice獲取controller介面@ResponseBody返回值

前言: 介面類專案開發時,為了便於後期查詢問題,一般會攔截器或過濾器中記錄每個介面請求的引數與響應值記錄, 請求引數很容易從request中獲取,但controller的返回值無法從response中獲取,有一個簡單的方法,在controller介面的最後將返回值儲存到request域中,這種方法雖然簡單,但是開發起來太麻煩,需要在每個controller的最後新增一行程式碼,且該功能不屬於業務功能,不應該介面中去實現,應該有個全域性的處理方法。 ControllerAdvice是springmvc controller增強器 ControllerAdvice三個用處: 1. ModelAttribute: 暴露@RequestMapping 方法返回值為模型資料:放在功能處理方法的返回值上時,是暴露功能處理方法的返回值為模型資料,用於檢視頁面展示時使用。 2. InitBinder : 用於自定義@RequestMapping 方法引數繫結 3. ResponseBodyAdvice : 用於@ResponseBody返回值增加處理 ControllerAdvice初始化: Spring mvc 啟動時呼叫RequestMappingHandlerAdapter類的initControllerAdviceCache()方法進行初始化
private void initControllerAdviceCache() {
	if (getApplicationContext() == null) {
		return;
	}
	if (logger.isInfoEnabled()) {
		logger.info("Looking for @ControllerAdvice: " + getApplicationContext());
	}
	// 獲取所有新增ControlerAdvice註解的Bean
	List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
	Collections.sort(beans, new OrderComparator());

	List<Object> responseBodyAdviceBeans = new ArrayList<Object>();

	for (ControllerAdviceBean bean : beans) {
		Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
		if (!attrMethods.isEmpty()) {
			this.modelAttributeAdviceCache.put(bean, attrMethods);
			logger.info("Detected @ModelAttribute methods in " + bean);
		}
		Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
		if (!binderMethods.isEmpty()) {
			this.initBinderAdviceCache.put(bean, binderMethods);
			logger.info("Detected @InitBinder methods in " + bean);
		}
		// 如果ControllerAdvice是ResponseBodyAdvice型別則注入到responseBodyAdviceBeans列表中
		if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {
			responseBodyAdviceBeans.add(bean);
			logger.info("Detected ResponseBodyAdvice bean in " + bean);
		}
	}

	if (!responseBodyAdviceBeans.isEmpty()) {
		this.responseBodyAdvice.addAll(0, responseBodyAdviceBeans);
	}
}


ResponseBodyAdvice : 可以對@ResponseBody的返回值進行加工處理,它是一個介面類,具體處理可以自定義實現類注入到responseBodyAdviceBeans中既可,注入過程由RequestMappingHandlerAdapter類的initControllerAdviceCache去做,開發者只需要自定義實現類實現ResponseBodyAdvice 介面並新增類註解@ControllerAdvice ResponseBodyAdviceChain : 維護ResponseBodyAdvice列表,迴圈呼叫所有的ResponseBodyAdvice
@SuppressWarnings("unchecked")
public <T> T invoke(T body, MethodParameter returnType,
	MediaType selectedContentType, Class<? extends HttpMessageConverter<?>> selectedConverterType,
	ServerHttpRequest request, ServerHttpResponse response) {

	if (this.advice != null) {
		if (logger.isDebugEnabled()) {
			logger.debug("Invoking ResponseBodyAdvice chain for body=" + body);
		}
		for (Object advice : this.advice) {
			if (advice instanceof ControllerAdviceBean) {
				ControllerAdviceBean adviceBean = (ControllerAdviceBean) advice;
				if (!adviceBean.isApplicableToBeanType(returnType.getContainingClass())) {
					continue;
				}
				advice = adviceBean.resolveBean();
			}
			if (advice instanceof ResponseBodyAdvice) {
				ResponseBodyAdvice<T> typedAdvice = (ResponseBodyAdvice<T>) advice;
				if (typedAdvice.supports(returnType, selectedConverterType)) {
					body = typedAdvice.beforeBodyWrite(body, returnType,
					selectedContentType, selectedConverterType, request, response);
				}
			} else {
				throw new IllegalStateException("Expected ResponseBodyAdvice: " + advice);
			}
		}
		if (logger.isDebugEnabled()) {
			logger.debug("After ResponseBodyAdvice chain body=" + body);
		}
	}
	return body;
}

例子:自定義ResponseBodyAdvice
/**
 * Controller增強器,將Controller介面的響應(Response)放到請求上下文中
 * @author zsc
 * @datetime 2017年12月11日 下午3:09:08
 */
@ControllerAdvice
@Component
public class RespBodyAdvice implements ResponseBodyAdvice<Object>{

	@Override
	public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converter			Type) {
		// 注意,這裡必須返回true,否則不會執行beforeBodyWrite方法
		return true;
	}

	@Override
	public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
		Class<? extends HttpMessageConverter<?>> selectedConverterType, ServerHttpRequest request,
		ServerHttpResponse response) {
		// 將返回的body放到請求上下文中
		if(null == body) {
			return null;
		}
		if(body instanceof Response) {
			RequestContextUtil.getContext().setResponse((Response)body);
		}
		return body;
	}
}