@RestController与@Controller

发布于:2023-01-24 ⋅ 阅读:(14) ⋅ 点赞:(0) ⋅ 评论:(0)

一、简述

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,其方法无法返回 jsp 页面,配置的视图解析器InternalResourceViewResolver不起作用,返回的内容就是 return 里的内容。例如:本来应该到 success.jsp 页面的,则其显示 success。
  3. 如果需要返回到指定页面,则需要用@Controller配合视图解析器InternalResourceViewResolver才行。
  4. 如果需要返回 JSON,XML 或自定义 mediaType 内容到页面,则需要在对应的方法上加上@ResponseBody

总结:

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

五、Spring的Controller是单例的

性能原因,设计为单例模式,不用每次 new 浪费资源/时间。多线程访问的时候有线程安全问题,不要用同步,会影响性能的。解决方案是避免在控制器中定义属性。

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 变量