ControllerAdvice 除了可以完成全局异常的处理,同时也可以在真正调用方法之前对body 中的请求参数进行处理,以及在对body 数据相应写回之前对数据进行处理;
只需要增加@ControllerAdvice 注解 并实现RequestBodyAdvice,ResponseBodyAdvice 覆盖其中的方法即可;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpInputMessage;
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.stereotype.Component;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.RequestBodyAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.io.IOException;
import java.lang.reflect.Type;
@Component
@ControllerAdvice
public class AdviceController implements RequestBodyAdvice,ResponseBodyAdvice {
@Override
public boolean supports(MethodParameter methodParameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
// 返回true ,beforeBodyRead 和 afterBodyRead 才会起作用
return true;
}
// 在读取body 数据之前,可以对数据进行一些验证和加工
@Override
public HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
return inputMessage;
}
// 在body 数据解析完成之后,调用方法。可以对读取到的数据进行加工
@Override
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return body;
}
@Override
public Object handleEmptyBody(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
return null;
}
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
// 返回true ,beforeBodyWrite 才会起作用
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
// 请求的数据相应之前 可以对数据进行加工
return body;
}
}
在使用方面需要注意一下几个方面:
@ControllerAdvice 注解的解析在 RequestMappingHandlerAdapter bean 初始化之后的afterPropertiesSet 方法中
public void afterPropertiesSet() {
// 对 @ControllerAdvice 进行了解析
this.initControllerAdviceCache();
List handlers;
if (this.argumentResolvers == null) {
handlers = this.getDefaultArgumentResolvers();
this.argumentResolvers = (new HandlerMethodArgumentResolverComposite()).addResolvers(handlers);
}
if (this.initBinderArgumentResolvers == null) {
handlers = this.getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = (new HandlerMethodArgumentResolverComposite()).addResolvers(handlers);
}
if (this.returnValueHandlers == null) {
handlers = this.getDefaultReturnValueHandlers();
this.returnValueHandlers = (new HandlerMethodReturnValueHandlerComposite()).addHandlers(handlers);
}
}
initControllerAdviceCache 解析
private void initControllerAdviceCache() {
if (this.getApplicationContext() != null) {
// 获取所有类上有 ControllerAdvice 注解的bean
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(this.getApplicationContext());
List<Object> requestResponseBodyAdviceBeans = new ArrayList();
Iterator var3 = adviceBeans.iterator();
while(var3.hasNext()) {
ControllerAdviceBean adviceBean = (ControllerAdviceBean)var3.next();
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) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
requestResponseBodyAdviceBeans.add(adviceBean);
}
}
if (!requestResponseBodyAdviceBeans.isEmpty()) {
// 最终加入到 List<Object> requestResponseBodyAdvice 属性中
this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
}
if (this.logger.isDebugEnabled()) {
int modelSize = this.modelAttributeAdviceCache.size();
int binderSize = this.initBinderAdviceCache.size();
int reqCount = this.getBodyAdviceCount(RequestBodyAdvice.class);
int resCount = this.getBodyAdviceCount(ResponseBodyAdvice.class);
if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0) {
this.logger.debug("ControllerAdvice beans: none");
} else {
this.logger.debug("ControllerAdvice beans: " + modelSize + " @ModelAttribute, " + binderSize + " @InitBinder, " + reqCount + " RequestBodyAdvice, " + resCount + " ResponseBodyAdvice");
}
}
}
}
在创建RequestResponseBodyAdviceChain 对象时通过其构造方法完成了 requestBodyAdvice&responseBodyAdvice 属相的赋值:
public RequestResponseBodyAdviceChain(@Nullable List<Object> requestResponseBodyAdvice) {
// 传入之前扫描到的 ControllerAdvice bean 集合
// 获取到实现了RequestBodyAdvice 接口的bean
this.requestBodyAdvice.addAll(getAdviceByType(requestResponseBodyAdvice, RequestBodyAdvice.class));
// 获取到实现了ResponseBodyAdvice接口的bean
this.responseBodyAdvice.addAll(getAdviceByType(requestResponseBodyAdvice, ResponseBodyAdvice.class));
}
static <T> List<T> getAdviceByType(@Nullable List<Object> requestResponseBodyAdvice, Class<T> adviceType) {
if (requestResponseBodyAdvice != null) {
List<T> result = new ArrayList();
Iterator var3 = requestResponseBodyAdvice.iterator();
while(var3.hasNext()) {
Object advice = var3.next();
Class<?> beanType = advice instanceof ControllerAdviceBean ? ((ControllerAdviceBean)advice).getBeanType() : advice.getClass();
if (beanType != null && adviceType.isAssignableFrom(beanType)) {
result.add(advice);
}
}
return result;
} else {
return Collections.emptyList();
}
}
在对body 体进行参数解析时会调用 RequestResponseBodyAdviceChain 的 beforeBodyRead 和 afterBodyRead ,beforeBodyWrite 方法:
public boolean supports(MethodParameter param, Type type, Class<? extends HttpMessageConverter<?>> converterType) {
throw new UnsupportedOperationException("Not implemented");
}
public boolean supports(MethodParameter returnType, Class<? extends HttpMessageConverter<?>> converterType) {
throw new UnsupportedOperationException("Not implemented");
}
public HttpInputMessage beforeBodyRead(HttpInputMessage request, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) throws IOException {
Iterator var5 = this.getMatchingAdvice(parameter, RequestBodyAdvice.class).iterator();
while(var5.hasNext()) {
RequestBodyAdvice advice = (RequestBodyAdvice)var5.next();
if (advice.supports(parameter, targetType, converterType)) {
request = advice.beforeBodyRead(request, parameter, targetType, converterType);
}
}
return request;
}
public Object afterBodyRead(Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
Iterator var6 = this.getMatchingAdvice(parameter, RequestBodyAdvice.class).iterator();
while(var6.hasNext()) {
RequestBodyAdvice advice = (RequestBodyAdvice)var6.next();
if (advice.supports(parameter, targetType, converterType)) {
body = advice.afterBodyRead(body, inputMessage, parameter, targetType, converterType);
}
}
return body;
}
@Nullable
public Object beforeBodyWrite(@Nullable Object body, MethodParameter returnType, MediaType contentType, Class<? extends HttpMessageConverter<?>> converterType, ServerHttpRequest request, ServerHttpResponse response) {
return this.processBody(body, returnType, contentType, converterType, request, response);
}
@Nullable
public Object handleEmptyBody(@Nullable Object body, HttpInputMessage inputMessage, MethodParameter parameter, Type targetType, Class<? extends HttpMessageConverter<?>> converterType) {
Iterator var6 = this.getMatchingAdvice(parameter, RequestBodyAdvice.class).iterator();
while(var6.hasNext()) {
RequestBodyAdvice advice = (RequestBodyAdvice)var6.next();
if (advice.supports(parameter, targetType, converterType)) {
body = advice.handleEmptyBody(body, inputMessage, parameter, targetType, converterType);
}
}
return body;
}
可以看到先调用getMatchingAdvice 获取到相应的 ControllerAdvice 类,然后进行遍历 先调用 supports 方法,只有supports 方法返回true 才会继续调用到我我们自己业务中的 beforeBodyRead 和 afterBodyRead ,beforeBodyWrite 方法;
以上对@ControllerAdvice 注解的使用,及其实现原理进行了分析,使用RequestMappingHandlerAdapter 对注解进行了解析,并在创建RequestResponseBodyAdviceChain 对象时通过其构造方法完成了 requestBodyAdvice&responseBodyAdvice 属相的赋值,最终通过遍历的方式调用每个ControllerAdvice 类中的方法。