SpringMVC常用注解使用

  • 三、SpringMVC常用注解使用
  • 3.1 @RequestMapping
  • 3.1.1 属性
  • 3.1.2 请求路径Ant Path匹配规则
  • 3.1.3 指定请求方法
  • 3.1.4 一般的RequestMapping格式
  • 3.2 @ResponseBody
  • 3.2.1 作用
  • 3.2.2 示例
  • 3.3 @ResponseStatus
  • 3.3.1常见的HTTP状态码
  • 3.3.2 @ResponseStatus使用
  • 3.4 @Mapping相关注解
  • 3.5 @RequestAttribute
  • 3.5.1 http请求中的属性(attribute)和参数(parameter)的区别
  • 3.5.2 @RequestAttribute的使用
  • 3.5.3 扩展:`/`与`/*`的区别
  • 3.6 @ModelAttribute
  • 3.6.1 Model对象
  • 3.6.2 @ModelAttribute使用
  • 3.7 @RestController


三、SpringMVC常用注解使用

3.1 @RequestMapping

之前我们使用RequestMapping最多的是使用value属性指定URL与Controller方法上的映射,但是如果我们有一个查询用户GET /user/{id}接口和删除用户接口DELETE /user/{id},那该怎么区分呢?下面就来具体介绍一下RequestMapping各种属性来解决上诉类似问题

3.1.1 属性
  • value
    指定请求路径上的url,符合AntMatcher匹配规则
  • path
    同value
  • method
    指定方法的请求方式RequestMethodGET、HEAD、POST、PUT、PATCH、DELETE、OPTIONS、TRACE
  • params
    指定限制请求参数的条件
  • 要求请求映射所匹配的请求必须携带param请求参数 param
  • 要求请求映射所匹配的请求必须不能携带params请求参数 !param
  • 要求请求映射所匹配的请求必须携带param请求参数且param=value param=value
  • 要求请求映射所匹配的请求必须携带param请求参数但是param!=value param!=value
  • headers
    发送的请求中必须包含的请求头
    类似于params的四种条件
  • consumes
    指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
    MediaType.APPLICATION_JSON_VALUE、MediaType.TEXT_HTML_VALUE
  • produces
    指定返回的内容类型
    取值为MediaType

spring mvc可以打成jar包运行吗 spring mvc使用_spring

3.1.2 请求路径Ant Path匹配规则

RequestMapping的value属性指定的请求路径满足ant路径匹配规则,且Spring还提供了一个AntPathMatcher工具类。下面介绍一下ant路径匹配规则。

特殊符号

作用


匹配任意单个字符

*

匹配当前路径下(以/作为一层路径)的0个或任意个字符

**

匹配0个或任意个路径

{id: [a-z0-9A-Z_-]+}

将正则表达式[a-z0-9A-Z_-]+匹配到的值,赋值给名为id 的路径参数,且必须完全匹配

可以在工程下建立测试类,使用AntPathMatcher进行测试,新建一个AntPathMatcherTest测试类

spring mvc可以打成jar包运行吗 spring mvc使用_JSON_02

编写测试用例如下:

package org.numb.common.util;

import org.junit.Assert;
import org.junit.Test;
import org.springframework.util.AntPathMatcher;

public class AntPathMatcherTest {

    @Test
    public void testAntPath() {
        AntPathMatcher matcher = new AntPathMatcher();
        boolean result;
        String path = "/user/00112233";
        result = matcher.match("/user/{id}", path);
        Assert.assertTrue(result);

        path = "/user/00112233";
        result = matcher.match("/user/?", path);
        Assert.assertFalse(result);

        path = "/user/00112233";
        result = matcher.match("/user/*", path);
        Assert.assertTrue(result);

        path = "/user/00112233/ZhangSan";
        result = matcher.match("/user/*", path);
        Assert.assertFalse(result);

        path = "/user/00112233/ZhangSan";
        result = matcher.match("/user/**", path);
        Assert.assertTrue(result);

        path = "/user/00112233";
        result = matcher.match("/user/{id:[a-z0-9A-Z_-]+}", path);
        Assert.assertTrue(result);

        path = "/user/00112233$";
        result = matcher.match("/user/{id:[a-z0-9A-Z_-]+}", path);
        Assert.assertFalse(result);
    }
}

注意AntPathMatchermatch(String pattern, String path)方法,要匹配的路径在前,请求的路径在后,换言之RequestMapping的value属性类比于这里的pattern参数。分析一下上面的测试用例

请求路径

匹配路径

结果

说明

/user/00112233

/user/{id}

true

一般的请求路径映射

/user/00112233

/user/?

false

?只能匹配一个字符,00112233有多个字符

/user/00112233

/user/*

true

*匹配当前路径下的任意字符

/user/00112233/ZhangSan

/user/*

false

*只能匹配一层路径,/00112233/ZhangSan是两层路径,所以无法匹配

/user/00112233/ZhangSan

/user/**

true

**匹配任意层路径

/user/00112233

/user/{id:[a-z0-9A-Z_-]+}

true

满足[a-z0-9A-Z_-]+正则表达式

/user/00112233$

/user/{id:[a-z0-9A-Z_-]+}

false

不满足[a-z0-9A-Z_-]+正则表达式

通过上面例子可以更好的理解RequestMapping的请求路径匹配规则。

3.1.3 指定请求方法

现在来解决如何区分相同请求路径参数的不同http方法,即类似查询用户GET /user/{id}接口和删除用户接口DELETE /user/{id}的区分。可以用RequestMapping的method属性指定请求方法,从而完成请求的映射。

可以改写上述UserController的接口如下,满足Restful风格:

@Controller
public class UserController {
    @Resource
    private UserService userService;

    @RequestMapping(value = "/user/{user_id}", method = RequestMethod.GET)
    @ResponseBody
    public String getUser(@PathVariable(value = "user_id") String id) {
        return userService.getUserName(id);
    }

    @RequestMapping(value = "/user/{user_id}", method = RequestMethod.DELETE)
    @ResponseBody
    public String deleteUser(@PathVariable(value = "user_id") String userId) {
        System.out.println(userId);
        return "success";
    }
}
3.1.4 一般的RequestMapping格式

一般常用的valuemethodconsumesproduces来确定一个接口,如下所示

package org.numb.controller;

import org.numb.model.User;
import org.numb.service.UserService;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.util.List;

@Controller
public class UserController {
    @Resource
    private UserService userService;

    @RequestMapping(value = "/user/{user_id}", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody public String getUser(@PathVariable(value = "user_id") String id) {
        return userService.getUserName(id);
    }

    @RequestMapping(value = "/users", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody public String getUser(@RequestBody List<User> users) {
        System.out.println(users);
        return "success";
    }

    @RequestMapping(value = "/user/{user_id}", method = RequestMethod.DELETE, consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ResponseBody
    public String deleteUser(@PathVariable(value = "user_id") String userId) {
        System.out.println(userId);
        return "success";
    }
}

3.2 @ResponseBody

3.2.1 作用

将函数返回值装入responsebody中返回到前端,也就是将return返回值作为请求返回值,return的数据不会解析成返回跳转路径。

3.2.2 示例

当使用@ResponseBody标注时,如果返回的是一个对象,则直接将这个对象序列化成字符串返回。这里注意必须依赖序列化组件,由于在2.2章节中已经引入jackson,在这里直接引用。增加UserService中的getUser方法,并用Map模拟数据库操作,返回一个User对象

UserService

public User getUser(String userId) {
    String userName =  getUserName(userId);
    User user = new User();
    user.setId(userId);
    user.setName(userName);
    return user;
}

UserController

@RequestMapping(value = "/user/{user_id}", method = RequestMethod.GET, consumes = 			MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
public User getUser(@PathVariable(value = "user_id") String id) {
    return userService.getUser(id);
}

测试:

spring mvc可以打成jar包运行吗 spring mvc使用_restful_03

注意要增加请求头Content-Type:application/json,否则由于我们指定了RequestMapping的consumes属性,会报415不支持的媒体类型(Unsupported media type)错误。

3.3 @ResponseStatus

用应返回的状态代码和原因消息标记方法或异常类。 调用处理程序方法时或抛出指定的异常时,状态代码将应用于 HTTP 响应。 它会覆盖通过其他方式设置的状态信息,例如ResponseEntityredirect:。这里我们简单讲一下controller方法利用@ResponseStatus返回http状态码。

3.3.1常见的HTTP状态码

状态码

状态

说明

200

OK

客户端请求成功

202

Accepted

已接受处理请求,但处理尚未完成

204

No Content

服务器已成功完成请求,并且不会返回响应体

302

Found

临时跳转,跳转的地址通过Location指定

400

Bad Requet

客户端请求有语法错误,不能被服务器识别

401

Unauthorized

客户端尝试在受保护资源上运行而未提供适当的授权

403

Forbidden

服务器收到请求,但是拒绝提供服务

404

Not Found

请求的资源不存在

405

Method Not Allowed

表示客户端尝试使用资源不允许的HTTP方法

415

Unsupported Media Type

表明无法处理客户端提供的媒体类型

500

Internal Server Error

服务器发生不可预期的错误

509

Bandwidth Limit Exceeded

服务器达到带宽限制。当前访问太频繁被限流

3.3.2 @ResponseStatus使用

这里希望post:/users请求返回202,但是默认返回200,此时可以用@ResponseStatus返回202。

@RequestMapping(value = "/users", method = RequestMethod.POST, consumes = 					MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
@ResponseStatus(code = HttpStatus.ACCEPTED)
public String getUser(@RequestBody List<User> users) {
    System.out.println(users);
    return "success";
}

测试:

spring mvc可以打成jar包运行吗 spring mvc使用_restful_04

3.4 @Mapping相关注解

@Mapping@RequestMapping@GetMapping等相关注解的关系。

从@RequestMapping的源码可以看出之间的关系:

spring mvc可以打成jar包运行吗 spring mvc使用_restful_05

spring mvc可以打成jar包运行吗 spring mvc使用_springmvc_06

  • @Mapping:表明请求映射的元注解
  • @RequestMapping:实际表示将一个 Web 请求映射到Controller类中方法的注解,可以理解为@Mapping的实现者。
  • @GetMapping:在@GetMapping注解上加了@RequestMapping(method = RequestMethod.GET),即指定了get的请求映射注解,其余类似。
  • @PostMapping:表明post请求映射的注解。
  • @PutMapping:表明put请求映射的注解。
  • @DeleteMapping:表明delete请求映射的注解。
  • @PatchMapping:表明patch请求映射的注解。

关系图为:

spring mvc可以打成jar包运行吗 spring mvc使用_springmvc_07

3.5 @RequestAttribute

@RequestAttribute注解的作用是获取请求中的属性值(attribute),属性(attribute)和参数(parameter)容易混淆,特在此说明。

3.5.1 http请求中的属性(attribute)和参数(parameter)的区别
  • 来源不同:
    参数(parameter)是从客户端(浏览器)中由用户提供的,是直接由前端传到后端来的值。
    属性(attribute)是服务器端的组件(JSP或者Servlet)利用requst.setAttribute()设置的,在SpringMVC体系中一般在**过滤器(filter)拦截器(interceptor)**中进行设置。
  • 操作不同:
    参数(parameter)的值只能读取不能修改,读取可以使用request.getParameter()读取;
    属性(attribute)的值既可以读取也可以修改,读取可以使用request.getAttribute(),设置可使用request.setAttribute()
  • 数据类型不同:
    参数(parameter)不管前台传来的值语义是什么,在服务器获取时都以String类型看待,并且客户端的参数值只能是简单类型的值,不能是复杂类型,比如一个对象。
    属性(attribute)的值可以是任意一个Object类型

ServletRequest接口源码中便可以看出

spring mvc可以打成jar包运行吗 spring mvc使用_JSON_08

参考链接:request中参数(parameter)和属性(Attribute)的区别

3.5.2 @RequestAttribute的使用

可以用filter验证@RequestAttribute的使用。在成熟项目的中,会对一个用户的登录信息进行身份验证,此时会引入token信息来作为验证手段。本节中可以在filter中设置一个token的attribute,然后在controller中获取到token信息并进行验证,这里仅实现filter中request.setAttribute("token", token)来做@RequestAttribute使用的演示说明。

  • 添加一个自定义filter
  1. 自定义AuthenticationFilter
package org.numb.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import org.springframework.stereotype.Component;

// 生成一个AuthenticationFilter的bean,被spring托管
@Component
public class AuthenticationFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        if (!(request instanceof HttpServletRequest)
            || !(response instanceof HttpServletResponse)) {
            throw new ServletException("AuthenticationFilter can handle http requests");
        }
        request.setAttribute("token", "I am root");
        chain.doFilter(request, response);
    }

    @Override
    public void destroy() {

    }
}
  1. 在web.xml中配置filter
<filter>
    <filter-name>authenticationFilter</filter-name>
    <filter-class>org.numb.filter.AuthenticationFilter</filter-class>
</filter>
<filter-mapping>
    <filter-name>authenticationFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

spring mvc可以打成jar包运行吗 spring mvc使用_springmvc_09

  • 测试@RequestAttribute的使用
  1. 在UserController中添加@RequestAttribute
@RequestMapping(value = "/user/{user_id}", method = RequestMethod.GET, consumes = 	MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
@ResponseBody
@ResponseStatus(code = HttpStatus.OK)
public User getUser(@PathVariable(value = "user_id") String id,
                    @RequestAttribute(value = "token") String tokenInfo) {
    System.out.println(tokenInfo);
    return userService.getUser(id);
}
  1. postman测试
3.5.3 扩展:<url-pattern>/</url-pattern><url-pattern>/*</url-pattern>的区别

/:表示匹配所有请求,其中包含除.jsp和.jspx外的所有后缀。

/*:会覆盖其他所有的servlet,包括servlet容器提供的所有servlet。即无论你发出什么请求,都会在该servlet拦截处理,当然也包括.jsp和.html等静态资源。

/**:什么请求都不能拦截处理,相当于没有设置。

3.6 @ModelAttribute

@ModelAttribute,用来将请求参数绑定到 Model 对象。

3.6.1 Model对象

模型(Mode)对象的作用主要是保存数据,可以借助它们将数据带到前端。前端可以获取model数据,并且实现相应的业务逻辑

3.6.2 @ModelAttribute使用
  • 应用于Controller内普通方法上
    @ModelAttribute标注的方法会在Controller的每个请求映射方法(带@RequestMapping的方法)执行之前都执行,因此对于一个Controller中包含多个请求映射方法的时候,要谨慎使用。
    方法一:方法参数上带Model参数,使用 model.addAttribute()设置。
/**
* ModelAttribute的使用 <br/>
* 方法一:方法参数上带Model参数,使用 model.addAttribute()设置
*
* @param model
*/
@ModelAttribute
public void addModel(Model model) {
    model.addAttribute("requestId", UUID.randomUUID().toString());
}

方法二:使用返回值,返回值会被自动调用设置进隐含的Model。

/**
* ModelAttribute的使用 <br/>
* 方法二:使用返回值,返回值会被自动调用设置进隐含的Model。
* 在Model中的key为返回值首字母小写,value为返回的值
*/
@ModelAttribute
public String addModel() {
    return UUID.randomUUID().toString();
}
  • 应用于方法参数上
    可以获取Model中的attribute,这一点有点类似于@RequestAttribute
@ModelAttribute
public void addModel(Model model) {
    model.addAttribute("requestId", UUID.randomUUID().toString());
}

@RequestMapping("/hello")
@ResponseBody
public String helloUser(@RequestParam(value = "user_name", required = false) String userName, @ModelAttribute(value = "request_id") String requestId) {
    System.out.println(requestId);
    return "Hi, " + userName;
}

测试:

spring mvc可以打成jar包运行吗 spring mvc使用_restful_10

  • 应用于Controller内请求映射方法(带@RequestMapping的方法)上
    表明请求映射方法的返回值不再是视图,而是将返回值放入到Model

3.7 @RestController

@RestController的作用于一个Controller类上,表明这个类是一个Controller,且会给每个请求映射方法都会追加@ResponseBody注解,表明方法返回值不是一个视图,直接将结果返回至前台处理。

@RestController的源码如下:

spring mvc可以打成jar包运行吗 spring mvc使用_JSON_11

被@RestController注解的Controller类,可以不用在每个方法上再加@ResponseBody,但是注意一点,如果是重定向方法,可能会导致重定向失效。因为表明了方法返回值是直接返回至前端的,所以就不再重定向方法,返回值被当作字符串返回到前端