zoukankan      html  css  js  c++  java
  • Sping MVC 框架学习二:Spring MVC 中 @RequestMapping、@PathVariable、@RequestParam 等常用注解用法

     

    @RequestMapping 注解用法

    在POJO类定义处标注 @Controller,再通过 <context:component-scan/> 扫描相应的类包,即可使POJO成为一个能处理 HTTP 请求的控制器。

    你可以创建数量不限的控制器,分别处理不同的业务逻辑,如 LoginController、UserController、ForumController等。每个控制器可拥有多个处理请求的方法,每个方法负责不同的请求操作。如何将请求映射到对应的控制器方法中是 Spring MVC 框架最重要的任务之一,这项任务就是由 @RequestMapping 来承担的。DispatchServlet 截获请求后,就通过控制器上 @RequestMapping 提供的映射信息确定请求所对应的处理方法。

    在控制器的 类定义处 及 方法定义处 都可标注 @RequestMapping,它们分别代表:

    1. 类定义处:提供初步的请求映射信息。相对于 WEB 应用的根目录。

    2. 方法处:提供进一步的细分映射信息。相对于类定义处的 URL。若类定义处未标注 @RequestMapping,则方法标记处的 URL 相对于 WEB 应用的根目录。

     1 @Controller
     2 public class HelloWorld 
     3 {
     4     private static final String SUCCESS = "success";
     5     
     6   @RequestMapping("/helloworld")
     7     public String hello()
     8     {
     9             ... ...
    10    }
    1 1     <!-- 类定义处未添加映射信息时,只需匹配方法处的映射信息 -->
    2 2     <a href="helloworld">Hello World</a>
     1 @RequestMapping("/springmvc")
     2 @Controller
     3 public class HelloWorld 
     4 {
     5     private static final String SUCCESS = "success";
     6 
     7     @RequestMapping("/helloworld")
     8     public String hello()
     9     {
    10             ... ...
    1     <!-- 类定义处添加了映射信息时,需把类定义处与方法定义处的映射信息都写上 -->
    2     <a href="springmvc/helloworld">Hello World</a>

    @RequestMapping 除了可以使用请求 URL 映射之外,还可以使用请求方法、请求参数及请求头映射请求。

    @RequestMapping 的 value、method、params heads 属性分别表示请求 URL、请求方法、请求参数及请求头的映射条件,它们之间是与的关系,联合使用多个条件可以让请求映射更加精确化。

    1. method属性

     1 @RequestMapping("/springmvc")
     2 @Controller
     3 public class SpringTest
     4 {
     5     private static final String SUCCESS = "success";
     6     
     7     //使用 method 属性来指定请求方式
     8     @RequestMapping(value="/testMethod", method=RequestMethod.POST)
     9     public String testMethod()
    10     {
    11         System.out.println("Test Method");
    12         return SUCCESS;
    13     }
    14 }
    <!-- 编写一个index.jsp请求页面,以post方式提交一个表单 -->
    <form action="springmvc/testMethod" method="post"> <input type="submit" value="submit"/> </form>

    编写一个 success.jsp 跳转结果页面

     1 <%@ page language="java" contentType="text/html; charset=ISO-8859-1"
     2     pageEncoding="ISO-8859-1"%>
     3 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
     4 <html>
     5 <head>
     6 <title>Insert title here</title>
     7 </head>
     8 <body>
     9     <h4>Success Page</h4>
    10 </body>
    11 </html>

    点击 “submit” 按钮可以看到 成功跳转至 success.jsp 页面,同时控制台打印出 Test Method。

    注:若此时请求为 GET 请求,则程序会报错。

    2. params 和 headers 属性

    它们支持简单的表达式

    1. param1:表示请求必须包含名为 param1 的请求参数

    2. !param1:表示请求不能包含名为 param1 的请求参数

    3. param1 != value1:表示请求包含名为 param1 的请求参数,但其值不能为 value1

    4. {"param1=value1", "param2"}:请求必须包含名为 param1 和 param2 的两个请求参数,且param1 参数的值必须为 value1

     1 @RequestMapping("/springmvc")
     2 @Controller
     3 public class SpringTest
     4 {
     5     private static final String SUCCESS = "success";
     6     //测试params属性
     7     @RequestMapping(value="/testParamAndHeader", params={"username", "age!=10"})
     8     public String testParamAndHeader()
     9     {
    10         System.out.println("Test ParamAndHeader");
    11         return SUCCESS;
    12     }
    13 }
        <!-- 当age值不为10时,程序能正常执行,若将age值改为10,则程序将不能正常执行 -->
        <a href="springmvc/testParamAndHeader?username=a&age=1">Test ParamAndHeader</a>

    3. heads属性

    1     //此时的 headers 属性能使程序正常执行,若将属性值改为 Accept-Language:en-US,zh;q=0.8 ,程序将报错。请求jsp使用上面的jsp请求即可
    2     @RequestMapping(value="/testParamAndHeader", params={"username", "age!=10"}, headers={"Accept-Language:zh-CN,zh;q=0.8"})
    3     public String testParamAndHeader()
    4     {
    5         System.out.println("Test ParamAndHeader");
    6         return SUCCESS;
    7     }

    @RequestMapping 不但支持标准的URL,还支持 Ant 风格的 URL

    Ant 风格资源地址支持3种匹配符:

    1. ?:匹配文件名中的一个字符

    2. *:匹配文件名中的任意字符

    3. **:匹配多层路径

    @RequestMapping 支持 Ant 风格的URL:

    1. /user/*/createUser:匹配如 /user/aaa/createUser、/user/bbb/createUser 等 URL

    2. /user/**/createUser:匹配如 /user/createUser、/user/aaa/bbb/createUser 等 URL

    3. /user/createUser??:匹配如 /user/createUseraa、/user/createUserbb 等 URL

    在前面的SpringTest类中添加如下方法,在index.jsp页面增加 Test AntPath 超链接

    1     @RequestMapping("/testAntPath/*/abc")
    2     public String testAntPath()
    3     {
    4         System.out.println("Test AntPath");
    5         return SUCCESS;
    6     }
        <a href="springmvc/testAntPath/xyz/abc">Test AntPath</a>

    启动服务器,程序可以正常运行,将超链接中xyz改为其他任何字符,程序也是可以运行的

     

    @PathVariable 注解用法

    @RequestMapping 还支持带占位符的 URL,该功能在 Spring MVC 向 REST 目标挺进的发展过程中具有里程碑的意义。

    通过 @PathVariable 可以将 URL中的占位符绑定到控制器处理方法的入参中

    在SpringTest中新增一个方法,相应index.jsp页面增加一个超链接

    1     //可以映射URL中的占位符到目标方法的参数中
    2     @RequestMapping("/testPathVariable/{id}")
    3     public String testPathVariable(@PathVariable("id") Integer id)
    4     {
    5         System.out.println("testPathVariable:" + id);
    6         return SUCCESS;
    7     }
    <a href="springmvc/testPathVariable/1">Test PathVariable</a>

    类定义处的 @RequestMapping 的 URL 如果使用占位符的参数,也可以绑定到处理方法的入参中

     1 @RequestMapping("/springmvc/{Msg}")
     2 @Controller
     3 public class SpringTest
     4 {
     5     private static final String SUCCESS = "success";
     6 
     7     @RequestMapping("/testPathVariable/{id}")
     8     public String testPathVariable(@PathVariable String Msg, @PathVariable Integer id)
     9     {
    10         System.out.println("testPathVariable:" + Msg + ", " + id);
    11         return SUCCESS;
    12     }
        <a href="springmvc/Msg/testPathVariable/1">Test PathVariable</a>

    由结果可以看 URL中的占位符已经绑定到控制器处理方法的入参中了

    注:为避免传递参数时可能出现的空指针异常,@PathVariable 参数接收类型最好使用对象类型(原声类型使用其包装类型)

     

     

    @RequestParam 注解用法

    Spring MVC 通过分析处理方法的签名,将HTTP请求信息绑定到处理方法的相应的入参中,并根据方法的返回值类型做出相应的后续出理。

    前面我们讲过的 @PathVariable 是一个 REST 风格的注解,它通过占位符放入方式携带一个参数,但这个参数不是正真意义上的请求参数。

    在处理方法入参处使用 @RequestParam 可以把请求参数传递给请求方法,它有三个参数

    1. value:参数名

    2. required:是否必须,默认为true,表示请求中必须包含对应的参数名,如果不存在将抛出异常

    3. defaultValue:默认参数,表示如果请求中没有同名参数时的默认值。设置该参数时,自动将 required 设置为 false。不推荐使用

    SpringTest类新增如下方法,此时请求参数的键值必须与@RequestParam中value参数相同

    1     @RequestMapping(value="/testRequestParam")
    2     public String testRequestParam(@RequestParam(value="username")String name,
    3             @RequestParam("age")Integer age)
    4     {
    5         System.out.println("testRequestParam:" + name + ", " + age);
    6         return SUCCESS;
    7     }

    index.jsp新增超链接

        <a href="springmvc/testRequestParam?username=tom&age=10">Test RequestParam</a>

    启动服务器,程序正常运行。此时如果我们将超链接改为

        <a href="springmvc/testRequestParam?username=tom">Test RequestParam</a>

    程序执行时报错,解决方法在 @RequestParam 添加属性 required=false,或者还可以使用 defaultValue 为参数赋默认值

        @RequestMapping(value="/testRequestParam")
        public String testRequestParam(@RequestParam(value="username")String name,
                @RequestParam(value="age", required=false, defaultValue="0")Integer age)
        {
            System.out.println("testRequestParam:" + name + ", " + age);
            return SUCCESS;
        }

    需要注意

    @RequestParam 参数类型最好都写为对象类型,不然可能会发生空指针异常。 如将上面代码中的 Integer 改为 int 类型,当传入的 age 参数为空时,在方法处的 age 值会被赋值为 null,但是 int 类型的变量不能被赋值为 null,会发生空指针异常,所以最好写为对象类型或包装类型。

    如果实在没办法必须写为原生数据类型,那么就需要使用 required 和 defaultValue 来避免异常产生,比如 设置 required=false;设置一个默认值 defaultValue=默认值。

    @RequestHeader 

    在请求头中包含了若干属性,服务器可根据此获取和客户端的信息,通过 @RequestHeader 即可将请求头中的属性值绑定到处理方法的入参中

        @RequestMapping("/testRequestHeader")
        public String testRequestHeader(@RequestHeader(value="Accept-language")String lag)
        {
            System.out.println("testRequestHeader: " + lag);
            return SUCCESS;
        }
        <a href="springmvc/testRequestHeader">Test RequestHeader</a>

    @CookieValue

    Spring MVC 还提供了绑定 Cookie 值的注解 @CookieValue ,它可以让方法入参绑定某个 Cookie 的值,它和 @RequestParam 拥有3个一样的参数

        @RequestMapping("/testCookieValue")
        public String testCookieValue(@CookieValue(value="JSESSIONID")String sessionId)
        {
            System.out.println("testCookieValue: " + sessionId);
            return SUCCESS;
        }

    使用 POJO 对象绑定请求参数

    当我们要映射一个表单中的多个项目时,可以使用将表单中的内容放入一个 POJO 对象中,当绑定表单到处理方法入参时就不必一个一个的去写实现,而只需将 POJO 对象绑定到入参,可以提高效率。Spring MVC 会按照请求参数名和 POJO 属性名进行自动装配,自动为该对象填充属性值,并且支持级联属性。

    新增两个 POJO 对象

    package com.bupt.springmvc.entity;
    
    public class User
    {
        private String username;
        private Address address;
       
      //生成 get 和 setter 方法,重写 toString 方法
    }
    package com.bupt.springmvc.entity;
    
    public class Address
    {
        private String city;
    
      //生成 get 和 setter方法,重写 toString 方法
    }

    SpringTest类新增处理方法

        @RequestMapping("/testPojo")
        public String testPojo(User user)
        {
            System.out.println("testPojo: " + user);
            return SUCCESS;
        }

    index.jsp新增表单

        <form action="springmvc/testPojo" method="post">
            username: <input type="text" name="username"/><br>
            city: <input type="text" name="address.city"/><br>
            <input type="submit" value="submit"/>
        </form>

    使用 Servlet API 作为入参

    在Spring MVC 中,控制器类可以不依赖任何 Servlet API 对象,但是 Spring MC 并不阻止我们使用 Servlet API 的类作为处理方法的入参。

    Spring MVC 的 Handler 方法接受的 Servlet API 包括:HttpServletRequest、HttpServletResponse、HttpSession、java.security.Principal、Locale、InputStream、OutputStream、Reader、Writer。

    继续添加处理方法和超链接

    1     @RequestMapping("/testServletAPI")
    2     public void testServletAPI(HttpServletRequest request, HttpServletResponse response, Writer out) throws IOException
    3     {
    4         System.out.println("testServlet: " + request + ", " + response);
    5         out.write("Hello");
    6     }

     

    REST 风格的请求

    REST :即 Representational State Transfer,一般翻译为(资源)表现层状态转化。

    表现层:把资源具体呈现出来的形式,叫做它的表现层。比如,文本可以使用txt格式表现,也可以用HTML格式、XML格式、JSON格式表现,甚至可以采用二进制格式。

    状态转化:没法送一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,就是一个无状态协议,即所有的状态都保存在在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”。而这种转化是建立在表现层之上的,所以就是“表现层状态转化”。具体说,就是 HTTP 协议里,四个表示操作的动词:GET、POST、PUT、DELETE。它们分别对应四种基本操作:GET用来获取资源,POST用来新建资源,PUT用来更新资源,DELETE用来删除资源。

    浏览器表单只支持 GET 与 POST 请求,而 DELETE、PUT 等并不支持。所以Spring 3.0 提供了一个 HiddenHttpMethodFilter,允许通过 “_method” 的表单参数指定这些特殊的HTTP方法(实际上还是通过POST提交表单)。服务器端配置了 HiddenHttpMethodFilter 后,Spring 会根据 _method 参数指定的值模拟出相应的 HTTP 方法,这样就可以使用这些HTTP 方法对处理方法进行映射了。

    示例:

    - /order/1 HTTP GET:得到 id = 1 的 order

    - /order/1 HTTP DELETE:删除 id = 1 的 order

    - /order/1 HTTP PUT:更新 id = 1 的 order

    - /order HTTP POST:新增 id = 1 的 order

    在SpringTest里新增如下方法

     1     /*
     2      * 如何发出 PUT 请求和 DELETE 请求呢
     3      * 1. 配置 HiddenHttpMethodFilter
     4      * 2. 发出POST请求
     5      * 3. 发出POST请求时携带一个name="_method"的隐藏域,值为 DELETE 或 PUT
     6      */
     7     @RequestMapping(value="/testRest/{id}", method=RequestMethod.GET)
     8     public String testRestGet(@PathVariable Integer id)
     9     {
    10         System.out.println("testRest GET:" + id);
    11         return SUCCESS;
    12     }
    13     
    14     @RequestMapping(value="/testRest", method=RequestMethod.POST)
    15     public String testRestPost()
    16     {
    17         System.out.println("testRest POST");
    18         return SUCCESS;
    19     }
    20     
    21     @RequestMapping(value="/testRest/{id}", method=RequestMethod.DELETE)
    22     public String testRestDelete(@PathVariable Integer id)
    23     {
    24         System.out.println("testRest DELETE:" + id);
    25         return SUCCESS;
    26     }
    27     
    28     @RequestMapping(value="/testRest/{id}", method=RequestMethod.PUT)
    29     public String testRestPut(@PathVariable Integer id)
    30     {
    31         System.out.println("testRest PUT:" + id);
    32         return SUCCESS;
    33     }

    index.jsp中新增如下超链接

     1     <form action="springmvc/testRest/1" method="post">
     2         <input type="hidden" name="_method" value="PUT">
     3         <input type="submit" value="Test REST PUT">
     4     </form>
     5     <br><br>
     6     <form action="springmvc/testRest/1" method="post">
     7         <input type="hidden" name="_method" value="DELETE">
     8         <input type="submit" value="Test REST DELETE">
     9     </form>
    10     <br><br>
    11     <form action="springmvc/testRest" method="post">
    12         <input type="submit" value="Test REST POST">
    13     </form>
    14     <br><br>
    15     <a href="springmvc/testRest/1">Test REST GET</a>

    启动服务器,运行程序,如果你运行环境是在 tomcat 8 下,当运行到 DELETE 和 PUT 请求时,程序报错

    出现的原因,根据 stackoverflow 上的解释说是因为,JSP 2.3 要求只能响应 GET、HEAD 和 POST,对于其他的HTTP方法不支持。Tomcat选择不支持PUT、DELETE方法来防止出现篡改HTTP方法的攻击。

    直接从网上找了三种解决方法:

    1. 将Tomcat 8 改为 Tomcat 7,在 Tomcat 7 环境下运行是正常的

    2. 将请求转发(forward) 改为请求重定向 (redirect)

    3. 自己手动写一个 Filter 来包装 HttpRequest 中的 getMethod()方法

    下面介绍第三种做法,也就是自己写一个 Filter 来包装从服务器发送过来的 HttpRequest 请求

    大致流程:

    1. 客户端向服务器端发送请求,若发送的是 POST 请求且带有以 _method 为名的参数会被 Spring 的 HanddenHttpMethodFilter给拦截

    2. HiddenHttpMethodFilter 内有一个静态内部类通过继承 HttpServletRequestWrapper 类重写 getMethod()方法,将该方法返回值设为 _method 隐藏域的值。

    3. HiddenHttpMethodFilter 在包装好 Request 后,将请求发往服务器中对应的方法处理器,这时请求变成了3中的 WrapperRequest by SpringFilter

    4. 服务器处理完请求后,产生一个 forward 请求,产生相应的请求信息发往客户端,注意这时的 request 的 getMethod() 方法仍然是 HiddenHttpMethodFilter 包装过的

    5. 我们需要在请求到达客户端前拦截它,通过自定义的滤波器 MyHttpMethodFilter进一步包装请求,将getMethod() 方法返回值改成 POST 或  GET 即可。

    6. 在 web.xml 中配置该filter 注意 dispatcher节点值必须为FORWARD

    <filter-mapping>
        <filter-name>myFilter</filter-name>
        <url-pattern>/*</url-pattern>
        <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

    <dispatcher>元素的作用:

    这个元素有四个可能值:REQUEST、FORWARD、INCLUDE 和 ERROR,可以在一个 <filter-mapping> 元素中加入任意数目的 <dispatcher>,使得filter将会作用于直接从客户端过来的request,通过forward过来的request,通过include过来的request和通过 <error-page> 过来的request。若没有指定任何 <dispatcher>元素,默认值是REQUEST。

  • 相关阅读:
    IO多路复用
    事件驱动模型
    协程
    进程
    py2与py3的编码问题
    Linux Centos7 网卡无法启动
    监控的法则
    如何优雅的采集activeMQ性能指标
    一分钟性能分析
    beta版 tomcat 应用监控指标
  • 原文地址:https://www.cnblogs.com/2015110615L/p/5625679.html
Copyright © 2011-2022 走看看