【SpringMVC笔记、教程】

一、SpringMVC概述

1、SpringMVC简介

SpringMVC:是基于Spring的一个框架。实际上就是Spring的一个模块,专门是做web开发的,可以理解为是Servlet的一个升级。

Spring的使用原理:

Spring中IOC管理对象,AOP动态代理。SpringMVC也能够创建对象,放入到SpringMVC容器中,区别就是SpringMVC容器中放的是控制器对象。

在SpringMVC中我们使用@Controller注解创建控制器对象,把对象放入到SpringMVC容器中,把创建好的对象当作Servlet使用,即能够接收用户的请求,显示处理结果。

然而我们知道@Controller控制器创建的是普通的控制器对象,并不是Servlet,所以SpringMV赋予了控制器对象一些额外的功能,这样我们才能把它当作Servlet使用。

Web开发的底层是servlet。 在springmvc中有一个对象是servlet:DispatcherServlet(中央调度器)。

  1. DispatcherServlet叫做中央调度器,是一个servlet,它的父类继承的是HttpServlet
  2. DispatcherServlet也叫做前端控制器(front controller)
  3. DispatcherServlet负责接收用户提交的所有请求。用户把请求给了DispatcherServlet之后,DispatcherServlet会把请求转发给我们的Controller对象,最后是Controller对象处理请求。处理完请求后会将结果返回给DispatcherServlet,DispatcherServlet再将结果返回到请求端。

2、SpringMVC优点

  1. 基于 MVC 架构

    基于 MVC 架构,功能分工明确。解耦合,

  2. 容易理解,上手快;使用简单。

    就可以开发一个注解的 SpringMVC 项目,SpringMVC 也是轻量级的,jar 很小。不依赖的 特定的接口和类。

  3. 作 为 Spring 框 架 一 部 分 , 能 够 使 用 Spring 的 IoC 和 Aop 。 方便整合Strtus,MyBatis,Hiberate,JPA 等其他框架。

  4. SpringMVC 强化注解的使用,在控制器,Service,Dao 都可以使用注解。方便灵活。

    使用@Controller 创建处理器对象,@Service 创建业务对象,@Autowired 或者@Resource 在控制器类中注入 Service, Service 类中注入 Dao。

3、第一个SpringMVC程序

1、实现步骤

1、加入依赖
SpringMVC依赖

使用SpringMVC时,不需要我们手动加入Spring的依赖,因为SpringMVC本身依赖于Spring,所以根据Maven的依赖传递性会自动帮我们加入Spring的依赖。

<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.5.RELEASE</version>
</dependency>
Servlet、Jsp依赖
<!-- servlet的依赖 -->
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>3.1.0</version>
    <scope>provided</scope>
</dependency>

<!-- jsp的依赖 -->
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
<dependency>
    <groupId>javax.servlet.jsp</groupId>
    <artifactId>jsp-api</artifactId>
    <version>2.1</version>
    <scope>provided</scope>
</dependency>

2、在web.xml中注册SpringMVC框架核心对象DispatcherServlet
  1. 配置Servlet程序:声明注册DispatcherServlet
  2. 指定自定义SpringMVC读取配置文件的位置
  3. 指定创建DispatcherServlet的实例
  4. 配置Servlet访问地址
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- 声明注册SpringMVC的核心对象DispatcherServlet -->
    <servlet>
        <!-- 给Servlet取别名 -->
        <servlet-name>springmvc</servlet-name>
        
        <!-- Servlet全类名 -->
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        
        <!-- 自定义springmvc读取配置文件的位置 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        
        <!--
            在tomcat启动后创建DispatcherServlet对象
            load-on-startup:表示tomcat启动后创建对象的顺序。
                            值为大于0的整数,值越小tomcat创建对象的时间越早。
        -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <!-- 给Servlet配置访问地址 -->
    <servlet-mapping>
        <!-- Servlet名称,一般使用别名 -->
        <servlet-name>springmvc</servlet-name>
        
        <!--
			配置访问地址
			使用框架的时候,url-pattern可以使用两种值
			1. 使用*。
				*.do、*.action、*.mvc
			2. 使用/。
		-->
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>
为什么要创建DispatcherServlet对象的实例呢?

因为DispatcherServlet在创建过程中,会同时创建SpringMVC容器对象读取SpringMVC的配置文件,把配置文件中的对象都创建好,当用户发起请求时就可以直接使用对象了。

DispatcherServlet的初始化会执行init()方法:

init(...){
    // 创建容器,读取配置文件
    WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml")//把容器对象放入到ServletContext中
    getServletContext().setAttribute(key,ctx);
}

创建DispatcherServlet的过程中,创建容器对象时默认读取的配置文件是**/WEB-INF/<servlet-name>-servlet.xml**
例如:

<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

这里的默认配置文件名为:springmvc-servlet.xml

我们可以使用<init-param>标签自定义springmvc读取配置文件的位置。


3、创建页面发起请求、接收结果

index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>请求</title>
</head>
<body>
<p><a href="show.do">访问/WEB-INF/view/下的show.jsp页面</a></p>
</body>
</html>

show.jsp 在/WEB-INF/view/目录下创建

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>受保护的页面</title>
</head>
<body>
<h1>您目前访问量的是/WEB-INF/view/show.jsp页面</h1>
${msg}
</body>
</html>

4、创建控制器类
  1. 在类的上面加上@Controller注解,创建对象并放到SpringMVC容器中
  2. 在类的上面加上@RequestMapping注解
package com.study.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class TestController {
    /**
     * 自定义方法处理用户的请求。
     * @RequestMapping:请求映射,作用是把一个请求地址和方法绑定在一起。一个请求一个方法处理。
     * 被@RequestMapping修饰的方法叫做处理器方法或控制器方法。类似于Servlet的doGet、doPost方法。
     * 属性:
     * 		value:请求的uri地址,值唯一,不能重复。
     *				可以是一个字符串;也可以是一个数组,这样该控制类就有多个访问入口了。
     *
     */
    @RequestMapping(value = "/show.do")
    public ModelAndView doSome(){
        /**
         * ModelAndView:表示请求的处理结果。
         *      Model:数据,请求处理完后,要显示给用户的数据
         *      View:视图,如jsp等。
         */
        ModelAndView mv = new ModelAndView();
        // 添加数据。框架最后会把数据放入到request作用域
        mv.addObject("msg","Hello,SpringMVC!");
        /**
         * 指定视图。
         *      1. 如果访问的不是WEB-INF里的文件,则指定视图的完整路径
         *      2. 如果访问的是WEB-INF里的文件,则指定文件名即可,后缀都不需要。
         *          因为springmvc.xml文件中的视图解析器会处理:
         *              前缀 + 传过来的文件名 + 后缀
         *          即   /WEB-INF/view/ + show + .jsp
         */
        mv.setViewName("show");
        return mv;
    }
}

5、创建SpringMVC配置文件

SpringMVC的配置文件和Spring的配置文件是一样的。

  1. 声明组件扫描器,指定@contorller注解所在的包名

  2. 声明视图解析器。用于指定视图文件的路径。

    什么是视图?就是页面。当我们点击跳转的时候,是跳转到了另外一个页面,可是如果我们直接访问跳转目标地址呢?当然我们也会访问到相应的页面,可这是属于非法访问,也就是没有按照我们正常的规矩来,所以为了防止这样的事情发生,我们一般会将需要保护的页面放到项目的WEB-INF目录下:/WEB-INF/view/。因为WEB-INF目录下的文件默认是受保护的

springmvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 使用注解就需要声明组件扫描器 -->
    <context:component-scan base-package="com.study.controller"/>
    
    <!-- 声明框架的中的视图解析器,帮助开发人员设置视图的开发路径 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 前缀:试图文件的路径 -->
        <property name="prefix" value="/WEB-INF/view/"/>
        <!-- 后缀:试图文件的扩展名 -->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

2、SpringMVC请求处理流程

在这里插入图片描述

组件说明
DispatcherServlet中央调度器web.xml中配置到的访问地址都会交由中央调度器处理转发
HandleMapping处理器映射器springmvc框架中的一种对象,框架把实现了HandlerMapping接口的类都叫做映射器(多个);
映射器会根据请求,从springmvc容器对象中获取处理器对象,即Conteoller类对象。获取到的对象会放到处理器执行链中保存。
处理器执行链框架中的一个类,叫做HandlerExecutionChain。它里面会存储 Controller对象 和 项目中所有的拦截器
HandleAdaptor处理器适配器springmvc框架中的对象,需要实现HandlerAdapter接口。
作用:执行处理器方法,并将结果返回给中央调度器(调用Controller类中的方法,得到返回值ModelAndview)
ViewResolver视图解析器springmvc框架中的对象,需要实现ViewResolver接口。
作用:组成视图完整的路径(前缀+传过来的值+后缀),并创建View对象返回给中央调度器。
View是一个接口,表示视图的,在框架中jsp、html不是String表示的,而是使用View和它的实现类表示视图的。例如:jsp对应的就是InternalResourceView实现类。
  1. index.jsp发起请求。请求地址:some.do
  2. tomcat(根据web.xml中的url-pattern知道*.do的请求给DispatcherServlet)
  3. DispatcherServlet(根据springmvc.xml配置知道some.do请求由doSome()方法处理)
  4. DispatcherServlet把some.do转发给TestController类中的doSome()方法
  5. 框架执行doSome()把得到的ModelAndView进行处理,转发到index.jsp

二、SpringMVC注解式开发

前面第一个SpringMVC程序我们已经使用到了@RequestMapping和@Controller两个注解,接下来我们将详细介绍SpringMVC中常用的几种注解。

1、@RequestMapping

1、使用在方法上

@RequestMapping:请求映射,可以定义处理器对于请求的映射规则,如把一个请求地址和方法绑定在一起。一个请求一个方法处理。被@RequestMapping修饰的方法叫做处理器方法或控制器方法。类似于Servlet的doGet、doPost方法。

  • 属性:

    • value:请求的uri地址,值唯一,不能重复。

      可以是一个字符串,常以“/”开始;也可以是一个数组,这样该控制类就有多个访问入口了。

    • method:指定请求方式。值为RequestMethod类枚举值。

      get:RequestMethod.GET

      post:RequestMethod.POST

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

@RequestMapping(value = {"/show.do","/other.do"})
public ModelAndView doSome(){...}

2、使用在类上

@RequestMapping:请求映射,当这里和使用在方法上不一样,这里是声明请求地址的公共部分的。

例如:类中的方法的访问地址为:/test/some.do、/test/other.do等等,他们都有/test/部分,那么这一部分就可以使用@RequestMapping进行统一声明、定义。

  • 属性:
    • value:所有请求地址的公共部分
@Controller
@RequestMapping("/test")
public class TestController {
    @RequestMapping(value = "/show.do")
    public ModelAndView doSome(){
        ModelAndView mv = new ModelAndView();
        mv.setViewName("show");
        return mv;
    }
}

这样doSome方法的访问地址为:/test/show.do


2、处理器方法的参数

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

  1. HttpServletRequest
  2. HttpServletResponse
  3. HttpSession
  4. 请求中所携带的请求参数

HttpServletRequest、HttpServletResponse和HttpSession三个和Servlet中的使用方法是一样的,不做赘述。

请求中所携带的请求参数分为两类情况:逐个参数接收、对象参数接收。

1、逐个参数接收

使用这种方式的前提是:请求中的参数名和处理器方法的形参名一样

使用示例

页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>请求</title>
</head>
<body>
    <form action="form.do" method="post">
        姓名:<input type="text" name="name"><br>
        年龄:<input type="text" name="age"><br>
        <input type="submit" value="提交">
    </form>
</body>
</html>

处理器方法

@RequestMapping(value = "/form.do")
public ModelAndView doForm(String name, int age) {	// 参数名和表单中的属性名一样
    ModelAndView modelAndView = new ModelAndView();
    modelAndView.addObject("name", name);
    modelAndView.addObject("age", age);
    modelAndView.setViewName("show");
    return modelAndView;
}
原理
  1. 框架使用request对象接收请求参数

    String strName = request.getParameter("name");
    String strAge = request.getParameter("age");
    
  2. SpringMVC框架通过DispatcherServlet调用Controller类中的doForm方法,调用时按名称对应把接收的参数赋值给形参

    doForm(strName, Integer.valueOf(strAge);
    

2、@RequestParam

如果逐个接收参数时,请求中参数和处理器方法形参名不一样,可以使用@RequestParam进行配置,使其一一对应。

属性:

  1. value:请求参数名。配上后会和形参一一对应接收。

  2. required:boolean类型,默认是true。

    true:请求中必须包含此参数;

    false:请求中可以不包含此参数。

@RequestMapping(value = "/form.do")
public ModelAndView doForm(@RequestParam(value = "userName")String name,
                           @RequestParam(value = "userAge")int age) {...}

3、对象参数接收

将处理器方法的参数定义为一个对象,只要保证请求参数名与这个对象的属性同名即可。

框架会创建形参的Java对象,然后给属性赋值。例如,请求中的参数是name,则会调用setName方法赋值。

注意:这种放方式接收值,是不能使用@RequestParam注解的


4、解决编码问题

当使用GET请求时,中文不会出现编码问题;而使用POST请求时,则会出现编码问题。我们需要使用过滤器处理乱码的问题。

我们可以使用自定义的过滤器,也可以使用框架中的过滤器。当然我们选择框架中的过滤器,因为方便。

框架中的过滤器:CharacterEncodingFilter

在web.xml文件中声明使用

<!-- 注册字符集过滤器:解决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>
    <!-- 强制HttpServletRequest请求对象使用encoding编码的值 -->
    <init-param>
        <param-name>forceRequestEncoding</param-name>
        <param-value>true</param-value>
    </init-param>
    <!-- 强制HttpServletResponse响应对象使用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>

3、处理器方法的返回值

  1. ModelAndView:返回数据 + 视图;
  2. String:返回视图;
  3. void:Ajax;
  4. Object:返回数据。

1、返回ModelAndView:视图+数据

返回数据和视图。

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

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

@RequestMapping(value = "/form.do")
public ModelAndView doForm(String name, int age) {
    // 创建ModelAndView对象
    ModelAndView modelAndView = new ModelAndView();
    // 传递的数据
    modelAndView.addObject("name", name);
    modelAndView.addObject("age", age);
    // 跳转页面(类似重定向)
    modelAndView.setViewName("show");
    return modelAndView;
}

2、返回String:视图

只返回视图。

处理器方法返回的字符串可以是:

  1. 逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址;
  2. 也可以是完整视图路径,使用完整路径则springmvc配置文件中就不能配置视图解析器。
@RequestMapping(value = "/form.do")
public String doForm(String name, int age) {
    return "show";
}

如果想要在这种情况下,处理数据并返回,则需要在处理器方法中加入HttpServletRequest参数:

@RequestMapping(value = "/form.do")
public String doForm(HttpServletRequest request, String name, int age) {
    request.setAttribute("name",name);
    request.setAttribute("age",age);
    return "show";
}

3、返回void:null(了解)

对于处理器方法返回 void 的应用场景,AJAX 响应。若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回 void。

1、加入jQuery库文件

2、加入jackson依赖

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

3、请求页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>请求</title>
    <script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
</head>
<body>
姓名:<input id="name" type="text" name="name"><br>
年龄:<input id="age" type="text" name="age"><br>
<button id="ajax" type="button">异步请求姓名和年龄</button>
</body>

<script type="text/javascript">
    $(function () {
        $("#ajax").click(function () {
            $.ajax({
                async: true,                         // 是否异步处理
                contentType: "application/json",     // 前端发送请求的数据格式
                dataType: "json",                    // 希望后端传回来的数据格式
                error: function () {                 // 请求失败时运行的函数
                    console.log("请求出错!")
                },
                success: function (data) {           // 请求成功时运行的函数,data是返回来的数据
                    $("#name").val(data.name);
                    $("#age").val(data.age);
                },
                type: "get",                         // 请求的类型
                url: "form.do"                       // 发送请求的 URL
            })
        })
    })
</script>
</html>

4、处理器方法

@RequestMapping(value = "/form.do")
public void doForm(HttpServletResponse response) throws IOException {
    // 准备数据
    Data data = new Data();
    data.setName("张三");
    data.setAge(19);
    // 转为json
    ObjectMapper om = new ObjectMapper();
    String s = om.writeValueAsString(data);
    // 输出数据
    response.setContentType("application/json;charset=utf-8");
    PrintWriter writer = response.getWriter();
    writer.print(s);
    writer.flush();
    writer.close();
}

4、返回对象Object:数据

用于响应Ajax请求。

1、实现步骤
  1. 加入处理json的工具库的依赖,SpringMVC默认使用的jackson;

  2. 在SpringMVC配置文件中加入<mvc:annotation-driven/>标签:注解驱动,用于将对象转为json字符串;

    <mvc:annotation-driven/>
    选http://www.springframework.org/schema/mvc这个的
    

    原理:<mvc:annotation-driven/>在加入到springmvc配置文件后,会自动创建HttpMessageConverter接口的7个实现类对象,包括MappingJackson2HttpMessageConverter(用jacksonL具库中的objectMapper实现java对象转为json字符串的)

    HttpMessageConverter 接口实现类作用
    ByteArrayHttpMessageConverter负责读取二进制格式的数据和写出二进制格 式的数据
    StringHttpMessageConverter负责读取字符串格式的数据和写出字符串格 式的数据
    ResourceHttpMessageConverter负责读取资源文件和写出资源文件数据
    SourceHttpMessageConverter能够读 / 写来自 HTTP 的请求与响应的 javax.xml.transform.Source ,支持DOMSource, SAXSource, 和StreamSource 的 XML 格式
    AllEncompassingFormHttpMessageConverter负责处理表单(form)数据
    Jaxb2RootElementHttpMessageConverter使用 JAXB 负责读取和写入 xml 标签格式的数 据
    MappingJackson2HttpMessageConverter负责读取和写入 json 格式的数据。利用 Jackson 的 ObjectMapper 读写 json 数据,操作 Object 类型数据,可读取 application/json,响应媒体类型为 application/json

    HttpMessageConverter接口:消息转换器。里面定义了Java对象转为json、xml、二进制等数据格式的方法。

    boolean canWrite(Class<?> var1, @Nullable MediaType var2);
    void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3)
    

    1)canwrite作用:检查处理器方法的返回值,能不能转为var2表示的数据格式。如果检查能转为var2表示的数据格式,canwrite返回true。MediaType:表示数格式的,例如json,xml等等。

    2)write:把处理器方法的返回值对象,调用jackson中的objectMapper转为json字符串。

  3. 在处理器方法上加入@ResponseBody注解,用于将数据返回到前端。通过HttpServletResponse输出数据,响应Ajax请求。

2、返回值是自定义对象

页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>请求</title>
    <script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
</head>
<body>
姓名:<input id="name" type="text" name="name"><br>
年龄:<input id="age" type="text" name="age"><br>
<button id="ajax" type="button">异步请求姓名和年龄</button>
</body>

<script type="text/javascript">
    $(function () {
        $("#ajax").click(function () {
            $.ajax({
                async: true,                         // 是否异步处理
                contentType: "application/json",     // 前端发送请求的数据格式
                dataType: "json",                    // 希望后端传回来的数据格式
                error: function () {                 // 请求失败时运行的函数
                    console.log("请求出错!")
                },
                success: function (data) {           // 请求成功时运行的函数,data是返回来的数据
                    $("#name").val(data.name);
                    $("#age").val(data.age);
                },
                type: "get",                         // 请求的类型
                url: "form.do"                       // 发送请求的 URL
            })
        })
    })
</script>
</html>

处理器方法

@ResponseBody	// 使用该注解说明返回值当作数据传给前端,而不是视图
@RequestMapping(value = "/form.do")
public Data doForm() {
    // 准备数据
    Data data = new Data();
    data.setName("张三");
    data.setAge(19);
    // 直接return
    return data;
}
/**
* 1. 框架会把返回Data类型,调用框架的中ArrayList<HttpMessageConverter>中每个类的canWrite()方法
*      检查哪个HttpMessageConverter接口的实现类能处理Data类型的数据,
*	   最后找到了MappingJackson2HttpMessageConverter;
* 2. 框架会调用实现类的write(),即MappingJackson2HttpMessageConverter的write()方法
*      把data对象转为json,调用Jackson的objectMapper实现转为json;
* 3. 框架会调用@ResponseBody把2的结果数据输出到浏览器,ajax请求处理完成。
*/
3、返回值是List

页面中得到的数据就会是,json数组,每一个元素都是对象,在页面中循环遍历即可使用

4、返回值是String

这里返回String表示的是数据,不是视图。

怎么区分?就看你的处理器方法上是否有@ResponseBody注解,如果有就返回的是数据;没有返回的就是视图。

注意:返回String时默认使用的是"text/plain;charset=ISO-8859-1"作为contentType(数据类型/编码),导致中文有乱码

解决方法:在@RequestMapping注解中添加一个produce属性即可

@RequestMapping(value = "/form.do", produce="text/plain;charset=utf-8")

4、解读url-patten——使用/

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- 声明注册SpringMVC的核心对象DispatcherServlet -->
    <servlet>
        <!-- 给Servlet取别名 -->
        <servlet-name>springmvc</servlet-name>
        
        <!-- Servlet全类名 -->
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        
        <!-- 自定义springmvc读取配置文件的位置 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        
        <!--
            在tomcat启动后创建DispatcherServlet对象
            load-on-startup:表示tomcat启动后创建对象的顺序。
                            值为大于0的整数,值越小tomcat创建对象的时间越早。
        -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <!-- 给Servlet配置访问地址 -->
    <servlet-mapping>
        <!-- Servlet名称,一般使用别名 -->
        <servlet-name>springmvc</servlet-name>
        
        <!--
			配置访问地址
			使用框架的时候,url-pattern可以使用两种值
			1. 使用*。
				*.do、*.action、*.mvc
			2. 使用/。
		-->
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>

前面在web.xml中给控制器类配置访问地址时使用的是*.do的方式,我们还可以使用/的方式,但是会和tomcat中的一个默认的servlet冲突:

静态资源:html、js、css、图片等等

静态资源请求:比如我们前端显示的页面,都是从服务端请求过来的,也是url请求得到的

TomCat中有一个默认的Servlet,叫default,在 Tomcat 安装目录/conf/web.xml。除了由我们自定义Servlet(处理器方法)处理的请求,其余的一切静态资源请求 以及 未映射到其他Servlet的请求都由default Servlet处理。

tomcat中默认的servlet
<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>

<!-- The mapping for the default servlet -->
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

如果我们这里也使用了/,则tomcat中的servlet就被覆盖了,所有的静态资源请求就会走我们配置的servlet了。最终会导致静态资源显示不了。

解决方式:

1、在SpringMVC配置文件中加入<mvc:default-servlet-handler/>+注解驱动。

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

注意:这里也需要加上注解驱动:<mvc:annotation-driven/>,因为default-servlet-handler和@RequestMapping注解有冲突,如果没有注解驱动,则所有的请求都会给tomcat中的default Servlet了。即加上注解驱动后,注解的优先级就高于default-servlet-handler了。

2、使用<mvc:resources/>

如果我们的Web应用服务器没有默认的Servlet呢?或者我们不想依赖Web应用服务器默认的Servlet?

我们就可以使用mvc:resources标签,在 Spring3.0 版本后,Spring 定义了专门用于处理静态资源访问请求的处理器ResourceHttpRequestHandler,它不会依赖于Web应用服务器默认的Servlet。并且添加了标签,专门用于解决静态资源无法访问问题。需要在springmvc 配置文件中添加如下形式的配置:

<!-- mapping:访问静态资源的地址;location:项目中静态资源的位置 -->
<mvc:resources mapping="/images/**" location="/images/"/>
<mvc:resources mapping="/html/**" location="/html/"/>

<!-- 注解驱动 -->
<mvc:annotation-driven/>

虽然这种方式很好,但是如果有很多静态资源的话,则需要使用多次mvc:resources标签,很麻烦。

我们常用的解决方式是:在webapp下创建一个static目录,里面专门存放我们的静态资源

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

5、相对路径和绝对路径

绝对地址:带有协议名称的是绝对地址,http://www.baidu.com,ftp://202.122.23.12。

相对地址:没有协议开头的,例如user/some.do,/user/some.do相对地址不能独立使用,必须有一个参考地址。通过参考地址+相对地址本身才能指定资源。

访问路径加/和不加/的区别:

首先加/和不加/的访问地址都是相对地址。

<!--不加 / -->
<a href="show.do"></a>
<!--加 / -->
<a href="/show.do"></a>
  • 不加/

    访问地址为:当前页面地址+访问路径,即:http://localhost:8080/study_ssm/ + show.do

    当前页面地址理解:

    eg1:访问show.do时url:http://localhost:8080/study_ssm/show.do

    当前页面地址为:http://localhost:8080/study_ssm/

    eg2:访问user/show.do时url:http://localhost:8080/study_ssm/user/show.do

    当前页面地址为:http://localhost:8080/study_ssm/user/

    总结:当前页面地址为,删除当前url最后一个 / 后面的路径,取剩下的url,即为当前页面地址。

    bug:当两次访问eg2中的路径时会报错:

    1. 第一次访问url:http://localhost:8080/study_ssm/user/show.do
    2. 第二次访问url:http://localhost:8080/study_ssm/user/user/show.do

    解决方法:

    1. 使用EL表达式

      <a href="${pageContext.request.contextPath}/user/show.do"></a>
      
    2. 使用html中的<bean>标签:表示当前页面中访问地址的基地址。即页面中所有没有/开头的访问地址,都是以base标签中的地址为参考地址

      <%
          String basePath = request.getScheme() + "://" +
                  request.getServerName() + ":" + request.getServerPort() +
                  request.getContextPath() + "/";
      %>
      <html>
      <head>
          <title></title>
          <base href="<%=basePath%>" />
      </head>
      
  • /

    访问地址为:服务器地址+访问路径,即:http://localhost:8080 + /show.do

    很明显,如果加/的话,我们的访问地址是错误的,所以我们不能机械的加上/

    解决方法:

    1. 设置访问地址为:项目名+访问地址

      <a href="/study_ssm/show.do"></a>
      
    2. 使用EL表达式

      <a href="${pageContext.request.contextPath}/show.do"></a>
      

三、SSM整合开发

1、分析

  • Spring——业务层
  • SpringMVC——视图层
  • MyBatis——持久层

基本流程:用户发起请求——SpringMVC接收——Spring中的Service对象——MyBatis处理数据(处理完后按源路径返回结果)

容器:

  • SpringMVC容器:管理Controller控制器对象
  • Spring容器:管理Service、Dao(dao接口中定义每张表/实体类对应的操作)、工具类对象

虽然两个容器都是独立的,但是SpringMVC容器是Spring容器的子容器,是继承关系,即SpringMVC中的Controller可以访问Spring中的Service对象。

2、整合步骤

详细步骤

1、依赖:SpringMVC、Spring、MyBatis、jackson、mysql、druid、jsp、servlet、Lombok

2、web.xml

  1. 注册中央调度器DispatcherServlet。作用:
    1. 创建SpringMVC容器对象;
    2. 所有的请求交给DispatcherServlet转发。我们默认使用的是/的访问路径。
  2. 注册Spring监听器ContextLoaderListener。作用:创建Spring容器对象。
  3. 注册字符集过滤器CharacterEncodingFilter。作用:解决Post请求乱码问题。

3、SpringMVC配置文件

  1. 声明组件扫描器。作用:使用注解就得使用。
  2. 声明视图解析器。作用:帮助开发人员设置视图的访问路径。
  3. 声明注解驱动。作用:Ajax、访问静态资源都需要注解驱动。
  4. 声明静态资源处理方式。作用:解决静态资源访问的问题。

4、Spring配置文件

  1. 声明数据源。作用:连接数据库的配置信息。

  2. 声明SqlSessionFactoryBean。作用:创建SqlSessionFactory对象

  3. 声明MyBatis扫描器。作用:创建dao对象

  4. 声明注解扫描器。作用:让spring自动扫描base-package对应的路径或者该路径的子包下面的java文件,如果扫描到文件中带有@Service,@Component,@Repository,@Controller等这些注解的类,则把这些类注册为bean。这里目的是让spring帮我们创建service实现类的对象

    注意:这里我们不使配置文件配置自动注入。而是使用@Service + @Resouce注解进行自动注入的。因为配置文件写多了,可读性会很差,程序员也看的眼花。

  5. 配置事务、配置注解、配置aspectj等等…

5、MyBatis配置文件(由于使用了数据库连接池,这里就不需要配置数据库配置信息了)

  1. MyBatis自带的日志(可选)
  2. 统一给实体类取别名
  3. 声明mapper文件

6、数据库属性配置文件:url、username、password

7、创建包

  1. controller:里面存放控制器方法,就是Servlet。
  2. dao:存放dao接口和mapper文件(mapper文件名和dao接口文件名一样),dao接口中定义表操作相关的方法,mapper文件中定义每个dao接口中定义的方法对应的sql语句。
  3. entity:实体类,每个实体类对应数据库中的一个表。
  4. service:存放service接口和思想类,实现类利用spring+mybatis帮我们创建出来的dao接口对象实现业务方法。这里利用到的dao接口对象我们使用注解的方式自动注入:@Service + @Resource

pom.xml文件

<dependencies>
    <!-- 测试工具依赖 -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.11</version>
        <scope>test</scope>
    </dependency>
    <!-- servlet的依赖 -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <!-- jsp的依赖 -->
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.1</version>
        <scope>provided</scope>
    </dependency>
    <!-- spring-mvc依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!-- spring的依赖 -->
    <!-- Spring IOC依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!-- Spring AspectJ依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!-- Spring事务依赖 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>4.3.14.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>4.3.14.RELEASE</version>
    </dependency>
    <!--spring的监听器-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!-- MyBatis依赖 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.4.1</version>
    </dependency>
    <!-- MySQL依赖 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.18</version>
    </dependency>
    <!-- MyBatis和Spring集成的依赖 -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.2</version>
    </dependency>
    <!-- 阿里数据库连接池druid的依赖 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.8</version>
    </dependency>
    <!-- jackson依赖:json-->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.9.0</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.0</version>
    </dependency>

    <!-- 自动生成get、set方法Lombok的依赖 -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
        <scope>provided</scope>
    </dependency>

    <!-- MyBatis分页插件依赖 -->
    <dependency>
        <groupId>com.github.pagehelper</groupId>
        <artifactId>pagehelper</artifactId>
        <version>4.2.1</version>
    </dependency>
</dependencies>

<build>
    <resources>
        <resource>
            <!--资源文件(非Java文件)所在的目录-->
            <directory>src/main/java</directory>
            <includes>
                <!--包括目录下的.properties和.xml 文件都会扫描到-->
                <include>**/*.properties</include>
                <include>**/*.xml</include>
            </includes>
            <!--filtering 选项 false 不启用过滤器, *.property 已经起到过滤的作用了 -->
            <filtering>false</filtering>
        </resource>
    </resources>
</build>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <!-- 1、注册中央调度器:DispatcherServlet -->
    <servlet>
        <servlet-name>springmvc</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>springmvc</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- 2、注册Spring监听器 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <context-param>
        <!-- contextConfigLocation表示配置文件的路径 -->
        <param-name>contextConfigLocation</param-name>
        <!-- 自定义配置文件的路径 -->
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!-- 3、注册字符集过滤器:解决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>
        <!-- 强制HttpServletRequest请求对象使用encoding编码的值 -->
        <init-param>
            <param-name>forceRequestEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
        <!-- 强制HttpServletResponse响应对象使用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>
</web-app>

SpringMVC配置文件

dispatcherServlet.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/mvc
       https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!-- 1、声明组件扫描器 -->
    <context:component-scan base-package="com.study.controller"/>
    <!-- 2、声明框架的中的视图解析器,帮助开发人员设置视图的开发路径 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 前缀:试图文件的路径 -->
        <property name="prefix" value="/WEB-INF/jsp/"/>
        <!-- 后缀:试图文件的扩展名 -->
        <property name="suffix" value=".jsp"/>
    </bean>
    <!-- 3、声明注解驱动 -->
    <mvc:annotation-driven/>

    <!-- 4、解决静态资源 / 冲突 -->
    <!-- 方式一:使用服务器默认servlet -->
    <mvc:default-servlet-handler/>
    <!-- 方式二:彻底摆脱服务器servlet,使用框架中的servlet -->
    <!-- mapping:访问静态资源的地址;location:项目中静态资源的位置 -->
    <!-- <mvc:resources mapping="/static/**" location="/static/"/> -->
</beans>

Spring配置文件

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 添加外部配置文件 -->
    <context:property-placeholder location="classpath:db.properties" file-encoding="utf-8"/>

    <!-- 1、数据源DataSource配置:即配置数据库 -->
    <!-- init-method:初始方法;destroy-method:销毁方法 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>

        <property name="filters" value="stat"/>
        <!-- 设置连接池最大容量 -->
        <property name="maxActive" value="${jdbc.maxActive}"/>
        <!-- 设置初始创建连接数 -->
        <property name="initialSize" value="1"/>
        <property name="maxWait" value="6000"/>
        <property name="minIdle" value="1"/>
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
        <property name="minEvictableIdleTimeMillis" value="300000"/>
        <property name="testWhileIdle" value="true"/>
        <property name="testOnBorrow" value="false"/>
        <property name="testOnReturn" value="false"/>
        <property name="poolPreparedStatements" value="true"/>
        <property name="maxOpenPreparedStatements" value="20"/>
        <property name="asyncInit" value="true"/>
    </bean>

    <!-- 2、SqlSessionFactory -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!-- 配置数据库连接池的信息,引用类型注入,值用ref给 -->
        <property name="dataSource" ref="dataSource"/>
        <!-- 配置MyBatis主配置文件的信息 -->
        <property name="configLocation" value="classpath:SqlMapConfig.xml"/>
    </bean>

    <!-- 3、声明MyBatis扫描器,创建dao对象 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!-- 指定sqlSessionFactory -->
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <!-- 指定mapper接口,mapper接口中是实体类对应的操作方法 -->
        <property name="basePackage" value="com.study.dao"/>
    </bean>

    <!-- 4、声明注解扫描器,让spring帮我们创建service实现类的对象 -->
    <context:component-scan base-package="com.study.service"/>
</beans>

MyBatis配置文件

mybatis.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    <!-- 1、MyBatis自带的日志 -->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

    <!-- 2、统一给实体类取别名 -->
    <typeAliases>
        <package name="com.study.entity"/>
    </typeAliases>

    <!-- 3、映射器:声明mapper文件 -->
    <mappers>
        <mapper resource="mapper_employees.xml"/>
    </mappers>
</configuration>

数据库属性配置文件

jdbc.properties

jdbc.url=jdbc:mysql://127.0.0.1:3306/mysqldemo?serverTimezone=UTC&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=password
jdbc.maxActive=20

四、SpringMVC核心技术

1、请求重定向和转发

请求转发:前端请求次数为 1 次,转发是在服务器内部进行的。

重定向:前端请求次数为 2 次。

在这里插入图片描述

SpringMVC 框架把原来 Servlet 中的请求转发和重定向操作进行了封装。现在可以使用简单的方式实现转发和重定向。

  • forward:表示转发,实现 request.getRequestDispatcher(“xx.jsp”).forward()

    虽然视图解析器默认也是使用的转发,但是这种方式使用转发可以访问到视图解析器以外的页面

  • redirect:表示重定向,实现 response.sendRedirect(“xxx.jsp”),注意:重定向是不能访问WEB-INF目录下受保护的页面的

    框架中对于重定向会把Model中的简单类型的数据,转为string使用,作为新请求的get请求参数。目的就是为了在两次请求之间传递数据。

    在第二次请求中怎么拿到第一次请求传过来的数据(就是get请求后的参数)呢?

    http://localhost:8080/study_ssm/show.jsp?name=lisi&age=19
    在页面中使用参数集合对象param获取请求参数值
    <p> ${param.name} </p>
    <p> ${param.age} </p>
    
// 使用转发
@RequestMapping(value = "/show.do", method = RequestMethod.GET)
public ModelAndView doSome() {
    ModelAndView mv = new ModelAndView();
    // 添加数据。框架最后会把数据放入到request作用域
    mv.addObject("msg", "Hello,SpringMVC!");
    // 指定视图,使用请求转发的方式,路径为视图的完整路径
    mv.setViewName("forward:/WEB-INF/jsp/show.jsp");
    return mv;
}

// 使用重定向
// 访问 /show.do 是一个request作用域
// 访问 /WEB-INF/jsp/show.jsp 就是另一个request作用域了
@RequestMapping(value = "/show.do", method = RequestMethod.GET)
public ModelAndView doSome() {
    ModelAndView mv = new ModelAndView();
    // 添加数据。框架最后会把数据放入到request作用域
    mv.addObject("msg", "Hello,SpringMVC!");
    mv.setViewName("redirect:/WEB-INF/jsp/show.jsp");
    return mv;
}

2、异常处理

SpringMVC中采用的是统一的、全局的异常处理。

它采用的是AOP的思想,把controller中所有的异常全部集中到一个地方进行统一编写,再使用注解使其与业务逻辑代码结合起来。编写时却是分开写的,解耦合。

  • @ExceptionHandler
  • @ControllerAdvice

使用步骤:

背景:目前我有一个类运行起来会抛异常,这个异常可以是Java中的异常,也可以是我们自己定义的异常:继承Exception实现方法即可。

  1. 自定义异常类。指定可能会出现的异常的信息。

  2. 定义一个类专门处理异常。当对应的异常出现时会自动走这里面的方法。

    注意:这里需要再springmvc的主配置文件中配置组件扫描器,让springmvc知道我们使用@ControllerAdvice注解的位置 + 注解驱动

/**
 * @ControllerAdvice:控制器增强注解,作用是给控制器增加一些功能——异常处理功能
 * 使用位置:必须再类的上面
 * 使用时必须再springmvc配置文件中声明组件扫描器,让springmvc知道知道这里使用了@ControllerAdvice注解
 */
@ControllerAdvice
public class MyExceptionHandler {
    /**
     * 自定义方法处理处理发生的异常
     * @ExceptionHandler(异常的class):表示异常的类型,当发生此类型的异常时,由当前方法来处理
     *
     * @param exception 表示controller中抛出的异常对象,可以通过它获取发生的异常信息
     * @return 返回值是多样的,可以是ModelAndView、String、void等等
     */
    @ExceptionHandler(Exception.class)
    public ModelAndView handlerException(Exception exception) {
        /**
         * 异常发生时,一般处理方法:
         *  1. 记录异常,发生的额时间、谁发生的、异常内容。记录到数据库 或者 日志文件等等
         *  2. 发送通知。把异常信息,通过邮件、短信、微信发送给相关人员。
         *  3. 给用户友好的提示。
         */
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","不好意思,程序出现了一点状况,请稍等,我们的程序员正在处理!");
        modelAndView.addObject("exception",exception);
        modelAndView.setViewName("exceptionErro");
        return modelAndView;
    }

    // 如果没有找到对应的异常,就会走这里。一般这种处理只能有一个,即其他所有未处理异常都会走这里。
    @ExceptionHandler
    public ModelAndView handlerDefaultException(Exception exception) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("msg","不好意思,程序出现了一点状况,请稍等,我们的程序员正在处理!");
        modelAndView.addObject("exception",exception);
        modelAndView.setViewName("defaultErro");
        return modelAndView;
    }
}

3、拦截器

SpringMVC 中的 Interceptor 拦截器是非常重要和相当有用的,它的**主要作用是拦截指定 Java 框架 SpringMVC的用户请求,并进行相应的预处理与后处理**。

拦截器执行时间点:

  1. 在请求处理之前,也就是controller类中的方法执行之前先被拦截。

  2. 在控制器方法执行之后也会执行拦截器。

  3. 在请求处理完成后也会执行拦截器。

    请求处理完:视图处理完成后,对试图执行了forward,就认为请求处理完成。

多个拦截器执行的顺序:

  • 先声明先执行

  • 在框架中保存拦截器实际上使用的是一个ArrayList,按照声明的现后顺序存储到ArrayList中

在这里插入图片描述

拦截器和过滤器的区别:

  1. 过滤器是servlet中的对象,由tomcat服务器创建;拦截器是框架中的对象,在springmvc容器中创建;

  2. 过滤器实现Filter接口的对象,拦截器是实现HandlerInterceptor

  3. 过滤器是用来设置request,response的参数,属性,编码字符集的,侧重对数据过滤;
    拦截器是用来验证请求的,能截断请求,一般用在用户登陆处理、权限检查、记录日志等方面;

  4. 过滤器在拦截器之前先执行;

  5. 过滤器是一个执行时间点;拦截器有三个执行时间点;

  6. 过滤器可以处理jsp,js,html等等;
    拦截器是侧重拦截对Controller的对象。如果你的请求不能被DispatcherServlet接收,这个请求不会执行拦截器内容;

  7. 拦截器拦截普通类方法执行,过滤器过滤servlet请求响应。

拦截器的使用步骤:

  1. 定义类实现HandlerInterceptor接口,实现接口中的三个方法
  2. 在springmvc配置文件中,声明拦截器,让框架知道拦截器的存在。
public class MyInterceptor implements HandlerInterceptor {
    /**
     * 预处理方法。在控制器方法执行前先执行,用户的请求首先到达此发方
     * 我们可以在里面验证请求是否符合要求、验证用户是否登陆、验证用户是否有权限访问某个链接(url)
     *
     * @param request
     * @param response
     * @param handler  被拦截的控制器对象
     * @return true:验证成功,放行请求;false:验证失败,截断请求
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("【拦截器】预处理方法preHandle");
        return true;
    }

    /**
     * 后处理方法。在控制器方法执行后执行
     * 可以获取到控制器方法的返回值ModelAndView,可以修改其中的数据和视图,影响到最终的执行结果
     * 主要是对原来的执行结果进行二次修正
     *
     * @param request
     * @param response
     * @param handler      被拦截的控制器对象
     * @param modelAndView 控制器方法的返回值
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("【拦截器】后处理方法preHandle");
    }

    /**
     * 最后执行的方法。在请求处理完成后执行。
     * 一般做资源回收工作的
     *
     * @param request
     * @param response
     * @param handler  被拦截的控制器对象
     * @param ex       程序中发生的异常
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("【拦截器】最后执行方法afterCompletion");
    }
}
<!-- 声明拦截器,拦截器可以有0或多个 -->
<mvc:interceptors>
    <mvc:interceptor>
        <!-- 声明访问拦截器的uri地址,即该uri路径下的所有资源都会被当前拦截器拦截 -->
        <mvc:mapping path="/interceptor/**"/>
        <!-- 声明拦截器对象  -->
        <bean class="com.study.handler.MyInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。、可私 6信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 、可私信6博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 、可私信6博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

BUG_GUB

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值