在使用Spring MVC开发Web应用程序时,控制器Controller的开发非常重要,虽然说视图(JSP或者是Thymeleaf)也很重要,因为它才是直接呈现给用户的,不过由于现在前端越来越重要,很多公司都开始采用前后端分离的开发模式,所以我们暂时可以将精力放在开发控制器上。

使用Spring MVC开发控制器主要使用以下7个注解:

  1. @Controller
  2. @RequestMapping
  3. @ResponseBody
  4. @RequestParam
  5. @PathVariable
  6. @RequestBody
  7. @RestController

接下来,我们依次讲解每个注解的使用方法。

先回顾下上篇博客中新建的简单控制器HelloController:

  1. package chapter05.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.RequestMethod;
  5. @Controller
  6. public class HelloController {
  7. @RequestMapping(value = "index", method = RequestMethod.GET)
  8. public String hello() {
  9. // 这里返回的逻辑视图名
  10. return "index";
  11. }
  12. }

这里@Controller注解的作用是用来声明控制器,它的源码如下所示:

  1. package org.springframework.stereotype;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. @Target({ElementType.TYPE})
  8. @Retention(RetentionPolicy.RUNTIME)
  9. @Documented
  10. @Component
  11. public @interface Controller {
  12. String value() default "";
  13. }

这里值得注意的是,@Controller注解使用了@Component注解,而@Component注解我们并不陌生,它用来声明一个Bean。

虽然有些书中说可以把@Controller注解替换为@Component注解,运行没有差别,只是表意性差一点,但是如果你将上面代码中的@Controller注解修改为@Component注解,然后重新打包发布到Tomcat,会发现访问地址http://localhost:8080/spring-action-1.0-SNAPSHOT/index时,报如下所示的404错误:

@Component注解还原为@Controller注解,然后重新打包发布到Tomcat,再次访问地址http://localhost:8080/spring-action-1.0-SNAPSHOT/index时,访问正常:

所以,在Spring MVC中声明控制器时,推荐使用@Controller注解。

注意事项:程序员在阅读技术书籍时,要多思考,多尝试,因为书籍中讲解的,很可能是错的。

@RequestMapping注解用来映射Web请求,它有2种使用形式:

  1. 应用在方法级别,如上面的代码中展示的那样。
  2. 应用在类级别,当控制器在类级别上添加@RequestMapping注解时,这个注解会应用到控制器的所有处理器方法上,处理器方法上的@RequestMapping注解会对类级别上的@RequestMapping注解的声明进行补充。

@RequestMapping注解常用的3个参数如下所示:

  1. value:指定映射的URL地址,如index
  2. method:指定映射的请求类型,如GET请求、POST请求等
  3. produces:指定返回的response的媒体类型和字符集,如application/json;charset=UTF-8。

指定method值时使用org.springframework.web.bind.annotation.RequestMethod枚举:

  1. package org.springframework.web.bind.annotation;
  2. public enum RequestMethod {
  3. GET,
  4. HEAD,
  5. POST,
  6. PUT,
  7. PATCH,
  8. DELETE,
  9. OPTIONS,
  10. TRACE;
  11. private RequestMethod() {
  12. }
  13. }

指定produces值时一般使用org.springframework.http.MediaType类下的常量:

  1. public static final String APPLICATION_JSON_VALUE = "application/json";
  2. public static final MediaType APPLICATION_JSON_UTF8 = valueOf("application/json;charset=UTF-8");
  3. public static final String APPLICATION_JSON_UTF8_VALUE = "application/json;charset=UTF-8";

为了更好的理解,我们在HelloController类上添加如下代码:

  1. package chapter05.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.RequestMethod;
  5. @Controller
  6. @RequestMapping("/hello")
  7. public class HelloController {
  8. @RequestMapping(value = "index", method = RequestMethod.GET)
  9. public String hello() {
  10. // 这里返回的逻辑视图名
  11. return "index";
  12. }
  13. }

重新打包并部署到Tomcat中,此时的访问地址从之前的http://localhost:8080/spring-action-1.0-SNAPSHOT/index变成了http://localhost:8080/spring-action-1.0-SNAPSHOT/hello/index,如下所示:

@RequestMapping注解的value属性还支持接受一个String类型的数组,如下所示:

  1. @RequestMapping({"/hello", "/index"})
  2. public class HelloController {
  3. // 省略其它代码
  4. }

此时也可以通过地址http://localhost:8080/spring-action-1.0-SNAPSHOT/index/index进行访问:

在上面的代码中,我们的方法是返回逻辑视图名index,然后由视图解析器最终找到运行时的/WEB-INF/classes/views/index.jsp视图,但有时我们不需要返回一个页面,而是直接返回数据给到前端。

此时我们可以使用@ResponseBody注解,该注解可以放在返回值前或者方法上,用于将返回值放在response体内,而不是返回一个页面。

为了更好的理解,我们新建个DemoAnnoController控制器如下所示:

  1. package chapter05.controller;
  2. import org.springframework.http.MediaType;
  3. import org.springframework.stereotype.Controller;
  4. import org.springframework.web.bind.annotation.RequestMapping;
  5. import org.springframework.web.bind.annotation.RequestMethod;
  6. import org.springframework.web.bind.annotation.ResponseBody;
  7. import javax.servlet.http.HttpServletRequest;
  8. @Controller
  9. @RequestMapping("/anno")
  10. public class DemoAnnoController {
  11. @RequestMapping(value = "/index", method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE)
  12. public @ResponseBody
  13. String index(HttpServletRequest request) {
  14. return "url:" + request.getRequestURI() + " can access";
  15. }
  16. }

重新打包并部署到Tomcat中,访问地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/index,效果如下所示:

也可以将@ResponseBody注解放在方法上,如下所示:

  1. @RequestMapping(value = "/index", method = RequestMethod.GET, produces = MediaType.TEXT_PLAIN_VALUE)
  2. @ResponseBody
  3. public String index(HttpServletRequest request) {
  4. return "url:" + request.getRequestURI() + " can access";
  5. }

@RequestParam注解用于接收URL中的参数信息。

为了更好的理解 ,我们在DemoAnnoController控制器中添加如下方法:

  1. @RequestMapping(value = "/requestParam", method = RequestMethod.GET, produces = "text/plain;charset=UTF-8")
  2. @ResponseBody
  3. public String passRequestParam(@RequestParam("id") Long id, @RequestParam("name") String name, HttpServletRequest request) {
  4. return "url:" + request.getRequestURI() + " can access,id: " + id + ",name=" + name;
  5. }

重新打包并部署到Tomcat中,访问地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/requestParam?id=1&name=zwwhnly ,效果如下所示:

注意事项:上面示例中,Url中的参数名称和方法中的变量名称完全一致,所以可以省略掉@RequestParam注解,不过为了代码的易读性,建议保留@RequestParam注解。

如果不传递参数,访问地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/requestParam,则会提示如下信息:

或者只传递其中1个参数,访问地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/requestParam?id=1,则会提示如下信息:

由此也说明,使用了@RequestParam注解的参数,在Url中必须传递。

不过,@RequestParam注解提供了defaultValue属性,可以给参数指定默认值,比如我们给参数id设置默认值1,给参数name设置默认值zwwhnly,然后访问地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/requestParam,效果如下所示:

或者访问地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/requestParam?id=2,效果如下所示:

不过,还是有一个异常场景需要注意,就是Url中传递的参数和方法中定义的参数类型不匹配,比如我们将id的值传错,访问地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/requestParam?id=zwwhnly&name=zwwhnly,会看到如下报错信息:

@PathVariable注解也是用于接收URL中的参数信息,不过和@RequestParam注解稍有不同。

@PathVariable注解用于解析Url中的路径参数,如https://www.cnblogs.com/zwwhnly/中的zwwhnly部分,而@RequestParam注解用于解析Url中的查询参数,如https://i.cnblogs.com/posts?page=2中的page部分。

为了更好的理解 ,我们在DemoAnnoController控制器中添加如下方法:

  1. @RequestMapping(value = "/pathvar/{str}", produces = "text/plain;charset=UTF-8")
  2. public @ResponseBody
  3. String demoPathVar(@PathVariable("str") String str, HttpServletRequest request) {
  4. return "url:" + request.getRequestURI() + " can access,str: " + str;
  5. }

重新打包并部署到Tomcat中,访问地址http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/pathvar/zwwhnly ,效果如下所示:

注意事项:如果@PathVariable注解中指定value属性的话,它会假设占位符的名称与方法的参数名相同。

因为这里方法的参数名正好与占位符的名称相同,所以我们可以去掉@PathVariable注解的value属性:

  1. @RequestMapping(value = "/pathvar/{str}", produces = "text/plain;charset=UTF-8")
  2. public @ResponseBody
  3. String demoPathVar(@PathVariable String str, HttpServletRequest request) {
  4. return "url:" + request.getRequestURI() + " can access,str: " + str;
  5. }

@RequestBody注解允许request的参数在request体中,而不是直接链接在地址后面,该注解放在参数前。

为了更好的理解 ,我们在DemoAnnoController控制器中添加如下方法:

  1. @RequestMapping(value = "/obj", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  2. @ResponseBody
  3. public String passObj(@RequestBody DemoObj demoObj, HttpServletRequest request) {
  4. return "url:" + request.getRequestURI() + " can access,demoObj id:" + demoObj.getId() +
  5. " demoObj name:" + demoObj.getName();
  6. }

重新打包并部署到Tomcat中,然后使用Postman工具调用接口http://localhost:8080/spring-action-1.0-SNAPSHOT/anno/passObj,效果如下所示:

@RestController是一个组合注解,它组合了@Controller注解和@ResponseBody注解,源码如下所示:

  1. package org.springframework.web.bind.annotation;
  2. import java.lang.annotation.Documented;
  3. import java.lang.annotation.ElementType;
  4. import java.lang.annotation.Retention;
  5. import java.lang.annotation.RetentionPolicy;
  6. import java.lang.annotation.Target;
  7. import org.springframework.stereotype.Controller;
  8. @Target({ElementType.TYPE})
  9. @Retention(RetentionPolicy.RUNTIME)
  10. @Documented
  11. @Controller
  12. @ResponseBody
  13. public @interface RestController {
  14. String value() default "";
  15. }

因此,如果某个控制器中所有的方法都只是返回数据而不是页面的话,就可以使用@RestController注解。

为了更好的理解 ,我们举个具体的示例。

首先,在pom.xml中添加如下依赖,用于对象和json之间的转换:

  1. <dependency>
  2. <groupId>com.fasterxml.jackson.core</groupId>
  3. <artifactId>jackson-databind</artifactId>
  4. <version>2.9.9</version>
  5. </dependency>

然后新建控制器DemoRestController如下所示:

  1. package chapter05.controller;
  2. import chapter05.model.DemoObj;
  3. import org.springframework.http.MediaType;
  4. import org.springframework.web.bind.annotation.RequestBody;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.RequestMethod;
  7. import org.springframework.web.bind.annotation.RestController;
  8. @RestController
  9. @RequestMapping("/rest")
  10. public class DemoRestController {
  11. @RequestMapping(value = "/getjson", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  12. public DemoObj getjson(@RequestBody DemoObj demoObj) {
  13. return new DemoObj(demoObj.getId(), demoObj.getName());
  14. }
  15. }

因为使用@RestController注解,相当于同时使用了@Controller注解和@ResponseBody注解,所以上面的代码等价于下面的代码:

  1. package chapter05.controller;
  2. import chapter05.model.DemoObj;
  3. import org.springframework.http.MediaType;
  4. import org.springframework.stereotype.Controller;
  5. import org.springframework.web.bind.annotation.*;
  6. @Controller
  7. @ResponseBody
  8. @RequestMapping("/rest")
  9. public class DemoRestController {
  10. @RequestMapping(value = "/getjson", method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
  11. public DemoObj getjson(@RequestBody DemoObj demoObj) {
  12. return new DemoObj(demoObj.getId(), demoObj.getName());
  13. }
  14. }

重新打包并部署到Tomcat中,然后使用Postman工具调用接口http://localhost:8080/spring-action-1.0-SNAPSHOT/rest/getjson,效果如下所示:

源码地址:https://github.com/zwwhnly/spring-action.git,欢迎下载。

Craig Walls 《Spring实战(第4版)》

汪云飞《Java EE开发的颠覆者:Spring Boot实战》

欢迎扫码关注微信公众号:「申城异乡人」,定期分享Java技术干货,让我们一起进步。

版权声明:本文为zwwhnly原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.cnblogs.com/zwwhnly/p/11572302.html