SpringMVC

第1章 SpringMVC概述

概述

  1. springMVC:基于spring的框架,实际上就是spring的一个模块

    这个模块专门做web开发

    基于web开发的框架的底层都是基于servlet

    springMVC可以理解成是servlet的一个升级,就像把mybatis理解成JDBC的升级

    直接用servlet不方便,所以我们用框架来让web开发更加流畅简单

    servlet是在controller层,是tomcat服务器的核心组件之一,用于接受请求,调用业务层处理请求,返回请求

  2. springMVC框架是在servlet基础上加入一些功能,让我们做web开发更加方便。

  3. springMVC实际上就是一个spring,spring是一个容器,通过ioc管理对象,使用<bean>标签或者注解的形式管理对象

    springMVC能够创建对象,放入到容器中(springMVC的容器),springMVC容器中放的是控制器对象,用的是@Controller注解

  4. 在web开发中,spring容器applicationContext是通过监听器结合ServletContext来存储这个容器,使spring容器成为一个单例创建,并且可以通过servletContext全局作用域来获取到这个容器

  5. 我们要做的就是使用@Controller创建控制器对象,把对象放入到spring MVC容器中,把创建的对象作为控制器使用,这个控制器对象能够接收用户的请求,显示处理的结果,就当作一个servlet使用。

    使用@Controller这个注解创建的就是一个普通类的对象---作为控制器对象,不是Servlet,但是springmvc赋予了控制器对象一些额外的功能,让它可以像servlet一样工作。加了这个@Controller注解,就能让这个类完成servlet的功能。

    不是servlet的原因是servlet类都要继承于HttpServlet类。

  6. web开发的底层都是servlet

    springmvc中有一个对象是servlet:DispatcherServlet---中央调度器

    DispatcherServlet:负责接收用户的所有请求用户把请求给了DispatcherServlet,之后DispatcherServlet把请求转发给我们的Controller对象,最后是Controller对象处理请求。

    Controller对象是由springMVC容器帮我们创建的,用的就是IOC技术

  7. xxx.jsp-----DispatcherServlet(Servlet)----转发、分配给Controller对象(@Controller注解创建的对象(ioc中的注解注入))-----Controller类处理请求。

    比之前的过程多了一个转发, DispatcherServlet不处理请求, 而是转发请求给Controller对象

  8. image-20210919142829981

搭建springmvc项目步骤

  1. 新建maven-web工程

  2. 加入依赖

    • spring-webmvc依赖----代表springmvc框架,会间接地把spring的依赖都加入到项目中,因为springmvc是基于spring的

    • servlet依赖,底层还是基于servlet

        <dependency>
          <groupId>javax.servlet</groupId>
          <artifactId>javax.servlet-api</artifactId>
          <version>3.1.0</version>
        </dependency>
        <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-webmvc</artifactId>
          <version>5.2.5.RELEASE</version>
        </dependency>
    
  3. 重点:要在web.xml中注册springmvc的核心对象DispatcherServlet

    • DispatcherServlet是中央调度器,是一个servlet,它的父类是集成HttpServlet

    • DispatcherServlet也叫做前端控制器(front controller)

    • DispatcherServlet负责接收用户提交的请求,调用其他的控制器对象,并把请求的处理结果显示给用户

      接收用户请求的仍然是DispatcherServlet而不是Controller

      只要是springmvc项目,必须要有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
            DispatcherServlet有一个作用是创建springmvc容器对象的,容器对象创建好了,那么容器里的声明的对象也都会随着容器对象的创建而创建好
            所以DispatcherServlet要随着tomcat服务器的启动而创建
            DispatcherServlet的实例化对象,需要在tomcat服务器启动后创建
            为什么要创建DispatcherServlet实例化对象呢?
            因为DispatcherServlet在创建过程中,会同时创建springmvc容器对象,会读取springmvc的配置文件,把这个配置文件中的对象都创建好,当用户发起请求时,就可以直接使用对象了。
    		
    
    		servlet初始化,会执行init方法。DispathcerServlet在init()中,会执行如下创建容器对象:
     		WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
    		springmvc容器对象创建好之后,会和之前一样,放在ServletContext全局作用域中。
            springmvc创建容器对象时,读取的配置文件默认是/WEB-INF/<servlet-name>-servlet.xml
        -->
        <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>
            <!--
            希望在tomcat启动后,创建DispatcherServlet对象
            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等等
                http://localhost:8080/myweb/some.do
                
                2. 使用 “/”
            -->
            <url-pattern>*.do</url-pattern>
        </servlet-mapping>
        
    </web-app>
    

    对象创建流程如下:

    tomcat服务器启动

    DispathcherServlet对象创建(这类似于监听器)

    同时,DispathcherServlet对象创建过程的初始化过程中,springmvc容器对象创建并放在ServletContext中

    同时,写在springmvc配置文件中的对象会创建。

    总结:

    tomcat服务器启动--DispathcherServlet对象创建---springmvc容器对象创建--springmvc容器中的对象会创建

  4. 创建一个发起请求的页面index.jsp

  5. 创建控制器类:

    • 在类的上面加入@Controller注解,创建对象,并放入到springmvc容器中
    • 在类中的方法上面加入@RequestMapping注解
    /**
     * 创建控制器对象的,这个对象是放在springmvc容器中。
     * 位置:在类的上面
     * 和之前spring讲的@Service @Repository是一样的,是创建的对象的,即通过注解方式实现依赖注入,@Autowired @Resource 是给引用类型变量赋值
     */
    @Controller
    public class MyController {
        /*
         * 处理用户提交的请求,springmvc中是使用方法来处理的
         * 方法是自定义的,可以有多种返回值,多种参数
         */
    
        /**
         * 准备使用doSome来处理some.do请求
         * 我们需要@RequsetMapping,请求映射,作用是把一个请求地址和一个方法绑定在一起。位置是在方法的上面
         * 属性:1.value:是一个string,是表示请求的url地址,value值是唯一的。在使用时,推荐地址以"/"开头("/"表示是跟地址,"/"映射到工程路径)
         *  说明:
         *  使用@RequestMapping修饰的方法叫做处理器方法或者控制器方法,一个请求地址和一个方法绑定在一起
         *  使用@RequestMapping修饰的方法是可以处理请求的,类似于Servlet中的doGet、doPost方法
         *  (但是仍然要记住,DispatcherServlet才是接受请求,是中央调度器,Controller类是处理请求。)
         *
         * @return ModelAndView
         *  Model:数据,请求处理完成后要显示给用户的数据
         *  View:视图,比如jsp等等
         *  ModelAndView:表示本次处理请求的结果,包含数据和视图两部分
         */
        @RequestMapping(value = "/some.do")
        public ModelAndView doSome() {
            //处理请求--相当于service层即业务层调用处理完成
            ModelAndView modelAndView = new ModelAndView();
            // 框架在请求的最后把数据(请求处理的结果)放入到request作用域,这个不需要我们做,我们只需要把请求处理的结果添加到modelAndView
            modelAndView.addObject("msg", "欢迎使用springmvc做web开发");
            modelAndView.addObject("fun", "执行的是doSome()方法");
    
            // 指定视图,指定视图的完整路径
            // 框架对视图执行的forward操作,request.getRequestDispatcher("/show.jsp").forward(request, response) ,框架做的,不需要我们写
            modelAndView.setViewName("/show.jsp");
            return modelAndView;
        }
    }
    
  6. 创建一个作为结果的jsp,显示请求的处理结果

  7. 创建springmvc的配置文件(和spring配置文件一样,要用注解方式进行依赖注入,必须要有一个组件扫描器)

    • 声明组件扫描器,指定@Controller注解所在的包名

    • 声明视图解析器,帮助处理视图的。

          <!-- 声明视图解析器,帮助开发人员设置视图文件的路径-->
          <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
              <property name="prefix" value="/WEB-INF/view/"></property>
              <property name="suffix" value=".jsp"></property>
          </bean>
      

注意:

  1. 声明:注册springmvc的核心对象DispatcherServlet
    DispatcherServlet有一个作用是创建springmvc容器对象的,容器对象创建好了,那么容器里的声明的对象也都会随着容器对象的创建而创建好
    所以DispatcherServlet要随着tomcat服务器的启动而创建
    DispatcherServlet的实例化对象,需要在tomcat服务器启动后创建
    为什么要创建DispatcherServlet实例化对象呢?
    因为DispatcherServlet在创建过程中,会同时创建springmvc容器对象,会读取springmvc的配置文件,把这个配置文件中的对象都创建好,
    当用户发起请求时,就可以直接使用对象了。
    
    就像在spring中使用监听器来进行spring容器applicationContext的创建,因为要使spring容器只创建一次,并且在创建容器对象的过程中,会读取spring配置文件,配置文件读取完毕,这个配置文件里声明的所有对象也都创建好了,applicationContex存放在ServletContext中,能够拿到applicationContext,那么就能通过applicationContext拿到所有在spring配置文件里声明的对象。整个过程spring容器只创建一次,目的就是为了让spring容器对象是一个单例创建的模式。
    
    而在整个地方,没有用到监听器,DispatcherServlet的作用就和监听器的作用差不多,因为,DispatcherServlet就是随着Tomcat服务器的启动而创建好,DispatcherServlet在创建过程中,会同时创建springmvc容器对象,会读取springmvc的配置文件,把这个配置文件中的对象都创建好。
    
    整个思想和之前的监听器都是有联系的,差不多的。
    
  2. image-20210919150621163

  3. 控制器类能处理请求,但是控制器类只是相当于Servlet,但是控制器类不等于Servlet

  4. DispatcherServlet也叫做前端控制器,自己写的控制器类也叫做后端控制器

springmvc请求的处理流程

  1. 用户发起请求。这个请求会发送给tomcat服务器

  2. web.xml----url-pattern知道 *.do 的请求给DispatcherServlet

    DispatcherServlet对象是随着Tomcat启动而创建好的

    DispatcherServlet有一个作用就是创建springmvc容器对象,创建springmvc容器对象(容器对象创建好,那么配置文件就说明读取完毕,配置文件读取完毕,配置文件里声明的所有对象就创建好了。),就要读取springmvc配置文件,就能通过里面声明的组件扫描器,去对应的包下进行扫描。

  3. 根据springmvc.xml配置知道some.do---doSome()

  4. DispatcherServlet把some.do请求转发给MyController的doSome()方法

  5. 框架执行doSome(),把得到ModelAndView进行处理,转发到show.jsp

image-20210919164639662

DispatcherServlet的作用

  1. 负责创建springmvc容器对象,读取springmvc的xml配置文件,创建文件中的controller对象。

    创建容器对象,会读取配置文件,配置文件读取完毕,配置文件中声明的所有对象就创建好了。(如果是采用基于xml文件进行依赖注入的方式的话),通过注解也是一样,因为springmvc配置文件里会声明组件扫描器

  2. 负责接收用户的请求,分派给自定义的controller对象

分析:

  1. image-20210919170054085

  2. image-20210919171204648

    image-20220104183916856

    doDispatch方法的里面,最终会调用我们的MyController的方法

  3. 创建springmvc容器对象,仍然是只创建一次,是单例的,放在ServletContext(全局作用域)中,通过ServletContext拿到容器对象,那么就可以拿到配置文件中声明的类的对象。

    ServletContext是随着Tomcat的启动而创建,随着Tomcat的结束而销毁

  4. spring容器和springmvc容器都是放在ServletContext中

第2章 SpringMVC注解式开发

@RequestMapping

  1. 把所有地址中,公用的部分放到类上面的@RequestMapping注解定义

    image-20210919182247070

  2. @RequestMapping注解的属性

    image-20210919182437190

    若不指定请求方式,那么就没有限制。

接收用户提交的参数

概述

  1. image-20210919190234487

    上面的三种代表请求、应答和会话,spring框架会自动给他们赋值。

    image-20210919190342317

  2. 接收请求参数,使用的是控制器方法的形参

    • HttpServletRequest
    • HttpServletResponse
    • HttpSession
    • 用户提交的数据
      • 逐个参数接收
      • 对象接收

逐个接收请求参数

要求:控制器方法的形参名和请求中参数名必须一致,同名的请求参数赋值给同名的形参

框架接收请求参数:

  1. 仍然是使用request对象接收请求参数,只不过是框架来做了,不用我们自己写了

    image-20210919191148781

  2. springmvc框架通过中央调度器DispatcherServlet调用MyController的doSome()方法,调用方法时,按参数名称对应,把接收的参数赋值给形参

    框架会提供类型转换的功能,能把String转为int、long、float、double等类型

  3. post请求中文需要解决乱码问题,get请求方式中文没有乱码(提交表单也可以用get方法,只是不安全,请求参数会显示在地址栏上。)

    需要使用过滤器解决post请求中文的乱码问题

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

        <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>
    
  4. 若请求中参数名和控制器方法的形参名不一样

    image-20210919200724445

    image-20210920140127871

用一个对象来接收请求参数

  1. 用对象来接收请求参数,属性来保存参数值

    要求属性名和请求中参数名一样!

  2. 控制器方法形参是Java对象,那么这个对象的属性名和请求中参数名是一样的,

    框架会创建形参的Java对象,给属性赋值(ioc的set注入),请求中的参数是name,那么对象的属性名也是name,框架会调用setName

    在ioc中,如果是采用注解的方式给属性赋值,就不需要属性的set方法,但不管是注解方式还是XML方式实现依赖注入,底层都是通过反射的机制,注解方式是不会用到set方法的,其中只有XML方式的set注入用到了属性的set方法

  3. 用对象来接收请求参数,就不需要用@RequestParam

  4. 这种方式,controller方法的参数就相当于是RequestBody

控制器方法的返回值

返回ModelAndView

image-20210919202649206

返回String

  • 返回的String代表逻辑视图名称,需要配置视图解析器

    控制器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址

    如果只是需要跳转页面,不需要返回数据,那么返回值类型用String是最方便的

    返回的字符串可以代表视图,可以是逻辑名称,也可以是完整的名称。

    image-20210919203342232

    image-20210919203954551

  • 返回的String代表完整视图名称,不能配置视图解析器

返回void

  1. void返回值既不能表示数据,也不能表示视图-----这种返回值用于服务器处理ajax请求。

    虽然void返回值既不能表示数据,也不能表示视图,但是可以通过HttpServletResponse的输出来完成响应。

    若控制器对请求处理后,无需跳转到其他任何资源(资源就是指的工程路径后面的就叫资源路径,代表的就是资源),此时可以让控制器方法返回void

    例如,对于AJAX的异步请求的响应

    注意:不管是ajax请求还是表单请求,逐个接收请求参数和用一个对象来接收请求参数是完全一样的语法。

    返回值类型是void的时候,完全可以通过HttpServletResponse的输出来完成AJAX请求的异步响应

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

    ajax请求常用jQuery来发起请求

    image-20220105155912701

    此PrintWriter对象输出的内容json,就会被传递到前端jsp页面的ajax请求的success

    image-20220105160320190

  2. 完成ajax请求响应的步骤

    • controller调用service处理请求
    • 将请求的返回结果转为json格式的字符串
    • 将json格式的字符串通过response得到的PrintWriter输出
    • 输出的json格式字符串被jQuery转换为json对象

    image-20220105161557655

返回对象Object----需加@ResponseBody

  1. 概述

    返回对象代表的是数据,不是视图,而ajax请求正好要的就是数据,也跟视图无关,所以返回对象主要用来响应ajax请求,返回对象代表的是json数据,代表的是ajax请求的处理结果

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

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

    @Controller + @ResponseBody = @RestController

    返回的是对象,对象有属性,属性就是数据,所以返回Object表示数据(这里说的数据都是请求参数经过处理后的结果数据!!!结果数据才有返回的意义,返回的就是请求的处理结果。),和视图无关

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

  2. 实现步骤:

    • 加入处理json的工具库的依赖,springmvc默认使用的jackson

    • 在springmvc配置文件中加入<mvc:annotation-driven>注解驱动

      代替我们自己完成 json = om.writeValueAsString(student)

      作用:把控制器方法返回的对象转为json格式的字符串

    • 在控制器方法的上面加入@ResponseBody

      作用:将json格式的字符串,通过HttpServletResponse输出给浏览器

      位置:方法的上面,和其他的注解没有顺序的先后关系

      @ResponseBody注解相当于以下几行代码的作用

      image-20220105164452466

  3. 在springmvc配置文件中加入<mvc:annotation-driven>注解驱动的原理

    springmvc控制器方法返回Object,可以转为json输出到浏览器,响应ajax的内部原理

    • <mvc:annotation-driven>注解驱动

      实现的功能是完成Java对象到json或xml或text等数据格式的转换

      HttpMessageConvert接口:消息转换器

      <mvc:annotation-driven>注解驱动加入到springmvc配置文件后,会自动创建HttpMessageConvert接口的7个实现类对象

      功能:定义了Java对象转为json、xml等数据格式的方法,这个接口有很多的实现类

      这些实现类完成Java对象到json或Java对象到xml等数据格式的转换

      • canWrite 判断能否转换成指定格式,默认转换成json

        image-20210920163153323

        image-20210920163451874

      • write 将Java对象转换成指定格式

        image-20210920163503962

  4. HttpMessageConvert接口的7个实现类

    image-20210920164452868

    • MappingJackson2HttpMessageConverter

      使用jackson工具库中的ObjectMapper实现Java对象转换为json格式字符串,转换后的JSON格式的数据放到响应体中

    • StringHttpMessageConverter

      负责读取字符串格式的数据和写出字符串格式的数据

  5. 控制器方法返回的是对象这种方式,框架的处理流程

    • 框架会把返回Student类型,调用框架中的ArrayList<HttpMessageConverter>集合中的每个类canWrite方法,来检查哪个HttpMessageConverter接口的实现类能处理Student类型的数据

    • 框架会调用第一步找出来的实现类的write(),MappingJackson2HttpMessageConverter的write()方法

      把student对象转为json,调用Jackson的ObjectMapper实现转为json格式的字符串

    • 框架会调用@ResponseBody把第二步的结果数据输出到浏览器,ajax请求处理完成

  6. DispatcherServlet怎么分配请求给Controller的

    • service方法
    • doService方法
    • doDispatch方法----会找到Controller类里的具体的方法,把请求交给那个方法执行,@RequestMapping让一个方法对应于一个地址
  7. 在项目中可能返回的不是一个Object,而是多个对象的集合List<Object>

    那么通过加入jackson依赖、添加注解驱动、添加@ResponseBody注解,也能得到json格式的字符串的数组

  8. 返回字符串对象

    此时代表的就不是视图,代表的是数据

    怎么区分String返回的字符串代表的视图还是数据,看有没有注解@ResponseBody

    如果有@ResponseBody注解,那么代表的是数据---字符串文本数据

    如果没有@ResponseBody注解,代表的是视图

    注意:dataType要进行修改。

    image-20220105180230245

    image-20210920175243206

  9. 控制器方法返回的是字符串对象这种方式,框架的处理流程

    • 框架会把返回String类型,调用框架中的ArrayList<HttpMessageConverter>集合中的每个类的canWrite方法,来检查哪个HttpMessageConverter接口的实现类能处理String类型的数据

    • 框架会调用第一步找出来的实现类的write(),StringHttpMessageConverter的write()方法

      把String对象按照指定的编码来处理,即text/plain;charset=ISO-8859-1

    • 框架会调用@ResponseBody把第二步的结果数据输出到浏览器,ajax请求处理完成

解读<url-pattern/>

  1. image-20210920181532578

    tomcat本身能处理静态资源的访问,像html、图片、js文件都是静态资源

    tomcat的web.xml文件中有一个servlet,名称是default,在服务器启动时创建的!!

    image-20210920181757832

    image-20210920183131910

    这个地方的/表示静态资源和未映射的请求都给这个defaultServlet处理。

  2. tomcat的default servlet的作用

    image-20210920181853129

    • 处理静态资源

    • 处理未映射到其他servlet的请求

      如果没有用springmvc来做web开发的话,在web.xml文件中会写很多servlet的类的声明和mapping(要么是通过web.xml来写,要么是通过注解,**这些servlet就和资源里比如jsp里的href对应上,或者表单的action对应上。不管是没使用springmvc时候的servlet类的地址还是使用springmvc的@RequestMapping的地址,都一定要和资源文件里的请求地址对应上!!!**意思就是点了超链接,或者提交了表单,就交给对应的servlet执行。servlet的作用就是接受请求、处理请求、返回请求),用了springmvc,只需要在web.xml里写中央调度器的声明和mapping,底层仍然是servlet。中央调度器DispatcherServlet会通过service、doService、doDispatch方法最终把对应的链接交给Controller的某个方法去执行!!

      没有使用springmvc时,web.xml里的url-pattern(就是servlet类的地址,也可以不写在web.xml里,通过注解也可以)要和资源文件的请求地址对应上

      使用springmvc时,@RequestMapping的地址要和资源文件的请求地址对应上

  3. 当我们的项目中的web.xml文件中的url-pattern用了/,那么它会替代tomcat的default servlet,对静态资源和未映射到servlet的请求进行处理

    导致所有的静态资源都给DispatcherServlet处理,默认情况下,DispatcherServlet没有处理静态资源的能力,没有控制器对象能处理静态资源的访问,所以静态资源都是404

  4. 那么既要在<url-pattern>用/,又想处理静态资源,有两种方式。

    • 使用<mvc:default-servlet-handler/>

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

      Tomcat中,专门用于处理静态资源访问的Servlet名叫DefaultServlet

      image-20210920222737796

      <mvc:default-servlet-handler/>这个配置和之前的@RequestMapping有冲突,需要加入注解驱动来解决冲突:<mvc:annotation-driven>

    • 使用<mvc:resources/>

      **spring定义了专门用于处理静态资源访问请求的处理器ResourceHttpRequestHandler,**并且添加了<mvc:resources/>标签,专门用于解决静态资源无法访问的问题,需要在springmvc配置文件中添加如下形式的配置

      image-20210920225628379

      和第一种方式区别是,不依赖于tomcat服务器的默认的DefaultServlet来完成静态资源的处理,也就是不需要DefaultServletHttpRequestHandler来筛查url即把静态资源仍然分配给tomcat的DefaultServlet来进行处理,这种方式就由我们自己的处理器来进行处理

      image-20210920230143754

      image-20220105200647952

      <mvc:resources/>这个配置和之前的@RequestMapping有冲突,需要加入注解驱动来解决冲突:<mvc:annotation-driven>

  5. <mvc:annotation-driven>作用

    1. 将返回对象转换为json、xml、txt等格式,用于响应ajax请求。
    2.解决静态资源访问,静态资源访问会添加另外的配置,而另外的配置会和@RequestMapping冲突,所以必须加上这个注解。(冲突是动态请求也会交给处理静态资源的servlet处理,加上之后,保证静态资源给对应的处理器处理,比如DefaultServlet或者spring定义的ResourceHttpRequestHandler,保证动态资源扔给之前的DispatcherServlet处理)
    
  6. 使用一个配置语句,指定多种静态资源的访问:即静态资源统一放在一个目录下即static。

    image-20220105201211270

访问地址

  1. html文件或jsp文件中的访问地址或请求地址

    • 前面不加斜杠,在浏览器地址栏中映射到ip地址:端口号/项目名/
    • 前面加斜杠,在浏览器地址栏中映射到ip地址:端口号/
  2. 所以最好保证工程路径永远是一致的,即使用以下两种方式

    • 开头不加斜杠,配合base标签

      image-20210921001040805

    • 开头加斜杠,但是在前面加EL表达式

      image-20210920235208614

    两种方式在开头都没有单纯地加斜杠,但是却保证了访问的参考地址!!

    image-20210921000533055

第3章 SSM整合开发

概述

  1. SpringMVC: 视图层、界面层,负责接受请求,显示处理结果的。

    spring:业务层,管理service、dao、工具类对象的

    mybatis:持久层,访问数据库的

  2. 用户发起请求--springmvc接收请求----请求给spring的service实现类对象进行处理-----service对象调用dao,通过mybatis访问数据库,处理数据

  3. 整合中有容器

    • 第一个容器SpringMVC容器,管理Controller控制器对象
    • 第二个容器是spring容器,管理service、dao、工具类对象的

    我们要做的是把使用的对象交给合适的容器创建、管理,把Controller还有web开发的相关对象交给springmvc容器,这些web用的对象写在springmvc配置文件中

    把service、dao对象定义在spring的配置文件中,让spring管理这些对象

    这两个容器对象的职责不同,两个容器是独立的

    那么两个容器中的对象怎么互相访问到呢?

    比如Controller对象怎么访问service对象呢?一个容器中的对象怎么访问到另一个容器中的对象呢?

    如果两个容器没有任何的关联肯定不行,所以这两个容器存在关系

  4. springmvc容器和spring容器是有关系的,他们的关系已经确定好了,不需要人为去做

    springmvc容器是spring容器的子容器,类似Java中的继承

    子可以访问父的内容。

    在子容器中的Controller可以访问父容器中的Service对象,就可以实现controller使用service对象。

    这个整合开发,已经把这个关系确定好了,我们只需要确定对应的对象创建要放在对应的容器中。

整合开发步骤

  1. 新建maven项目

  2. 加入依赖

    springmvc、spring、mybatis三个框架的依赖,jackson依赖,mysql驱动,druid连接池、jsp、servlet依赖

  3. 写web.xml

    • 注册DispatcherServlet中央调度器,目的是

      • 创建springmvc容器对象,于是就创建好了springmvc容器中的所有对象
      • 用于接收用户请求,把请求分配给Controller去执行
    • 注册spring的监听器:ContextLoaderListener,目的:

      • 创建spring的容器对象,通过监听器结合ServletContext这个作用域,能够实现spring容器对象的单例创建

        监听器的作用:因为ServletContext是随着tomcat的启动而创建,随着tomcat的结束而销毁,监听器可以监听到全局作用域ServletContext的创建,监听到之后,那么就创建spring容器对象,并把spring容器对象放进ServletContext全局作用域

    • 注册字符集过滤器:解决post请求乱码的问题

  4. 创建包,Controller包,service、dao、实体类包名创建好

  5. 写springmvc、spring、mybatis的配置文件

    • springmvc配置文件
    • spring配置文件
    • mybatis主配置文件
    • 数据库的属性配置文件
  6. 写代码,dao接口(不用写dao实现类),service接口和service实现类(类是要写的,对象是交给容器创建的。)controller,实体类

  7. 写jsp页面

第4章 SpringMVC核心技术

请求重定向和转发

  1. 在javaweb中请求转发和请求重定向的区别

    • 请求转发只有一次请求,请求重定向是两次请求
    • 请求重定向的地址栏的url会发生变化,而请求转发不会
    • 请求重定向不会共享Request域的数据,而请求转发是共享request域的数据,因为tomcat,会把每一次请求都封装到request域中,请求转发能够共享,正是因为只有一次请求,而请求重定向不可以共享,是因为是两次请求
    • 请求重定向不能访问WEB-INF目录下的资源,而请求转发可以!
    • 请求重定向可以访问外部资源,而请求转发不可以

    image-20210921154408642

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

    forward:表示转发,实现request.getRequestDispatcher("xx.jsp").forward(request,response);

    redirect:表示重定向,实现response.sendRedirect("xxx.jsp")

    他们有一个共同的特点:就是不和视图解析器一起工作,就当项目中没有视图解析器一样

  3. forward:

    image-20220105213801557

    image-20210921155010180

    setViewName()方法的形参这样写,这种方式叫显式转发

  4. redirect

    image-20210921155520211

    • 框架会把Model中的简单类型的数据,转为String使用,作为hello.jsp的get请求参数使用

      目的是在doRedirect.do和hello.jsp两次请求之间传递数据

      image-20210921160111005

    • 在目标hello.jsp页面可以使用参数集合对象${param}获取请求参数值

      请求重定向这种方式,直接通过EL表达式${参数名}这种方式取不到,因为是两次请求,是不同的request域。但是在重定向后的URL地址上有请求的参数,那么可以通过以下方式取

      image-20210921160602922

异常处理

概述

  1. springmvc框架处理异常的常用方式---统一全局异常处理---aop思想

    这样的话可以不写try-catch代码,让try-catch代码和我们的业务分离,解耦合,让方法里面只保留业务逻辑代码,让异常放到一个统一的地方处理,用的思想就是AOP,业务功能和非业务功能的解耦合!!

    叫做统一全局异常处理方案

    把controller的所有异常处理都集中到一个地方

  2. 使用两个注解

    • @ExceptionHandler
    • @ControllerAdvice

异常处理步骤

  1. 新建maven web项目

  2. 加入依赖

  3. 新建一个自定义异常类,MyUserException,再定义它的子类NameException,AgeException

  4. 在controller抛出NameException,AgeException,抛出之后,就可以处理异常了

  5. 创建一个普通类,作为全局异常处理类

    • 在类的上面加入@ControllerAdvice
    • 在类中定义方法,方法上面加入@ExceptionHandler
  6. 创建处理异常的视图页面

  7. 创建springmvc的配置文件

    • 组件扫描器,扫描@Controller注解
    • 组件扫描器,扫描@ControllerAdvice注解所在的包
    • 声明注解驱动!!---两个作用
      • 返回对象Object的时候,将对象进行格式的转换,添加了这个注解驱动就会创建一个接口,接口有7个实现类,<mvc:annotation-driven>注解驱动加入到springmvc配置文件后,会自动创建HttpMessageConverter接口的7个实现类对象,并且会将Java对象转换为json、xml、txt等格式的字符串
      • 解决静态资源访问问题的两种方式会和@RequestMapping产生冲突,需要这个注解驱动来解决
  8. 如图:

    image-20210921165533405

    image-20210921165714849

    方法体里,对异常的处理逻辑:

    1. 需要把异常记录下来,记录到数据库、日志文件

      记录异常发生的事件,哪个方法发生的,异常错误内容。

    2. 发送通知,把异常的信息通过邮件、短信或微信发送给相关人员

    3. 给用户友好的提示

拦截器

概述

  1. 拦截器必须实现接口HandlerInterceptor

  2. 过滤器是过滤请求参数的,拦截器是拦截请求的,对请求做预先的判断处理工作

  3. 拦截器是springmvc中的一种对象,需要实现HandlerInterceptor接口

    拦截器和过滤器类似,功能方向侧重点不同,

    • 过滤器是用来过滤请求参数,设置编码字符集等工作
    • 拦截器是拦截用户的请求,做请求的判断处理的
  4. 拦截器的特点:

    是全局的,可以对多个Controller做拦截

    一个项目中可以有0个或多个拦截器,他们在一起拦截用户的请求

    拦截器常用在用户的登陆处理,权限的检查,记录日志方面

  5. 我们需要定义类实现HandlerInterceptor接口

    需要在springmvc配置文件中声明拦截器,让springmvc框架知道拦截器的存在

  6. 拦截器的执行时间:

    • 在请求处理之前,也就是Controller类中的方法执行之前先被拦截(首次执行时间)
    • 在控制器方法执行之后,也会执行拦截器
    • 请求处理完成后也会执行拦截器

拦截器的使用步骤

  1. 新建maven web项目

  2. 加入依赖

  3. 创建Controller类

  4. 创建一个普通类,作为拦截器使用

    • 实现HandlerInterceptor接口
    • 实现接口中的三个方法
  5. 创建show.jsp结果页面

  6. 创建springmvc配置文件

    • 组件扫描器,扫描@Controller注解

    • 声明拦截器,并指定拦截的请求uri地址

      image-20210921174059063

HandlerInterceptor接口的三个方法

  1. preHandle(HttpServletRequest request,HttpServletResponse response,Object handler)

    预处理方法

    这个方法很重要,是整个项目的入口、门户

    参数:HttpServletRequest request,HttpServletResponse response,Object handler

    • Object handler: 被拦截的控制器对象,也就是Controller类的对象

    返回值:boolean

    • true:表示请求通过了拦截器的验证,可以执行控制器类的方法
    • false:请求没有通过拦截器的验证,请求到达拦截器就截止了,请求没有被处理

    特点:

    • 方法在控制器类的方法执行之前先执行的

      用户的请求首先到达此方法

    • 在这个方法中可以获取请求的信息,验证请求是否符合要求

      可以验证用户是否登录,验证用户是否有权限去访问某个链接地址(url)

      如果验证失败,我们可以截断请求,这个请求不能被处理也就是不能到达控制器类的方法

      如果验证成功,可以放行请求,此时控制器方法才能执行

    示例:

    image-20220106173346685

  2. postHandle(HttpServletRequest request,HttpServletResponse response,Object handler, ModelAndView modelAndView)

    后处理方法

    参数:

    • Object handler: 被拦截的控制器对象,也就是Controller类的对象
    • ModelAndView modelAndView:是控制器类的方法的返回值

    特点:

    • 在处理器类的方法之后执行的。
    • 能够获取到处理器方法的返回值ModelAndView,可以修改modelAndView中的数据和视图,可以影响到最后的执行结果
    • 主要对原来的执行结果做二次修正。
  3. afterComletion(HttpServletRequest request,HttpServletResponse response,Object handler, Exception ex)

    最后执行的方法,视图处理完成后,即请求处理完成后,给出的响应

    参数:

    • Object handler: 被拦截的控制器对象,也就是Controller类的对象
    • Exception ex:程序中发生的异常

    特点:

    • 在请求处理完成后执行的,框架中规定是当视图处理完成后,对视图执行了forward,就认为请求处理完成
    • 一般做资源回收工作的。比如程序请求过程中,创建了一些对象,在这里可以删除,把占用的内存回收
  4. 拦截器看作是多个Controller中共用的功能(比如用户是否登录),集中到拦截器统一处理,仍然是aop的思想!!

  5. 一个拦截器的三个方法的执行顺序

    image-20210921183533644

多个拦截器的三个方法执行顺序

  1. 在框架中保存多个拦截器是ArrayList

    按照声明的先后顺序放入到ArrayList,先声明的先执行,后声明的后执行。

    两个拦截器,

    • 第一个拦截器的preHandle方法返回true,第二个拦截器的preHandle也返回true,执行顺序如下

      image-20210921184133610

      image-20210921184311653

    • 第一个拦截器preHandle=true,第二个拦截器preHandle=false

      image-20210921185032302

    • 第一个拦截器preHandle=false,第二个拦截器preHandle=true或false对结果没有任何影响

      image-20210921185147330

  2. image-20210921184643169

  3. 不管有几个拦截器,只要其中一个拦截器的preHandle方法返回了false,控制器方法一定不会执行!

  4. 我们可以有多个拦截器,每个拦截器负责不同的功能,比如第一个拦截器验证登录,第二个验证功能的用户,第三个拦截器记录用户的访问日志。

拦截器和过滤器的区别

  1. 过滤器是servlet中的对象(是servlet的三大组件之一),拦截器是框架(springmvc)中的对象

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

  3. 过滤器是用来设置request,response的参数,属性的,侧重对数据过滤的(过滤器可以解决post请求中文乱码的问题

    拦截器是用来验证请求的,能截断请求

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

  5. 过滤器是tomcat服务器创建的对象

    拦截器是springmvc容器中创建的对象

  6. 过滤器是一个执行时间点

    拦截器有三个执行时间点

  7. 过滤器可以处理jsp、js、html等等

    拦截器是侧重拦截对Controller的对象,如果请求不能被DispatcherServlet接收,这个请求不会执行拦截器内容

springmvc内部请求的处理流程

  1. springmvc内部请求的处理流程也就是springmvc接收请求,到处理完成的过程

  2. 步骤

    • 用户发起请求some.do

    • DispatcherServlet接收请求some.do,把请求转发给处理器映射器

      处理器映射器:springmvc框架中的一种对象,框架把实现了HandlerMapping接口的类叫做处理器映射器(多个),处理器映射器就是实现了HandlerMapping接口的类,每一个类都有它对应的能够处理的请求

      处理器映射器作用:根据请求,从springmvc容器对象中获取处理器对象

      类似于spring框架中,这个功能:

      ApplicationContext ctx = new ClassPathXmlApplication("bean.xml");
      StudentService studentService = (StudentService) ctx.getBean("service");
      
    • 框架把找到的处理器对象放到一个叫做处理器执行链的类保存

      处理器执行链:HandlerExecutionChain

      HandlerExecutionChain:类中保存着

      • 处理器对象
      • 项目中所有的拦截器(用的是List集合)
    • DispatcherServlet把HandlerExecutionChain中的处理器对象,交给处理器适配器对象(多个)

      处理器适配器:springmvc框架中的对象,需要实现HandlerAdapter接口

      处理器适配器作用:执行处理器方法即控制器类中的方法,得到返回值

    • DispatcherServlet把上一步获取的ModelAndView(返回值)交给了视图解析器对象

      视图解析器:springmvc中的对象,需要实现ViewResolver接口(可以有多个)

      视图解析器作用:组成视图完整路径,使用前缀、后缀,并创建View对象

      image-20220106192731365

      比如上图这个"show"在框架中就会被转成View对象。

      View是一个接口,表示视图的,在框架中jsp、html不是string表示,而是使用View和它的实现类表示视图

      InternalResourceView:视图类,表示jsp文件,视图解析器会创建InternalResourceView对象

      这个对象里面,有一个属性url=xxx

    • DispatcherServlet把上一步创建的View接口的实现类对象获取到,调用View类自己的方法,把Model数据放入到Request作用域,执行对象视图的forward,请求结束

  3. 总结:

    • 处理器映射器--得到Controller对象
    • 处理器执行链--得到Controller对象放入其中
    • 处理器适配器--执行Controller对象的方法--service()、doService()、doDispatch()、处理器方法
    • 视图解析器--DispatcherServlet将返回的ModelAndView对象交给视图解析器,创建view对象
    • view对象--DispatcherServlet调用View类自己的方法,把Model数据放入到Request作用域,执行对象视图的forward,请求结束
  4. 处理器映射器:获取处理器对象

    处理器适配器:执行控制器类的方法

  5. image-20210921202019817

Last Updated:
Contributors: 陈杨