Spring-MVC--ControllerAdvice 全局控制器增强

文章目录

  • 前言
  • 一、ControllerAdvice 使用:
  • 二、ControllerAdvice 的原理:
    • 2.1 对于@ControllerAdvice 注解的解析
    • 2.2 requestBodyAdvice&responseBodyAdvice 赋值:
    • 2.3 方法的调用:
  • 总结:


前言

ControllerAdvice 除了可以完成全局异常的处理,同时也可以在真正调用方法之前对body 中的请求参数进行处理,以及在对body 数据相应写回之前对数据进行处理;


一、ControllerAdvice 使用:

只需要增加@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 加强器,只用于带有@RequestMapping注解的Controller类,因为其是在RequestMappingHandlerAdapter 初始化是对加了@ControllerAdvice 注解的类进行的解析;
  • 在调用方法之前对于body 数据读取的增强,只适用有body 体的 post /put 请求对 get 请求并不生效;

二、ControllerAdvice 的原理:

2.1 对于@ControllerAdvice 注解的解析

@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 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");
             }
         }

     }
 }
 
  

2.2 requestBodyAdvice&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();
     }
 }

2.3 方法的调用:

在对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 类中的方法。

你可能感兴趣的:(Spring框架篇,java工具篇,spring,mvc,java)