SpringMVC框架中@ControllerAdvice和对应Advice切面使用原理

Java第三方集成框架 同时被 2 个专栏收录
51 篇文章 1 订阅
6 篇文章 0 订阅

目录

一、简介

二、@ControllerAdvice注解的处理获取

1.@ControllerAdvice注解

2.ControllerAdviceBean类

三、Request请求和Response响应对应接口切面

1.RequestBodyAdvice接口

2.ResponseBodyAdvice接口

四、Advice切面的读取和调用原理

1.RequestMappingHandlerAdapter类读取@ControllerAdvice注解类

2.RequestResponseBodyAdviceChain解析类

3.具体实现源码分析

3.1 使用HandlerMethodArgumentResolverComposite处理RequestBodyAdvice接口

3.2 HandlerMethodReturnValueHandlerComposite处理ResponseBodyAdvice接口

3.3 RequestResponseBodyMethodProcessor处理两种Advice切面接口


一、简介

@ControllerAdvice注解从名字上就可以看出来这个是针对Controller的切面增强处理注解,类似与@Controller和@RestController一样,@ControllerAdvice也有对应的@RestControllerAdvice注解用来返回序列化之后的对象。

@ControllerAdvice实际上也是一个Component,因为该注解同时也被@Component注解,搭配这个注解通常有一个注解和两个接口:

  • @ExceptionHandler注解:统一处理从Controller抛出的异常,在@ControllerAdvice中可以拥有多个@ExceptionHandler和实现方法,SpringMVC将会自动判断哪个异常优先处理;
  • RequestBodyAdvice接口:用来对Request请求方法体和对应方法参数进行处理,如果方法参数有多个,将会顺序调用到接口方法中来;
  • ResponseBodyAdvice接口:类似RequestBodyAdvice,只是处理的对象变成了Response响应,处理的对象包括请求体和返回类型。

另外需要注意的是,@ControllerAdvice注解是一定需要搭配其它的注解或者接口使用的,否则就算这个注解类被识别到,没有具体的处理方法,SpringMVC是不会进行任何操作的。

二、@ControllerAdvice注解的处理获取

1.@ControllerAdvice注解

在分析具体实现原理前先看到其源码:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
   // 和下面的basePackages代表的是同一个意思,只针对某些包下的类
   @AliasFor("basePackages")
   String[] value() default {};
   @AliasFor("value")
   String[] basePackages() default {};
   // 只处理某些类所在包路径
   Class<?>[] basePackageClasses() default {};
   // 处理某些类和这些类的子类
   Class<?>[] assignableTypes() default {};
   // 处理被某些注解过的类
   Class<? extends Annotation>[] annotations() default {};
}

2.ControllerAdviceBean类

这个类便是@ControllerAdvice注解对应的实体类,在这个类里面将会完成从bean工厂中获取所有被@ControllerAdvice注解过的bean,并读取注解属性将其全部返回的操作。部分关键源码如下:

public class ControllerAdviceBean implements Ordered {
   // 这个bean指的就是被@ControllerAdvice注解的类对象
   private final Object bean;
   // bean工厂,要从bean工厂中获取bean和判断bean的类型
   @Nullable
   private final BeanFactory beanFactory;
   // 这个类有兴趣的可以去看下,代码不多,大致作用便是存放并且判断注解的属性
   private final HandlerTypePredicate beanTypePredicate;
   private ControllerAdviceBean(Object bean, 
           @Nullable BeanFactory beanFactory) {
      this.bean = bean;
      this.beanFactory = beanFactory;
      Class<?> beanType;
      // 设置bean和beanType等属性
      if (bean instanceof String) {
         String beanName = (String) bean;
         beanType = this.beanFactory.getType(beanName);
         this.order = initOrderFromBeanType(beanType);
      } else {
         beanType = bean.getClass();
         this.order = initOrderFromBean(bean);
      }
      // 从bean类型获取ControllerAdvice注解
      ControllerAdvice annotation = (beanType != null ?
              AnnotatedElementUtils.findMergedAnnotation(beanType, 
                  ControllerAdvice.class) : null);
      // 如果注解不为空则将注解的属性通过HandlerTypePredicate的构造器放入到
      // HandlerTypePredicate对象中,以便后续判断注解属性
      if (annotation != null) {
         this.beanTypePredicate = HandlerTypePredicate.builder()
               .basePackage(annotation.basePackages())
               .basePackageClass(annotation.basePackageClasses())
               .assignableType(annotation.assignableTypes())
               .annotation(annotation.annotations())
               .build();
      } else {
         // 创建一个默认的HandlerTypePredicate对象
         this.beanTypePredicate = HandlerTypePredicate.forAnyHandlerType();
      }
   }
   public boolean isApplicableToBeanType(@Nullable Class<?> beanType) {
      // 这个便是外部调用判断注解属性是否满足要求的方法
      return this.beanTypePredicate.test(beanType);
   }
   public static List<ControllerAdviceBean> findAnnotatedBeans(
           ApplicationContext context) {
      // 这个方法大致作用便是从bean工厂中获取所有被@ControllerAdvice注解过的
      // bean对象,因为ControllerAdvice中也有@Component注解,当获得bean之后
      // 再把这个bean使用ControllerAdviceBean的构造函数实例化
      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());
   }
}

三、Request请求和Response响应对应接口切面

其大致流程如下:

前面说过Request请求和Response响应都会有一个对应的接口切面,接下来先看下其接口方法。

1.RequestBodyAdvice接口

其源码如下:

public interface RequestBodyAdvice {
   // 在调用本接口三个对方法体和参数进行处理的方法前都需要调用supports方法来
   // 判断是否支持该方法
   boolean supports(MethodParameter methodParameter, Type targetType,
         Class<? extends HttpMessageConverter<?>> converterType);
   // 如果方法体不为空,则在读取方法体前先执行该方法
   HttpInputMessage beforeBodyRead(HttpInputMessage inputMessage, 
           MethodParameter parameter,
           Type targetType, Class<? extends HttpMessageConverter<?>> 
                   converterType) throws IOException;
   // 在已经读取了方法体的内容后调用的方法,可以直接对方法体进行操作
   Object afterBodyRead(Object body, HttpInputMessage inputMessage, 
           MethodParameter parameter,
           Type targetType, Class<? extends HttpMessageConverter<?>> 
                   converterType);
   // 如果方法体为空所进行的处理
   @Nullable
   Object handleEmptyBody(@Nullable Object body, 
           HttpInputMessage inputMessage, MethodParameter parameter,
           Type targetType, Class<? extends HttpMessageConverter<?>> 
                   converterType);
}

2.ResponseBodyAdvice接口

这个接口相比于RequestBodyAdvice要简单很多,其源码如下:

public interface ResponseBodyAdvice<T> {
   // 在调用本接口对返回方法体进行处理的方法前都需要调用supports方法来
   // 判断是否支持该方法
   boolean supports(MethodParameter returnType, 
           Class<? extends HttpMessageConverter<?>> converterType);
   // 接口只有一个操作方法,具体可以操作的对象有返回方法体、方法返回类型和选择的
   // 内容类型,如是Json或者文件等
   @Nullable
   T beforeBodyWrite(@Nullable T body, MethodParameter returnType, 
           MediaType selectedContentType,
           Class<? extends HttpMessageConverter<?>> 
                   selectedConverterType,
           ServerHttpRequest request, ServerHttpResponse response);
}

四、Advice切面的读取和调用原理

1.RequestMappingHandlerAdapter类读取@ControllerAdvice注解类

其读取@ControllerAdvice注解获取切面类源码如下:

public class RequestMappingHandlerAdapter 
        extends AbstractHandlerMethodAdapter
        implements BeanFactoryAware, InitializingBean {
    // 保存解析参数的解析器,RequestBodyAdvice接口将会在这里面完成处理
    @Nullable
    private HandlerMethodArgumentResolverComposite argumentResolvers;
    // 保存解析方法返回值的处理器,ResponseBodyAdvice接口将会在这里面完成处理
    @Nullable
    private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
    // 具体保存RequestBodyAdvice和ResponseBodyAdvice接口实现类的集合对象
    private List<Object> requestResponseBodyAdvice = new ArrayList<>();
    @Override
    public void afterPropertiesSet() {
       // 首先调用该方法去获取所有被@ControllerAdvice注解且符合是两个Advice
       // 切面实现类,@RequestMapping、@ModelAttribute和@InitBinder略过
       initControllerAdviceCache();
       // 如果参数解析器集合为空则获取默认的
       if (this.argumentResolvers == null) {
          List<HandlerMethodArgumentResolver> resolvers = 
                  getDefaultArgumentResolvers();
          this.argumentResolvers = 
                  new HandlerMethodArgumentResolverComposite()
                      .addResolvers(resolvers);
       }
       ...
       // 如果返回值处理器集合为空则获取默认的
       if (this.returnValueHandlers == null) {
          List<HandlerMethodReturnValueHandler> handlers = 
                  getDefaultReturnValueHandlers();
          this.returnValueHandlers = 
                  new HandlerMethodReturnValueHandlerComposite()
                      .addHandlers(handlers);
       }
    }
    private void initControllerAdviceCache() {
       // 如果spring上下文为空则无需进行下面的操作,因为都是基于bean工厂操作的
       if (getApplicationContext() == null) {
          return;
       }
       // 调用ControllerAdviceBean的方法,获取bean工厂中所有@ControllerAdvice
       // 注解bean对象
       List<ControllerAdviceBean> adviceBeans = 
               ControllerAdviceBean
                   .findAnnotatedBeans(getApplicationContext());
       // 进行排序,排序的依据便是根据@Order等排序类来完成
       AnnotationAwareOrderComparator.sort(adviceBeans);
       List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
       // 对获取到的@ControllerAdvice注解类进行判断
       for (ControllerAdviceBean adviceBean : adviceBeans) {
          Class<?> beanType = adviceBean.getBeanType();
          if (beanType == null) {
             throw new IllegalStateException();
          }
          ...
          // 如果实现类的类型是RequestBodyAdvice和ResponseBodyAdvice接口的
          // 实现子类则将其添加到保存其对象的集合中
          if (RequestBodyAdvice.class.isAssignableFrom(beanType) || 
                  ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
             requestResponseBodyAdviceBeans.add(adviceBean);
          }
       }
       // 不为空则添加到保存两种切面集合中
       if (!requestResponseBodyAdviceBeans.isEmpty()) {
          this.requestResponseBodyAdvice
                  .addAll(0, requestResponseBodyAdviceBeans);
       }
    }
    private List<HandlerMethodArgumentResolver> 
            getDefaultArgumentResolvers() {
       List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
       // 等下只展示使用到了两种Advice切面的类
       ...
       // 对Request处理的重要对象,基本上都是通过这个类完成处理的
       resolvers.add(new RequestResponseBodyMethodProcessor(
               getMessageConverters(), this.requestResponseBodyAdvice));
       // 这个类是用来处理@RequestPart注解的解析类
       resolvers.add(new RequestPartMethodArgumentResolver
               getMessageConverters(), this.requestResponseBodyAdvice));
       ...
       // 对参数和返回值是HttpEntity和RequestEntity类型进行处理的类
       resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(),
               this.requestResponseBodyAdvice));
       ...
       return resolvers;
    }
    private List<HandlerMethodReturnValueHandler> 
            getDefaultReturnValueHandlers() {
        // 等下只展示使用到了两种Advice切面的类
        ...
        // 对参数和返回值是HttpEntity和RequestEntity类型进行处理的类
        handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
              this.contentNegotiationManager, 
              this.requestResponseBodyAdvice));
        ...
        // 对Response处理的重要对象,基本上都是通过这个类完成处理的
        handlers.add(new RequestResponseBodyMethodProcessor(
                getMessageConverters(),this.contentNegotiationManager,
                        this.requestResponseBodyAdvice));
        ...
        return handlers;
    }
}

2.RequestResponseBodyAdviceChain解析类

这个类的大致作用便是存储前面获得的requestResponseBodyAdvice集合对象,并提供只获取RequestBodyAdvice和ResponseBodyAdvice接口的方法,起到管理两类接口实现类的作用。其部分关键源码如下:

class RequestResponseBodyAdviceChain 
        implements RequestBodyAdvice, ResponseBodyAdvice<Object> {
    // 保存RequestBodyAdvice接口实现类的集合对象
    private final List<Object> requestBodyAdvice = new ArrayList<>(4);
    // 保存ResponseBodyAdvice接口实现类的集合对象
    private final List<Object> responseBodyAdvice = new ArrayList<>(4);
    public RequestResponseBodyAdviceChain(
            @Nullable List<Object> requestResponseBodyAdvice) {
       // 分别获取集合中的RequestBodyAdvice和ResponseBodyAdvice接口实现类
       // 并添加到对应的集合中,getAdviceByType方法则是对传入的集合遍历
       // 一一判断是不是类是不是集合元素的父类接口,在此略过实现方法
       this.requestBodyAdvice.addAll(
               getAdviceByType(requestResponseBodyAdvice, 
               RequestBodyAdvice.class));
       this.responseBodyAdvice.addAll(
               getAdviceByType(requestResponseBodyAdvice, 
               ResponseBodyAdvice.class));
    }
    private <A> List<A> getMatchingAdvice(MethodParameter parameter,
            Class<? extends A> adviceType) {
       // 这个是等下要分析几个方法都会都用的方法,因此先大致分析一遍
       // getAdvice方法是根据adviceType类型去分别获取requestBodyAdvice或者
       // responseBodyAdvice集合
       List<Object> availableAdvice = getAdvice(adviceType);
       // 如果切面集合为空则直接返回空集合
       if (CollectionUtils.isEmpty(availableAdvice)) {
          return Collections.emptyList();
       }
       List<A> result = new ArrayList<>(availableAdvice.size());
       for (Object advice : availableAdvice) {
          // 如果切面是被@ControllerAdvice注解的类
          if (advice instanceof ControllerAdviceBean) {
             ControllerAdviceBean adviceBean = 
                     (ControllerAdviceBean) advice;
             // 转换成ControllerAdviceBean类型去调用isApplicableToBeanType
             // 方法判断当前参数对象是否满足处理条件
             if (!adviceBean.isApplicableToBeanType(parameter
                     .getContainingClass())) {
                continue;
             }
             // 如果满足则获取切面bean
             advice = adviceBean.resolveBean();
          }
          // 判断切面bean是否是要获取类的实现子类,是则添加
          if (adviceType.isAssignableFrom(advice.getClass())) {
             result.add((A) advice);
          }
       }
       // 返回结果集
       return result;
    }
    @Override
    public HttpInputMessage beforeBodyRead(HttpInputMessage request, 
            MethodParameter parameter, 
            Type targetType, Class<? extends HttpMessageConverter<?>> 
                    converterType) throws IOException {
       // RequestBodyAdvice几个方法的大致执行流程都差不多,因此只取一个分析
       // 获取满足条件的切面bean
       for (RequestBodyAdvice advice : 
               getMatchingAdvice(parameter, RequestBodyAdvice.class)) {
          // 一一调用切面bean的supports方法判断是否满足条件
          if (advice.supports(parameter, targetType, converterType)) {
             // 若满足则调用切面bean的相应方法
             request = advice.beforeBodyRead(request, parameter, 
                     targetType, converterType);
          }
       }
       return request;
    }
    @Override
    @Nullable
    public Object beforeBodyWrite(@Nullable Object body, 
            MethodParameter returnType, MediaType contentType,
            Class<? extends HttpMessageConverter<?>> converterType,
            ServerHttpRequest request, ServerHttpResponse response) {
       // 调用processBody方法实现
       return processBody(body, returnType, contentType, converterType, 
               request, response);
    }
    @Nullable
    private <T> Object processBody(@Nullable Object body, 
            MethodParameter returnType, MediaType contentType,
            Class<? extends HttpMessageConverter<?>> converterType,
            ServerHttpRequest request, ServerHttpResponse response) {
       // 和RequestBodyAdvice的执行流程差不多
       // 获取满足条件的切面bean
       for (ResponseBodyAdvice<?> advice : 
               getMatchingAdvice(returnType, ResponseBodyAdvice.class)) {
          // 一一调用切面bean的supports方法判断是否满足条件
          if (advice.supports(returnType, converterType)) {
              // 若满足则调用切面bean的相应方法
              body = ((ResponseBodyAdvice<T>) advice)
                     .beforeBodyWrite((T) body, returnType, contentType,
                     converterType, request, response);
          }
       }
       return body;
    }
}

3.具体实现源码分析

因为两个接口都是从ServletInvocableHandlerMethod类进去的,因此从这个类开始分析起:

public class ServletInvocableHandlerMethod extends InvocableHandlerMethod {
    public void invokeAndHandle(ServletWebRequest webRequest, 
            ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
       // 在invokeForRequest这个方法里将会解析参数并调用实际的处理方法处理请求
       // RequestBodyAdvice接口便是在解析参数时被调用的
       Object returnValue = invokeForRequest(webRequest, mavContainer, 
               providedArgs);
       // 处理@ResponseStatus注解
       setResponseStatus(webRequest);
       // 处理返回方法值的,略过
       ...
       try {
          // 调用handleReturnValue方法来处理方法返回值,在这里面将会调用
          // ResponseBodyAdvice接口方法
          this.returnValueHandlers.handleReturnValue(
                returnValue, getReturnValueType(returnValue), 
                mavContainer, webRequest);
       } catch (Exception ex) {
          throw ex;
       }
    }
    @Nullable
    public Object invokeForRequest(NativeWebRequest request, 
            @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
       // 解析参数
       Object[] args = getMethodArgumentValues(request, mavContainer, 
               providedArgs);
       // 调用实际的bean方法处理请求
       return doInvoke(args);
    }
    protected Object[] getMethodArgumentValues(NativeWebRequest request, 
            @Nullable ModelAndViewContainer mavContainer,
            Object... providedArgs) throws Exception {
       // 获取方法参数数组
       MethodParameter[] parameters = getMethodParameters();
       // 参数为空则直接返回
       if (ObjectUtils.isEmpty(parameters)) {
          return EMPTY_ARGS;
       }
       Object[] args = new Object[parameters.length];
       for (int i = 0; i < parameters.length; i++) {
          // 获得方法参数后开始遍历参数
          MethodParameter parameter = parameters[i];
          parameter.initParameterNameDiscovery(
                  this.parameterNameDiscoverer);
          args[i] = findProvidedArgument(parameter, providedArgs);
          if (args[i] != null) {
             continue;
          }
          // 判断resolvers是否支持该参数,如果不支持则抛出异常
          if (!this.resolvers.supportsParameter(parameter)) {
             throw new IllegalStateException();
          }
          try {
             // 支持处理该参数则调用解析器解析参数,RequestBodyAdvice将被处理
             args[i] = this.resolvers.resolveArgument(parameter, 
                     mavContainer, request, this.dataBinderFactory);
          } catch (Exception ex) {
             throw ex;
          }
       }
       return args;
    }
}

3.1 使用HandlerMethodArgumentResolverComposite处理RequestBodyAdvice接口

直接从该类的resolveArgument方法开始分析,其源码如下:

public class HandlerMethodArgumentResolverComposite 
        implements HandlerMethodArgumentResolver {
    // 封装的解析器集合
    private final List<HandlerMethodArgumentResolver> argumentResolvers = 
            new LinkedList<>();
    // 方法参数和对应解析器的缓存集合
    private final Map<MethodParameter, HandlerMethodArgumentResolver> 
            argumentResolverCache = new ConcurrentHashMap<>(256);
    @Override
    @Nullable
    public Object resolveArgument(MethodParameter parameter, 
            @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, 
            @Nullable WebDataBinderFactory binderFactory) throws Exception{
       // 根据参数类型获取具体的参数解析器将会遍历集合调用解析器的
       // supportsParameter方法来判断
       HandlerMethodArgumentResolver resolver = 
               getArgumentResolver(parameter);
       if (resolver == null) {
          throw new IllegalArgumentException();
       }
       // 调用具体的解析器解析参数,这里的resolver类型将是
       // RequestResponseBodyMethodProcessor类
       return resolver.resolveArgument(parameter, mavContainer, 
               webRequest, binderFactory);
    }
    @Nullable
    private HandlerMethodArgumentResolver getArgumentResolver(
            MethodParameter parameter) {
       // 先从缓存中获取
       HandlerMethodArgumentResolver result = this.argumentResolverCache
               .get(parameter);
       if (result == null) {
          // 如果缓存为空则遍历解析器来判断哪个满足条件
          for (HandlerMethodArgumentResolver methodArgumentResolver : 
                  this.argumentResolvers) {
             // 调用解析器的supportsParameter方法判断参数是否支持
             if (methodArgumentResolver.supportsParameter(parameter)) {
                result = methodArgumentResolver;
                // 把参数类型和解析器的对应关系放到缓存中
                this.argumentResolverCache.put(parameter, result);
                break;
             }
          }
       }
       return result;
    }
}

3.2 HandlerMethodReturnValueHandlerComposite处理ResponseBodyAdvice接口

直接从handleReturnValue方法开始分析,其源码如下:

public class HandlerMethodReturnValueHandlerComposite 
        implements HandlerMethodReturnValueHandler {
    // 封装的返回参数处理器集合对象
    private final List<HandlerMethodReturnValueHandler> 
            returnValueHandlers = new ArrayList<>();
    @Override
    public void handleReturnValue(@Nullable Object returnValue, 
            MethodParameter returnType, ModelAndViewContainer mavContainer,
                    NativeWebRequest webRequest) throws Exception {
       // 类似前面的参数解析器一样,这里先获取具体的处理器,支持没有缓存
       // 每次获取都需要从结果处理器集合中遍历获取
       HandlerMethodReturnValueHandler handler = selectHandler(returnValue,
               returnType);
       if (handler == null) {
          throw new IllegalArgumentException();
       }
       // 调用真正的处理器处理返回值
       handler.handleReturnValue(returnValue, returnType, mavContainer,
               webRequest);
    }
    @Nullable
    private HandlerMethodReturnValueHandler selectHandler(
            @Nullable Object value, MethodParameter returnType) {
       // 首先判断是否是异步的返回值
       boolean isAsyncValue = isAsyncReturnValue(value, returnType);
       for (HandlerMethodReturnValueHandler handler : 
               this.returnValueHandlers) {
          // 如果是异步的返回值和不是AsyncHandlerMethodReturnValueHandler类型
          // 则跳过本次循环
          if (isAsyncValue && 
              !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
             continue;
          }
          // 调用处理器的supportsReturnType方法判断是否支持返回值类型
          if (handler.supportsReturnType(returnType)) {
             return handler;
          }
       }
       return null;
    }
}

3.3 RequestResponseBodyMethodProcessor处理两种Advice切面接口

前面两步分析了,解析参数和处理返回值都会使用到RequestResponseBodyMethodProcessor对象,因此具体解析RequestBodyAdvice和ResponseBodyAdvice接口的逻辑将会在这里面完成。其部分关键源码如下:

public class RequestResponseBodyMethodProcessor 
        extends AbstractMessageConverterMethodProcessor {
    /**
     * 这是处理RequestBodyAdvice接口的方法入口
     */
    @Override
    public Object resolveArgument(MethodParameter parameter, 
            @Nullable ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest, 
            @Nullable WebDataBinderFactory binderFactory) throws Exception{
       // 解析RequestBodyAdvice接口的方法入口
       parameter = parameter.nestedIfOptional();
       // 调用readWithMessageConverters读取解析方法参数和方法体
       Object arg = readWithMessageConverters(webRequest, parameter, 
               parameter.getNestedGenericParameterType());
       // 后面的略过,解析RequestBodyAdvice接口只在readWithMessageConverters
       // 方法中
       ...
       return adaptArgumentIfNecessary(arg, parameter);
    }
    @Override
    protected <T> Object readWithMessageConverters(
            NativeWebRequest webRequest, MethodParameter parameter,
            Type paramType) throws IOException, 
                    HttpMediaTypeNotSupportedException, 
                    HttpMessageNotReadableException {
       // 将HttpRequest封装成特定的Request对象,最终的request对象将会
       // 实现HttpInputMessage接口
       HttpServletRequest servletRequest = webRequest
               .getNativeRequest(HttpServletRequest.class);
       ServletServerHttpRequest inputMessage = 
               new ServletServerHttpRequest(servletRequest);
       // 将封装后的inputMessage对象传入readWithMessageConverters方法解析
       Object arg = readWithMessageConverters(inputMessage, parameter, 
               paramType);
       if (arg == null && checkRequired(parameter)) {
          throw new HttpMessageNotReadableException();
       }
       return arg;
    }
    @Nullable
    protected <T> Object readWithMessageConverters(
            HttpInputMessage inputMessage, MethodParameter parameter,
            Type targetType) throws IOException, 
                HttpMediaTypeNotSupportedException, 
                HttpMessageNotReadableException {
       // 前面是获取判断contentType和参数class类型的逻辑,略过
       ... 
       EmptyBodyCheckingHttpInputMessage message;
       try {
          // 先使用EmptyBodyCheckingHttpInputMessage封装inputMessage对象
          message = new EmptyBodyCheckingHttpInputMessage(inputMessage);
          // 遍历messageConverters对象
          for (HttpMessageConverter<?> converter : this.messageConverters){
             // 先获取内容转换器的class对象类型
             Class<HttpMessageConverter<?>> converterType = 
                     (Class<HttpMessageConverter<?>>) converter.getClass();
             // 转换成GenericHttpMessageConverter类型
             GenericHttpMessageConverter<?> genericConverter =
                   (converter instanceof GenericHttpMessageConverter ?
                       (GenericHttpMessageConverter<?>) converter : null);
             // 如果这个内容转换器不为空且可以转换当前文本类型
             if (genericConverter != null ? genericConverter.
                     canRead(targetType, contextClass, contentType) :
                     (targetClass != null && 
                         converter.canRead(targetClass, contentType))) {
                // 如果内容的方法体不为空
                if (message.hasBody()) {
                   // 先执行beforeBodyRead方法
                   HttpInputMessage msgToUse = getAdvice()
                           .beforeBodyRead(message, parameter, 
                               targetType, converterType);
                   // 使用转换器将经过advice切面前置处理的内容转换成方法体
                   body = (genericConverter != null ? genericConverter
                           .read(targetType, contextClass, msgToUse) :
                           ((HttpMessageConverter<T>) converter)
                                   .read(targetClass, msgToUse));
                   // 对经过转换器转正具体的方法体对象进行后置处理
                   body = getAdvice().afterBodyRead(body, msgToUse, 
                           parameter, targetType, converterType);
                } else {
                   // 如果方法体为空则调用handleEmptyBody处理方法体为空的情况
                   body = getAdvice().handleEmptyBody(null, message, 
                           parameter, targetType, converterType);
                }
                break;
             }
          }
       }
       // 对获得的body方法体进行特殊的判断
       return body;
    }
    // -------------------------方法分割线----------------------------
    /**
     * 这是处理ResponseBodyAdvice接口的方法入口
     */
    @Override
    public void handleReturnValue(@Nullable Object returnValue,
            MethodParameter returnType, ModelAndViewContainer mavContainer,
            NativeWebRequest webRequest) throws IOException, 
                HttpMediaTypeNotAcceptableException, 
                HttpMessageNotWritableException {
       // 使用ResponseBodyAdvice切面处理返回值的方法入口
       mavContainer.setRequestHandled(true);
       // 根据webRequest分别创建对应的inputMessage和outputMessage对象
       ServletServerHttpRequest inputMessage = 
               createInputMessage(webRequest);
       ServletServerHttpResponse outputMessage = 
               createOutputMessage(webRequest);
       // 真正执行ResponseBodyAdvice切面增强接口方法的方法
       writeWithMessageConverters(returnValue, returnType, inputMessage, 
               outputMessage);
    }
    protected <T> void writeWithMessageConverters(@Nullable T value, 
            MethodParameter returnType,
            ServletServerHttpRequest inputMessage, 
            ServletServerHttpResponse outputMessage)
            throws IOException, HttpMediaTypeNotAcceptableException, 
                HttpMessageNotWritableException {
        // 前面是对body方法体的大致判断如String类型的判断和内容类型
        ...
        if (selectedMediaType != null) {
           selectedMediaType = selectedMediaType.removeQualityValue();
           // 遍历内容转换器
           for (HttpMessageConverter<?> converter : 
                   this.messageConverters) {
              GenericHttpMessageConverter genericConverter = 
                      (converter instanceof GenericHttpMessageConverter ?
                      (GenericHttpMessageConverter<?>) converter : null);
              // 如果转换器不为空且这种的内容类型可以转换
              if (genericConverter != null ?
                    ((GenericHttpMessageConverter) converter)
                            .canWrite(targetType, valueType, 
                            selectedMediaType) :
                    converter.canWrite(valueType, selectedMediaType)) {
                 // 获取ResponseBodyAdvice切面调用其处理方法
                 body = getAdvice().beforeBodyWrite(body, returnType, 
                         selectedMediaType,
                         (Class<? extends HttpMessageConverter<?>>) 
                                 converter.getClass(),
                         inputMessage, outputMessage);
                 // 后面略过
                 ...
                 return;
              }
           }
        }
        ...
    }
}

至此,对于RequestBodyAdvice和ResponseBodyAdvice接口的处理调用源码便分析完毕,其中需要注意的是getAdvice()方法返回的对象类型是RequestResponseBodyAdviceChain,这里的处理逻辑都是在RequestResponseBodyAdviceChain中完成的。

 

 

  • 0
    点赞
  • 0
    评论
  • 6
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:数字20 设计师:CSDN官方博客 返回首页

打赏作者

Armyyyyy丶

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值