3、SpringMVC注解式开发

学习资源:动力节点的2020最新SpringMVC教程【IDEA版】-springmvc从入门到精通



1、@RequestMapping 请求映射

在控制器的方法上使用,表示要对 value 属性所指定的 URI 请求进行处理与响应。

1.1、指定模块名称

通过 @RequestMapping 注解可以定义控制器对于请求的映射规则。 该注解可以注解在方法上,也可以注解在类上,但意义是不同的。 value 属性值常以 "/" 开始。

@RequestMapping 的 value 属性用于定义所匹配请求的 URI。但对于注解在方法上与类上,其 value 属性所指定的 URI,意义是不同的。

一个 @Controller 所注解的类中,可以定义多个控制器方法。当然,不同的控制器方法所匹配的 URI 是不同的。 这些不同的 URI 被指定在注解于方法之上的 @RequestMapping 的 value 属性中。 但若这些请求具有相同的 URI 部分,则这些相同的 URI, 可以被抽取到注解在类之上的 @RequestMapping 的 value 属性中,此时的这个 URI 表示模块的名称。 URI 的请求是相对于 Web 的根目录。

换个角度说,要访问控制器的指定方法,必须要在方法指定 URI 之前加上控制器类前定义的模块名称。

@Controller
public class FirstController {

    @RequestMapping("/test/some.do")
    public ModelAndView doSome(){
    }

    @RequestMapping("/test/other.do")
    public ModelAndView doOther(){
    }
}
//可将相同的 uri 提取到类上
@Controller
@RequestMapping("/test")
public class FirstController {

    @RequestMapping("some.do")
    public ModelAndView doSome(){
    }

    @RequestMapping("other.do")
    public ModelAndView doOther(){
    }
}

1.2、定义请求提交方式

对于 @RequestMapping,其有一个属性 method,用于对被注解方法所处理请求的提交方式进行限制,即只有满足该 method 属性指定的提交方式的请求,才会执行该被注解方法。

Method 属性的取值为 RequestMethod 枚举常量。常用的为 RequestMethod.GETRequestMethod.POST,分别表示提交方式的匹配规则为 GET 与 POST 提交。

客户端浏览器常用的请求方式,及其提交方式:

image-20200904105606401

不指定 method 属性,则无论是 GET 还是 POST 提交方式,均可匹配,即对于请求的提交方式无要求.

@RequestMapping("/other.do")
public ModelAndView doOther(){
}

指定了控制器方法匹配的请求提交方式为 POST,则相当于指定了请求发送的方式: 要么使用表单请求,要么使用 AJAX 请求,其它请求方式都会被被禁用。

@RequestMapping(value = {"/other.do"}, method = RequestMethod.POST)
public ModelAndView doOther(){
}

指定请求方式为 GET ,则超链接、GET 型表单都可发送请求到控制器。

@RequestMapping(value = {"/other.do"}, method = RequestMethod.GET)
public ModelAndView doOther(){
}

2、控制器方法的参数

控制器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用:

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession
  • Model
  • ModelMap
  • 请求中所携带的请求参数
@RequestMapping(value = "请求地址")
public 返回值 自定义方法名(HttpRequest request, 
                  HttpServletResponse response, 
                  HttpSession session, 
                  Model model, 
                  请求中携带参数...){
    
    
}

2.1、逐个参数接收(控制器方法形参接收)

若请求中的参数个数较少,则只要保证 uri 中的请求参数名与该请求处理的控制器方法的参数名相同即可,框架会把同名的请求参数赋值给同名的形参,对形参的顺序没有要求。

SpringMVC 使用 request 对象接收请求参数,String name= request.getParameter("name");String age= request.getParameter("age");,框架会提供类型转换的功能,能把String转为 int ,long , float, double等类型。

该方式是用于请求参数数量少的情况。

GET方式方便查看 uri
<form action="test/register.do" method="GET">
    姓名:<input type="text" name="name"/><br/>
    年龄:<input type="text" name="age" /><br/>
    <input type="submit" value="注册"/>
</form>

该表单的请求 uri:http://localhost:8082/springmvc/test/register.do?name=Linda&age=15

@Controller
@RequestMapping("/test")
public class FirstController {

    @RequestMapping("/register.do", method= RequestMethod.GET)
    public ModelAndView doRegister(Integer age, String name){
        
        ModelAndView mv = new ModelAndView();
        mv.addObject("registerName", );
        mv.addObject("registerAge", );

       	mv.setViewName("show");
      
        return mv;
    }
}

image-20200904114228038


2.1.2、字符集过滤器解决请求中参数乱码问题

请求使用 POST 方式,会出现请求中文参数乱码问题。

<form action="test/register.do" method="POST">
    姓名:<input type="text" name="name"/><br/>
    年龄:<input type="text" name="age" /><br/>
    <input type="submit" value="注册"/>
</form>

image-20200904114434612

Spring 对于请求参数中的中文乱码问题,给出了专门的字符集过滤器: spring-web-5.2.5.RELEASE.jar 的
org.springframework.web.filter 包下的 CharacterEncodingFilter 类。

<!-- 注册字符集过滤器:解决post请求乱码的问题 -->
<filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!-- 指定字符集 -->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
    <!-- 强制 request 使用 encoding -->
    <init-param>
        <param-name>forceRequestEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
    <!-- 强制 response 使用 encoding -->
    <init-param>
        <param-name>forceResponseEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
</filter>
<!-- 指定哪些 url 使用该过滤器,/* 表示所有请求 -->
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

2.1.2、@RequestParam 校正请求参数名

校正请求参数名,是指若请求 URL 所携带的参数名称与处理方法中指定的参数名不相同时, 则需在处理方法参数前, 添加一个注解@RequestParam("请求参数名"),指定请求 URL 所携带参数的名称。该注解是对控制器方法参数进行修饰的。

value 属性,String 类型,指定请求参数的名称。

required boolean 类型,默认是 true,true 表示请求中必须带有参数,false 则反之。如,设置为 true,发送请求:http://localhost:8082/springmvc/test/register.do?r_name=Linda&r_age=15 会出现 400 的错误。

<form action="test/register.do" method="POST">
    姓名:<input type="text" name="r_name"/><br/>
    年龄:<input type="text" name="r_age" /><br/>
    <input type="submit" value="注册"/>
</form>
@Controller
@RequestMapping("/test")
public class FirstController {

    @RequestMapping("/register.do", method= RequestMethod.POST)
    public ModelAndView doRegister(@RequestParam(value="r_age", required=false)Integer age, 
                                   @RequestParam(value="r_name", required=false)String name){
        
    }
}

2.2、对象接收参数

若请求中有多个参数,逐个接收参数较为繁琐,可以将控制器方法的参数定义为一个对象,只要保证请求参数名与这个对象的属性同名即可。

当控制器方法的形参是 Java 对象,SpringMVC 框架会使用参数类的无参构造器创建一个参数对象,并调用 setter 将请求参数一一赋值给参数对象的同名属性。

定义接收请求参数的类:

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Registrant {
    
    private String name;
    private Integer age;
}
<form action="test/register.do" method="POST">
    姓名:<input type="text" name="r_name"/><br/>
    年龄:<input type="text" name="r_age" /><br/>
    <input type="submit" value="注册"/>
</form>
@Controller
@RequestMapping("/test")
public class FirstController {

    @RequestMapping("/register.do", method= RequestMethod.GET)
    public ModelAndView doRegister(Registrant registrant){
        
        ModelAndView mv = new ModelAndView();
        mv.addObject("name",registrant.getName());
        mv.addObject("age",registrant.getAge());
        mv.addObject("registrant",registrant);

       	mv.setViewName("show");
        return mv;
    }
}

3、控制器方法的返回值

3.1、返回 ModelAndView

表示返回 数据+视图。

若控制器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据, 此时控制器方法返回 ModelAndView 比较好。当然,若要返回 ModelAndView,则控制器方法中需要定义 ModelAndView 对象。

Model 中的数据被存储在 request 作用域中,SringMVC 默认采用 转发 的方式跳转到视图,本次请求结束后,模型中的数据被销毁。

modelAndView.addObject("registerName", name);
modelAndView.addObject("registerAge", age);
modelAndView.setViewName("show");

在使用时, 若该控制器方法只是进行跳转而不传递数据,或只是传递数据而并不向任何资源跳转(如对页面的 Ajax 异步响应),此时若返回 ModelAndView,则将总是有一部分多余: 要么 Model 多余,要么 View 多余。 即此时返回 ModelAndView 将不合适。


3.2、返回 String(视图)

表示返回视图。

控制器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址;也可以去除视图解析器,使用视图的完整路径名。

@RequestMapping(value = "/test/register.do", method = RequestMethod.POST)
public String doRegister(HttpRequest request, Integer age, String name){
    
    request.setAttribute("name", name);
    request.setAttribute("age", age);

    //有视图解析器的情况
    return "show";
}

3.3、返回 void

对于控制器方法返回 void 的主要应用场景:AJAX 响应(异步请求,页面局部更新)。

若控制器对请求处理后,无需跳转到其它任何资源,此时可以让控制器方法返回 void。


导入 GSON 依赖:

<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.10.3</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.3</version>
</dependency>

引入 jquery 库,页面发起 ajax 请求:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
     <script type="text/javascript" src="js/jquery-3.4.1.js"></script>
     <script type="text/javascript">
         $(function(){
             $("button").click(function(){
                 $.ajax({
                     url:"returnVoid-ajax.do",
                     data:{
                         name:"张三",
                         age:20
                     },
                     type:"post",
                     // dataType 加不加都可以
                     dataType:"json",
                     success:function(resp){
                         //resp从服务器端返回的是json格式的字符串 {"name":"zhangsan","age":20}
                         //jquery会把字符串转为json对象, 赋值给resp形参。
                         alert(resp.name + "" + resp.age);
                     }
                 })
             })
         })
     </script>
</head>
<body>
    <button id="btn">发起ajax请求</button>
</body>
</html>

控制器方法:

@RequestMapping(value = "/returnVoid-ajax.do")
public void doAjax(HttpServletResponse response, Integer age, String name) throws IOException {

    // 处理ajax,使用json做数据的格式,使用 registrant 表示处理结果
    Registrant registrant =  new Registrant("张三", 19);
    // 把结果的对象转为json格式的数据

    ObjectMapper objectMapper = new ObjectMapper();
    String json = objectMapper.writeValueAsString(registrant);

    // 输出数据,响应ajax的请求
    PrintWriter writer = response.getWriter();
    writer.print(json);
    writer.flush();
    writer.close();
}

3.4、返回 Object

控制器方法也可以返回 Object 对象。 这个 Object 可以是 Integer, String, 自定义对象,Map, List 等。但返回的对象不是作为逻辑视图出现的,而是作为直接在原页面显示的数据出现。

主要使用场景:响应 ajax 请求,将响应数据以 json 格式输出到页面。

返回对象,需要使用 @ResponseBody 注解, 将转换后的 JSON 数据放入到响应体中。

实现步骤:

  1. 加入处理 json 的依赖。
    由于返回 Object 数据,一般都是将数据转化为了 JSON 对象后传递给浏览器页面的。而这个由 Object 转换为 JSON,是由 Jackson 工具完成的。所以需要导入 Jackson 的相关 Jar 包(springmvc 默认使用 jackson)。
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-core</artifactId>
    <version>2.10.3</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-databind</artifactId>
    <version>2.10.3</version>
</dependency>
  1. 在 springmvc 配置文件中配声明注解驱动。
    将 Object 数据转化为 JSON 数据,需要由消息转换器 HttpMessageConverter 完成。而转换器的开启,需要由 <mvc:annotation-driven/> 来完成。
    SpringMVC 使用消息转换器 HttpMessageConverter 实现请求数据和对象,控制器方法返回对象和响应输出之间的自动转换。
<!-- 注解驱动 -->
<!-- 开启消息转换器 -->
<mvc:annotation-driven/>
  1. 在控制器方法上使用 @ResponseBody 注解,作用是完成下面代码的功能
PrintWriter pw = reponse.getWrite();
pw.print(json);
pw.flush;
pw.close;

3.4.1、返回自定义类型对象

@RequestMapping(value = "/returnRegistrant.do")
@ResponseBody
public List<Registrant> doReturn() {

    Registrant registrant =  new Registrant("张三", 31);
    return registrant;
}

3.4.2、返回 List

@RequestMapping(value = "/returnList.do")
@ResponseBody
public List<Registrant> doReturnList() {

    Registrant registrant1 =  new Registrant("张三", 31);
    Registrant registrant2 =  new Registrant("张三", 31);
    List<Registrant> list = new ArrayList<>();
    list.add(registrant1);
    list.add(registrant2);
    return list;
}

3.4.3、返回 String

若要返回非中文字符串,将前面返回数值型数据的返回值直接修改为字符串即可。但若返回的字符串中带有中文字符,则接收方页 面将会出现乱码(中文字符转换为 json 默认使用的是 IOS-8859-1),此时需要使用 @RequestMapping 的 produces 属性指定字符集。

produces,产品,结果,即该属性用于设置输出结果类型。

@RequestMapping(value = "/returnString.do", produces="text/plain;charset=utf-8")
@ResponseBody
public String doreturnString() {
    
    return "SpringMVC 注解开发真快!";
}

4、url-pattern

<url-pattern/> 指定了请求地址与 servlet 的映射关系,如 *.do 的请求要交与 "springmvc " 这个框架的 servlet 处理。

<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.do</url-pattern>
</servlet-mapping>

然而 工程名/index.jsp、工程名/静态资源(.js、.html、.jpg等)在浏览器中可以直接访问到,是谁处理的它们呢?—— Tomcat。为什么 Tomcat 能处理这些请求呢,其原理也是使用 servlet ,其内部有一个名为 default 的 servlet,用来处理静态资源和未映射到其他 servlet 的请求。 "/" 表示 default 要处理的是静态资源。

<!-- The default servlet for all web applications, that serves static     -->
<!-- resources.  It processes all requests that are not mapped to other   -->
<!-- servlets with servlet mappings (defined either here or in your own   -->
<!-- web.xml file).  												  -->
<servlet>
    <servlet-name>default</servlet-name>
    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
    <init-param>
        <param-name>debug</param-name>
        <param-value>0</param-value>
    </init-param>
    <init-param>
        <param-name>listings</param-name>
        <param-value>false</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

4.1、*.后缀

在没有特殊要求的情况下, SpringMVC 的中央调度器 DispatcherServlet 的 <url-pattern/> 常使用后辍匹配方式,如写为 *.do 或者 *.action 、 *.mvc 等。

<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>*.do</url-pattern>
    <url-pattern>*.action</url-pattern>
</servlet-mapping>

4.2、/

/ 表示映射所有的请求到 DispatcherServlet,包括静态资源和动态资源的请求。

当给 DispatcherServlet 的映射配置为 "/" 后,DispatcherServlet 会替代 default ,但是默认情况下 DispatcherServlet 没有处理静态资源的能力,因为控制器不能访问静态资源,这样就会造成所有静态资源无法访问,404。

<servlet-mapping>
    <servlet-name>springmvc</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

4.3、静态资源访问

<url-pattern/> 的值并不是说写为 / 后,静态资源就无法访问了,经过一些配置后,该问题也是可以解决的。

4.3.1、声明 <mvc:default-servlet-handler/>

在 springmvc 配置文件中声明 <mvc:default-servlet-handler/>

声明了 <mvc:default-servlet-handler /> 后,springmvc 框架会在容器中创建 DefaultServletHttpRequestHandler 处理器对象,它会像一个检查员,对进入 DispatcherServlet 的 URL 进行筛查,如果发现是访问静态资源的请求,就将该请求转由 Web 应用服务器默认的 Servlet 处理(一般的服务器都有默认的 Servlet)。

<mvc:default-servlet-handler/>

4.3.2、声明 <mvc:resources/>

在 Spring3.0 版本后, Spring 定义了专门用于处理静态资源访问请求的处理器 ResourceHttpRequestHandler。并且添加了<mvc:resources/>标签,专门用于解决静态资源无法访问问题。 需要在 springmvc 配置文件中添加如下形式的配置:

<!-- 为webapp下的每个类别的静态资源一一进行映射 -->
<!-- url 中的 ** 表示该路径的所有请求 -->
<mvc:resources mapping=" /images/**" location="/images/"/>
<mvc:resources mapping="/html/**" location="/html/" />
<mvc:resources mapping="/js/**" location="/js/"/>
......

还是太麻烦!将静态资源统一放置在 webapp/static/ 下:

<mvc:resources mapping="/static/**" location="/static/"/>

4.3.3、为1、2方式声明注解驱动

不使用服务器自带的 servlet 处理静态资源,使用上面两种方式处理的话,会发生动态资源和静态资源冲突的问题,为此需要在 springmvc 配置文件加入:

<mvc:annotation-driven/>
posted @ 2020-09-06 17:31  卡文迪雨  阅读(247)  评论(0编辑  收藏  举报