从零开始学习springmvc(3)——SpringMVC常用注解使用

【项目地址】 从零开始学习springmvc

三、SpringMVC常用注解使用

3.1 @RequestMapping

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

3.1.1 属性
  • value

    指定请求路径上的url,符合AntMatcher匹配规则

  • path

    同value

  • method

    指定方法的请求方式RequestMethod

    GET、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

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测试类

编写测试用例如下:

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);
}

测试:

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

3.3 @ResponseStatus

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

3.3.1常见的HTTP状态码
状态码状态说明
200OK客户端请求成功
202Accepted已接受处理请求,但处理尚未完成
204No Content服务器已成功完成请求,并且不会返回响应体
302Found临时跳转,跳转的地址通过Location指定
400Bad Requet客户端请求有语法错误,不能被服务器识别
401Unauthorized客户端尝试在受保护资源上运行而未提供适当的授权
403Forbidden服务器收到请求,但是拒绝提供服务
404Not Found请求的资源不存在
405Method Not Allowed表示客户端尝试使用资源不允许的HTTP方法
415Unsupported Media Type表明无法处理客户端提供的媒体类型
500Internal Server Error服务器发生不可预期的错误
509Bandwidth 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";
}

测试:

3.4 @Mapping相关注解

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

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

  • @Mapping:表明请求映射的元注解

  • @RequestMapping:实际表示将一个 Web 请求映射到Controller类中方法的注解,可以理解为@Mapping的实现者。

  • @GetMapping:在@GetMapping注解上加了@RequestMapping(method = RequestMethod.GET),即指定了get的请求映射注解,其余类似。

  • @PostMapping:表明post请求映射的注解。

  • @PutMapping:表明put请求映射的注解。

  • @DeleteMapping:表明delete请求映射的注解。

  • @PatchMapping:表明patch请求映射的注解。

关系图为:

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接口源码中便可以看出

参考链接: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() {
      
          }
      }
      
      
    2. 在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>
      

  • 测试@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);
      }
      
    2. 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;
    }
    

    测试:

  • 应用于Controller内请求映射方法(带@RequestMapping的方法)上

    表明请求映射方法的返回值不再是视图,而是将返回值放入到Model

3.7 @RestController

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

@RestController的源码如下:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值