深入分析RestController与Controller

2023-11-12

@RestController和@Controller注解

我们都知道RestController默认都只提供Rest风格接口返回值,针对不需要返回页面的Controller都采用RestController进行注解,下面根据源码简单分析一下两者处理上的区别。

@RestController源码如下。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {

   /**
    * The value may indicate a suggestion for a logical component name,
    * to be turned into a Spring bean in case of an autodetected component.
    * @return the suggested component name, if any
    * @since 4.0.1
    */
   String value() default "";

}

@RestController的编写方式依赖注解组合,@RestController被@Controller和@ResponseBody标注,表示@RestController具有两者的注解语义,因此在注解处理时@RestController比@Controller多具有一个@ResponseBody语义,这就是@RestController和@Controller的区别,也是@RestController的返回值为何都是经过转换的json的原因。

所以小结就是:@RestController = @Controller + @ResponseBody;

@ResponseBody注解的处理过程

既然知道@RestController与@Controller的区别是多了一个@ResponseBody语义,我们不妨了解一下@ResponseBody的处理过程。

首先,可以知道,@ResponseBody是一个针对方法返回值进行处理的注解。如果熟悉Spring MVC处理过程的话,可以知道在根据requesturl映射获取到HandlerMethod之后,根据HandlerMethod调度请求方法的对象是HandlerAdapter,方法调用结束,返回值处理的调度对象也是HandlerAdapter。所以,@ResponseBody注解的处理应该也是在HandlerAdapter中完成。(ps:不清楚Spring MVC可以看深入分析Spring MVC

在RequestMappingHandlerAdapter#invokeHandlerMethod方法里面,有下面几句比较重要的代码

//创建方法调用对象
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
//......
//设置返回值处理器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
//......
//调用方法
invocableMethod.invokeAndHandle(webRequest, mavContainer);

returnValueHandlers这个变量听名字就像返回值处理,实际上也是对返回值处理的处理器集合。首先创建一个调用方法的对象,然后注入处理器,最后调用方法,这就是完整的一个流程。

我们可以再分析一下这里面的处理器初始化时有没有我们的针对@ResponseBody注解的处理器。

@ResponseBody注解处理器初始化

搜索一下returnValueHandlers初始化的地方,可以看到是这么个调用链:

  • RequestMappingHandlerAdapter#afterPropertiesSet

    • handlers = RequestMappingHandlerAdapter#getDefaultReturnValueHandlers

    • returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers)

所以是在RequestMappingHandlerAdapter的bean初始化完成时,就会进行返回值处理器的初始化,在RequestMappingHandlerAdapter#getDefaultReturnValueHandlers方法内部执行,代码如下。

private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
   List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();

   // Single-purpose return value types
   handlers.add(new ModelAndViewMethodReturnValueHandler());
   handlers.add(new ModelMethodProcessor());
   handlers.add(new ViewMethodReturnValueHandler());
   handlers.add(new ResponseBodyEmitterReturnValueHandler(getMessageConverters()));
   handlers.add(new StreamingResponseBodyReturnValueHandler());
   handlers.add(new HttpEntityMethodProcessor(getMessageConverters(),
         this.contentNegotiationManager, this.requestResponseBodyAdvice));
   handlers.add(new HttpHeadersReturnValueHandler());
   handlers.add(new CallableMethodReturnValueHandler());
   handlers.add(new DeferredResultMethodReturnValueHandler());
   handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));

   // Annotation-based return value types
   handlers.add(new ModelAttributeMethodProcessor(false));
   //@ResponseBody注解处理器
   handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(),
         this.contentNegotiationManager, this.requestResponseBodyAdvice));

   // Multi-purpose return value types
   handlers.add(new ViewNameMethodReturnValueHandler());
   handlers.add(new MapMethodProcessor());

   // Custom return value types
   if (getCustomReturnValueHandlers() != null) {
      handlers.addAll(getCustomReturnValueHandlers());
   }

   // Catch-all
   if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
      handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
   }
   else {
      handlers.add(new ModelAttributeMethodProcessor(true));
   }

   return handlers;
}

可以看到非常对处理器,RequestResponseBodyMethodProcessor就是@ResponseBody的处理器。

@ResponseBody注解处理器调用

进入调用方法invocableMethod.invokeAndHandle(webRequest, mavContainer)/ServletInvocableHandlerMethod#invokeAndHandle,继续进行调用,跟踪调用链如下。

  • ServletInvocableHandlerMethod#invokeAndHandle
    • this.returnValueHandlers.handleReturnValue/HandlerMethodReturnValueHandlerComposite#handleReturnValue

HandlerMethodReturnValueHandlerComposite#handleReturnValue代码如下所示。

public void handleReturnValue(Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
//选择一个合适的处理器
   HandlerMethodReturnValueHandler handler = selectHandler(returnValue, returnType);
   if (handler == null) {
      throw new IllegalArgumentException("Unknown return value type: " + returnType.getParameterType().getName());
   }
   //处理返回值
   handler.handleReturnValue(returnValue, returnType, mavContainer, webRequest);
}

so,基本就是从所有处理器中选出目标处理器,处理返回值。进入HandlerMethodReturnValueHandlerComposite#selectHandler

private HandlerMethodReturnValueHandler selectHandler(Object value, MethodParameter returnType) {
    boolean isAsyncValue = isAsyncReturnValue(value, returnType);
    for (HandlerMethodReturnValueHandler handler : this.returnValueHandlers) {
        if (isAsyncValue && !(handler instanceof AsyncHandlerMethodReturnValueHandler)) {
            continue;
        }
        //判断处理器是否支持
        if (handler.supportsReturnType(returnType)) {
            return handler;
        }
    }
    return null;
}

RequestResponseBodyMethodProcessor#supportsReturnType,代码如下。


public boolean supportsReturnType(MethodParameter returnType) {
   return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
         returnType.hasMethodAnnotation(ResponseBody.class));
}

明显如果类上有@ResponseBody或者方法上有的话,就能适配处理器,@RestController具有@ResponseBody语义能够适配,所以进行RequestResponseBodyMethodProcessor#handleReturnValue进行返回值处理。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

深入分析RestController与Controller 的相关文章

  • EncodedResource类解读

    EncodedResource类解读 EncodedResource介绍 EncodedResource是spring中Resource编码相关的封装类 EncodedResource里面封装了一个Resource成员属性 其实主要功能就是
  • 【Spring源码】Spring流程

    1 初始化AnnotationBeanDefinitionReader 2 初始化ClassPathBeanDefinitionScanner 3 执行register 注册配置类 4 执行refresh 先初始化比如BeanFactory
  • Spring源码之事件监听机制(下)

    文章目录 前言 一 手写事件监听机制框架 1 准备 2 事件监听接口 3 事件管理器 4 事件发布器 5 需求 6 编码 二 观察者模式 1 概述 2 UML图 3 Coding验证 小结 前言 这篇文章接的是上篇文章Spring源码之事件
  • Spring之Bean生命周期源码解析-Bean销毁

    这篇文章是我在系统学习Spring源码之后 基于自己对Spring源码的理解 来详细分析Spring之Bean的销毁过程 目录 前言 一 注册有销毁逻辑的Bean 1 判断当前Bean是否需要销毁 1 1 判断当前Bean是否有销毁方法 1
  • 【Spring源码】BeanPostProcessor

    org springframework beans factory support AbstractAutowireCapableBeanFactory 八次调用时机 1 是否需要代理 resolveBeforeInstantiation
  • Spring源码剖析之IOC容器创建流程

    ApplicationContextConfiguration为核心配置类 ApplicationContext applicationContext new AnnotationConfigApplicationContext Appli
  • Spring事务(三)——传播属性之REQUIRED

    事务是与连接对象紧密相关的 事务属性用来控制事务流转 Spring事务的传播属性有以下几种 Propagation REQUIRED 如果当前没有事务 就新建一个事务 如果已经存在一个事务中 则加入到这个事务中 默认属性 也是最常使用 Pr
  • Spring源码分析之createBean主流程分析

    我们知道 在调用getBean获取bean实例的实例 首先会从缓存中获取bean实例 如果没有获取到 就会去创建bean的时候 关于获取bean实例 可以参考Spring源码分析之getBean主流程分析 而本文将会对创建bean实例的主流
  • BeanFactoryPostProcessor扩展

    Configuration ComponentScan com example public class AppConfig Component public class User public User private String na
  • spring源码之@Autowired属性注入

    注入现象 当我们在属性上面加上 Autowired的时候 spring就要根据Type来注入实例了 那么到底会找哪个实例的如果有多个怎么办 今天就来实验一下 多接口注入 当注入的属性接口下有多个实现 这个时候运行的话是 public cla
  • spring加载流程之ConfigurationClassPostProcessor

    spring加载流程之ConfigurationClassPostProcessor ConfigurationClassPostProcessor postProcessBeanDefinitionRegistry processConf
  • Spring之启动过程源码解析

    Spring创建Bean 会经过一系列生命周期的流程 而Spring启动 其实就是为了后续创建Bean做一些准备工作 本篇以及下一篇文章都是来详细分析Spring的启动过程 目录 一 Spring启动的大致流程 二 Spring加载流程之A
  • 深入分析RestController与Controller

    RestController和 Controller注解 我们都知道RestController默认都只提供Rest风格接口返回值 针对不需要返回页面的Controller都采用RestController进行注解 下面根据源码简单分析一下
  • 浅谈Spring框架应用的设计模式(一)——工厂模式

    文章目录 前言 一 工厂模式介绍 1 简单工厂模式 1 静态工厂模式 2 利用反射机制实现的简单工厂 2 工厂方法模式 3 抽象工厂模式 二 Spring框架中工厂模式的重要应用 1 BeanFactory 2 FactoryBean 总结
  • IDEA导入Spring源码环境搭建

    一 环境准备 1 Spring源码包 下载地址 https github com spring projects spring framework 2 gradle工具 下载地址 http downloads gradle org dist
  • Spring源码:PropertyValues类及属性注入二

    主代码 1 RuntimeBeanReference类型 2 RuntimeBeanNameReference类型 3 BeanDefinitionHolder类型 4 BeanDefinition类型 5 ManagedArray类型 6
  • 【Spring源码】createBeanInstance()

    目录 创建实例 createBeanInstance 有参构造 autowireConstructor 无参构造 instantiateBean 实例化策略 instantiate createBeanInstance英文版 autowir
  • Spring源码:PropertyValues类及属性注入一

    概要 相关类 属性注入 概要 Spring获取Bean的实例时 需要把配置的属性值解析到PropertyValues 然后填充入BeanWrapper中 相关类 MutablePropertyValues类 PropertyValues接口
  • 四、spring源码循环依赖的处理之doCreateBean方法的执行流程(文字描述)

    还写了一篇 内容和这个差不多的 emm 那篇更简洁 算是半伪码形式 https blog csdn net qq 36951116 article details 100078947 其实createBean方法没做什么事 主要就是 1 调
  • Spring源码之Bean的生命周期

    Spring已经成为了目前最流行的第三方开源框架之一 我们在充分享受Spring IOC容器带来的便捷时 也应该考虑一下Spring这个大工厂是如何将一个个的Bean生产出来的 我们一起来讨论一下Spring中Bean的生命周期 Sprin

随机推荐

  • FPGA中的output or inout port xxx must be connected to a structural net expression错误

    主模块的output不能加reg 只在子模块的output 加reg 关于子模块调用 有两种调用方式 第一种是位置对应 如 bcd accbcd in8 out71 ou72 out73 如上图所示 第二种是信号名对应方式 此时不必按顺序
  • 数据结构---栈&&队列

    目录 什么是数据结构 什么是算法 Algorithm 生活中的数据结构和算法 数组结构 栈结构 stack 栈结构的实现 十进制转二进制 队列结构 Queue 队列的应用 对列类的创建 击鼓传花面试题 优先级队列 优先级队列的实现 什么是数
  • C语言中排序函数的用法

    C语言中没有预置的sort函数 如果在C语言中 遇到有调用sort函数 就是自定义的一个函数 功能一般用于排序 一 可以编写自己的sort函数 如下函数为将整型数组从小到大排序 void sort int a int l a为数组地址 l为
  • TensorFlow和Caffe、MXNet、Keras等其他深度学习框架的对比

    转 http www leiphone com news 201702 T5e31Y2ZpeG1ZtaN html 雷锋网按 本文作者黄文坚 PPmoney 大数据算法总监 TensorFlow 实战 作者 本文节选自 TensorFlow
  • SQL Server日期格式的多种转换方法

    MSSQL Server的日期字段是datetime 其默认格式是yyyy mm dd hh mm ss mmm 如在查询分析器里面执行 select getdate 会得到如下结果 2006 03 30 22 09 33 763 但对于我
  • 双因素方差分析(R)

    目录 原理 双因素等重复试验的方差分析 假设前提和模型设定 离差平方和分解 检验统计量和拒绝域 例题 应用 双因素无重复试验的方差分析 假设前提和模型设定 离差平方和分解 检验统计量和拒绝域 例题 应用 原理 在单因素方差分析的基础上 双因
  • 微信小程序 WXBizDataCrypt 解密 报错

    在使用微信官方WXBizDataCrypt js解密encryptedData获取敏感数据的时候 偶尔会报错 DeprecationWarning Buffer is deprecated due to security and usabi
  • 字符串的分割、截取

    文章目录 分割 截取的简单使用 简单分割 简单截取 升级版分割 截取的使用 url分割 id截取 或者截取其他由某种规则拼接起来的串儿 结尾 分割 截取的简单使用 简单分割 老师上课的时候大概会举类似的例子把 String str a b
  • 时序数据库InfluxDB介绍

    时序数据库InfluxDB介绍 1 什么是InfluxDB 2 那么时序数据有什么特点呢 3 对于时序数据 我们总结了以下特点 4 业务方常见需求 5 时序数据库为了解决什么问题 6 InfluxDB的优势 实时数据库与时序数据库 DBen
  • Unity中的资源管理-对象池技术(1)

    本文分享Unity中的资源管理 对象池技术 1 接下来几天 作者会按照自己的理解写几篇关于Unity中的资源管理相关的文章 大概会涉及到 对象池 分为普通类和GameObject 主要是预制 的对象池 引用计数技术 Unity中的资源基本概
  • Tesseract-OCR-05-主要API功能介绍

    Tesseract 05 主要API功能介绍 tesseract本身代码是由c c 混编而成的 其中有用的简单的接口函数几乎都是在baseapi h中 从其处理过程中 不难得出 它还需要有一个image处理的类 及相关的方法 这样子 读取图
  • 程序员的酸甜苦辣—写在即将告别coding的时刻

    毕业找工作时 我曾对朋友说 程序员这个称谓 是一个荣誉 过去的一切仿佛还在眼前 然而三天后 我就要告别程序员这个职业了 将来 我或许还会偶尔写写代码自娱自乐 我还会保持着单词第一个字母大写的 职业病 程序员 这普普通通三个字 凝聚着我一年多
  • chrome 扩展开发手册(一)——准备

    chrome Extensions翻译过来就是谷歌扩展 但我们更喜欢叫它谷歌浏览器扩展或chrome扩展 chrome扩展是定制浏览体验的小型软件程序 它们使用户能够根据个人需求或偏好定制Chrome功能和行为 对网页进行样式修改 增添功能
  • C compiler cc is not found

    安裝nginx是如果运行这个命令 configure prefix opt nginx出现以下错误 configure 45 auto init cannot create Makefile Permission denied config
  • CAN/CANFD 总线负载率及计算(源码和工具)

    CAN BUS的总线负载率是CAN总线架构协议设计时的一个重要的指标 一般建议负载率峰值不要高于80 平均负载率不要超过50 当然这只是一般建议 具体根据使用场景和系统设计而定 负载率定义 关于CAN负载率的定义和计算 很多文章写得不求甚解
  • K8s基础8——svc基础使用、应用暴露、iptables代理、ipvs代理

    文章目录 一 Service基本了解 二 Service定义与创建 2 1 相关命令 2 2 yaml文件参数大全 2 3 创建svc 2 3 1 两种创建方式类比 2 3 2 验证集群内A应用访问B应用 2 3 3 将集群外服务定义为K8
  • AAAI 2023

    单幅图像去运动模糊一直以来都是研究的热点 随着深度学习的兴起 基于端到端学习式的图像去模糊逐渐成为主流 运动模糊可分为全局和局部两类 其中全局运动模糊得到广泛关注 而局部运动模糊问题则较为被忽视 该文章从实际工业应用需求出发 聚焦于解决单图
  • 样本不平衡,欠采样,过采样

    什么是不平衡分类 失衡的分类是有监督的学习问题 其中一个类的人数远远超过其他类 与多级分类问题相比 该问题在二进制分类问题中面临的频率更高 术语不平衡是指因变量 响应 中遇到的差异 因此 分类失衡问题是因变量的类别比例失衡的问题 换句话说
  • 通过cmd命令窗口下载python模块(注:简洁明了,绝不拖泥带水)

    Win键 r 打开运行窗口 输入cmd 然后回车进入命令窗口 直接通过pip模块进行方法输入 四个基本命令 pip install 模块名 版本号 下载并安装模块 不标明版本号则默认下载最新版 pip show 模块名 显示模块版本号 路径
  • 深入分析RestController与Controller

    RestController和 Controller注解 我们都知道RestController默认都只提供Rest风格接口返回值 针对不需要返回页面的Controller都采用RestController进行注解 下面根据源码简单分析一下