SpringMVC

文章目录

一.概述

1.1 概念

SpringMVC:spring web mvc.是Spring内置的一个MVC框架,支持RESTful风格的URL请求。

1.2 原理

在没有使用SpringMVC之前我们都是使用Servlet在做Web开发。但是使用Servlet开发在接收请求参数,数据共享,页面跳转等操作相对比较复杂。servlet是java进行web开发的标准,既然springMVC是对servlet的封装,那么很显然SpringMVC底层就是Servlet,SpringMVC就是对Servlet进行深层次的封装。

二.案例入门

2.1 环境准备

2.1.1 创建maven项目

2.1.2 pom.xml添加依赖和插件

	<dependencies>
        <!--spring-webmvc依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.2.13.RELEASE</version>
        </dependency>
        <!--springmvc底层还是servlet,所以必须添加serlvet依赖-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
            <!--插件运行的时候没有范围插件启动会失败-->
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 编码和编译和JDK版本 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
            <!--tomcat插件-->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <path>/</path>
                    <port>8080</port>
                </configuration>
            </plugin>
        </plugins>
    </build>

2.1.3 创建Spring和SpringMVC的配置文件

在这里插入图片描述

2.1.4 web.xml配置

<!--spring配置-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <!--javaweb中通过applicationContext读取路径,springmvc通过这种方法进行简化-->
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!--springmvc配置-->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

2.2 实际开发

2.2.1 创建控制器

@Controller 
public class TeamController { 
	@Autowired 
	private TeamService teamService; 
	
	@RequestMapping("hello.do") 
	public ModelAndView add(){
	 System.out.println("TeamController----add---"); 
	 teamService.add(); 
	 ModelAndView mv=new ModelAndView();
	 //相当于 request.setAttrubuite("teanName","湖人"); 	
	 mv.addObject("teamName","湖人");
	 //未来经过springmvc的视图解析器处理,转换成物理资源路径
	 // 相当于request.getRequestDispatcher("index.jsp").forward(); 
	 //经过InternalResourceViewResolver对象的处理之后加上前后缀就变为了/jsp/index.jsp 
	 mv.setViewName("index");
	return mv; 
}

2.2.2 配置视图解析器

在springmvc.xml配置

<bean id="internalResourceViewResolver" 
class="org.springframework.web.servlet.view.InternalResourceViewResolver"> 
<property name="prefix" value="/jsp/"></property> <!--逻辑路径的前缀-->
<property name="suffix" value=".jsp"></property> <!--逻辑路径的后缀-->
</bean> 
<!--配置后springmvc可通过注解形式进行驱动,即会处理@RequestMapping注解,将其注册到映射表中,还可以处理请求的适配器,确定调用哪个类的哪个方法-->
<mvc:annotation-driven/>

2.2.3 编写jsp页面,调试

2.3 流程解析

当 Spring 和 SpringMVC 同时出现,我们的项目中将存在两个容器,一个是 Spring 容器,另一个是SpringMVC 容器,Spring 容器通过 ContextLoaderListener 来加载,SpringMVC 容器则通过DispatcherServlet 来加载,这两个容器不一样:
在这里插入图片描述

  • ContextLoaderListener 初始化的上下文加载的 Bean 是对于整个应用程序共享的,一般如 dao层、service层 的bean;
  • DispatcherServlet 初始化的上下文加载的 bean 是只对 Spring Web MVC 有效的 bean,如Controller、HandlerMapping、HandlerAdapter 等等,该初始化上下文应该只加载 Web相关组件。

1.为何Spring不能扫描所有bean?
当用户发送的请求达到服务端后,会寻找前端控制器DispatcherServlet去处理,只在SpringMVC容器中找,所以Controller必须在SpringMVC容器中扫描。
2. SpringMVC中的bean为何不放在Spring中一起扫描?
理论上可以放在Spring中扫描,但实际开发中一般不这样做:
(1)为了方便配置文件的管理
(2)在SSM组合中,要写的配置内容很多,一般根据功能分开编写。

三.SpringMVC工作流程
在这里插入图片描述

三.@RequestMapping注解

3.1 出现位置

可出现在上或方法

3.2 指定请求提交方式

通过method属性对被注解方法所处理的请求提交方式进行限制

  • method=RequestMethod.GET
  • method=RequestMethod.POST

3.3 补充url-pattern解析

在web.xml配置SpringMVC的前端控制器时有这个节点,该节点的值的写法有两种:

  1. .do
    在没有特殊要求的情况下,SpringMVC 的前端控制器 DispatcherServlet 的常使用后辍匹配方 式,可以写为
    .do 或者 *.action, *.mvc 等。
  2. /
    当写成 / 时,DispatcherServlet会将静态资源也当成是一个Controller请求,从而去找对应的HandlerMapping,但是访问路径中不存在,因此会报错。
    为了解决这个问题,引入了静态资源访问

3.3.1 静态资源访问

任意一种方式,在springmvc.xml中进行配置

法一: <mvc:default-servlet-handler/>
声 明 了 <mvc:default-servlet-handler /> 后 ,springmvc框架会在容器中创建DefaultServletHttpRequestHandler处理器对象。该对象会对所有进入DispatcherServlet的URL进行检查如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理
法二: <mvc:resources location="xxx" mapping="xxx/**"/>
例:<mvc:resources location="/images/" mapping="/images/**"/
location: 表示静态资源所在目录。当然,目录不要使用/WEB-INF/及其子目录。 mapping: 表示对该资源的请求。注意,后面是两个星号**。

四.处理器参数传递

4.1 使用方法的参数逐个接收

用户请求的参数名称(前端传来的值)和方法的参数名称必须保持一致
好处:不需要类型转换(以前接收前端数据都是以字符串形式得到)

在这里插入图片描述

4.2 使用对象接收多个参数(常用)

  • 用户请求的参数名称和对象的属性保持一致

在这里插入图片描述

4.3 请求参数和方法名称不一致

通过 @RequestParam进行矫正
required属性表示参数是否是必须的:

  • true:必须赋值(默认),否则报出400错;
  • false:可以不赋值, 结果就是null。
    即前端参数和@RequestParam里的参数名不一致时,required为false,此时不会报错,而是当做获取到了null值。

在这里插入图片描述

4.4 Request对象获取参数(原生方式)

在这里插入图片描述

4.5 URL地址传参(常用)

借助 @PathVariable注解,形参名字任意
使用场景:
1.测试(下图)
2.ajax请求路径传值

在这里插入图片描述

4.6 日期类型参数

  • 实体类日期属性加上DateTimeFormat(pattern=“xxxx-xx-xx”)

在这里插入图片描述

4.7 数组类型参数

在这里插入图片描述

4.8 集合类型参数

通过 @RequestParam注解实现
对象集合不支持直接获取,要封装在类中,作为属性操作(即相当于传一个对象,同4.2)

在这里插入图片描述

五.请求参数中文乱码

5.1 解决方案

  • 在web.xml进行以下配置
<!--配置中文乱码的过滤-->
    <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>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

5.2 原理

  • 如果在某个类中设置了编码格式,最后还是以web.xml配置文件中的为准

在这里插入图片描述

六.处理器方法的返回值

6.1 页面跳转

  • 两种方法都可通过视图解析器逻辑视图转换为物理视图

在这里插入图片描述

6.2 数据返回

借助 @ResponseBody注解
使用场景:结合Ajax,返回json格式的数据
pom.xml添加两个依赖

	<!--springmvc返回json格式数据-->
        <dependency> 
           	<groupId>com.fasterxml.jackson.core</groupId>    
            <artifactId>jackson-core</artifactId> 
            <version>2.9.0</version> 
        </dependency> 
        <!--json数据绑定-->
        <dependency> 
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId> 
            <version>2.9.0</version> 
            </dependency>
        </dependencies>

6.2.1 返回基础类型

	@RequestMapping("test03")
    @ResponseBody
    public String test03() {
        return "111";//返回字符串111
    }

6.2.2 返回自定义类型

	@RequestMapping("test04")
    @ResponseBody
    public Team test04() {
        Team team = new Team();
        team.setTeamId(11);
        return team;
    }

6.2.3 返回集合类型

list类型较为常见,这里主要讲解map类型,前端对其取值与list略有不同

 @RequestMapping("test05")
    @ResponseBody
    public Map<String, Team> test05() {
        Map<String, Team> map = new HashMap<>();
        for (int i = 0; i < 5; i++) {
            Team team = new Team();
            team.setTeamId(i + 1);
            map.put(team.getTeamId() + "", team);
        }
        return map;
    }

通过each函数可简化操作

$.ajax({
        type: "POST",
        url:/result/test05,
        data: "",
        success:function (map) {
            var str = "";
            $.each(map, function (i, obj) {//obj名字任意
                str+=obj.teamName;
            });
        }
        }
    );

七.页面导航的方式

7.1 分类

页面跳转主要有两种:转发重定向
springmvc实现方式有两种:返回字符串ModelAndView,返回字符串相当于原生的javaweb。

7.2 转发

在这里插入图片描述

7.3 重定向

在这里插入图片描述

7.4 转发或重定向到控制器

在这里插入图片描述

总结:只有转发才可以不写绝对路径,而重定向无论是哪种方式,都需要添加redirect前缀

八.异常处理

8.1 @Exception注解

该注解可以将一个方法指定为异常处理方法
属性value默认缺省,其值为一个Class<?>数组

在这里插入图片描述

8.2 优化异常类

  • 对于一些需要特殊处理的异常,一般用单独的方法进行处理,而这些方法又全部放在一个异常处理类中。
  • 借助 @ControllerAdvice注解对类修饰,而其内部的方法可借助 @ExceptionHandler注解
    当使用 @RequestMapping注解修饰的类抛出异常时,会执行@ControllerAdvice修饰的类中的异常处理方法。
  • @ControllerAdvice注解修饰的类需在springmvc.xml进行包扫描

在这里插入图片描述

九.拦截器(Interceptor)

9.1 概念介绍

  • 作用:拦截指定的用户请求,并执行相应的预处理和后处理。
  • 拦截时间点: HandlerMapping根据用户提交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,在处理器适配器HandlerAdaptor执行处理器之前
  • 在处理器映射器映射出所要执行的处理器类时,已经将拦截器与处理器组合为了一个处理器执行链HandlerExecutionChain,并返回给了前端控制器。

9.2 自定义拦截器

实现HandlerInterceptor接口中的三个方法
在这里插入图片描述
在这里插入图片描述
在springmvc.xml进行拦截器配置
在这里插入图片描述
测试结果
在这里插入图片描述
测试结果分析

  • 当preHandle返回值为true时就会执行处理器方法,同时将afterCompletion()方法放入到一个专门的方法栈中进行执行,而栈有着先进后出的特点。
  • aftercCompletion方法是在前端控制器渲染(数据填充)了响应页面后才执行的,此时对ModelAndView再操作也无意义,一般用于消除资源,例如在Controller方法中加入数据。

十.文件上传和下载

10.1 背景介绍

SpringMVC对于文件上传是通过即插即用的MultipartResolver实现,常用的实现类为CommonsMultipartResolver

10.2 环境配置

  1. 添加依赖
		<dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>
  1. 配置MultipartResolver

在这里插入图片描述

10.3 文件上传

10.3.1 开发流程

  1. 前端页面
    在这里插入图片描述
  2. 文件的保存路径
    在这里插入图片描述
  3. controller
    在这里插入图片描述

10.3.2 优化

  • 限制文件大小
    在这里插入图片描述
  • 限制文件类型
    在这里插入图片描述
 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, 
 						Object handler) throws Exception {
        //1.    判断是否是文件上传的请求
        boolean flag=true;
        if (request instanceof MultipartHttpServletRequest) {
            MultipartHttpServletRequest multipartHttpServletRequest = 
            					(MultipartHttpServletRequest) request;
            //2.    获取文件集合
            Map<String, MultipartFile> fileMap = multipartHttpServletRequest.getFileMap();
            //3.    遍历文件
            Iterator<String> iterator = fileMap.keySet().iterator();
            while (iterator.hasNext()) {
                String key = iterator.next();
                MultipartFile file = multipartHttpServletRequest.getFile(key);
                //4.    获取文件名
                String originalFilename = file.getOriginalFilename();
                //5.    获取文件后缀
                String hz = originalFilename.substring(originalFilename.lastIndexOf("."));
                //6.    判断后缀是否是jpg或png格式
                if (!hz.toLowerCase().equals("jpg") && !hz.toLowerCase().equals("png")) {
                    request.getRequestDispatcher("/fileTypeError.jsp").
                    						forward(request, response);
                    flag=false;
                }
            }
        }
        return flag;
    }

10.4 文件下载

在这里插入图片描述
在这里插入图片描述

十一 Restful风格

11.1 概念介绍

11.1.1 REST

REST:Representational State Transfer,即表述性状态转换,用来描述一个架构样式的网络系统。它本身并没有什么使用性,其价值在于如何设计出符合REST风格的网络接口。

11.1.2 RESTful

满足REST架构的应用程序或设计就是RESTful
http协议对资源的操作有四类五种
GET:获取资源
POST:新建资源
PUT:更新资源
PATCH:更新部分资源
DELETE:删除资源

  • 该风格主要体现在前端通过ajax获取数据以及后台通过访问路径确定该方法是什么类型:新增、删除、更新、查询。

11.2 API/URL设计

11.2.1 动宾结构

GET:/expresses 正确
/getExpress 错误,因为路径包含了动词

  • 此外,对于复数资源才使用复数,例如查询全部数据

11.2.2 避免多级URL

GET/team/1001/player/1005 不推荐,此写法语义不明确
GET/team/1001?player=1005 推荐,即属性通常在url中通过问号拼接

11.3 HTTP状态码

状态码一般分为五类
1xx:相关信息
2xx:操作成功(最希望看到的)
3xx:重定向
4xx:客户端错误
5xx:服务器错误

  • API不需要1xx状态码,此类别可直接忽略
  • 后端开发人员关注2xx,4xx,5xx即可

11.3.1 2xx

GET: 200 OK 表示一切正常
POST: 201 Created 表示新的资源已经成功创建
PUT: 200 OK
PATCH: 200 OK
DELETE: 204 No Content 表示资源已经成功删除

11.3.2 3xx

  • API 用不到 301 状态码(永久重定向)和 302 状态码(暂时重定向, 307 也是这个含义),因为它们可
    以由应用级别返回,浏览器会直接跳转,API 级别可以不考虑这两种情况。
  • API 用到的 3xx 状态码,主要是 303 See Other ,表示参考另一个 URL。它与 302 和 307 的含义一
    样,也是"暂时重定向",区别在于 302 和 307 用于 GET 请求,而 303 用于 POST 、 PUT 和 DELETE 请求。
  • 收到 303 以后,浏览器不会自动跳转,而会让用户自己决定下一步怎么办。我们只需要关注一下304状态码就可以了
    304 : Not Modified 客户端使用缓存数据

11.3.3 4xx

400 Bad Request:服务器不理解客户端的请求,未做任何处理
401 Unauthorized:用户未提供身份验证凭据,或者没有通过身份验证
403 Forbidden:用户通过了身份验证,但是不具有访问资源所需的权限
404 Not Found:所请求的资源不存在,或不可用。
405 Method Not Allowed:用户已经通过身份验证,但是所用的 HTTP 方法****不在他的权限之内。(get/post方式请求写混淆)
410 Gone:所请求的资源已从这个地址转移,不再可用。
415 Unsupported Media Type:客户端要求的返回格式不支持。比如,API 只能返回 JSON 格式,但是 客户端要求返回 XML 格式。
422 Unprocessable Entity :客户端上传的附件无法处理,导致请求失败。
429 Too Many Requests:客户端的请求次数超过限额。

11.3.4 5xx

  • 一般来说,API 不会向用户透露服务器的详细信息,所以只要两个状态码就够了。
    500 Internal Server Error:客户端请求有效,服务器处理时发生了意外。(请求路径写错)
    503 Service Unavailable:服务器无法处理请求,一般用于网站维护状态。

11.4 案例分析

GET和POST,DELETE和PUT大同小异,此处各选其中一种进行分析。

  1. GET–查询
    在这里插入图片描述
  • 接下来的DELETE和PUT需要注意,需要修改Type的类型,具体的原因后面分析,现在先说解决方案。
  1. 视需求修改相应的type的类型
    data中有数据,DELETE和PUT的type都需要写成POST,且在data后加上"&_method=PUT/DELETE",
    data中无数据,则type可写成原本的类型,不过推荐写成POST类型,这样以后数据修改也方便,同时在data处写上 “_method=PUT/DELETE”
  2. 修改web.xml配置文件,添加过滤器
    在这里插入图片描述
  1. DELETE–删除
  • data无数据
    在这里插入图片描述
  • data有数据
    在这里插入图片描述
  1. 总结

对于四种业务类型,有些url传参,有些data传参,下面的总结只针对常见的业务。.

  • GET
    url传参:查询单个
    url不传参:查询所有
  • POST
    data传值:封装页面数据,添加内容
  • DELETE
    url传参:删除单个
    url不传参:删除所有(比较少见)
  • PUT
    url传参:更新内容
    data传值:封装页面数据,更新内容

11.4 Restful更新和删除

11.4.1 遇到的问题

在Ajax中,采用Restful风格PUT和DELETE请求传递参数无效,传递到后台的参数值为null

11.4.2 产生的原因

Tomcat封装请求参数的过程:
1.将请求体中的数据,封装成一个map
2.request.getParameter(key)会从这个map中取值
3.SpringMvc封装POJO对象的时候,会把POJO中每个属性的值进行request.getParamter();

  • AJAX发送PUT或者DELETE请求时,请求体中的数据通过request.getParamter()拿不到。
  • Tomcat一旦检测到是PUT或者DELETE就不会封装请求体中的数据为map,只有POST形式的请求才封装请求为 map。

11.4.3 解决的原理

通过查看spring框架中的HiddenHttpMethodFilter类的源码发现,只要type类型是POST,就可以通过配置该过滤器,添加_method属性,覆盖POST原有的请求。

在这里插入图片描述

11.5 封装响应结果

通过封装响应结果,可以不用通过@ResponseBody注解在当前页面返回数据,如果需要跳转页面,可以直接返回逻辑视图名称。

 private Integer code;//状态码
    private String msg;//返回信息
    private List<T> list;//返回集合
    private T obj;//返回对象

    //构造方法一般不包含所有属性
    public AjaxResultVo() {
        code=200;
        msg = "ok";
        list=null;
        obj=null;
    }

    public AjaxResultVo(Integer code, String msg) {
        this.code=code;
        this.msg = msg;
    }

    public AjaxResultVo(Integer code, String msg, List<T> list) {
        this.code = code;
        this.msg = msg;
        this.list = list;
    }

    public AjaxResultVo(Integer code, String msg, T obj) {
        this.code = code;
        this.msg = msg;
        this.obj = obj;
    }

版权声明:本文为qq_43820523原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。