• 一篇文章带你掌握主流服务层框架——SpringMVC


    一篇文章带你掌握主流服务层框架——SpringMVC

    在之前的文章中我们已经学习了Spring的基本内容,SpringMVC隶属于Spring的一部分内容

    但由于SpringMVC完全针对于服务层使用,所以我们在介绍时常常把SpringMVC单独当作一个大章节来学习

    温馨提醒:在学习SpringMVC前请确保已学习Spring内容

    SpringMVC简介

    首先我们先来简单了解一下SpringMVC:

    • SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
    • SpringMVC致力于服务层,同Servlet一样应用于服务层,用于服务层开发
    • SpringMVC隶属于Spring,同样具有简化代码,使用简单,开发便捷,灵活性强的优点

    SpringMVC入门案例

    在未学习SpringMVC之前,我们的服务端开发通常采用Servlet:

    package com.itheima.web.servlet.old;
    
    import com.alibaba.fastjson.JSON;
    import com.itheima.pojo.Brand;
    import com.itheima.service.BrandService;
    import com.itheima.service.impl.BrandServiceImpl;
    
    import javax.servlet.ServletException;
    import javax.servlet.annotation.WebServlet;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.io.BufferedReader;
    import java.io.IOException;
    import java.util.List;
    
    //@WebServlet("/addServlet")
    public class AddServlet extends HttpServlet {
    
        private BrandService brandService = new BrandServiceImpl();
    
        @Override
        protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    
            //1. 接收品牌数据
            BufferedReader br = request.getReader();
            String params = br.readLine();//json字符串
    
            //转为Brand对象
            Brand brand = JSON.parseObject(params, Brand.class);
    
            //2. 调用service添加
            brandService.add(brand);
    
            //3. 响应成功的标识
            response.getWriter().write("success");
        }
    
        @Override
        protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
            this.doGet(request, response);
        }
    }
    

    我们可以注意到其过程非常繁琐,因为我们需要获取参数并进行类型转换,包括添加至Service等过程

    但是SpringMVC秉承着简化代码的原则,将大部分内容转化为Java代码进行封装,大大减少了繁琐的过程

    接下来我们来介绍SpringMVC版:

    1. 导入jar包
    
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0modelVersion>
    
      <groupId>com.itheimagroupId>
      <artifactId>springmvc_01_quickstartartifactId>
      <version>1.0-SNAPSHOTversion>
      <packaging>warpackaging>
    
      <dependencies>
    
        <dependency>
          <groupId>javax.servletgroupId>
          <artifactId>javax.servlet-apiartifactId>
          <version>3.1.0version>
          <scope>providedscope>
        dependency>
    
        <dependency>
          <groupId>org.springframeworkgroupId>
          <artifactId>spring-webmvcartifactId>
          <version>5.2.10.RELEASEversion>
        dependency>
      dependencies>
    
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.tomcat.mavengroupId>
            <artifactId>tomcat7-maven-pluginartifactId>
            <version>2.1version>
            <configuration>
              <port>80port>
              <path>/path>
            configuration>
          plugin>
        plugins>
      build>
    
    
    project>
    
    1. 创建SpringMVC控制类(等同于Servlet类)
    package com.itheima.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    //定义表现层控制器bean
    @Controller
    public class UserController {
    
        //设置映射路径为/save,即外部访问路径
        @RequestMapping("/save")
        //设置当前操作返回结果为指定json数据(本质上是一个字符串信息)
        @ResponseBody
        public String save(){
            System.out.println("user save ...");
            return "{'info':'springmvc'}";
        }
    
        //设置映射路径为/delete,即外部访问路径
        @RequestMapping("/delete")
        @ResponseBody
        public String delete(){
            System.out.println("user save ...");
            return "{'info':'springmvc'}";
        }
    }
    
    1. 初始化SpringMVC环境(同Spring一样创建Config配置Java类)
    package com.itheima.config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    //springmvc配置类,本质上还是一个spring配置类
    @Configuration
    @ComponentScan("com.itheima.controller")
    public class SpringMvcConfig {
    }
    
    1. 初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求
    /*
    我们服务层的实际操作都是放置于Servlet容器中
    我们配置的SpringMVC和Spring环境都是用于服务层,所以我们需要把相关Config加载仅Servlet容器中
    */
    
    package com.itheima.config;
    
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
    import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
    
    // web容器配置类
    // AbstractDispatcherServletInitializer是SpringMVC为我们设置好的类,继承并实现相关方法即可
    public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
        //加载springmvc配置类,产生springmvc容器(本质还是spring容器)
        protected WebApplicationContext createServletApplicationContext() {
            //初始化WebApplicationContext对象
            AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
            //加载指定配置类
            ctx.register(SpringMvcConfig.class);
            return ctx;
        }
    
        //设置由springmvc控制器处理的请求映射路径
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    
        //加载spring配置类
        protected WebApplicationContext createRootApplicationContext() {
            return null;
        }
    }
    

    我们对上述新的内容进行解析:

    1. @Controller

      • 名称:@Controller

      • 类型:类注解

      • 位置:SpringMVC控制类定义上方

      • 作用:设定SpringMVC的核心控制器Bean

    2. @RequestMapping

      • 名称:@RequestMapping

      • 类型:方法注解

      • 位置:SpringMVC控制器方法定义上方

      • 作用:设置当前控制器方法请求访问路径

      • 相关属性:value(请求访问路径)

    3. @ResponseBody

      • 名称:@ResponseBody

      • 类型:方法注释

      • 位置:SpringMVC控制器方法定义上方

      • 作用:设置当前控制器方法响应内容为当前返回值,无需解析

    4. AbstractDispatcherServletInitializer类

      • AbstractDispatcherServletInitializer是SpringMVC提供的快速初始化Web3.0容器的抽象类
      • AbstractDispatcherServletInitializer提供三个接口方法供用户实现
      • createServletApplicationContext方法用于创建Servlet容器时,加载SpringMVC对应的Bean并放入
      • AnnotationConfigWebApplicationContext的作用范围对应整个Web容器范围,必须使用WebApplicationcontext类型

    最后我们总结一下上述操作的出现频率:

    • 一次性工作
      • 创建工程,设置服务器,加载工程
      • 导入坐标
      • 创建Web容器启动类,加载SpringMVC配置,并设置SpringMVC请求拦截路径
      • SpringMVC核心配置类(设置配置类,扫描controller包,加载Controller控制器Bean)
    • 常态工作
      • 定义处理请求的控制类
      • 定义处理请求的操作方法,并设置映射路径(@RequestMapper)与返回Json数据(@ResponseBody)

    SpringMVC工作流程

    在分析SpringMVC工作流程前,我们需要知道服务层是由下面的框架组成的:

    启动服务器初始化过程:

    1. 服务器启动,执行ServletContainersInitConfig类,初始化Web容器
    2. 执行createServletApplicationContext方法,创建了WebApplicationContext对象
    3. 加载SpringMvcConfig
    4. 执行@ComponentScan加载对应的bean
    5. 加载UserController,每个@RequestMapping的名称对应一个具体的方法
    6. 执行getServletMappings方法,定义所有的请求都通过SpringMVC

    单次请求过程:

    1. 发送请求localhost/save
    2. Web容器发现所有请求都经过SpirngMVC,将请求交给SpringMVC处理
    3. 解析请求路径/save
    4. 由/save匹配执行对应的方法save()
    5. 执行save()
    6. 检测到有@ResponseBody直接将save()方法的返回值作为响应求体返回给请求方

    SpringMVC加载控制

    在学习SpringMVC之后,我们的Bean的范围逐渐变大:

    • SpringMVC相关bean(表现层bean)
    • Spring相关bean(业务层Service,功能DataSource等)

    但是我们在使用时,需要区分相关bean的导入路径:

    • SpringMVC加载的bean对应的包均在com.itheima.controller包内
    • Spring加载的bean却包含有多个文件夹

    因而我们给出两种方法来解决Spring的扫描问题:

    1. Spring加载的bean设定范围为com.itheima,并排除掉controller包内的bean
    package com.itheima.config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.stereotype.Controller;
    
    @Configuration
    /*
    @ComponentScan注解设置扫描范围
    @ComponentScan中包含有value,excludeFilters属性
    value:用于控制扫描范围
    excludeFilters:用于控制排除范围,需要采用@ComponentScan.Filter过滤器
    type:设置排除规则,当前使用按照bean定义时的注解类型进行排除
    classes属性:设置排除的具体注解类,当前设置排除@Controller定义的bean
    */
    @ComponentScan(value="com.itheima",
        excludeFilters = @ComponentScan.Filter(
            type = FilterType.ANNOTATION,
            classes = Controller.class
        )
    )
    public class SpringConfig {
    }
    
    /*
    这里做一个小补充内容:
    @ComponentScan中除了excludeFilters,还包括有includeFilters
    includeFilters:加载指定的bean,需要指定类型(type)和具体项(classes)
    */
    
    1. Spring加载的bean设定范围为精准范围,例如service包,dao包等
    package com.itheima.config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.FilterType;
    import org.springframework.stereotype.Controller;
    
    @Configuration
    @ComponentScan({"com.itheima.service","com.itheima.dao"})
    public class SpringConfig {
    }
    

    Servlet容器简化写法

    我们的Servlet容器中可以定义Spring和SpringMVC的配置文件

    package com.itheima.config;
    
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
    
    public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
        
        // 配置SpringMVC配置文件
        protected WebApplicationContext createServletApplicationContext() {
            AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
            ctx.register(SpringMvcConfig.class);
            return ctx;
        }
        
        // 配置Spring配置文件
        protected WebApplicationContext createRootApplicationContext() {
            AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
            ctx.register(SpringConfig.class);
            return ctx;
        }
        
        // 配置拦截路径
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    
    }
    

    我们可以注意到:

    Spring和SpringMVC导入方法中均采用AnnotationConfigWebApplicationContext来创建对象

    两者之间的区别仅仅是class包的不同

    Spring给了我们一种新的继承类用于简化开发:

    package com.itheima.config;
    
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
    //web配置类简化开发,仅设置配置类类名即可
    public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    
        protected Class[] getRootConfigClasses() {
            return new Class[]{SpringConfig.class};
        }
    
        protected Class[] getServletConfigClasses() {
            return new Class[]{SpringMvcConfig.class};
        }
    
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    }
    

    注意:

    AbstractAnnotationConfigDispatcherServletInitializer是createServletApplicationContext的继承类

    我们同样继承它的三个方法,但这次我们只需要在里面标明相关类和路径即可

    常用工具推荐Postman

    在我们的SpringMVC中岔开一个话题:

    • 关于我们的网页调试的复杂性

    我们在一个网页开发中,会不断的调试网页,通过各种路径反复查询或者采用不同的访问方式(GET/POST)

    如果我们采用正常的网页进行测试,无疑会出现非常麻烦的步骤

    所以我们推荐采用Postman软件,下面我们将会简单做一下介绍

    Postman链接

    首先为大家附上链接:

    Postman操作讲解

    在了解操作前,我们需要明白Postman的作用:

    • 用于分类存储网页请求
    • 用于发送请求进行测试

    关于安装注册的过程我们不再赘述

    Postman页面展示

    我们先来查看Postman的主页:

    首先我们可以看到左上角的Workspaces,这个是最大的分类空间

    我们可以看到左上角SpringMVC,这是我所创建的WorkSpaces,关于我在SpringMVC所做的网页测试部分将都在这里进行

    除此之外,我们可以看到右侧的DEMO1,以及内部的测试用例文件夹,以及项目save

    以上就是我们的Postman的基本页面

    Postman具体使用

    我们的Postman的具体使用流程如下:

    1. 创建新的Workspaces

    1. 选定主界面,创建对应文件夹

    1. 创建项目(点击中间区域的加号)

    1. 书写项目内容(GET可以更换其他类型,后面书写URL,下方key,value书写传递数据)

    1. 下方的数据传递可以更换类型,例如更换为body体的raw来书写JSON格式

    1. 书写后保存到相应列表并标注名称

    到这里,我们Postman的基本使用基本就结束了,到后面我们会对具体内容做具体补充~

    SpringMVC设置请求与响应

    SpringMVC和Servlet同属于服务层的工具,那么必不可少的就是请求与响应的反馈问题

    接下来我们将一一介绍请求与响应的相关知识

    请求映射路径设置

    首先我们先来想一想我们之前的路径设置是否有那么一点点缺陷?

    // Book的服务层
    
    package com.itheima.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    public class BookController {
        //请求路径映射
        @RequestMapping("/save")
        @ResponseBody
        public String save(){
            System.out.println("book save ...");
            return "{'module':'book save'}";
        }
    }
    
    // User的服务层
    
    package com.itheima.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    public class UserController {
        //请求路径映射
        @RequestMapping("/save")
        @ResponseBody
        public String save(){
            System.out.println("user save ...");
            return "{'module':'user save'}";
        }
        //请求路径映射
        @RequestMapping("/delete")
        @ResponseBody
        public String delete(){
            System.out.println("user delete ...");
            return "{'module':'user delete'}";
        }
    
    }
    

    我们可以注意到我们的单个项目中不可能只包括有一个服务层

    但我们的请求映射路径却只是简单设计为相同的名称,就会导致我们访问该页面时,系统无法匹配

    所以我们需要给他们采用不同的映射路径,我们常有的操作是直接在前面加上一层该类的路径名:

    package com.itheima.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    public class BookController {
        //请求路径映射
        @RequestMapping("/book/save")
        @ResponseBody
        public String save(){
            System.out.println("book save ...");
            return "{'module':'book save'}";
        }
    }
    

    但当项目逐渐增多,我们多次书写路径名就有可能导致错误,所以我们采用类注解@RequestMapping来解决:

    package com.itheima.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    @Controller
    //类上方配置的请求映射与方法上面配置的请求映射连接在一起,形成完整的请求映射路径
    @RequestMapping("/user")
    public class UserController {
        //请求路径映射
        @RequestMapping("/save")
        @ResponseBody
        public String save(){
            System.out.println("user save ...");
            return "{'module':'user save'}";
        }
        //请求路径映射
        @RequestMapping("/delete")
        @ResponseBody
        public String delete(){
            System.out.println("user delete ...");
            return "{'module':'user delete'}";
        }
    
    }
    

    注意:@RequestMapping不仅仅可以用于方法表示映射,也可以用于整个Bean类中表示映射前缀

    参数传递问题

    关于参数传递我们从三个方面来讲解:

    • 传递方式
    • 传递参数类型
    • 特殊参数类型

    按传递方式

    我们的传递方式通常采用GET或者POST方式

    但在前面的学习中我们可以知道我们的传递方式是有不同的,我们在Postman的书写形式也是不同的

    例如我们先给出一个简单的参数传递函数

    package com.itheima.controller;
    
    import com.itheima.domain.User;
    import org.springframework.format.annotation.DateTimeFormat;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.Arrays;
    import java.util.Date;
    import java.util.List;
    //请求参数
    @Controller
    public class UserController {
    
        //普通参数:请求参数与形参名称对应即可完成参数传递
        @RequestMapping("/commonParam")
        @ResponseBody
        public String commonParam(String name ,int age){
            System.out.println("普通参数传递 name ==> "+name);
            System.out.println("普通参数传递 age ==> "+age);
            return "{'module':'common param'}";
        }
    
    }
    

    我们的GET方式直接在网页后用?和&来书写传递参数:

    我们的POST方式只能在下方的body中书写参数:

    然后我们需要注意到的是这里的中文同样会出现乱码行为

    这次我们选择在ServletContainersInitConfig中处理数据:

    // 下述代码基本属于我们创建项目的固定代码
    
    package com.itheima.config;
    
    import org.springframework.web.filter.CharacterEncodingFilter;
    import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
    
    import javax.servlet.Filter;
    
    public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
        protected Class[] getRootConfigClasses() {
            return new Class[0]{SpringConfig.class};
        }
    
        protected Class[] getServletConfigClasses() {
            return new Class[]{SpringMvcConfig.class};
        }
    
        protected String[] getServletMappings() {
            return new String[]{"/"};
        }
    
        //乱码处理
        @Override
        protected Filter[] getServletFilters() {
            // CharacterEncodingFilter 属于处理中文编码的过滤器,我们直接创建即可(一次性操作)
            CharacterEncodingFilter filter = new CharacterEncodingFilter();
            filter.setEncoding("UTF-8");
            return new Filter[]{filter};
        }
    }
    

    按参数方式

    我们按参数来分类主要分为五种:

    • 普通参数
    • POJO类
    • 嵌套式POJO类
    • 数组参数
    • 集合参数

    我们下面来一一介绍

    普通参数

    普通参数:请求参数和形参变量名相同时,自动匹配

    package com.itheima.controller;
    
    import com.itheima.domain.User;
    import org.springframework.format.annotation.DateTimeFormat;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.Arrays;
    import java.util.Date;
    import java.util.List;
    //请求参数
    @Controller
    public class UserController {
    
        //普通参数:请求参数与形参名称对应即可完成参数传递
        @RequestMapping("/commonParam")
        @ResponseBody
        public String commonParam(String name ,int age){
            System.out.println("普通参数传递 name ==> "+name);
            System.out.println("普通参数传递 age ==> "+age);
            return "{'module':'common param'}";
        }
    
    }
    

    Postman操作:

    这里需要注意:当请求参数名与形参名不同时,使用@RequestParam注解关联请求参数名称与形参名称之间的关系

    package com.itheima.controller;
    
    import com.itheima.domain.User;
    import org.springframework.format.annotation.DateTimeFormat;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.Arrays;
    import java.util.Date;
    import java.util.List;
    //请求参数
    @Controller
    public class UserController {
    
        //普通参数:请求参数名与形参名不同时,使用@RequestParam注解关联请求参数名称与形参名称之间的关系
        @RequestMapping("/commonParamDifferentName")
        @ResponseBody
        public String commonParamDifferentName(@RequestParam("name") String userName , int age){
            System.out.println("普通参数传递 userName ==> "+userName);
            System.out.println("普通参数传递 age ==> "+age);
            return "{'module':'common param different name'}";
        }
    
    }
    

    Postman操作:

    @RequestParam:绑定请求参数与处理器方法形参间的关系

    包含有两个参数

    required:是否为必传参数

    defaultValue:参数默认值

    POJO参数

    POJO参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数

    package com.itheima.controller;
    
    import com.itheima.domain.User;
    import org.springframework.format.annotation.DateTimeFormat;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.Arrays;
    import java.util.Date;
    import java.util.List;
    //请求参数
    @Controller
    public class UserController {
    
        //POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
        @RequestMapping("/pojoParam")
        @ResponseBody
        public String pojoParam(User user){
            System.out.println("pojo参数传递 user ==> "+user);
            return "{'module':'pojo param'}";
        }
    
    }
    

    Postman操作:

    嵌套POJO参数

    嵌套POJO参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数

    package com.itheima.controller;
    
    import com.itheima.domain.User;
    import org.springframework.format.annotation.DateTimeFormat;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.Arrays;
    import java.util.Date;
    import java.util.List;
    //请求参数
    @Controller
    public class UserController {
    
        //嵌套POJO参数:嵌套属性按照层次结构设定名称即可完成参数传递
        @RequestMapping("/pojoContainPojoParam")
        @ResponseBody
        public String pojoContainPojoParam(User user){
            System.out.println("pojo嵌套pojo参数传递 user ==> "+user);
            return "{'module':'pojo contain pojo param'}";
        }
    
    }
    

    Postman操作:

    数组参数

    数组参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型形参即可接收参数

    package com.itheima.controller;
    
    import com.itheima.domain.User;
    import org.springframework.format.annotation.DateTimeFormat;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.Arrays;
    import java.util.Date;
    import java.util.List;
    //请求参数
    @Controller
    public class UserController {
    
        //数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中
        @RequestMapping("/arrayParam")
        @ResponseBody
        public String arrayParam(String[] likes){
            System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes));
            return "{'module':'array param'}";
        }
    
    }
    

    Postman操作:

    集合保存普通参数

    集合保存普通参数:请求参数与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系

    package com.itheima.controller;
    
    import com.itheima.domain.User;
    import org.springframework.format.annotation.DateTimeFormat;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.Arrays;
    import java.util.Date;
    import java.util.List;
    //请求参数
    @Controller
    public class UserController {
    
        //集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
        @RequestMapping("/listParam")
        @ResponseBody
        public String listParam(@RequestParam List likes){
            System.out.println("集合参数传递 likes ==> "+ likes);
            return "{'module':'list param'}";
        }
    
    }
    

    Postman参数:

    按特殊参数方式

    我们的特殊参数主要介绍两种:

    • JSON类型
    • 日期类型

    我们下面一一介绍

    JSON类型

    JSON类型是我们Web开发中最常用的类型,所以这一部分算是一个小重点

    我们将一一讲解JSON类型传递的步骤:

    1. 导入JSON坐标
    
    
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0modelVersion>
    
      <groupId>com.itheimagroupId>
      <artifactId>springmvc_04_request_paramartifactId>
      <version>1.0-SNAPSHOTversion>
      <packaging>warpackaging>
    
      <dependencies>
        <dependency>
          <groupId>javax.servletgroupId>
          <artifactId>javax.servlet-apiartifactId>
          <version>3.1.0version>
          <scope>providedscope>
        dependency>
        <dependency>
          <groupId>org.springframeworkgroupId>
          <artifactId>spring-webmvcartifactId>
          <version>5.2.10.RELEASEversion>
        dependency>
          
        <dependency>
          <groupId>com.fasterxml.jackson.coregroupId>
          <artifactId>jackson-databindartifactId>
          <version>2.9.0version>
        dependency>
      dependencies>
    
      <build>
        <plugins>
          <plugin>
            <groupId>org.apache.tomcat.mavengroupId>
            <artifactId>tomcat7-maven-pluginartifactId>
            <version>2.1version>
            <configuration>
              <port>80port>
              <path>/path>
            configuration>
          plugin>
        plugins>
      build>
    project>
    
    
    1. 在SpringMVC配置类中添加JSON类型转换注解
    package com.itheima.config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.filter.CharacterEncodingFilter;
    import org.springframework.web.servlet.config.annotation.EnableWebMvc;
    
    import javax.servlet.Filter;
    import javax.servlet.annotation.WebFilter;
    
    @Configuration
    @ComponentScan("com.itheima.controller")
    //开启json数据类型自动转换
    @EnableWebMvc
    public class SpringMvcConfig {
    }
    
    1. 在Controller中书写相关Web代码(注意:需要使用@RequestBody表示将请求体数据传递给请求参数)
    package com.itheima.controller;
    
    import com.itheima.domain.User;
    import org.springframework.format.annotation.DateTimeFormat;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.Arrays;
    import java.util.Date;
    import java.util.List;
    //请求参数
    @Controller
    public class UserController {
    
        //集合参数:json格式
        //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
        //2.使用@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据
        @RequestMapping("/listParamForJson")
        @ResponseBody
        public String listParamForJson(@RequestBody List likes){
            System.out.println("list common(json)参数传递 list ==> "+likes);
            return "{'module':'list common for json param'}";
        }
    
        //POJO参数:json格式
        //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
        //2.使用@RequestBody注解将外部传递的json数据映射到形参的实体类对象中,要求属性名称一一对应
        @RequestMapping("/pojoParamForJson")
        @ResponseBody
        public String pojoParamForJson(@RequestBody User user){
            System.out.println("pojo(json)参数传递 user ==> "+user);
            return "{'module':'pojo for json param'}";
        }
    
        //集合参数:json格式
        //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
        //2.使用@RequestBody注解将外部传递的json数组数据映射到形参的保存实体类对象的集合对象中,要求属性名称一一对应
        @RequestMapping("/listPojoParamForJson")
        @ResponseBody
        public String listPojoParamForJson(@RequestBody List list){
            System.out.println("list pojo(json)参数传递 list ==> "+list);
            return "{'module':'list pojo for json param'}";
        }
        
    }
    

    Postman操作:

    在上面我们有两个注解需要特别注意一下:

    1. @EnableWebMvc
    • 名称:@EnableWebMvc
    • 类型:配置类注解
    • 位置:SpringMVC配置类定义上方
    • 作用:开启SpringMVC多项辅助功能
    1. @RequestBody
    • 名称:@RequestBody
    • 类型:形参注解
    • 位置:SpringMVC控制器方法形参定义前面
    • 作用:将请求中请求体所包含的数据传递给请求参数,此注解一个处理器方法只能使用一次

    @RequestBody和@RequestParam区别

    • 区别
      • @RequestParam用于接收url地址传参,表单传参[application/x-www-form-urlencoded]
      • @RequestBody用于接收JSON数据[application/json]
    • 应用
      • 后期开发中,发送json数据为主,@RequestBody应用较广
      • 如果发送非json格式数据,选用@RequestParam接收请求参数
    日期型参数类型

    我们的日期类型数据基于系统不同格式也不相同,大致有以下几种:

    • 2022-10-05
    • 2022/10/05
    • 10/05/2022

    接收形参时,我们根据不同的日期格式设置不同的接收方式

    package com.itheima.controller;
    
    import com.itheima.domain.User;
    import org.springframework.format.annotation.DateTimeFormat;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.Arrays;
    import java.util.Date;
    import java.util.List;
    //请求参数
    @Controller
    public class UserController {
    
        //日期参数
        //使用@DateTimeFormat注解设置日期类型数据格式,默认格式yyyy/MM/dd
        @RequestMapping("/dataParam")
        @ResponseBody
        public String dataParam(Date date,
                                @DateTimeFormat(pattern="yyyy-MM-dd") Date date1,
                                @DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2){
            System.out.println("参数传递 date ==> "+date);
            System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1);
            System.out.println("参数传递 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2);
            return "{'module':'data param'}";
        }
    
    }
    
    /*
    名称:@DateTimeFormat
    类型:形参注解
    位置:SpringMVC控制器方法前
    作用:设定日期时间型数据格式
    属性:pattern:日期时间格式字符串
    */
    

    Postman操作:

    这里我们简单介绍一下@DateTimeFormat的转换原理Converter接口:

    public interface Converter{
    	@Nullable
        T convert(S var1)
    }
    

    Converter接口属于顶层接口,由它为起源创建了许多相关的接口与类用于各种转化:

    • 请求参数年龄数据(String->Integer)
    • 日期格式转发(String->Date)

    @EnableWebMvc功能之一:根据类型匹配对应的类型转换器

    设置响应

    在了解请求的相关知识之后,我们回到Controller代码中学习一下响应

    跳转响应

    在正常情况下,我们的响应给出的是当前项目的文档,相当于页面的跳转效应:

    package com.itheima.controller;
    
    import com.itheima.domain.User;
    import org.springframework.format.annotation.DateTimeFormat;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    @Controller
    public class UserController {
    
        //响应页面/跳转页面
        //返回值为String类型,设置返回值为页面名称,即可实现页面跳转
        @RequestMapping("/toJumpPage")
        public String toJumpPage(){
            System.out.println("跳转页面");
            return "page.jsp";
        }
    
    }
    

    信息响应

    如果我们希望得到一些信息响应,就需要采用注解解释:

    package com.itheima.controller;
    
    import com.itheima.domain.User;
    import org.springframework.format.annotation.DateTimeFormat;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import java.util.ArrayList;
    import java.util.Date;
    import java.util.List;
    
    @Controller
    public class UserController {
        
        //响应文本数据
        //返回值为String类型,设置返回值为任意字符串信息,即可实现返回指定字符串信息,需要依赖@ResponseBody注解
        @RequestMapping("/toText")
        @ResponseBody
        public String toText(){
            System.out.println("返回纯文本数据");
            return "response text";
        }
    
        //响应POJO对象
        //返回值为实体类对象,设置返回值为实体类类型,即可实现返回对应对象的json数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
        @RequestMapping("/toJsonPOJO")
        @ResponseBody
        public User toJsonPOJO(){
            System.out.println("返回json对象数据");
            User user = new User();
            user.setName("itcast");
            user.setAge(15);
            return user;
        }
    
        //响应POJO集合对象
        //返回值为集合对象,设置返回值为集合类型,即可实现返回对应集合的json数组数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
        @RequestMapping("/toJsonList")
        @ResponseBody
        public List toJsonList(){
            System.out.println("返回json集合数据");
            User user1 = new User();
            user1.setName("传智播客");
            user1.setAge(15);
    
            User user2 = new User();
            user2.setName("黑马程序员");
            user2.setAge(12);
    
            List userList = new ArrayList();
            userList.add(user1);
            userList.add(user2);
    
            return userList;
        }
    }
    
    /*
    名称:@ResponseBody
    类型:方法注解
    位置:SpringMVC控制器方法定义上方
    作用:设置当前控制器返回值作为响应体
    */
    

    当我们使用Postman访问该链接时就会给出对应反馈,这里就不做演示了

    REST风格

    首先我们来简单介绍一下REST:

    • REST(Representational State Transfer),表现形式状态转换

    我们给出正常风格和REST风格两种书写形式,我们可以明显看到REST的内容做出大规模的省略:

    REST风格优点:

    • 书写简化
    • 隐藏资源的访问行为,无法通过地址得知对资源的操作

    REST风格简介

    我们来对REST风格做出简单解释:

    • REST风格是采用访问资源的行为动作来区别对资源进行了何种操作

    我们给出五种常见行为动作:

    我们通常将根据REST风格进行的访问称为RESTful

    上述行为是约定方式,约定不是规范,是可以打破的,所以称为REST风格,而不是REST规范

    描述模块的名称通常使用负数,也就是加s的格式描述,表示此类,而非单个资源

    RESTful入门案例

    从本质上而言,REST只是一种规范形式,我们对于REST的风格修改仅针对于Controller

    我们下面将逐步进行RESTful的修改:

    1. 设置http请求动作
    package com.itheima.controller;
    
    import com.itheima.domain.Book;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    
    @Controller
    public class BookController {
    
        // RequestMapping中包含value和method两种属性
        // value:访问路径
        // method:访问方法
        @RequestMapping(value = "/users",method = Request.POST)
        @RequestBody
    	public String save(@RequestBody User user){
    		System.out.println("user save" + user);
            return "{'module':'user save'}"
        }
    }
    
    /*
    名称:@RequestMapping
    类型:方法注解
    位置:SpringMVC控制器方法定义上方
    作用:设置当前控制器方法请求访问路径
    属性:value访问路径,method请求动作
    */
    
    1. 设置请求参数(路径变量)
    package com.itheima.controller;
    
    import com.itheima.domain.Book;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    
    @Controller
    public class BookController {
    
        // 首先针对我们所需参数给出@PathVariable注解,并在访问路径中采用{}占位表示所传数据
        // 简单来说就是,系统根据请求路径,得到所需数据,再带入到方法中
        @RequestMapping(value = "/users/{id}" ,method = RequestMethod.DELETE)
        @RequestBody
        public String delete(@PathVariable Integer id){
            System.out.println("book delete..." + id);
            return "{'module':'book delete'}";
        }
    }
    
    /*
    名称:@PathVariable
    类型:形参注解
    位置:SpringMVC控制器方法形参定义前面
    作用:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应
    */
    

    下面我们给出所有情况案例:

    package com.itheima.controller;
    
    import com.itheima.domain.User;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    
    @Controller
    public class UserController {
    
        //设置当前请求方法为POST,表示REST风格中的添加操作
        @RequestMapping(value = "/users",method = RequestMethod.POST)
        @ResponseBody
        public String save(){
            System.out.println("user save...");
            return "{'module':'user save'}";
        }
    
        //设置当前请求方法为DELETE,表示REST风格中的删除操作
        //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同
        @RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)
        @ResponseBody
        public String delete(@PathVariable Integer id){
            System.out.println("user delete..." + id);
            return "{'module':'user delete'}";
        }
    
        //设置当前请求方法为PUT,表示REST风格中的修改操作
        @RequestMapping(value = "/users",method = RequestMethod.PUT)
        @ResponseBody
        public String update(@RequestBody User user){
            System.out.println("user update..."+user);
            return "{'module':'user update'}";
        }
    
        //设置当前请求方法为GET,表示REST风格中的查询操作
        //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同
        @RequestMapping(value = "/users/{id}" ,method = RequestMethod.GET)
        @ResponseBody
        public String getById(@PathVariable Integer id){
            System.out.println("user getById..."+id);
            return "{'module':'user getById'}";
        }
    
        //设置当前请求方法为GET,表示REST风格中的查询操作
        @RequestMapping(value = "/users",method = RequestMethod.GET)
        @ResponseBody
        public String getAll(){
            System.out.println("user getAll...");
            return "{'module':'user getAll'}";
        }
    
    }
    
    
    /*
    	下述为原有代码:
    
        @RequestMapping
        @ResponseBody
        public String delete(){
            System.out.println("user delete...");
            return "{'module':'user delete'}";
        }
    
        @RequestMapping
        @ResponseBody
        public String update(){
            System.out.println("user update...");
            return "{'module':'user update'}";
        }
    
        @RequestMapping
        @ResponseBody
        public String getById(){
            System.out.println("user getById...");
            return "{'module':'user getById'}";
        }
    
        @RequestMapping
        @ResponseBody
        public String getAll(){
            System.out.println("user getAll...");
            return "{'module':'user getAll'}";
        }
    */
    

    我们在这里给出@RequestBody,@RequestParam,@PathVariable区别

    区别:

    • @RequestParam用于接收url地址传参或表单传参
    • @RequestBody用于接收json数据
    • @PathVariable用于接收路径参数,使用{参数名称}描述路径参数

    应用:

    • 后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广
    • 如果发送非json格式数据,选用@RequestParam接受请求参数
    • 采用RESTful进行开发,当参数数量较少时,如1个,可以采用@PathVariable接收请求路径变量,常用来传递id值

    REST快速开发

    我们在上一小节中会发现有许多重复性的代码:

    // 每次都填写value,method导致代码繁冗
    // 包括每次填写ResponseBody使代码繁冗
    
    @RequestMapping(value = "/users",method = RequestMethod.GET)
    @ResponseBody
    

    所以我们可以采用一些小技巧来简化代码:

    • 将前缀地址和相关注解放于类中:
    package com.itheima.controller;
    
    import com.itheima.domain.Book;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    
    //@Controller
    //@ResponseBody
    @RestController
    @RequestMapping("/books")
    public class BookController {
    
    }
    /*
    正常情况下,我们的类本身具有@Controller,并且为了省略类中的@ResponseBody而直接标注在类头
    但Spring提供了一种新的注解@RestController,相当于@Controller和@ResponseBody的结合,我们只需要书写这一个注解即可 
    
    名称:@RestController
    类型:类注解
    位置:基于SpringMVC的RESTful开发控制器类定义上方
    作用:设置当前控制器为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能
    */
    
    • 采用新的地址注解代替老注解:
    package com.itheima.controller;
    
    import com.itheima.domain.Book;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.*;
    
    @RestController
    @RequestMapping("/books")
    public class BookController {
    
    	//@RequestMapping( method = RequestMethod.POST)
        //使用@PostMapping简化Post请求方法对应的映射配置
        @PostMapping        
        public String save(@RequestBody Book book){
            System.out.println("book save..." + book);
            return "{'module':'book save'}";
        }
    
    	//@RequestMapping(value = "/{id}" ,method = RequestMethod.DELETE)
        //使用@DeleteMapping简化DELETE请求方法对应的映射配置
        @DeleteMapping("/{id}")     
        public String delete(@PathVariable Integer id){
            System.out.println("book delete..." + id);
            return "{'module':'book delete'}";
        }
    
    	//@RequestMapping(method = RequestMethod.PUT)
        //使用@PutMapping简化Put请求方法对应的映射配置
        @PutMapping         
        public String update(@RequestBody Book book){
            System.out.println("book update..."+book);
            return "{'module':'book update'}";
        }
    
    	//@RequestMapping(value = "/{id}" ,method = RequestMethod.GET)
        //使用@GetMapping简化GET请求方法对应的映射配置
        @GetMapping("/{id}")    
        public String getById(@PathVariable Integer id){
            System.out.println("book getById..."+id);
            return "{'module':'book getById'}";
        }
    
    	//@RequestMapping(method = RequestMethod.GET)
        //使用@GetMapping简化GET请求方法对应的映射配置
        @GetMapping             
        public String getAll(){
            System.out.println("book getAll...");
            return "{'module':'book getAll'}";
        }
    }
    
    /*
    名称:@GetMapping @PostMapping @PutMapping @DeleteMapping
    类型:方法注解
    位置:基于SpringMVC的RESTful开发控制器方法定义上方
    作用:设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应GET请求
    参数:value请求访问路径
    */
    

    结束语

    好的,关于SpringMVC的内容就介绍到这里,希望能为你带来帮助!

    附录

    该文章属于学习内容,具体参考B站黑马程序员李老师的SSM框架课程

    这里附上链接:SpringMVC-01-SpringMVC简介_哔哩哔哩_bilibili

  • 相关阅读:
    uniapp 获取页面来源
    DBA 行为准则
    sychronized如何实现的?锁升级的过程?
    Linux 命令(199)—— arp 命令
    创新工具 | 快速创作高质量SEO博文的6个技巧
    Spark 之 HistoryServer and FsHistoryProvider
    SpringBoot事件发布监听
    UE4 MVP 坐标转换
    【浅学Java】Seervlet小项目——MessageWall
    linux(ARM)架构下的mysql安装使用(完整版)
  • 原文地址:https://www.cnblogs.com/qiuluoyuweiliang/p/16757184.html