Controller 是 Spring 中最基本的组件,主要处理用户交互,一般每个业务逻辑都会有一个 Controller,供用户请求接口进行数据访问;@RequestMapping 注解用于绑定URI到具体处理器。二者相辅相成,共同完成前后端数据交互。
一、简介
本文软件环境:IntelliJ IDEA version:2018.3 Spring Boot version: 2.1.4.RELEASE; Java version:1.8。
Controller 是 Spring 中最基本的组件,主要处理用户交互,一般每个业务逻辑都会有一个 Controller,供用户请求接口进行数据访问。
在Spring MVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示。Spring MVC 提供了一个非常简便的定义Controller 的方法,你无需继承特定的类或实现特定的接口,只需使用@Controller 标记一个类即可,然后使用@RequestMapping 和@RequestParam 等一些注解以定义URL 请求和Controller 方法之间的映射,这样Controller 就能被外界访问到。此外Controller 不会直接依赖于HttpServletRequest 和HttpServletResponse 等HttpServlet 对象。 @RequestMapping 注解可谓非常的强大,Spring MVC 和 Spring Boot 中都会用到这个注解。要学会 @RequestMapping 的用法,就需要从它的原理和实现机制说起,本文就和读者朋友们一起扒一扒Spring Boot中 @RequestMapping 的神秘面纱。 为了先对Controller 有一个初步的印象,我们定义一个简单的UserController: 例1 控制器类UserController@Controller @RequestMapping("/user") public class UserController { private static Logger logger = LoggerFactory.getLogger(UserController.class); /** * 示例地址 http://localhost:8087/user/viewUser?ownerId=100 * * @author Wiener * @date 2019/5/8 11:27 */ @RequestMapping("/viewUser") @ResponseBody public User viewUser(Long ownerId) { logger.info("请求参数 ownerId = " + ownerId); User user = new User(); user.setId(ownerId); user.setName(" --> Lucy"); return user; } } // 定义User Bean public class User { private Long id; private String name; // omit getter、setter and toString }
在上述示例中,@Controller 是标记在类UserController上面的,所以此类就是一个控制器类对象了。使用@RequestMapping("/user")标记控制器,用@RequestMapping(“/viewUser”) 标记方法,表示当请求“/user/viewUser”的时候访问的是UserController的viewUser方法,它返回了一个User 对象。这些在下文将会详细介绍。
二、@RestController介绍
在例1中,被@Controller标记的类就是一个Spring Boot Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。@Controller只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器,这个接下来就会讲到。 在Spring Boot Web项目中,单单使用@Controller 标记在一个类上就可以把这个控制器类交给Spring 来管理,无需额外配置。 @RestController:Spring 4 新增注解,同样可以注解 Controller 类,相当于@Controller + @ResponseBody,主要是为了使 http 请求返回 json 或者xml格式数据,一般情况下都是使用这个注解。下文都基于此注解进行验证。三、@RequestMapping 配置URI映射
要配置 Web 请求的映射,就需要用上 @RequestMapping 注解。@RequestMapping 注解可以在控制器类的级别和/或其中的方法的级别上使用。在类级别上的注解会将一个特定请求或者请求模式映射到一个控制器之上,之后还可以添加方法级别的注解来进一步绑定与具体处理方法的映射关系。类上的 “请求地址”是方法上的“请求地址”的父地址。 在例1中,因为UserController被@RequestMapping 标记,所以当访问被@RequestMapping 标记的viewUser方法时,需要使用相对路径“/user/viewUser”请求;如果去掉“/user”,使用绝对路径“/viewUser”就可以了。 @RequestMapping 注解的源码:@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Mapping public @interface RequestMapping { String name() default ""; String[] value() default {}; String[] path() default {}; RequestMethod[] method() default {}; String[] params() default {}; String[] headers() default {}; String[] consumes() default {}; String[] produces() default {}; }
注解@Target有两个属性,分别为 ElementType.METHOD 和 ElementType.TYPE,这表明@RequestMapping注解可以添加在类级别和方法级别上。
@RequestMapping 注解中的属性除了 name 返回类型是字符串,其它的方法均返回数组,也就是可以定义多个属性值。此处name属性,相当于方法的注释,使方法更易理解。四、@RequestMapping属性介绍
在RequestMapping 中除了指定请求路径value 属性外,还有其它的属性可以指定,如params 、method 和headers 。这些属性都可以达到缩小请求的映射范围的目的。- value属性
@RestController@RequestMapping("/home") public class IndexController { @RequestMapping(value = { "", "/page", "page*", "view/*,**/msg" }) String indexMultipleMapping() { return "Hello from index multiple mapping."; } }
前面这段代码中,如下的这些 URI 都会由 indexMultipleMapping() 来处理:
localhost:8080/home localhost:8080/home/page localhost:8080/home/pageabc localhost:8080/home/view/ localhost:8080/home/view/view localhost:8080/home/view2/msg
这个示例同时说明了可以将多个请求映射到一个方法上去,只需要添加一个带有请求路径值列表的 @RequestMapping 注解就行了。
- params属性
@RequestMapping (value= "/testParams" , params={ "param1=value1" , "param2" , "!param3" }) public String testParams() { System. out .println( "test Params..........." ); return "testParams" ; }
在上面的代码中,我们用@RequestMapping 的params 属性指定了三个参数,这些参数都是针对请求参数而言的,它们分别表示参数param1 的值必须等于value1,param2 必须存在,值无所谓,param3 必须不存在,只有当请求“/testParams”并且满足指定的三个参数条件的时候才能访问到该方法。所以当请求/testParams?param1=value1¶m2=value2 的时候testParams函数能够正确响应;当请求/testParams?param1=value1¶m2=value2¶m3=value3 的时候就无响应,因为在@RequestMapping 的params 参数里面指定了参数param3 是不能存在的。
- method属性
@RequestMapping (value= "testMethod" , method={RequestMethod. GET , RequestMethod. DELETE }) public String testMethod() { return "method" ; }
在上面的代码中就使用method 参数限制请求方式,以GET 或DELETE 方法请求/testMethod 的时候才能访问testMethod 方法。
- headers属性
@RequestMapping (value= "testHeaders" , headers={ "host=localhost" , "Accept" }) public String testHeaders() { return "headers" ; }
在上面的代码中当请求/testHeaders 的时候只有当请求头包含Accept 信息,且请求的host 为localhost 的时候才能正确的访问到testHeaders 方法。
- produces和consumes属性
@PostMapping(value = "/cons", consumes = { "application/JSON", "application/XML" }) public User getConsumes(Long ownerId) { User user = new User(); user.setId(ownerId); user.setName("Consumes attribute --> Lucy"); return user; }