@RestController与@Controller

一、简述

Spring 基于注释的 MVC 框架简化了创建 RESTful Web 服务的过程。传统的 SpringMVC 控制器和 RESTful Web 服务控制器之间的关键区别在于:创建 HTTP 响应主体的方式。虽然传统的 MVC 控制器依赖于 View 技术,但 RESTful Web 服务控制器只返回对象,对象数据作为 JSON/XML 直接写入 HTTP 响应。以下步骤描述了典型的 SpringMVC REST 工作流:

1️⃣客户端以 URI 形式向 Web 服务发送请求。

2️⃣该请求被 DispatcherServlet 拦截,该服务器查找 Handler Mappings 及其类型。

  • 应用程序上下文文件中定义的 Handler Mappings 部分告诉 DispatcherServlet 使用哪种策略根据传入请求查找控制器。
  • SpringMVC 支持三种不同类型的映射请求 URI 到控制器:注释,名称约定和显式映射。

3️⃣请求由 Controller 处理,响应返回到 DispatcherServlet,然后 DispatcherServlet 将调度到视图。

二、使用@Controller时需要用@ResponseBody注释

Spring 3.x 或使用@Controller情况下,在方法上使用@ResponseBody注释时,Spring 会转换返回值并自动将其写入 HTTP 响应。Controller 类中每个方法都必须使用@ResponseBody进行注释。Spring 有一个在后台注册的HttpMessageConverters列表。HTTPMessageConverter的职责是将请求主体转换为特定类并再次返回响应主体,具体取决于预定义的 mime 类型。每次发出请求命中@ResponseBody时,Spring 都会遍历所有已注册的HTTPMessageConverters,寻找符合给定 mime 类型和类的第一个,然后将其用于实际转换。

注意:
@ResponseBody添加到返回值中的每个@RequestMapping方法,Spring 将做两件事:

1️⃣将<context:component-scan><mvc:annotation-driven/>标记添加到 Spring 配置文件中。

  1. <context:component-scan>激活注释并扫描包以在应用程序上下文中查找和注册 bean。
  2. <mvc:annotation-driven/>如果 Jackson/JAXB 库在类路径上,则添加对读写 JSON/XML 的支持。
  3. 对于 JSON 格式,包括jackson-databind jar;对于 XML,包括项目类路径的jaxb-api-osgi jar。

2️⃣可在任何服务器(如Tomcat)上部署并运行应用程序。

  • http://localhost:8080/SpringRestControllerDemo/rest/Bob并显示输出JSON;
  • http://localhost:8080/SpringRestControllerDemo/rest/Bob.xml输出XML

三、@RestController相当于@ResponseBody + @Controller

Spring 4.0 引入了@RestController,这是一个控制器的专用版本,它是一个方便的注释,除了自动添加@Controller@ResponseBody注释之外没有其他作用。通过使用该注解对控制器类进行注释,不再需要将@ResponseBody添加到所有请求映射方法中。@ResponseBody注释默认处于活动状态。

使用@RestController非常简单,这是从 Spring v4.0 开始创建 MVC RESTful Web 服务或基于 SpringBoot 2 的首选方法。

  1. 【前后端分离】@RestController@Controller和@ResponseBody的合集,表示这是个控制器 bean,并且是将函数的返回值直接填入 HTTP 响应体中,是 REST 风格的控制器。
  2. 【前后端不分离】单独使用@Controller不加@ResponseBody的话一般使用在要返回一个视图的情况,这种情况属于比较传统的 SpringMVC 的应用。
  3. @Controller + @ResponseBody 返回 JSON 或 XML 形式数据。

四、区别

  1. @RestController注解相当于@ResponseBody@Controller合在一起的作用。
  2. 如果只是使用@RestController注解 Controller,则 Controller 中的方法无法返回 jsp 页面,配置的视图解析器InternalResourceViewResolver不起作用,返回的内容就是 return 里的内容。例如:本来应该到 success.jsp 页面的,则其显示 success。
  3. 如果需要返回到指定页面,则需要用@Controller配合视图解析器InternalResourceViewResolver才行。
  4. 如果需要返回 JSON,XML 或自定义 mediaType 内容到页面,则需要在对应的方法上加上@ResponseBody注解。

总之,使用@Controller注解在方法上,视图解析器可以解析 return 的 jsp、html 页面,并且跳转到相应页面。若要返回 json 等内容到页面,则需要加@ResponseBody。使用@RestController,返回 json 数据不需要在方法前面加@ResponseBody,但使用@RestController,就不能返回 jsp、html 页面,视图解析器无法解析。

五、Spring的Controller是单例

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

@Controller
public class ScopeTestController {
    private int num = 0;
    @RequestMapping("/testScope")
    public void testScope() {
        System.out.println(++num);
    }
    @RequestMapping("/testScope2")
    public void testScope2() {
        System.out.println(++num);
    }
}

首先访问 http://localhost:8080/testScope,得到的答案是 1;
然后再访问 http://localhost:8080/testScope2,得到的答案是 2。

得到的不同的值,这是线程不安全的。给 controller 增加作用域@Scope("prototype")

@Controller
@Scope("prototype")
public class ScopeTestController {
    private int num = 0;
    @RequestMapping("/testScope")
    public void testScope() {
        System.out.println(++num);
    }
    @RequestMapping("/testScope2")
    public void testScope2() {
        System.out.println(++num);
    }
}

首先访问 http://localhost:8080/testScope,得到的答案是 1;
然后再访问 http://localhost:8080/testScope2,得到的答案还是 1。

由此发现:单例 controller 类是不安全的,会导致属性重复使用。解决方案:

  1. 不要在 controller 中定义成员变量。
  2. 如若非要定义一个非静态成员变量,则通过@Scope("prototype"),将其设置为多例模式。
  3. 在 Controller 中使用 ThreadLocal 变量
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 223,924评论 6 521
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 95,776评论 3 402
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 170,954评论 0 366
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 60,576评论 1 300
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 69,589评论 6 399
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,105评论 1 314
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,480评论 3 427
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,457评论 0 278
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,998评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,026评论 3 343
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,174评论 1 354
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,812评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,502评论 3 336
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,979评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,101评论 1 275
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 49,674评论 3 380
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,204评论 2 363