1.概述

  1. SpringMVC是spring的一部分,能够使用spring的IOC和AOP。
  2. SpringMVC强化注解的使用,在控制器,Service,Dao都可以使用注解。所谓SpringMVC的注解式开发是指在代码中通过对类与方法的注解,便可完成处理器在springMVC容器的注册。
  3. springMVC的核心Servlet是DispactcherServlet。DispatcherServlet是框架中的一个Servlet对象,负责接收请求,响应处理请求结果。它也叫做前端控制器(front controller),其父类是一个HttpServlet。
springMVC核心对象DispatcherServlet的作用:
1. 在servlet中的init()方法中,创建spring MVC的容器对象。例如
    WebApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:springmvc.xml");
2. 作为Servlet接受请求
  1. SpringMVC是一种容器,用于创建控制器对象、管理控制器对象。

2.快速入门

示例:用户提交一个请求,服务端处理器在接收到这个请求后,给出一个欢迎消息,在响应页面中显示该消息。总体步骤如下:

1. 新建Maven web应用
2. 加入web依赖
    spring MVC框架依赖和Servlet依赖
3. 声明springMVC核心对象DispatcherServlet
4. 创建一个jsp,发起请求
5. 创建一个普通类,作为控制器使用(代替之前的Servlet)
    在类的上面加入@Controller注解
    在类中定义方法,方法的上面加入@RequestMapping注解,表示该方法用于处理请求的,相当于Servlet中的doGet和doPost方法。
6. 创建作为结果的jsp页面
7. 创建springmvc的配置文件
    声明组件扫描器,指定@Controller注解所在的包名
    声明视图解析器对象
8. 使用逻辑视图名称
1.新建Maven模块,选择从原型创建

image.png

2. 创建好web项目后,在pom.xml文件中添加Servlet,spring MVC依赖
<dependency>
  <groupId>javax.servlet</groupId>
  <artifactId>javax.servlet-api</artifactId>
  <version>3.1.0</version>
  <scope>provided</scope>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-webmvc</artifactId>
  <version>5.2.5.RELEASE</version>
</dependency>
3.在web.xml中注册中央调度器
  1. 中央调度器的全限定类名
// 中央调度器也是一个Servlet
org.springframework.web.servlet.DispatcherServlet
  1. 元素...
在<servlet/>中添加<load-on-startup/>的作用是,标记是否在Web服务器(这里是Tomcat)启动时会创建这个 Servlet 实例,即是否在 Web 服务器启动时调用执行该 Servlet 的 init()方法,而不是在真正访问时才创建。
它的值必须是一个整数。

1. 当值大于等于0时,表示容器在启动时就加载并初始化这个 servlet,数值越小,该Servlet的优先级就越高,其被创建的也就越早;
2. 当值小于 0 或者没有指定时,则表示该 Servlet 在真正被使用时才会去创建。
3. 当值相同时,容器会自己选择创建顺序。
  1. 元素:该元素的值表示将一些请求交给指定的servlet处理。
<!--url-pattern可以有两种值:
    1.使用 *.xxx  例如*.do、*.action、*.mvn等,不能使用*.jsp
    2.使用 /,如果使用/,静态资源获取请求会报404错误,需要进行
    相关的配置
-->
<url-pattern>*.do</url-pattern>
  1. 自定义配置文件的位置与名称(设置在指定目录下寻找springmvc配置文件)
<!-- 前提:在resources目录下创建一个springmvc.xml文件,-->
<!--在Servlet标签下,init-param标签用于查找SpringMVC
配置文件路径及文件名 -->
<init-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:springmvc.xml</param-value>
</init-param>
5.创建springmvc配置文件。
  1. 方式1在src/main/resources目录下创建springmvc配置文件,该文件名可以任意
  2. 方式2在WEB-INF下创建springmvc配置文件,该配置文件名必须为元素的值-servlet.xml
新创建的springmvc配置文件如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>
6.创建处理器,也叫做后端控制器(back controller)
  1. @Controller:在类的上面加上该注解表示当前类为处理器
  2. @RequestMapping:在方法上面加上该注解表示当前方法为处理器方法。该方法要对 RequestMapping的value
    属性所指定的 URI进行处理与响应
1. 作用:将指定的请求交给指定的方法处理
2. 属性:value,其值表示请求中的URI地址,唯一值,以"/"开头
7.声明组件扫描器
在springmvc的配置文件的beans标签下声明
<context:component-scan base-package="nrv"></context:component-scan>
8.声明视图解析器:ModelAndView 中只需给出要跳转页面的文件名即可(该文件名即逻辑视图名称),对于具体的文件路径与文件扩展名,视图解析器会自动完成拼接。
<!-- 在springmvc的配置文件中声明视图解析器:帮助处理视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- 前缀:指定视图文件的路径-->
    <property name="prefix" value="/WEB-INF/view/"></property>
    <!-- 后缀:视图文件的扩展名-->
    <property name="suffix" value=".jsp"></property>
</bean>

// view文件夹用于保护视图,因为在浏览器中是访问不到WEB-INF目录的
// mv.setViewName("/WEB-INF/view/show.jsp");

// 当配置了视图解析器,可以使用文件名称作为视图名使用,叫做视图逻辑名称
mv.setViewName("other");

3.配置文件相关

1.web开发中配置文件的说明
  1. web.xml:部署描述符文件,给服务器使用。作用是在服务器启动的时候读取web.xml,根据文件中的声明创建各种对象,根据文件中的声明知道请求和servlet等对象的关系。
  2. 框架的配置文件(springMVC的配置文件):作用是声明框架创建的项目中的各种对象,主要是创建Controller对象的
2.配置文件的加载顺序和功能
  1. tomcat服务器启动,读取web.xml,根据web.xml文件中的说明,创建对象。比如创建DispatcherServlet类型的对象,在其中会执行init方法。在init方法中会执行springMVC容器对象的创建。
<servlet>
    <servlet-name>myweb</servlet-name>
    <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>
    <load-on-startup>1</load-on-startup>
</servlet>
  1. springMVC框架中,new ClassPathXmlApplicationContext()将读取springMVC的配置文件。使用组件扫描器遍历com.nrv包下的所有类,根据注解创建对象。
<!--声明组件扫描器-->
    <context:component-scan base-package="com.nrv"></context:component-scan>
    <!-- 声明视图解析器:帮助处理视图-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 前缀:指定视图文件的路径-->
        <property name="prefix" value="/WEB-INF/view/"></property>
        <!-- 后缀:视图文件的扩展名-->
        <property name="suffix" value=".jsp"></property>
</bean>

4.@RequestMapping注解

  1. 属性value:其值表示请求中的URI地址,唯一值,以"/"开头
  2. 位置
1. 在方法上
2. 在类定义的上面
@Controller
@RequestMapping(value="/user")
public class UserController {
    // 浏览器访问地址:http://localhost:8080/mvc_war/user/some.do
    @RequestMapping(value = {"/some.do"})
    public ModelAndView doSome() {
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", "处理了some.do请求");
        mv.addObject("func", "执行了dosome方法");

         mv.setViewName("show");
        return mv;
    }
    // 浏览器请求访问地址:http://localhost:8080/mvc_war/user/other.do
    @RequestMapping(value="/other.do")
    public String doOnther() {
        return "other";
    }
}
  1. 作用:将指定的请求交给指定的方法处理
1.定义请求提交方式

@RequestMapping的属性method,用于对被注解方法所处理请求的提交方式进行限制,只有满足该method属性指定的提交方式的请求,才会指定该被注解方法。method属性的取值为RequestMethod枚举常量,常用的为RequestMethod.GET,RequestMethod.POST 如果不指定请求方式,无论get请求方式还是post请求方式都可以被匹配。

@Controller
@RequestMapping(value="/user")
public class UserController {
    // 浏览器访问地址:http://localhost:8080/mvc_war/user/some.do
    // 指定some.do使用get请求方式
    @RequestMapping(value = {"/some.do"}, method = RequestMethod.GET)
    public ModelAndView doSome() {
        ModelAndView mv = new ModelAndView();
        // Model中的数据存储在request作用域中
        // SpringMVC默认采用转发的方式跳转到视图
        // 本次请求结束,模型中的数据销毁
        mv.addObject("msg", "处理了some.do请求");
        mv.addObject("func", "执行了dosome方法");

         mv.setViewName("show");
        return mv;
    }
    // 浏览器请求访问地址:http://localhost:8080/mvc_war/user/other.do
    // 指定other.do使用post请求方式
    @RequestMapping(value="/other.do", method = RequestMethod.POST)
    public String doOnther() {
        return "other";
    }
    // 不指定method属性,请求方式没有限制
    @RequestMapping(value="/first.do")
    public String doFirst() {
        return "other";
    }
}

// method属性取值参考:
public enum RequestMethod {
    GET,
    HEAD,
    POST,
    PUT,
    PATCH,
    DELETE,
    OPTIONS,
    TRACE;

    private RequestMethod() {
    }
}

附: 客户端常用的请求方式:

  1. 表单请求:默认为GET,可以指定为POST
  2. AJAX请求:默认为GET,可以指定为POST
  3. 地址栏请求:GET请求
  4. 超链接请求:GET请求
  5. src资源路径请求:GET请求

5.处理器方法的参数

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

  1. HttpServletRequest
  2. HttpServletResponse
  3. HttpSession
  4. 请求中所携带的请求参数
1.接受请求中的参数
  1. 逐个接收:请求中的参数名和控制器方法的形参名一样,按照名称对应接收参数。
<form action="user/receive-args.do" method="post">
        姓名:<input type="text" name="name"/><br/>
        年龄:<input type="text" name="age"/><br/>
        <input type="submit" value="提交参数"/>
</form>

@Controller
@RequestMapping(value="/user")
public class UserController {
    // 浏览器访问路径:http://localhost:8080/mvc_war/user/receive-args.do
    @RequestMapping(value = "/receive-args.do")
    public ModelAndView doRequestArgs(String name, Integer age) {
        System.out.println("执行了doRequestArgs方法,name="+ name +",age="+age);
        ModelAndView modelAndView = new ModelAndView();
        // 添加数据
        modelAndView.addObject("myname", name);
        modelAndView.addObject("myage", age);
        modelAndView.setViewName("show");
        return modelAndView;
    }

}
  1. 逐个接收:请求中的参数名和控制器方法的形参名不一致。使用@RequestParam注解解决名称不一样的问题
@RequestParam的使用:
    属性value:值表示请求中的参数名称
    属性required:Boolean类型的,默认是true
    true表示请求中必须有这个参数,没有就报错。
    false表示请求中可以没有这个参数
    位置:在形参定义的前面

<form action="user/receive-argsname.do" method="post">
        姓名:<input type="text" name="rname"/><br/>
        年龄:<input type="text" name="rage"/><br/>
        <input type="submit" value="提交参数"/>
</form>

@Controller
@RequestMapping(value="/user")
public class UserController {
    // 浏览器访问路径:http://localhost:8080/mvc_war/user/receive-args.do
    // required = false表示即使表单中未提交数据也不会报错
    @RequestMapping(value = "/receive-argsname.do")
    public ModelAndView doRequestArgs(@RequestParam(value = "rname", required = false)String name,
                                      @RequestParam(value = "rage", required = false)Integer age) {
        System.out.println("执行了doRequestArgs方法,name="+ name +",age="+age);
        ModelAndView modelAndView = new ModelAndView();
        // 添加数据
        modelAndView.addObject("myname", name);
        modelAndView.addObject("myage", age);
        modelAndView.setViewName("show");
        return modelAndView;
    }

}
  1. 对象接收:在控制器方法的形参是Java对象,使用Java对象的属性接收请求中的参数值。要求Java对象的属性名和请求中的参数名一致。
<form action="user/receive-object.do" method="post">
        姓名:<input type="text" name="name"/><br/>
        年龄:<input type="text" name="age"/><br/>
        <input type="submit" value="提交参数"/>
</form>

public class Student {
    private String name;
    private Integer age;
    // getter,setter
}
@Controller
@RequestMapping(value="/user")
public class UserController {
    @RequestMapping(value="/receive-object.do")
    public ModelAndView doRecieveObject(Student student) {
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("myname", student.getName());
        modelAndView.addObject("myage", student.getAge());
        modelAndView.setViewName("show");
        return modelAndView;
    }

}
2.接收post请求的请求参数中中文乱码的问题

spring给出了专门的字符集过滤器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>
    <init-param>
      <!-- 强制请求对象request使用encoding的编码方式-->
      <param-name>forceRequestEncoding</param-name>
      <param-value>true</param-value>
    </init-param>
    <init-param>
      <!-- 强制应答对象response使用encoding的编码方式-->
      <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>

6.处理器方法的返回值

使用@Controller注解的处理器的处理器方法,其返回值常用的有四类类型。返回值代表本次请求的处理结果

  1. ModelAndView
  2. String
  3. 无返回值void
  4. 返回对象Object
1.ModelAndView(数据和视图)

请求的结果有数据和视图,使用ModelAndView最方便。数据存放在Request作用域,视图默认执行转发操作。

2.String(视图)

框架的返回值是String,则执行的是forward转发操作。视图可以表示为完整视图路径(物理视图名)或者视图的逻辑名称。

  1. 返回值是逻辑视图名称,需要在springMVC配置文件中配置视图解析器

<!-- springmvc.xml中声明视图解析器:帮助处理视图-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- 前缀:指定视图文件的路径-->
    <property name="prefix" value="/WEB-INF/view/"></property>
    <!-- 后缀:视图文件的扩展名-->
    <property name="suffix" value=".jsp"></property>
</bean>

<form action="user/return-string-view.do" method="post">
        姓名:<input type="text" name="name"/><br/>
        年龄:<input type="text" name="age"/><br/>
        <input type="submit" value="提交参数"/>
</form>

@Controller
@RequestMapping(value="/user")
public class UserController {
    // 控制器方法返回String,表示逻辑名称,需要在项目中配置视图解析器
    @RequestMapping(value = "/return-string-view.do")
    public String doReturnStringView(HttpServletRequest request, String name, Integer age) {
        System.out.println("name:"+name+",+age:" +age);
        // 处理数据
        request.setAttribute("myname", name);
        request.setAttribute("myage", age);
        // 返回结果,转发到show.jsp
        return "show";
    }
}
  1. 返回值是完整视图路径:项目中不能配置视图解析器
<form action="user/return-string-view.do" method="post">
        姓名:<input type="text" name="name"/><br/>
        年龄:<input type="text" name="age"/><br/>
        <input type="submit" value="提交参数"/>
</form>

@Controller
@RequestMapping(value="/user")
public class UserController {
    @RequestMapping(value="return-string-view.do")
    public String doReturnStringView(String name, Integer age) {
        System.out.println("name:"+name+",age:" +age);
        return "/WEB-INF/view/show.jsp";
    }
}
3.void(没有数据和视图)

void:没有数据和视图,可以使用HttpServletResponse对象输出数据,响应Ajax请求。示例:对于Ajax的异步请求的响应

  1. Maven中加入Jackson依赖
<!--jackson依赖 -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.12.2</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.12.2</version>
</dependency>
  1. 引入JQuery依赖:因为本项目需要使用JQuery的ajax方法提交请求。
  2. index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <!--引入jQuery文件 -->
    <script type="text/javascript" src="js/jquery-3.5.1.js"></script>
    <script type="text/javascript">
        $(function (){
            //绑定事件
            $("#btnAjax").on("click", function() {
                $.ajax({
                    url:"return-void-ajax.do",
                    //url:"doStudentJson.do",
                    data:{
                        name:"张三",
                        age:"23"
                    },
                    dataType:"json",
                    success:function (resp) {
                        alert("resp.name:"+resp.name+"resp.age:"+resp.age);
                    }
                })
            })
        })
    </script>
</head>
<body>
    <button id="btnAjax">发起Ajax请求</button>
</body>
</html>

  1. 控制器如下
@Controller
public class UserController {
    /**
     * 控制器方法返回是void,响应Ajax请求。使用HttpServletResponse输出数据
     */
    @RequestMapping(value="/return-void-ajax.do")
    public void returnVoidAjax(HttpServletResponse response, String name, Integer age) throws IOException {
        System.out.println("name="+name+",age="+age);
        Student student = new Student();
        student.setName(name);
        student.setAge(age);
        // 将对象转化为json
        ObjectMapper objectMapper = new ObjectMapper();
        String json = objectMapper.writeValueAsString(student);
        System.out.println("服务器端对象转化为的json"+json);

        // 输出json,响应Ajax
        response.setContentType("application/json;charset=utf-8");
        PrintWriter writer = response.getWriter();
        writer.println(json);
        writer.flush();
        writer.close();

    }
}
4.Object

返回的对象不是作为逻辑视图或者物理视图出现的,而是作为直接在页面显示的数据出现的。控制器方法返回对象Object,用来响应Ajax请求。返回的对象Object,可以是List,Student,Map,String,Integer,这些都是数据,正是Ajax请求需要的。在Ajax请求中,一般需要的是从服务器返回的json格式的数据,经常要处理Java对象到json的转换。而且还需要输出数据响应Ajax请求。

  1. HttpMessageConverter消息转换器,一个接口
作用如下:
1. 将请求的数据转为Java对象
2. 将控制器方法返回的对象转为json,xml,text,二进制等不同格式的数据
接口中方法说明:
public interface HttpMessageConverter<T> {
    //检查var1这个类型的对象能否转为MediaType表示的数据格式
    // 如果能转换,则返回true,返回true调用read()
    boolean canRead(Class<?> var1, @Nullable MediaType var2);
    //作用:接受请求中的数据,将数据转换为var1表示的对象
     T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;
    // 作用:检查var1这种数据类型能否转为MediaType表示的数据格式 
    boolean canWrite(Class<?> var1, @Nullable MediaType var2);
    // 作用:将var1对象按照var2说明的格式,把对象转为json或者XML
    void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
     List<MediaType> getSupportedMediaTypes();

}

  1. HttpMessageConverter接口的实现类:
1. MappingJackson2HttpMessageConverter:用Jackson工具库的ObjectMapper将Java对象转为json数据格式
2. StringHttpMessageConverter:将字符串类型的数据进行格式转换和编码
  1. 注册驱动标签:在springMVC的配置文件,加入注解驱动的标签mvc:annotation-driven.加入这个标签后,springMVC项目启动后,会创建HttpMessageConverter接口的七个实现类对象,包括上面两个。note:有关MVC的包不要选错,选择以MVC结尾的。

  2. @ResponseBody:该注解的作用就是将Java对象转换后的json数据通过HttpServletResponse对象输出给浏览器,转换后的JSON数据被放入到响应体中的。

// @ResponseBody作用类似于如下
// 输出json,响应Ajax
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.println(json);
writer.flush();
writer.close();

5.总结:将控制器方法返回的对象转为json的步骤 (返回自定义类型对象时,不能以对象的形式直接返回给客户端浏览器,而是将对象转换为JSON格式的数据发送给浏览器的。)

1. pom.xml中加入Jackson依赖,因为SpringMVC框架默认处理json就是使用Jackson
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.12.2</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.12.2</version>
    </dependency>
2. 在springMVC的配置文件中,加入注解驱动的标签mvc:annotation-driven
3. 在控制器方法的上面加入@ResponseBody注解,
表示将返回值数据(即转换后的数据)输出到浏览器
  1. 示例:控制器方法返回Student,转为json
// index.jsp
<html>
<head>
    <title>Title</title>
    <!--引入jQuery文件 -->
    <script type="text/javascript" src="js/jquery-3.5.1.js"></script>
    <script type="text/javascript">
        $(function (){
            //绑定事件
            $("#btnAjax").on("click", function() {
                $.ajax({
                    //url:"return-void-ajax.do",
                    url:"doStudentJson.do",
                    data:{
                        name:"张三",
                        age:"23"
                    },
                    dataType:"json",
                    success:function (resp) {
                        alert("resp.name:"+resp.name+"resp.age:"+resp.age);
                    }
                })
            })
        })
    </script>
</head>
<body>
    <button id="btnAjax">发起Ajax请求</button>
</body>
</html>

// springMVC配置文件中需要注册驱动标签
<mvc:annotation-driven/>

@Controller
public class UserController {
    // 控制器方法返回Student,将Student转为json
    @RequestMapping(value="/doStudentJson.do")
    @ResponseBody
    public Student doAjaxJson(String name, Integer age) {
        System.out.println("控制器方法返回自定义对象Student转为json"+ name+","+age);
        Student student = new Student();
        student.setName(name);
        student.setAge(age);

        return student;
    }
}
  1. 示例:控制器方法返回String-文本数据
// index.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <!--引入jQuery文件 -->
    <script type="text/javascript" src="js/jquery-3.5.1.js"></script>
    <script type="text/javascript">
        $(function (){
            //绑定事件
            $("#btnAjax").on("click", function() {
                $.ajax({
                    //url:"return-void-ajax.do",
                    //url:"doStudentJson.do",
                    url:"doStringData.do",
                    data:{
                        name:"张三",
                        age:"23"
                    },
                    dataType:"text",
                    success:function (resp) {
                        alert(resp);
                    }
                })
            })
        })
    </script>
</head>
<body>
    <button id="btnAjax">发起Ajax请求</button>
</body>
</html>

// springMVC配置文件中需要注册驱动标签
<mvc:annotation-driven/>

@Controller
public class UserController {
//解决中文乱码问题:使用@RequestMapping的produces属性,其属性值即为想指定content-type的值
//其值告诉浏览器怎么显示服务器返回的数据 

// 控制器方法返回String-数据
    // 区分返回值String是字符串还是视图:方法上面是否有@ResponseBody注解
    // 有就是数据,没有就是视图
    @RequestMapping(value="/doStringData.do", produces = "text/plain;charset=utf-8")
    @ResponseBody
    public String doStringData() {
        return "Hello,SpringMVC注解式开发";
    }
}

7.Tomcat中的静态资源的访问

1.Tomcat中的default Servlet
  1. 配置在/conf/web.xml下
<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>
  1. default叫做默认Servlet,作用:
1. 提供静态资源的处理
2. 处理所有未映射到其他控制器的请求的处理
2.中央调度器的url-pattern的设置
  1. 中央调度器的url-pattern不设置"/"
<servlet>
    <servlet-name>myweb</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>
  <!--*.do所有请求交给springmvc.xml中配置的控制器对象处理 -->
  <servlet-mapping>
    <servlet-name>myweb</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>

http://localhost:8080/mvc_war_exploded/imags/img.png
http://localhost:8080/mvc_war_exploded/html/test.html
http://localhost:8080/mvc_war_exploded/js/jquery-3.5.1.js
上述静态资源都可以访问,因为Tomcat中的default这个Servlet会处理。
2. 中央调度器的url-pattern设置"/"

<servlet>
    <servlet-name>myweb</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>myweb</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

上述三个静态资源都无法访问,使用"/"导致中央调度器成为了默认的default servlet。需要处理静态资源和其他的映射的请求。而默认的中央调度器没有处理静态资源的控制器对象,所以静态资源都是404.而some.do这个请求有MyController控制器对象,所以可以访问。

3.静态资源访问工作的处理

如果项目中,中央调度器设置了"/",动态资源能够访问,静态资源不能访问,则需要处理静态资源的访问工作。

  1. 方式1:使用<mvc:default-servlet-handler/>标签
    。声明了该标签后,springMVC框架会在容器中创建DefaultServletHttpRequestHandler处理器对象。他就像检察员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的Servlet处理。一般的服务器都有默认的Servlet。该方式依赖Web应用服务器
// 在SpringMVC的配置文件中加入如下内容
<mvc:default-servlet-handler/>

note:default-servlet-handler和@RequestMapping使用冲突的解决方案:声明注解驱动

<mvc:annotation-driven/>
  1. 方式2:在SpringMVC配置文件中加入一个mvc:resources标签,框架会创建一个ResourceHttpRequestHandler处理器对象,使用这个对象处理静态资源的访问。该方式不依赖Web应用服务器,推荐使用

当项目结构如下时:
在这里插入图片描述

<!-- mapping属性:其值表示访问静态资源的请求URI地址,可以使用通配符
         比如**表示任意的目录中的资源名称
        location属性:其值表示静态资源在项目中的位置,不要使用/WEB-INF目录
    -->
<mvc:resources mapping="/imags/**" location="/imags/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
<mvc:resources mapping="/html/**" location="/html/"/>
  1. 方式3:
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.jpg</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.js</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.css</url-pattern>
</servlet-mapping>
<servlet-mapping>
    <servlet-name>default</servlet-name>
    <url-pattern>*.html</url-pattern>
</servlet-mapping>

8.SSM整合开发

SSM编程,即SpringMVC、Spring、MyBatis整合。这三个框架对应三层架构的三层,SpringMVC是视图层,Spring是业务层,MyBatis是持久层。SSM整合的实现方式分为两种:基于XML配置方式和基于注解方式。

1.SSM整合思路

SSM整合就是将对象交给容器管理,让容器去创建项目中要使用到的Java对象。现在有两个容器:

  1. Spring容器:Spring容器是管理service和dao等对象的
  2. springMVC容器:管理控制器对象的
2.容器的创建
  1. spring容器的创建:在web.xml中声明了监听器ContextLoaderListener。 功能是创建spring的容器对象WebApplicationContext.在创建WebApplicationContext对象时,读取spring的配置文件,读取配置文件时,遇到bean标签或者注解,就能创建service、dao等对象,放到容器中。
  2. springMVC容器的创建:在web.xml中声明了中央调度器DispatcherServlet。 在这个Servlet中的init方法中,创建了容器对象WebApplicationContext,在创建WebApplicationContext对象时,读取SpringMVC的配置文件,读取文件的时候,遇到@Controller注解创建控制器controller对象,放到容器中。
3.SpringMVC容器和Spring容器的关系

Spring是父容器,SpringMVC是子容器,相当于Java中的继承关系。所以SpringMVC容器中的controller对象可以访问Spring容器中的service对象

3.SSM整合开发步骤
  1. 创建数据库表
  2. 创建Maven Web项目
  3. 修改pom.xml加入依赖:spring、springMVC、mybatis、mybatis-spring、MySQL驱动、druid、Jackson
  4. 编写web.xml:声明容器对象
    1. 声明spring的监听器ContextLoaderListener:创建spring容器对象,创建service、dao对象
    2. 声明springMVC的中央调度器DispatcherServlet:创建SpringMVC容器对象,创建controller对象
  5. 创建程序中的包,dao、service、controller、entity
  6. 编写spring、mybatis、springMVC配置文件
  7. 写Java代码,实体类,dao接口和Mapper文件,service类,controller类。使用注解声明对象和赋值
  8. 创建视图文件,各种jsp

9.相对路径

在页面中,存在路径的问题。例如:

<a href="test/some.do">没有斜杠开头</a>
<a href="/test/some.do">有斜杠开头</a>
<a href="http://www.baidu.com">以协议开头的地址</a>

上述页面中地址的区别:

  1. 有协议开头的地址,比如说http://www.baidu.com,称为绝对地址。地址是唯一的,可以直接访问。
  2. 没有协议开头的,比如说test/some.do,/test/some.do都称为相对地址。相对地址必须和参考地址组合在一起,才能表示一个资源的完整地址,才能访问。
    1. 没有斜杠开头的地址,参考地址为当前资源的访问路径。参考地址加上当前的相对地址组合为最后的访问地址
    2. 有斜杠开头的地址,参考地址就是服务器地址,比如说http://localhost:8080.这种组合的地址缺少项目访问路径,即Tomcat服务器配置中应用程序上下文的值,访问易出错
1.没有斜杠开头的相对地址,访问路径更不易出错的解决方式
  1. 使用EL表达式
<a href="${pageContext.request.ContextPath}/test/some.do">发起请求test/some.do请求</a>

ContextPath的值即为Tomcat服务器配置中的应用程序上下文。例如/springmvc_review_war_exploded
2. 使用HTMl中的base标签

1. 方式1(href的值写死了,不推荐使用)
在head标签下添加如下内容
<base href=""/>
2. 方式2
先在页面的头部添加如下内容
<%
 String basePath = request.getScheme() + "://" + 
request.getServerName() + ":" + request.getServerPort() + 
request.getContextPath() + "/";
%>
指定 base 标签
<head>
 <base href="<%=basePath%>">
 <title>title</title>
</head>

2.有斜杠开头的相对地址,访问路径更不易出错的解决方式
  1. 使用EL表达式
<a href="${pageContext.request.ContextPath}/test/some.do">发起请求test/some.do请求</a>

10.请求转发和重定向

当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发与重定向。 而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器。

1.使用forward指定转发操作
  1. 示例:控制器方法返回ModelAndView实现转发
// 语法:setViewName("forward:视图完整路径");
// forward特点:不和视图解析器一同工作
// setViewName默认是转发
modelAndView.setViewName("forward:/WEB-INF/view/show.jsp");
2.使用redirect实现重定向操作
  1. 示例:控制器方法返回ModelAndView实现重定向.note:重定向不能够访问受保护的目录WEB-INF
// 语法:setViewName("redirect:视图完整路径");
// redirect特点:不和视图解析器一同工作
modelAndView.setViewName("redirect:/other.jsp");

11.异常处理

SpringMVC框架使用的是集中的异常处理:将各个Controller中抛出的异常集中到一个地方处理。处理异常的叫做异常处理器。框架中使用两个注解完成异常的集中处理,这样每个Controller就不用单独处理异常。注解是:

  1. @ExceptionHandler:放在方法的上面,表示此方法可以处理某个类型的异常。当异常发生时,执行这个方法。
    1. 属性value:其值指定该注解的方法所有处理的异常类
  2. @ControllerAdvice:放在类的上面,表示这个类中有异常的处理方法。相当于AOP中的@Aspect.@Controller看作是控制器增强,就是给Controller类增加异常的处理(切面)功能

12.拦截器

1.单个拦截器
  1. 概念:拦截器是SpringMVC框架中的一种对象。
  2. 作用:拦截用户的请求,可以预先对请求做处理。根据处理结果,决定是否执行controller。
  3. 特点
1. 拦截器分为系统拦截器和自定义拦截器
2. 一个项目可以有多个拦截器。0个或者多个自定义拦截器
3. 拦截器侧重于拦截用户的请求
4. 拦截器是在请求处理之前先执行的
5. 可以将多个controller控制器共有的功能定义到拦截器
  1. 定义
1. 创建类实现拦截器接口HanderInterceptor,
根据需要实现接口中的三个方法
    1. preHandle:预处理方法,在处理器方法执行之前执行
    可以对请求做处理,可以做登陆的检查,权限的判断,统计数据等等。
    2. postHandle:后处理方法,在控制器方法之后执行
    可以修改处理器方法的处理结果数据还有跳转的视图
    3. afterCompletion:最后执行的方法
    可以做程序最后要做的工作,释放内存,清理临时变量
        例如:
        // 在控制器方法中
        session.setAttribute("data", "在控制器方法中往session中添加数据");
        // 在拦截器的afterCompletion方法中
        // 清除资源
        HttpSession session = request.getSession();
        session.removeAttribute("data");
        System.out.println(session.getAttribute("data")); // null
       
2. 在springMVC配置文件中,声明拦截器对象,并指定拦截器的拦截地址
2.多个拦截器

使用两个拦截器,查看拦截器的执行以及哪个方法控制请求的执行。

  1. 两个拦截器,第一个拦截器的preHandle返回true,第二个拦截器的preHandle也返回true.请求可以得到处理。(这两个拦截器在springMVC配置文件中具有先后关系)
先执行拦截器1的preHandle
拦截器2的preHandle
执行了控制器的处理与用户请求相应的控制器方法
拦截器2的postHandle
拦截器1的postHandle
拦截器2的afterCompletion
拦截器1的afterCompletion
  1. 两个拦截器,第一个拦截器的preHandle返回true,第二个拦截器的preHandle也返回false。控制器方法得不到执行,请求得不到处理。
先执行拦截器1的preHandle
拦截器2的preHandle
拦截器1的afterCompletion
  1. 两个拦截器,第一个拦截器的preHandle返回false,第二个拦截器的preHandle也返回false或者true。控制器方法得不到执行,请求得不到处理。
执行拦截器1的preHandle

使用多个拦截器的原因?

  1. 将验证功能分散到独立的拦截器。每个拦截器做单一的验证处理
  2. 组合多个拦截器
3.拦截器和过滤器的对比
  1. 拦截器是SpringMVC框架中的对象,过滤器是Servlet中的对象
  2. 拦截器对象是框架容器创建的,过滤器是Tomcat创建的对象
  3. 拦截器侧重对请求做判断,处理的,可以截断请求。过滤器侧重对request、response对象的属性,参数设置值的。例如request.setCharacterEncoding("utf-8")
  4. 拦截器的执行时间有三个:控制器方法之前,之后,请求完成后。过滤器是在请求之前。
  5. 拦截器是对controller,动态资源请求的。过滤器可以过滤所有请求,动态的和静态的。
  6. 拦截器和过滤器一起执行,先执行的是过滤器,后面是中央调度器,后面是拦截器,最后是控制器方法。

13.SpringMVC的内部执行流程

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