使用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;
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值