SpringMVC 简述

初始搭建步骤

  1. 创建 web maven 项目,完善项目结构,导入相关依赖(spring-webmvc、servlet、jsp)

    <!--Servlet依赖-->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <scope>provided</scope>
    </dependency>
    <!--SpringMVC依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    
  2. 在 web.xml 中注册中央调度器对象 DispatcherServlet

    <?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
            需要在tomcat服务器启动后,创建DispatcherServlet对象的实例。
            为什么要创建DispatcherServlet对象的实例呢?
            因为DispatcherServlet在他的创建过程中, 会同时创建springmvc容器对象,
            读取springmvc的配置文件,把这个配置文件中的对象都创建好, 当用户发起
            请求时就可以直接使用对象了。
            servlet的初始化会执行init()方法。 DispatcherServlet在init()中{
               //创建容器,读取配置文件
               WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
               //把容器对象放入到ServletContext中
               getServletContext().setAttribute(key, ctx);
            }
            启动tomcat报错,读取这个文件 /WEB-INF/springmvc-servlet.xml(/WEB-INF/myweb-servlet.xml)
            springmvc创建容器对象时,读取的配置文件默认是/WEB-INF/<servlet-name>-servlet.xml .
        -->
        <servlet>
            <servlet-name>myweb</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <init-param>
                <!--springmvc的配置文件的位置的属性-->
                <param-name>contextConfigLocation</param-name>
                <!--指定自定义文件的位置-->
                <param-value>classpath:springmvc.xml</param-value>
            </init-param>
            <!--在tomcat启动后,创建Servlet对象
                load-on-startup:表示tomcat启动后创建对象的顺序。它的值是整数,数值越小,
                                tomcat创建对象的时间越早。 大于等于0的整数。
            -->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>myweb</servlet-name>
            <!--
                使用框架的时候, url-pattern可以使用两种值
                1. 使用扩展名方式, 语法 *.xxxx , xxxx是自定义的扩展名。 常用的方式 *.do, *.action, *.mvc等等
                   不能使用 *.jsp
                   http://localhost:8080/myweb/some.do
                   http://localhost:8080/myweb/other.do
                2.使用斜杠 "/"
            -->
            <url-pattern>*.do</url-pattern>
        </servlet-mapping>
    </web-app>
    
  3. 创建 springmvc.xml 配置文件,声明组件扫描器(扫描注解所在的包)和视图解析器

    <!--声明组件扫描器,指定@Controller注解所在的包名-->
    <context:component-scan base-package="com.luis.controller" />
    <!--声明 springmvc框架中的视图解析器, 帮助开发人员设置视图文件的路径-->
    <bean  class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!--前缀:视图文件的路径-->
        <property name="prefix" value="/WEB-INF/view/" />
        <!--后缀:视图文件的扩展名-->
        <property name="suffix" value=".jsp" />
    </bean>
    
  4. 创建测试页面

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
        <p>请求测试</p>
        <a href="https://www.cnblogs.com/luisblog/archive/2022/11/01/some.do">发起请求</a>
    </body>
    </html>
    
  5. 创建控制器类,接收处理页面请求

    @Controller
    public class MyController {
        @RequestMapping("/some.do")
        public ModelAndView doSome() {
            ModelAndView mv = new ModelAndView();
            mv.addObject("msg", "hello springmvc");
            mv.addObject("fun", "doSome()");
            //指定视图, 指定视图的完整路径
            //框架对视图执行的forward操作, request.getRequestDispather("/result.jsp).forward(...)
            //mv.setViewName("/result.jsp");
            //mv.setViewName("/WEB-INF/view/result.jsp");
            //mv.setViewName("/WEB-INF/view/other.jsp");
            //当配置了视图解析器后,可以使用逻辑名称(文件名),指定视图
            //框架会使用视图解析器的前缀 + 逻辑名称 + 后缀 组成完整路径, 这里就是字符连接操作。
            ///WEB-INF/view/ + result+ .jsp
            mv.setViewName("result");
            return mv;
        }
    }
    
  6. 在 /WEB-INF/view/ 目录下创建结果显示页面【注意:结果显示相关页面记得创建在WEB-INF下面,配合视图解析器使用

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Title</title>
    </head>
    <body>
      <p>请求显示页面</p>
        msg:${msg}<br/>
        fun:${fun}
    </body>
    </html>
    
  7. 测试

以上注意点:

配置视图解析器作用

SpringMVC 注解式开发

核心就是熟悉控制器类各种开发

@RequestMapping 放在类上

主要使用场景:指定模块名称

@RequestMapping 放在类上作用:

  • 可以在其属性上再指定一个路径
  • 相当于在该控制器类中所有处理器方法的路径前都加上了该路径
  • 开发中常用,在类上使用,相当于指定一个公共的模块名
@Controller
@RequestMapping("/user")
public class MyController {
}

@RequestMapping 的 method 属性

作用:指定请求处理的方式

@RequestMapping 的 method 属性可以指定是 POST 请求处理还是 GET 请求处理

如果指定 GET 请求处理,就相当于使用 doGet() 处理

如果指定 POST 请求处理,就相当于使用 doPost() 处理

如果没有为 method 属性指定请求处理方式,则 get 和 post 方式的请求都能处理

@RequestMapping(value = "/some.do", method = RequestMethod.POST)
public ModelAndView doSome() {
    ModelAndView mv = new ModelAndView();
    mv.addObject("msg", "hello springmvc");
    mv.addObject("fun", "doSome()");
    // mv.setViewName("/WEB-INF/view/result.jsp");
    mv.setViewName("result");
    return mv;
}

处理器方法可用形参

处理器方法形参可以有以下四类,这些参数会在系统调用时由系统自动进行赋值,即我们在开发中可以在方法内直接使用!

需要用到哪种参数,只需要在形参位置声明后,由系统自动赋值,我们在方法中直接使用。

  1. HttpServletRequest

  2. HttpServletResponse

  3. HttpSession

  4. 请求中所携带的请求参数(用户提交的数据)

@Controller
@RequestMapping("/user")
public class MyController {
    @RequestMapping(value = "/some.do", method = RequestMethod.POST)
    public ModelAndView doSome(HttpServletRequest request,
                               HttpServletResponse response,
                               HttpSession session) {
        System.out.println(request.getParameter("name"));
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg", "hello springmvc");
        mv.addObject("fun", "doSome()");
        // mv.setViewName("/WEB-INF/view/result.jsp");
        mv.setViewName("result");
        return mv;
    }
}

接收用户提交的参数

核心需掌握:逐个接收和对象接收两种方式

逐个接收参数

核心要求:同名请求参数赋值给同名的形参

@Controller
public class MyController {
    /**
     * 逐个接收请求参数:
     *   要求: 处理器(控制器)方法的形参名和请求中参数名必须一致。
     *          同名的请求参数赋值给同名的形参
     * 框架接收请求参数
     *   1. 使用request对象接收请求参数
     *      String strName = request.getParameter("name");
     *      String strAge = request.getParameter("age");
     *   2. springmvc框架通过 DispatcherServlet 调用 MyController的doSome()方法
     *      调用方法时,按名称对应,把接收的参数赋值给形参
     *      doSome(strName,Integer.valueOf(strAge))
     *      框架会提供类型转换的功能,能把String转为 int ,long , float, double等类型。
     *
     *  400状态码是客户端错误, 表示提交请求参数过程中,发生了问题。
     */
    @RequestMapping(value = "/receiveproperty.do")
    public ModelAndView doSome(String name,Integer age){
        System.out.println("name=" + name + " age=" + age);
        ModelAndView mv  = new ModelAndView();
        mv.addObject("myname",name);
        mv.addObject("myage",age);
        mv.setViewName("show");
        return mv;
    }
}

接收参数-过滤器解决乱码

过滤器解决post请求乱码问题

在提交请求参数时,get 请求方式中文没有乱码。

使用 post 方式提交请求,中文有乱码,需要使用过滤器处理乱码的问题。

过滤器可以自定义,也可使用框架中提供的过滤器 CharacterEncodingFilter

该过滤器实际在代码中进行的设置:

request.setCharacterEncoding(uft-8);

response.setCharacterEncoding(uft-8);

使用步骤:

直接在 web.xml 中注册 CharacterEncodingFilter过滤器即可

<!--注册声明过滤器,解决post请求乱码的问题-->
<filter>
    <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!--设置项目中使用的字符编码-->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
    <!--强制请求对象(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>

@RequestParam

使用场景:只能用在逐个接收参数的情况下

两个核心作用:

  1. 解决请求参数名和处理器方法形参名不一致的问题
  2. 可以设置用户提交的请求中是否必须要包含此参数
/**
     * 请求中参数名和处理器方法的形参名不一样
     * @RequestParam: 逐个接收请求参数中, 解决请求中参数名形参名不一样的问题
     *      属性: 1. value 请求中的参数名称
     *            2. required 是一个boolean,默认是true
     *                true:表示请求中必须包含此参数。
     *      位置: 在处理器方法的形参定义的前面
     */
@RequestMapping(value = "/receiveparam.do")
public ModelAndView receiveParam(@RequestParam(value = "rname",required = false) String name,
                                 @RequestParam(value = "rage",required = false) Integer age){
    System.out.println("name=" + name + " age=" + age);
    ModelAndView mv  = new ModelAndView();
    mv.addObject("myname",name);
    mv.addObject("myage",age);
    mv.setViewName("show");
    return mv;
}

对象参数接收

注意:需要我们自己创建 Student 类,接收对象参数时,框架会自动创建形参对应的 java 对象,给属性赋值。

/**
     * 处理器方法形参是java对象, 这个对象的属性名和请求中参数名一样的
     * 框架会创建形参的java对象, 给属性赋值。 请求中的参数是name,框架会调用setName()
     * @return
     */
@RequestMapping(value = "/receiveobject.do")
public ModelAndView receiveParam(Student mystudent){
    //处理some.do请求了。 相当于service调用处理完成了。
    ModelAndView mv  = new ModelAndView();
    mv.addObject("myname",mystudent.getName());
    mv.addObject("myage",mystudent.getAge());
    mv.addObject("mystudent",mystudent);
    //show是视图文件的逻辑名称(文件名称)
    mv.setViewName("show");
    return mv;
}

处理器方法的返回值

常用的有四种类型:

  1. ModelAndView
  2. String
  3. 无返回值(了解)
  4. 返回自定义类型对象

返回 ModelAndView

使用场景:处理器方法处理完后,既需要传递数据,又需要跳转资源,使用 ModelAndView最合适不过。

特点:有数据和视图,对视图进行 forward

返回 String

使用场景:处理器方法处理完后,只需要跳转资源,此时使用 String 最合适。

String:表示视图,可以是逻辑名称,也可以是完整视图路径

注意:

  • 如果使用逻辑名称,则需要配置视图解析器
  • 如果使用完整视图路径,则不能配置视图解析器(有的话会有冲突,会报404)

SpringMVC

返回 void(了解)

void:不能够表示数据,也不能表示视图。

在处理 ajax 的时候,可以使用 void 返回值。通过 HttpServletResponse 输出数据,相应 ajax 请求。

ajax 请求服务器端返回的就是数据,和视图无关。

<!--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>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script type="text/javascript" src="https://www.cnblogs.com/luisblog/archive/2022/11/01/js/jquery-3.4.1.js"></script>
    <script type="text/javascript">
        $(function() {
            $("#btn").click(function() {
                $.ajax({
                    url:"ajax.do",
                    data:{
                        uname:"luis",
                        uage:25
                    },
                    type:"post",
                    dataType:"json",
                    success:function (resp) {
                        alert(resp.name + " " + resp.age)
                    }
                })
            })
        })
    </script>
</head>
<body>
    <input type="button" id="btn" value="发起ajax请求"/>
</body>
</html>
@RequestMapping(value = "/ajax.do", method = RequestMethod.POST)
public void doAjax(HttpServletResponse response,
                   @RequestParam("uname") String name,
                   @RequestParam("uage") int age) throws IOException {
    // 封装数据
    Student student = new Student(name, age);
    // 将对象转换为json格式字符串
    String json = "{}";
    ObjectMapper om = new ObjectMapper();
    json = om.writeValueAsString(student);
    // 设置响应格式,将数据写入响应体
    response.setContentType("application/json;charset=utf-8");
    PrintWriter out = response.getWriter();
    out.print(json);
    out.flush();
    out.close();
}

返回对象 Object

处理器方法可以返回 Object 对象,这个 Object 对象可以是 Integer,String,自定义对象,Map,List 等。

但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。

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

可以使用对象表示的数据,响应ajax请求。

使用 SpringMVC,返回对象是 Object,需要返回 json 字符串格式做输出只需三步:

  1. Jackson 依赖
  2. springmvc.xml 中添加 mvc 结尾的注解驱动
  3. 控制器方法上加 @ResponseBody 注解
1.返回自定义类型对象

返回自定义类型对象——如Studnet

可以使用对象表示的数据,响应ajax请求!

现在做ajax, 主要使用json的数据格式。 实现步骤:
   1.加入处理json的工具库的依赖, springmvc默认使用的jackson。
   2.在sprigmvc配置文件之间加入 <mvc:annotation-driven> 注解驱动。
     json  = om.writeValueAsString(student);
   3.在处理器方法的上面加入@ResponseBody注解
       response.setContentType("application/json;charset=utf-8");
       PrintWriter pw  = response.getWriter();
       pw.println(json);
  springmvc处理器方法返回Object, 可以转为json输出到浏览器,响应ajax的内部原理
  1. <mvc:annotation-driven> 注解驱动。
     注解驱动实现的功能是 完成java对象到json,xml, text,二进制等数据格式的转换。
     <mvc:annotation-driven>在加入到springmvc配置文件后, 会自动创建HttpMessageConverter接口
     的7个实现类对象, 包括 MappingJackson2HttpMessageConverter 
     (使用jackson工具库中的ObjectMapper实现java对象转为json字符串)
     HttpMessageConverter接口:消息转换器。
     功能:定义了java转为json,xml等数据格式的方法。 这个接口有很多的实现类。
           这些实现类完成 java对象到json, java对象到xml,java对象到二进制数据的转换
     下面的两个方法是控制器类把结果输出给浏览器时使用的:
     boolean canWrite(Class<?> var1, @Nullable MediaType var2);
     void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3)
     例如处理器方法
     @RequestMapping(value = "/returnString.do")
     public Student doReturnView2(HttpServletRequest request,String name, Integer age){
             Student student = new Student();
             student.setName("lisi");
             student.setAge(20);
             return student;
     }
     1)canWrite作用检查处理器方法的返回值,能不能转为var2表示的数据格式。
       检查student(lisi,20)能不能转为var2表示的数据格式。如果检查能转为json,canWrite返回true
       MediaType:表示数格式的, 例如json, xml等等
     2)write:把处理器方法的返回值对象,调用jackson中的ObjectMapper转为json字符串。
        json  = om.writeValueAsString(student);
 2. @ResponseBody注解
   放在处理器方法的上面, 通过HttpServletResponse输出数据,响应ajax请求的。
           PrintWriter pw  = response.getWriter();
           pw.println(json);
           pw.flush();
           pw.close();
==============================================================================================
没有加入注解驱动标签<mvc:annotation-driven /> 时的状态
org.springframework.http.converter.ByteArrayHttpMessageConverter
org.springframework.http.converter.StringHttpMessageConverter
org.springframework.http.converter.xml.SourceHttpMessageConverter
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter
加入注解驱动标签时<mvc:annotation-driven />的状态
org.springframework.http.converter.ByteArrayHttpMessageConverter
org.springframework.http.converter.StringHttpMessageConverter
org.springframework.http.converter.ResourceHttpMessageConverter
org.springframework.http.converter.ResourceRegionHttpMessageConverter
org.springframework.http.converter.xml.SourceHttpMessageConverter
org.springframework.http.converter.support.AllEncompassingFormHttpMessageConverter
org.springframework.http.converter.xml.Jaxb2RootElementHttpMessageConverter
org.springframework.http.converter.json.MappingJackson2HttpMessageConverter

使用 SpringMVC,返回对象是 Object,需要返回 json 字符串格式做输出只需三步:

  1. Jackson 依赖
  2. springmvc.xml 中添加 mvc 结尾的注解驱动
  3. 控制器方法上加 @ResponseBody 注解

示例:

<!--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>
<!--加入注解驱动 注意:需要使用的是 mvc 结尾的,这种约束文件格式才行!-->
<mvc:annotation-driven />
/**
 * 处理器方法返回一个Student,通过框架转为json,响应ajax请求
 * @ResponseBody:
 *    作用:把处理器方法返回对象转为json后,通过HttpServletResponse输出给浏览器。
 *    位置:方法的定义上面。 和其它注解没有顺序的关系。
 * 返回对象框架的处理流程:
 *  1. 框架会把返回Student类型,调用框架的中ArrayList<HttpMessageConverter>中每个类的canWrite()方法
 *检查哪个HttpMessageConverter接口的实现类能处理Student类型的数据-MappingJackson2HttpMessageConverter
 *
 *  2.框架会调用实现类的write(), MappingJackson2HttpMessageConverter的write()方法
 *    把李四同学的student对象转为json, 调用Jackson的ObjectMapper实现转为json
 *    contentType: application/json;charset=utf-8
 *
 *  3.框架会调用@ResponseBody把2的结果数据输出到浏览器, ajax请求处理完成
 */
@ResponseBody
@RequestMapping(value = "/returnStudentJson.do")
public Student doStudentJsonObject(String name, Integer age){
    //调用service,获取请求结果数据,Student对象表示结果数据。
    Student student = new Student();
    student.setName("李四");
    student.setAge(22);
    return student; //会被框架转为json
}
2.返回 List 集合

返回 List 集合——如 List<Student>

开发中常用!!!

当返回多个对象,最常用的就是 List 集合!

最终浏览器中得到的会是一个 json 数组。

/**
     *  处理器方法返回List<Student>
     * 返回对象框架的处理流程:
     *  1. 框架会把返回List<Student>类型,调用框架的中ArrayList<HttpMessageConverter>中每个类的canWrite()方法
     *     检查那个HttpMessageConverter接口的实现类能处理Student类型的数据--MappingJackson2HttpMessageConverter
     *
     *  2.框架会调用实现类的write(), MappingJackson2HttpMessageConverter的write()方法
     *    把李四的student对象转为json, 调用Jackson的ObjectMapper实现转为json array
     *    contentType: application/json;charset=utf-8
     *
     *  3.框架会调用@ResponseBody把2的结果数据输出到浏览器, ajax请求处理完成
     */
@RequestMapping(value = "/returnStudentJsonArray.do")
@ResponseBody
public List<Student> doStudentJsonObjectArray(String name, Integer age) {
    List<Student> list = new ArrayList<>();
    //调用service,获取请求结果数据 , Student对象表示结果数据
    Student student = new Student();
    student.setName("李四");
    student.setAge(20);
    list.add(student);
    student = new Student();
    student.setName("张三");
    student.setAge(28);
    list.add(student);
    return list;
}
3.返回字符串对象

注意:

  1. 返回字符串对象时需要在处理器方法上加@ResponseBody注解
  2. 需要注意,如果是 ajax 请求时,前端请求中 dataType 参数需要设置为 text,或者不进行设置
  3. 如果有中文时,需要处理中文乱码问题,需要设置@RequestMapping的属性produces

区分是返回字符串对象还是视图,看处理器方法有无@ResponseBody注解即可!

/**
     * 处理器方法返回的是String , String表示数据的,不是视图。
     * 区分返回值String是数据,还是视图,看有没有@ResponseBody注解
     * 如果有@ResponseBody注解,返回String就是数据,反之就是视图
     *
     * 默认使用“text/plain;charset=ISO-8859-1”作为contentType,导致中文有乱码,
     * 解决方案:给RequestMapping增加一个属性 produces, 使用这个属性指定新的contentType.
     * 返回对象框架的处理流程:
     *  1. 框架会把返回String类型,调用框架的中ArrayList<HttpMessageConverter>中每个类的canWrite()方法
     *     检查那个HttpMessageConverter接口的实现类能处理String类型的数据--StringHttpMessageConverter
     *
     *  2.框架会调用实现类的write(), StringHttpMessageConverter的write()方法
     *    把字符按照指定的编码处理 text/plain;charset=ISO-8859-1
     *
     *  3.框架会调用@ResponseBody把2的结果数据输出到浏览器, ajax请求处理完成
     */
@RequestMapping(value = "/returnStringData.do",produces = "text/plain;charset=utf-8")
@ResponseBody
public String doStringData(String name,Integer age){
    return "有@ResponseBody注解返回String就是数据,没有就代表视图。返回对象,表示数据。";
}

关于 url-pattern

关于 web.xml 中配置 servlet 时,配置 url-pattern 使用 *.do 和 /

当配置url-pattern为/时静态资源无法访问

问:下面发起的请求是由哪些服务器程序处理 ?
http://localhost:8080/ch05_url_pattern/index.jsp :tomcat(jsp会转为servlet)
http://localhost:8080/ch05_url_pattern/js/jquery-3.4.1.js : tomcat
http://localhost:8080/ch05_url_pattern/images/p1.jpg : tomcat
http://localhost:8080/ch05_url_pattern/html/test.html: tomcat
http://localhost:8080/ch05_url_pattern/some.do :  DispatcherServlet(springmvc框架处理的)
tomcat本身能处理静态资源的访问, 像html, 图片, js文件都是静态资源
tomcat的web.xml文件有一个servlet 名称是 default , 在服务器启动时创建的。
 <servlet>
        <servlet-name>default</servlet-name>
        <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
        <init-param>
            <param-name>debug</param-name>
            <param-value>0</param-value>
        </init-param>
        <init-param>
            <param-name>listings</param-name>
            <param-value>false</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
	  <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>  表示静态资源和未映射的请求都这个default处理
    </servlet-mapping>
default这个servlet作用: 
The default servlet for all web applications, that serves static  
resources.  It processes all requests that are not mapped to other  
servlets with servlet mappings (defined either here or in your own   
web.xml file).
1.处理静态资源
2.处理未映射到其它servlet的请求。
注意:当在web.xml中配置中央调度器对象的url-pattern时,使用斜杠 "/"
    当你的项目中使用了  / ,它会替代 tomcat中的default。
    导致所有的静态资源都给DispatcherServlet处理, 默认情况下DispatcherServlet没有处理静态资源的能力。
    没有控制器对象能处理静态资源的访问。所以静态资源(html,js,图片,css)都是404.
    动态资源some.do是可以访问,因为我们程序中有MyController控制器对象,能处理some.do请求。    

解决静态资源无法访问的第一种方式(不建议)

背景须知:

静态资源无法访问的原因是:在web.xml中配置中央调度器对象的url-pattern时,使用斜杠 "/"

方式一:在 springmvc.xml 主配置中同时添加注解驱动和下列的配置标签

缺点:需要依赖 Tomcat 所提供的默认 servlet 的处理能力。

<!--加入注解驱动-->
<!-- default-servlet-handler 和 @RequestMapping注解 有冲突, 需要加入annotation-driven 解决问题-->
<mvc:annotation-driven />
<!--第一种处理静态资源的方式:
    需要在springmvc配置文件加入 <mvc:default-servlet-handler>
    原理是: 加入这个标签后,框架会创健控制器对象DefaultServletHttpRequestHandler
    (类似我们自己创建的MyController).
    DefaultServletHttpRequestHandler这个对象可以把接收的请求转发给 tomcat的default这个servlet。
-->
<mvc:default-servlet-handler />

解决静态资源无法访问的第二种方式(建议)

背景须知:

静态资源无法访问的原因是:在web.xml中配置中央调度器对象的url-pattern时,使用斜杠 "/"

如何只配置一次,就能处理所有静态资源?

开发中最常用: 在 weapp 目录下新建一个static文件夹作为静态资源总文件夹,所有的静态资源都存放在该目录之下, 只需要配置该目录即可处理所有的静态资源。

注意:写静态资源路径时注意加上 static

方式二:在 springmvc.xml 主配置中同时添加注解驱动和下列的配置标签

此种方式不依赖Tomcat,开发中常用!

<!--第二种处理静态资源的方式(最常用)
        mvc:resources 加入后框架会创建 ResourceHttpRequestHandler这个处理器对象。
        让这个对象处理静态资源的访问,不依赖tomcat服务器。(弥补第一种方式的缺点)
        mapping:访问静态资源的uri地址, 使用通配符 **
        location:静态资源在你的项目中的目录位置。
        images/**:表示 images/p1.jpg  , images/user/logo.gif , images/order/history/list.png
    -->
<!--<mvc:resources mapping="/images/**" location="/images/" />
    <mvc:resources mapping="/html/**" location="/html/" />
    <mvc:resources mapping="/js/**" location="/js/" />-->
<!--使用一个配置语句,处理多个静态资源的访问-->
<mvc:resources mapping="/static/**" location="/static/" />
<!-- mvc:resources和@RequestMapping有一定的冲突,需要加注解驱动 -->
<mvc:annotation-driven />

解决路径访问问题

========================================================================
在jsp , html中使用的地址, 都是在前端页面中的地址,都是相对地址
地址分类:
 1.绝对地址 , 带有协议名称的是绝对地址,  http://www.baidu.com , ftp://202.122.23.1
 2.相对地址, 没有协议开头的, 例如 user/some.do  , /user/some.do
              相对地址不能独立使用,必须有一个参考地址。 通过参考地址+相对地址本身才能指定资源。
				  张三同学, 1班有张三, 2班也有张三
 3.参考地址
    1) 在你的页面中的,访问地址不加 "/"
	 访问的是: http://localhost:8080/ch06_path/index.jsp
      路径: http://localhost:8080/ch06_path/
		资源: index.jsp
    在index.jsp发起 user/some.do请求,访问地址变为 http://localhost:8080/ch06_path/user/some.do
	   当你的地址 没有斜杠开头,例如 user/some.do , 当你点击链接时, 访问地址是当前页面的地址
		加上链接的地址。
      http://localhost:8080/ch06_path/ + user/some.do
     -------------------------------------------------------------
	  index.jsp  访问 user/some.do  , 返回后现在的地址: http://localhost:8080/ch06_path/user/some.do
	  http://localhost:8080/ch06_path/user/some.do
	  路径:	  http://localhost:8080/ch06_path/user/
	  资源:   some.do
	  在index.jsp在 user/some.do ,就变为 http://localhost:8080/ch06_path/user/user/some.do
	  解决方案:
	   1.加入${pageContext.request.contextPath}
		2.加入一个base标签, 是html语言中的标签。 表示当前页面中访问地址的基地址。
		  你的页面中所有 没有“/”开头的地址,都是以base标签中的地址为参考地址
        使用base中的地址 + user/some.do 组成访问地址
   2)在你的页面中的,访问地址加 "/"
      访问的是: http://localhost:8080/ch06_path/index.jsp
      路径: http://localhost:8080/ch06_path/
		资源: index.jsp
		点击 /user/some.do, 访问地址变为 http://localhost:8080/user/some.do
		参考地址是 你的服务器地址, 也就是 http://localhost:8080
		如果你的资源不能访问: 加入${pageContext.request.contextPath}
		<a href="https://www.cnblogs.com/luisblog/archive/2022/11/01/${pageContext.request.contextPath}/user/some.do">发起user/some.do的get请求</a>

两种解决方案总结:

资源访问不加/解决方案

模板:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String basePath = request.getScheme() + "://" +
            request.getServerName() + ":" + request.getServerPort() +
            request.getContextPath() + "/";
%>
<html>
<head>
    <base href="https://www.cnblogs.com/luisblog/archive/2022/11/01/<%=basePath%>">
    <title>Title</title>
</head>
<body>
</body>
</html>

示例:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String basePath = request.getScheme() + "://" +
            request.getServerName() + ":" + request.getServerPort() +
            request.getContextPath() + "/";
%>
<html>
<head>
    <base href="https://www.cnblogs.com/luisblog/archive/2022/11/01/<%=basePath%>">
    <title>Title</title>
</head>
<body>
	<a href="https://www.cnblogs.com/luisblog/archive/2022/11/01/user/some.do"></a>
    <a href="https://www.cnblogs.com/luisblog/archive/2022/11/01/user/other.do"></a>
</body>
</html>

资源访问加/解决方案

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
	<a href="https://www.cnblogs.com/luisblog/archive/2022/11/01/${pageContext.request.contextPath}/user/some.do"></a>
    <a href="https://www.cnblogs.com/luisblog/archive/2022/11/01/${pageContext.request.contextPath}/user/other.do"></a>
</body>
</html>

总结:前端写资源访问路径的时候建议不加斜杠,然后只需要配置一次base标签,一劳永逸!

SSM 整合开发

整合细节参见gitee地址:https://gitee.com/lw2gitee/ssm.git

项目前准备:
mysql 数据库
数据库名:springdb
表:student
字段: id int auto_increment
      name varchar
      age int
1. 创建 maven web 项目,完善项目目录结构
2. 添加 pom 依赖
   相关依赖:spring、mybatis、springmvc、mysql、jackson、druid、jsp、servlet
3. 写 web.xml
   1. 注册中央调度器 DispatcherServlet
      目的:创建springmvc 容器对象,继而创建控制器对象;创建 servlet 才能接收用户请求
   2. 注册 spring 的监听器:ContextLoaderListener
      目的:创建 spring 的容器对象,才能创建 service,dao 等对象
   3. 注册字符集过滤器,解决 post 请求乱码的问题
4. 创建相关包,controller,service,dao,实体类包需创建好,写配置文件需要用
5. 写 springmvc,spring,mybatis,数据库的配置文件
6. 写代码:dao 接口和 mapper 文件;service 和实现类;controller 控制器类;实体类
7. 写 jsp 页面

项目前准备:
mysql 数据库
数据库名:springdb
表:student
字段: id int auto_increment
name varchar
age int

  1. 创建 maven web 项目,完善项目目录结构

  2. 添加 pom 依赖、资源插件、编译插件
    相关依赖:spring、mybatis、springmvc、mysql、jackson、druid、jsp、servlet

    <!--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.2.1-b03</version>
        <scope>provided</scope>
    </dependency>
    <!--springmvc依赖-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!--事务的-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!--事务相关的-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.2.5.RELEASE</version>
    </dependency>
    <!--jackson的-->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
        <version>2.9.0</version>
    </dependency>
    <!--jackson的-->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.0</version>
    </dependency>
    <!--mybatis-spring整合用到的-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>1.3.1</version>
    </dependency>
    <!--mybatis依赖-->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>3.5.1</version>
    </dependency>
    <!--mysql驱动依赖-->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.9</version>
    </dependency>
    <!--druid连接池(阿里巴巴的)-->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.1.12</version>
    </dependency>
    <build>
        <resources>
            <resource>
                <directory>src/main/java</directory><!--所在的目录-->
                <includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
                    <include>**/*.properties</include>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
    
  3. 写 web.xml

    1. 注册中央调度器 DispatcherServlet
      目的:创建springmvc 容器对象,继而创建控制器对象;创建 servlet 才能接收用户请求

    2. 注册 spring 的监听器:ContextLoaderListener
      目的:创建 spring 的容器对象,才能创建 service,dao 等对象

    3. 注册字符集过滤器,解决 post 请求乱码的问题

      <?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">
          <!--注册中央调度器DispatcherServlet-->
          <servlet>
              <servlet-name>dispatcherServlet</servlet-name>
              <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              <!--自定义springmvc配置文件路径-->
              <init-param>
                  <param-name>contextConfigLocation</param-name>
                  <param-value>classpath:conf/dispatcherServlet.xml</param-value>
              </init-param>
              <!--装载的时间是1,服务器启动时创建中央调度器对象-->
              <load-on-startup>1</load-on-startup>
          </servlet>
          <servlet-mapping>
              <servlet-name>dispatcherServlet</servlet-name>
              <url-pattern>*.do</url-pattern>
          </servlet-mapping>
          <!--注册spring的监听器-ContextLoaderListener-->
          <!--指定自定义的配置文件的位置(建议命名:applicationContext.xml)-->
          <context-param>
              <param-name>contextConfigLocation</param-name>
              <param-value>classpath:conf/applicationContext.xml</param-value>
          </context-param>
          <listener>
              <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
          </listener>
          <!--注册字符集过滤器-CharacterEncodingFilter-->
          <filter>
              <filter-name>characterEncodingFilter</filter-name>
              <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
              <!--指定字符串过滤器CharacterEncodingFilter的三个属性值,可点开该对象参考其属性值-->
              <init-param>
                  <param-name>encoding</param-name>
                  <param-value>utf-8</param-value>
              </init-param>
              <init-param>
                  <param-name>forceRequestEncoding</param-name>
                  <param-value>true</param-value>
              </init-param>
              <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>
      
  4. 创建相关包,controller,service,dao,实体类包需创建好,写配置文件需要用

  5. 写 springmvc,spring,mybatis,数据库的配置文件

    1. 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">
          <!--springmvc配置文件,声明controller和其他web相关的对象-->
          <!--声明组件扫描器,指定@Controller注解所在的包名-->
          <context:component-scan base-package="com.luis.controller"/>
          <!--声明springmvc框架中的视图解析器,帮助开发人员设置视图文件的路径-->
          <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
              <property name="prefix" value="/WEB-INF/jsp/"/>
              <property name="suffix" value=".jsp"/>
          </bean>
          <!--声明注解驱动:1、响应ajax请求,返回json;2、解决静态资源访问问题-->
          <mvc:annotation-driven/>
      </beans>
      
    2. 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: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">
          <!--spring的配置文件:声明service、dao、工具类等对象-->
          <!--声明数据库的属性配置文件位置-->
          <context:property-placeholder location="classpath:conf/jdbc.properties"/>
          <!--声明数据源,连接数据库-->
          <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
                init-method="init" destroy-method="clone">
              <property name="url" value="${jdbc.url}"/>
              <property name="username" value="${jdbc.username}"/>
              <property name="password" value="${jdbc.password}"/>
          </bean>
          <!--声明sqlSessionFactoryBean,创建sqlSessionFactory-->
          <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
              <property name="dataSource" ref="dataSource"/>
              <property name="configLocation" value="classpath:conf/mybatis.xml"/>
          </bean>
          <!--声明mybatis的扫描器,创建dao对象-->
          <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
              <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
              <property name="basePackage" value="com.luis.dao"/>
          </bean>
          <!--声明service的注解,@Service所在的位置-->
          <context:component-scan base-package="com.luis.service"/>
          <!--事务的配置:使用注解配置或aspectj的配置(二选一,后期再加)-->
      </beans>
      
    3. 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>
          <!--settings:控制mybatis全局行为-->
      <!--    <settings>-->
      <!--        &lt;!&ndash;设置mybatis输出日志&ndash;&gt;-->
      <!--        <setting name="logImpl" value="STDOUT_LOGGING"/>-->
      <!--    </settings>-->
          <!--设置别名-->
          <typeAliases>
              <!--这样设置后,实体类的类名就是该类全限定名称的别名,用别名代表了包名+类名-->
              <package name="com.luis.domain"/>
          </typeAliases>
          <!-- sql mapper(sql映射文件)的位置-->
          <mappers>
              <!--name:代表包名,这个包中所有mapper.xml一次都能加载,不用一个个配置了-->
              <package name="com.luis.dao"/>
          </mappers>
      </configuration>
      
  6. 写代码:dao 接口和 mapper 文件;service 和实现类;controller 控制器类;实体类

    public class Student {
        private Integer id;
        private String name;
        private Integer age;
        // ...
    }
    
    public interface StudentDao {
        int insertStudent(Student student);
        List<Student> selectStudents();
    }
    
    <?xml version="1.0" encoding="UTF-8" ?>
    <!DOCTYPE mapper
            PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
            "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    <mapper namespace="com.luis.dao.StudentDao">
        <insert id="insertStudent">
            insert into student(name,age) values(#{name},#{age})
        </insert>
        <select id="selectStudents" resultType="com.luis.domain.Student">
            select id,name,age from student
      </select>
    </mapper>
    
    public interface StudentService {
        int addStudent(Student student);
        List<Student> queryStudents();
    }
    @Service
    public class StudentServiceImpl implements StudentService {
        @Resource
        private StudentDao studentDao;
        @Override
        public int addStudent(Student student) {
            return studentDao.insertStudent(student);
        }
        @Override
        public List<Student> queryStudents() {
            return studentDao.selectStudents();
        }
    }
    
    @Controller
    @RequestMapping("/student")
    public class StudentController {
        @Autowired
        private StudentService studentService;
        @RequestMapping("/addStudent.do")
        public ModelAndView addStudent(Student student) {
            int ret = studentService.addStudent(student);
            String tips = "注册失败!";
            ModelAndView mv = new ModelAndView();
            if (ret > 0) {
                tips = "用户【"+student.getName()+"】注册成功!";
            }
            mv.addObject("tips", tips);
            mv.setViewName("show");
            return mv;
        }
    }
    
  7. 写 jsp 页面

    1. show.jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <%
          String basePath = request.getScheme() + "://" +
                  request.getServerName() + ":" + request.getServerPort() +
                  request.getContextPath() + "/";
      %>
      <html>
      <head>
          <base href="https://www.cnblogs.com/luisblog/archive/2022/11/01/<%=basePath%>">
          <title>tips</title>
      </head>
      <body>
          <font>
              show.jsp 结果页面 注册结果:${tips}
          </font>
      </body>
      </html>
      
    2. index.jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <%
          String basePath = request.getScheme() + "://" +
                  request.getServerName() + ":" + request.getServerPort() +
                  request.getContextPath() + "/";
      %>
      <html>
      <head>
          <base href="https://www.cnblogs.com/luisblog/archive/2022/11/01/<%=basePath%>">
          <title>index.jsp</title>
      </head>
      <body>
          <div align="center">
              <p>SSM 框架整合</p>
              <a href="https://www.cnblogs.com/luisblog/archive/2022/11/01/addStudent.jsp">学生注册</a><br/>
              <a href="https://www.cnblogs.com/luisblog/archive/2022/11/01/listStudent.jsp">学生查询</a><br/>
          </div>
      </body>
      </html>
      
    3. addStudent.jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <%
          String basePath = request.getScheme() + "://" +
                  request.getServerName() + ":" + request.getServerPort() +
                  request.getContextPath() + "/";
      %>
      <html>
      <head>
          <base href="https://www.cnblogs.com/luisblog/archive/2022/11/01/<%=basePath%>">
          <title>addStudent</title>
      </head>
      <body>
          <form action="student/addStudent.do" method="post">
              <tr>
                  <td>姓名:</td>
                  <td>
                      <input type="text" name="name"><br/>
                  </td>
              </tr>
              <tr>
                  <td>年龄:</td>
                  <td>
                      <input type="text" name="age"><br/>
                  </td>
              </tr>
              <tr>
                  <td colspan="2">
                      <input type="submit" value="注册">
                      &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
                      <input type="reset">
                  </td>
              </tr>
          </form>
      </body>
      </html>
      
    4. listStudents.jsp

      <%@ page contentType="text/html;charset=UTF-8" language="java" %>
      <%
          String basePath = request.getScheme() + "://" +
                  request.getServerName() + ":" + request.getServerPort() +
                  request.getContextPath() + "/";
      %>
      <html>
      <head>
          <base href="https://www.cnblogs.com/luisblog/archive/2022/11/01/<%=basePath%>">
          <title>listStudent</title>
          <script type="text/javascript" src="https://www.cnblogs.com/luisblog/archive/2022/11/01/js/jquery-3.4.1.js"></script>
          <script type="text/javascript">
              $(function () {
                  loadData();
                  $("#loadBtn").click(function () {
                      loadData();
                  })
              })
              function loadData() {
                  $.ajax({
                      url:"student/queryStudent.do",
                      type:"get",
                      dataType:"json",
                      success:function (data) {
                          $("#info").html("");
                          $.each(data, function (index,item) {
                              $("#info").append("<tr>")
                                  .append("<td>"+item.id+"</td>")
                                  .append("<td>"+item.name+"</td>")
                                  .append("<td>"+item.age+"</td>")
                                  .append("</tr>")
                          })
                      }
                  })
              }
          </script>
      </head>
      <body>
          <div align="center">
              <table>
                  <thead>
                      <tr>
                          <th>编号</th>
                          <th>姓名</th>
                          <th>年龄</th>
                      </tr>
                  </thead>
                  <tbody id="info">
                  </tbody>
              </table>
              <input type="button" value="刷新数据" id="loadBtn">
          </div>
      </body>
      </html>
      

总结:

  1. 多练习,熟悉相关配置作用
  2. 注重程序流程原理,不要停留于表面
  3. 注意细节,程序是非常严谨的
  4. 使用框架特别注意 set 方法和相关构造方法,这是创建对象和赋值的前提
  5. ajax 异步请求需要熟悉,相关资源跳转定位需明确和熟悉
  6. 后面完善修改和删除操作的坑:不要掉 set 方法!使用显示转发 forward 时,路径前需要加斜杠!

SpringMVC 核心技术

请求转发和重定向

  请求重定向和转发:
当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发与重定向。
而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器。
注意,对于请求转发的页面,可以是WEB-INF中页面;
而重定向的页面,是不能为WEB-INF中页面的。
因为重定向相当于用户再次发出一次请求,而用户是不能直接访问 WEB-INF 中资源的。
SpringMVC 框架把原来 Servlet 中的请求转发和重定向操作进行了封装。
现在可以使用简单的方式实现转发和重定向。
forward:表示转发,实现 request.getRequestDispatcher("xx.jsp").forward()
redirect:表示重定向,实现 response.sendRedirect("xxx.jsp")
forward:表示转发
redirect:表示重定向
forward和redirect都是关键字, 有一个共同的特点就是不和视图解析器一同工作
扩展:
forward和redirect他们都可以访问 视图文件,比如某个jsp ,html
 forward:/hello.jsp  forward:/main.html
forward和redirect他们都可以访问其它的controller
 forward:/some.do , redirect:/other.do
处理器方法可以返回ModelAndView, String , void 都可以使用forward,redirect    

SpringMVC

重点总结:

  1. 请求转发可以访问 WEB-INF 中的资源;重定向不能访问 WEB-INF 中资源
  2. 框架中将这两种实现方式进行了封装,直接在特定位置使用 forwardredirect关键字即可
  3. 使用请求转发 forward 和重定向 redirect,后面都需要填的是视图完整路径,也就是带有斜杠的,必须记住!

请求转发 forward

注意:路径需要是完整路径,必须带斜杠!

特点:

  1. 不需要视图解析器,无视视图解析器,不会受视图解析器的限制。
  2. 可以转发到 WEB-INF 下的资源
处理器方法返回 ModelAndView 时,需在 setViewName() 指定的视图前添加 forward:,
且此时的视图不再与视图解析器一同工作,这样可以在配置了解析器时指定不同位置的视图。
视图页面必须写出相对于项目根的路径。forward 操作不需要视图解析器。
处理器方法返回 String,在视图路径前面加入 forward: 视图完整路径。
请求转发也叫显示转发~
转发forward可以转发到任意符合条件的路径,不会受视图解析器的限制。
请求转发可以解决视图解析器转发不到的情况~(视图资源不在视图解析器指定的目录下)
使用方式:在视图路径前面加入 forward: 视图完整路径。    

示例:

SpringMVC

@RequestMapping("/updateStudent.do")
public ModelAndView updateStudent(Student student) {
    int result = studentService.modifyStudent(student);
    ModelAndView mv = new ModelAndView();
    if (result == 0) {
        System.out.println("======================== 修改失败");
    }
    // 注意:此listStudents.jsp在web根目录下
    // 配置有视图解析器,是无法直接跳转到的
    mv.setViewName("forward:/listStudents.jsp");
    return mv;
}

重定向 redirect

注意:路径需要是完整路径,必须带斜杠!

特点:

  1. 不需要视图解析器,无视视图解析器,不会受视图解析器的限制。
  2. 不能转发到 WEB-INF 下的资源
  3. 会将 model 中的简单类型数据转为 String 作为重定向的参数使用,方便在两次请求之间专递数据,前端页面中只能通过获取参数的方法取参

重定向传参数,前端取参的两种方式:

  1. ${param.xxx}
  2. <%=request.getParameter("xxx")%>

不能从request作用域中取,因为重定向是两次不同请求,有两个request作用域对象!

SpringMVC

SpringMVC

SpringMVC

统一异常处理

异常处理:
springmvc框架采用的是统一,全局的异常处理。
把controller中的所有异常处理都集中到一个地方。 采用的是aop的思想。把业务逻辑和异常处理代码分开。解耦合。
使用两个注解
1.@ExceptionHandler
2.@ControllerAdvice
说明:
项目中如果有异常,只需要抛出即可,由我们自己创建的全局异常处理类进行统一处理;
异常处理类需要在类上加@ControllerAdvice注解,在异常处理方法上加@ExceptionHandler注解;
异常处理方法上@ExceptionHandler的value属性可以指定处理的异常类型;
如果不指定value,即不指定处理的异常类型,则会自动为所有未匹配到的其他异常类型做统一处理;
异常方法的定义和处理器方法相同!    
PS:其实不需要指定value,全部异常给一个方法处理即可。

SpringMVC

springmvc框架中统一异常处理核心步骤:

几个词可参考:handler 目录, GlobalException,doxxxException()

  1. 添加 springmvc 相关依赖
  2. 可自定义异常类,在需要的地方抛出并添加提示信息
  3. 创建全局异常处理类,类上添加 @ControllerAdvice注解;异常处理方法上添加@ExceptionHandler注解,可选择指定其 value 属性,定义为特定异常类作专门的处理;如果不指定 value 属性,则默认处理所有未指定异常的所有异常。【其实不用指定value属性,所有异常由一个方法处理即可!】
  4. 异常处理方法的定义和处理器方法的定义相同;但异常处理方法的形参上需要添加上 Exception参数,用来接收异常信息!在方法中可以获取并使用。(在jsp页面中可以使用${异常对象.message}获取系统异常提示信息)
  5. 需要在 springmvc.xml 配置文件中同时声明 mvc 结尾的注解驱动<mvc:annotation-driven/>和指定全局异常处理类所在包的组件扫描器(用来扫描全局异常处理类上的@ControllerAdvice

以下为使用的参考示例:

SpringMVC

SpringMVC

@ControllerAdvice // 控制器增强(异常处理)
public class GlobalException { // 全局异常处理类
    // @ExceptionHandler(value = NameErrorException.class) // 可指定专为NameErrorException异常类型做处理的方法
    // @ExceptionHandler(value = AgeErrorException.class) // 可指定专为AgeErrorException异常类型做处理的方法
    @ExceptionHandler // 处理未匹配到指定异常的所有其他异常
    public ModelAndView doNameException(Exception exception) { // 参数为异常类,获取异常信息
        /*
            异常发生处理逻辑:
                1.记录异常信息:记录异常发生时间,哪个方法发生的,异常错误内容在数据库或日志文件中等
                2.发送通知,将异常信息通过邮件,短信,微信等发送给相关人员
                3.给用户友好提示
        */
        ModelAndView mv = new ModelAndView();
        String msg = "这里可以写特定的提示信息";
        mv.addObject("msg", msg); // 可以在页面获取显示信息提示数据
        mv.addObject("ex", exception); // 可以将异常类传过去,获取异常信息
        mv.setViewName("showExceptionMsg"); // 请求转发
        return mv;
    }
}
<!--注解驱动作用:1.响应ajax,对象转换为json 2.处理静态资源无法访问 3.配合全局异常处理类上@Controller的扫描-->
<mvc:annotation-driven/>
<!--  声明组件扫描器,扫描handler包下全局异常处理类所在的@ControllerAdvice注解  -->
<context:component-scan base-package="com.luis.handler"/>

SpringMVC

拦截器

拦截器接口Handlerinterceptor的预处理方法preHandle()实际开发中最常用,必须掌握!!!

拦截器:
1)拦截器是springmvc中的一种,需要实现HandlerInterceptor接口。
2)拦截器和过滤器类似,功能方向侧重点不同。 过滤器是用来过滤器请求参数,设置编码字符集等工作。
    拦截器是拦截用户的请求,做请求做判断处理的。
3)拦截器是全局的,可以对多个Controller做拦截。 
   一个项目中可以有0个或多个拦截器, 他们在一起拦截用户的请求。
	拦截器常用在:用户登录处理,权限检查, 记录日志。
拦截器的使用步骤:
 1.定义类实现HandlerInterceptor接口
 2.在springmvc配置文件中,声明拦截器, 让框架知道拦截器的存在。
拦截器的执行时间:
  1)在请求处理之前, 也就是controller类中的方法执行之前先被拦截。
  2)在控制器方法执行之后也会执行拦截器。
  3)在请求处理完成后也会执行拦截器。
拦截器:看做是多个Controller中公用的功能,集中到拦截器统一处理。使用的aop的思想
拦截器接口Handlerinterceptor的预处理方法preHandle()实际开发中最常用,必须掌握!!!
可以根据返回true或false决定控制器的方法是否可执行。
后处理方法postHandle()要想执行,preHandle()必须返回true.
后处理方法postHandle()最大的特点是能拿到处理器方法的返回值mv,拿到后可以对其数据和视图进行修改。
可以拿到返回值mv,添加数据,修改视图等操作。

单个拦截器

拦截器使用测试项目示例步骤参考:

SpringMVC

核心步骤参考:

//拦截器类,拦截用户的请求。
public class MyInterceptor implements HandlerInterceptor {
    /*
     * preHandle叫做预处理方法。
     *   重要:是整个项目的入口,门户。 当preHandle返回true 请求可以被处理。
     *        preHandle返回false,请求到此方法就截止。
     *
     * 参数:
     *  Object handler : 被拦截的控制器对象
     * 返回值boolean
     *
     *  特点:
     *   1.方法在控制器方法(MyController的doSome)之前先执行的。
     *     用户的请求首先到达此方法
     *
     *   2.在这个 方法中可以获取请求的信息, 验证请求是否符合要求。
     *     可以验证用户是否登录, 验证用户是否有权限访问某个连接地址(url)。
     *      如果验证失败,可以截断请求,请求不能被处理。
     *      如果验证成功,可以放行请求,此时控制器方法才能执行。
     */
    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response,
                             Object handler) throws Exception {
        System.out.println("拦截器的MyInterceptor的preHandle()");
        return true;
    }
    /*
       postHandle:后处理方法。
       参数:
        Object handler:被拦截的处理器对象MyController
        ModelAndView mv:处理器方法的返回值
        特点:
         1.在处理器方法之后执行的(MyController.doSome())
         2.能够获取到处理器方法的返回值ModelAndView,可以修改ModelAndView中的
         数据和视图,可以影响到最后的执行结果。
         3.主要是对原来的执行结果做二次修正,
         ModelAndView mv = MyController.doSome();
         postHandle(request,response,handler,mv);
     */
    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response,
                           Object handler,
                           ModelAndView modelAndView) throws Exception {
        System.out.println("拦截器的MyInterceptor的postHandle()");
    }
    /*
      afterCompletion:最后执行的方法
      参数
        Object handler:被拦截器的处理器对象
        Exception ex:程序中发生的异常
      特点:
       1.在请求处理完成后执行的。框架中规定是当你的视图处理完成后,对视图执行了forward。就认为请求处理完成。
       2.一般做资源回收工作的, 程序请求过程中创建了一些对象,在这里可以删除,把占用的内存回收。
     */
    @Override
    public void afterCompletion(HttpServletRequest request,
                                HttpServletResponse response,
                                Object handler,
                                Exception ex) throws Exception {
        System.out.println("拦截器的MyInterceptor的afterCompletion()");
    }
}

springmvc.xml 中声明:

<!--声明拦截器,拦截器可以有0个或者多个-->
<mvc:interceptors>
    <!--声明第一个拦截器-->
    <mvc:interceptor>
        <!--指定拦截的请求uri地址
                path:就是uri地址,可以使用通配符 **
                      ** : 表示任意的字符,文件或者多级目录和目录中的文件
                http://localhost:8080/myweb/user/listUser.do
                http://localhost:8080/myweb/student/addStudent.do
            -->
        <mvc:mapping path="/**"/>
        <!--声明拦截器对象-->
        <bean class="com.bjpowernode.handler.MyInterceptor" />
    </mvc:interceptor>
</mvc:interceptors>

多个拦截器(掌握)

当声明多个拦截器时,先声明的先执行。

多个拦截器可以负责不同的功能!层次分明,有条理。

只要有一个拦截器的preHandle返回false,请求不会被处理,controller方法不会执行!

	<!--声明拦截器,拦截器可以有0个或者多个
        在框架中保存多个拦截器是ArrayList,
        是按照声明的顺序放入到ArrayList中
    -->
<mvc:interceptors>
    <!--声明第一个拦截器-->
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <!--声明拦截器对象-->
        <bean class="com.bjpowernode.handler.MyInterceptor" />
    </mvc:interceptor>
    <!--声明第二个拦截器-->
    <mvc:interceptor>
        <mvc:mapping path="/**"/>
        <bean class="com.bjpowernode.handler.MyInterceptor2" />
    </mvc:interceptor>
</mvc:interceptors>
多个拦截器可以负责不同的功能!层次分明,有条理。
掌握:
1、多个拦截器的执行顺序
2、任何一个preHandle返回false,请求不会被处理,controller方法不会执行!
拦截器实现步骤:
1、创建拦截器类,实现HandlerInterceptor拦截器接口,对接口中三个方法进行重写;
(在spring 5.2.5版本中,HandlerInterceptor的三个方法为default默认的,用哪个就重写哪个即可!
preHandle() 在开发中最常用!它默认返回true)
2、在springmvc配置文件中声明拦截器
=================================================================================
多个拦截器:
第一个拦截器preHandle=true , 第二个拦截器preHandle=true 
111111-拦截器的MyInterceptor的preHandle()
22222-拦截器的MyInterceptor的preHandle()
=====执行MyController中的doSome方法=====
22222-拦截器的MyInterceptor的postHandle()
111111-拦截器的MyInterceptor的postHandle()
22222-拦截器的MyInterceptor的afterCompletion()
111111-拦截器的MyInterceptor的afterCompletion()
---------------------------------------------------
第一个拦截器preHandle=true , 第二个拦截器preHandle=false
111111-拦截器的MyInterceptor的preHandle()
22222-拦截器的MyInterceptor的preHandle()
111111-拦截器的MyInterceptor的afterCompletion()
----------------------------------------------------------
第一个拦截器preHandle=false , 第二个拦截器preHandle=true|false
111111-拦截器的MyInterceptor的preHandle()

SpringMVC

拦截器和过滤器区别

拦截器和过滤器的区别
1.过滤器是servlet中的对象,  拦截器是框架中的对象
2.过滤器实现Filter接口的对象, 拦截器是实现HandlerInterceptor
3.过滤器是用来设置request,response的参数,属性的,侧重对数据过滤的。
  拦截器是用来验证请求的,能截断请求。
4.过滤器是在拦截器之前先执行的。
5.过滤器是tomcat服务器创建的对象
  拦截器是springmvc容器中创建的对象
6.过滤器是一个执行时间点。
  拦截器有三个执行时间点
7.过滤器可以处理jsp,js,html等等
  拦截器是侧重拦截对Controller的请求。 如果你的请求不能被DispatcherServlet接收, 这个请求不会执行拦截器内容
8.拦截器拦截普通类方法执行,过滤器过滤servlet请求响应

拦截器模拟登陆验证

视频链接:https://www.bilibili.com/video/BV1sk4y167pD?p=70

测试项目步骤参考:

SpringMVC

SpringMVC 执行流程

https://www.bilibili.com/video/BV1sk4y167pD?p=74&spm_id_from=pageDriver

SpringMVC

要求了解:
springmvc内部请求处理流程
各个“器”的作用

SpringMVC

SpringMVC

SpringMVC

发表回复