SpringMvc核心相关知识

springMvc框架是servlet的升级

springmvc是什么?

SpringMVC:是基于spring的一个框架, 实际上就是spring的一个模块, 专门是做web开发的。理解是servlet的一个升级,web开发底层是servlet , 框架是在servlet基础上面加入一些功能,让你做web开发方便。

SpringMVC就是一个Spring。 Spring是容器,ioc能够管理对象,使用, @Component, @Repository, @Service, @Controller
SpringMVC能够创建对象, 放入到容器中(SpringMVC容器), springmvc容器中放的是控制器对象,

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

使用@Controller注解创建的是一个普通类的对象, 不是Servlet。 springmvc赋予了控制器对象一些额外的功能。只是和servlet有着相同的功能。它的升级版本。

web开发底层是servlet, springmvc中有一个对象是Servlet : DispatherServlet(中央调度器)
DispatherServlet: 负责接收用户的所有请求, 用户把请求给了DispatherServlet, 之后DispatherServlet把请求转发给我们的Controller对象, 最后是Controller对象处理请求。

第一个注解的 SpringMVC 程序

所谓 SpringMVC 的注解式开发是指,在代码中通过对类与方法的注解,便可完成处理器在 springmvc 容器的注册。注解式开发是重点。

1.在创建好 web 项目后,加入 Servlet 依赖,SpringMVC 依赖

spring-webmvc依赖,间接把spring的依赖都加入到项目
还要加入 jsp,servlet依赖

<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>

2.注册中央调度器

重点: 在web.xml中注册springmvc框架的核心对象DispatcherServlet
1)DispatcherServlet叫做中央调度器, 是一个servlet, 它的父类是继承HttpServlet
2)DispatcherServlet页叫做前端控制器(front controller)DispatcherServlet在他的创建过程中, 会同时创建springmvc容器对象,读取springmvc的配置文件,把这个配置文件中的对象都创建好, 当用户发起请求时,就可以直接使用对象了
3)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>

        <!--自定义springmvc读取的配置文件的位置-->
        <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.创建一个发起请求的页面 index.jsp

4.创建控制器(处理器)类

1)在类的上面加入@Controller注解,创建对象,并放入到springmvc容器中
在这里插入图片描述

2)在类中的方法上面加入@RequestMapping注解。
在这里插入图片描述
在这里插入图片描述

5.创建一个作为结果的jsp,显示请求的处理结果。

在这里插入图片描述

6.创建springmvc的配置文件(spring的配置文件一样)

1)声明组件扫描器, 指定@Contorller注解所在的包名
2)声明视图解析器。帮助处理视图的。有了视图解析器的路径 那么就不用写下图的第三行和第四行这么复杂的东西了,视图解析器之所以要写 是因为 不写之前jsp文件在webapp的目录下 那么我们直接访问该jsp不经过中央处理器的处理就可以访问到,这样出现的问题是没有经过控制层的数据逻辑输入,导致jsp里面的页面数据为空,所以我们把jsp放在WEMB-INF下 这样就算再在页面直接访问jsp的地址,也不会跳转到该jsp页面,而会弹出一个404,所以为了方便 我们以后就用配置视图解析器,把jsp文件放在idea中我们给它设置的路径下。也就是
在这里插入图片描述
在这里插入图片描述

<!--声明组件扫描器-->
    <context:component-scan base-package="com.zy.controller" />

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

在这里插入图片描述

执行流程简单分析

(1)浏览器提交请求到中央调度器
(2)中央调度器直接将请求转给处理器映射器。
(3)处理器映射器会根据请求,找到处理该请求的处理器,并将其封装为处理器执行链后返回给中央调度器。
(4)中央调度器根据处理器执行链中的处理器,找到能够执行该处理器的处理器适配器。
(5)处理器适配器调用执行处理器。
(6)处理器将处理结果及要跳转的视图封装到一个对象 ModelAndView 中,并将其返回给处理器适配器。
(7)处理器适配器直接将结果返回给中央调度器。
(8)中央调度器调用视图解析器,将 ModelAndView 中的视图名称封装为视图对象。
(9)视图解析器将封装了的视图对象返回给中央调度器
(10)中央调度器调用视图对象,让其自己进行渲染,即进行数据填充,形成响应对象。
(11)中央调度器响应浏览器。

springmvc请求的处理流程

1)发起some.do
2)tomcat(web.xml–url-pattern知道 *.do的请求给DispatcherServlet)
3)DispatcherServlet(根据springmvc.xml配置知道 some.do—doSome())
4)DispatcherServlet把some.do转发个MyController.doSome()方法
5)框架执行doSome()把得到ModelAndView进行处理, 转发到show.jsp

上面的过程简化的方式
some.do—DispatcherServlet—MyController

springmvc执行过程源代码分析

  1. tomcat启动,创建容器的过程
    通过load-on-start标签指定的1,创建DisaptcherServlet对象,
    DisaptcherServlet它的父类是继承HttpServlet的, 它是一个serlvet, 在被创建时,会执行init()方法。
    在init()方法中
    //创建容器,读取配置文件
    WebApplicationContext ctx = new ClassPathXmlApplicationContext(“springmvc.xml”);
    //把容器对象放入到ServletContext中
    getServletContext().setAttribute(key, ctx);

上面创建容器作用: 创建@controller注解所在的类的对象, 创建MyController对象,
这个对象放入到 springmvc的容器中, 容器是map , 类似 map.put(“myController”,MyController对象)

2.请求的处理过程
1)执行servlet的service()
protected void service(HttpServletRequest request, HttpServletResponse response)

protected void doService(HttpServletRequest request, HttpServletResponse response)

DispatcherServlet.doDispatch(request, response){

调用MyController的.doSome()方法
}
doDispatch:springmvc中DispatcherServlet的核心方法, 所有的请求都在这个方法中完成的。

@RequestMapping 定义请求规则

1.指定模块名称

在这里插入图片描述

2. 对请求提交方式的定义

对于@RequestMapping,其有一个属性 method,用于对被注解方法所处理请求的提交方式进行限制,即只有满足该 method 属性指定的提交方式的请求,才会执行该被注解方法。
Method 属性的取值为 RequestMethod 枚举常量。常用的为 RequestMethod.GET 与RequestMethod.POST,分别表示提交方式的匹配规则为 GET 与 POST 提交。

在这里插入图片描述
以上处理器方法只能处理 POST 方式提交的请求。客户端浏览器常用的请求方式,及其提交方式有以下几种:
在这里插入图片描述
也就是说,只要指定了处理器方法匹配的请求提交方式为 POST,则相当于指定了请求发送的方式:要么使用表单请求,要么使用 AJAX 请求。其它请求方式被禁用。

当然,若不指定 method 属性,则无论是 GET 还是 POST 提交方式,均可匹配。即对于请求的提交方式无要求。

处理器方法的参数

处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用。
➢ HttpServletRequest
➢ HttpServletResponse
➢ HttpSession
➢ 请求中所携带的请求参数
这些参数 如果想要用的话,我们就直接在controller里面的方法的参数里面直接添加就行了。

逐个参数接收

只要保证请求参数名与该请求处理方法的参数名相同即可。
在这里插入图片描述
在controller里面方法的参数:
在这里插入图片描述
在jsp文件里面:
在这里插入图片描述

校正请求参数名@RequestParam

所谓校正请求参数名,是指若请求 URL 所携带的参数名称与处理方法中指定的参数名不相同时,则需在处理方法参数前,添加一个注解@RequestParam(“请求参数名”),指定请求 URL 所携带参数的名称。该注解是对处理器方法参数进行修饰的。value 属性指定请求参数的名称。

Step1:修改 index 页面
将表单中的参数名称修改的与原来不一样。
在这里插入图片描述
Step2:修改处理器类 MyController
在这里插入图片描述
required属性:true表示请求中必须带有参数 不然就会报错,false表示请求中可以没有参数 即为null 不会报错
在这里插入图片描述

请求参数中文乱码问题

对于前面所接收的请求参数,若含有中文,则会出现中文乱码问题。Spring 对于请求参数中的中文乱码问题,给出了专门的字符集过滤器:spring-web-5.2.5.RELEASE.jar 的org.springframework.web.filter 包下的 CharacterEncodingFilter 类。

解决方案
在 web.xml 中注册字符集过滤器,即可解决 Spring 的请求参数的中文乱码问题。不过,最好将该过滤器注册在其它过滤器之前。因为过滤器的执行是按照其注册顺序进行的。

 <!--注册声明过滤器,解决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>

对象参数接收

将处理器方法的参数定义为一个对象,只要保证请求参数名与这个对象的属性同名即可。
1.定义student类
在这里插入图片描述
2.修改处理器类 MyController
在这里插入图片描述
3.修改 show 页面
在这里插入图片描述

处理器方法的返回值

使用@Controller 注解的处理器的处理器方法,其返回值常用的有四种类型:
➢ 第一种:ModelAndView
➢ 第二种:String
➢ 第三种:无返回值 void
➢ 第四种:返回自定义类型对象
根据不同的情况,使用不同的返回值。

1.返回 ModelAndView

若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据,此时处理器方法返回 ModelAndView 比较好。当然,若要返回 ModelAndView,则处理器方法中需要定义 ModelAndView 对象。
在使用时,若该处理器方法只是进行跳转而不传递数据,或只是传递数据而并不向任何资源跳转(如对页面的 Ajax 异步响应),此时若返回 ModelAndView,则将总是有一部分多余:要么 Model 多余,要么 View 多余。即此时返回 ModelAndView 将不合适。

2.返回 String(返回逻辑视图名)

1.处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址。也就是说只是跳转到指定的页面上,而没有数据。如果想要有数据,还不如直接用上面的第一种方法。
在这里插入图片描述
2.当我们需要返回完整的路径的时候也就是下面这幅图,此时不能再配置视图解析器,不然就会出现两个WEB-INF,就会报出错误,视图解析器一个WEB-INF,return返回一个WEB-INF。
在这里插入图片描述

返回 void(了解)

既不能返回数据,也不能返回视图,但是在处理Ajax的时候,我们可以使用void返回值。
Ajax请求服务器端返回的是数据 和 视图无关。

Step1:maven 加入 jackson 依赖
由于本项目中服务端向浏览器传回的是 JSON 数据,需要使用一个工具类将字符串包装
为 JSON 格式,所以需要导入 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>

Step2:引入 jQuery 库
由于本项目要使用 jQuery 的 ajax()方法提交 AJAX 请求,所以项目中需要引入 jQuery 的
库。在 WebRoot 下新建一个 Folder(文件夹),命名为 js,并将 jquery-1.11.1.js 文件放入其中

Step3:定义 index 页面
index 页面由两部分内容构成:一个是,用于提交 AJAX 请求;一个是

在这里插入图片描述
在这里插入图片描述
Step5:修改处理器类 MyController
处理器对于 AJAX 请求中所提交的参数,可以使用逐个接收的方式,也可以以对象的方式整体接收。只要保证 AJAX 请求参数与接收的对象类型属性同名。
以逐个方式接收参数:
在这里插入图片描述

在这里插入图片描述
Step6:删除视图页面
由于是服务端直接向浏览器发回数据,所以也就无需视图页面了,所以需要删除WEB-INF 中的 jsp 目录及其中的 show 页面

返回Object(以后不用void,用这个框架封装好的,省去了许多重复的代码)

使用手工的方式响应Ajax,我们需要重复的写相同的代码,现在我们的框架帮我们把这个工作已经做了,现在我们只需要 添加注解就可以实现响应Ajax,使得返回对象转换为json格式,然后输出到浏览器,这些框架都帮我们做了。下图就是我们重复的代码:
在这里插入图片描述

处理器方法也可以返回 Object 对象。这个 Object 可以是 Integer,String,自定义对象,Map,List 等。但返回的对象不是作为逻辑视图出现的,而是作为直接在页面显示的数据出现的。
返回对象,需要使用@ResponseBody 注解,将转换后的 JSON 数据放入到响应体中。

1.ModelAndView: 有数据和视图,对视图执行forward。
2.String:表示视图,可以逻辑名称,也可以是完整视图路径
3.void: 不能表示数据,也不能表示视图。
在处理ajax的时候,可以使用void返回值。 通过HttpServletResponse输出数据。响应ajax请求。
ajax请求服务器端返回的就是数据, 和视图无关。
4.Object: 例如String , Integer , Map,List, Student等等都是对象,
对象有属性, 属性就是数据。 所以返回Object表示数据, 和视图无关。返回Object之后,转为json格式输出到浏览器,响应ajax请求。
可以使用对象表示的数据。

现在做ajax, 主要使用json的数据格式。 实现步骤:
1.加入处理json的工具库的依赖, springmvc默认使用的jackson。
2.在sprigmvc配置文件之间加入 mvc:annotation-driven 注解驱动。
json = om.writeValueAsString(student);转为json格式
3.在处理器方法的上面加入@ResponseBody注解
response.setContentType(“application/json;charset=utf-8”);
PrintWriter pw = response.getWriter();
pw.println(json); 输出json格式的数据

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

  1. mvc:annotation-driven 注解驱动。
    注解驱动实现的功能是 完成java对象到json,xml, text,二进制等数据格式的转换。
    mvc:annotation-driven在加入到springmvc配置文件后, 会自动创建HttpMessageConverter接口
    的7个实现类对象,StringHttpMessageConverter 负责读取字符串格式的数据和写出字符串格式的数据
    包括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注解
    通过注解把请求返回对象转为Json之后,@ResponseBody注解里面实现了response.getWriter();输出到浏览器上面 放在处理器方法的上面, 通过HttpServletResponse输出数据,响应ajax请求的。
    PrintWriter pw = response.getWriter();
    pw.println(json);
    pw.flush();
    pw.close();

* 返回对象框架的处理流程:

 *  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请求处理完成

4.返回自定义类型

返回自定义类型对象时,不能以对象的形式直接返回给客户端浏览器,而是将对象转换为 JSON 格式的数据发送给浏览器的。
由于转换器底层使用了Jackson转换方式将对象转换为JSON数据,所以需要导入Jackson的相关 Jar 包。
加入Jackson依赖,添加注解mvc ,在controller方法上添加@ResponseBody

返回student

在这里插入图片描述
对应的ajax就是(输出到浏览器 在jsp里面要实现的东西):
在这里插入图片描述

返回 List 集合

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

返回 String字符串(注意和上面的string视图进行对比)

区分返回String是数据还是视图:
如果有@ResponseBody 那么就是返回的是数值
如果没有@ResponseBody 那么返回的就是视图逻辑名

若要返回非中文字符串,将前面返回数值型数据的返回值直接修改为字符串即可。但若
返 回 的 字 符 串 中 带 有 中 文 字 符 , 则 接 收 方 页 面 将 会 出 现 乱 码 。 此 时 需 要 使 用@RequestMapping 的 produces 属性指定字符集。
produces,产品,结果,即该属性用于设置输出结果类型。

@RequestMapping 的 produces 属性指定字符集和springmvc过滤器的区别:我觉得是 produces 属性是针对返回类型是String类型输出到浏览器中的数据 也就是在jsp里面的ajax里面的代码输出格式字符集而设置的(根本不走过滤器),而springmvc过滤器主要针对的是解决post请求在输出到浏览器时 中文乱码的问题(返回是没有转化为json的数据类型 也就是modelandview)

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

解读 即是:*.do和/

发起的请求是由哪些服务器程序处理的。

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的请求。
什么是未映射到其它servlet的请求:
在这里插入图片描述

所以我们在springmvc中把*.do改为/的意思就是 现在/替代了tomact的default

<url-pattern>/</url-pattern>//表示静态资源和未映射的请求都这个default处理

这样会导致所有的静态资源都交给了DispatcherServlet处理,默认情况下,DispatcherServlet并没有处理静态资源(图片、js的jar文件、html、css等)的能力,没有控制器对象能够处理静态资源的访问,这样静态资源就会都是404的情况。

但是some.do文件时可以访问的,因为DispatcherServlet处理并创建控制器对象来处理some.do的请求。

<url-pattern/>的值并不是说写为/后,静态资源就无法访问了。经过一些配置后,该问题也是可以解决的。

(1) 在配置文件中使用<mvc:default-servlet-handler/>

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

<mvc:default-servlet-handler/>表示使用 DefaultServletHttpRequestHandler 处理器对象。而该处理器调用了 Tomcat 的 DefaultServlet 来处理静态资源的访问请求。

而在加入<mvc:default-servlet-handler/>时,会出现下图的情况:
在这里插入图片描述
这是为什么呢?因为当我们加入<mvc:default-servlet-handler/>
之后,所有的访问都交给了tomact默认的DefaultServlet来处理动静态资源,所以some.do也被迫交给其处理,但是DefaultServlet处理不了some.do,所以我们此时为了解决这个冲突,我们还需要加入<mvc:annotation-driven/>.

在这里插入图片描述

(2)第二种处理静态资源的方式

<!--第二种处理静态资源的方式
       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和@RequestMapping有一定的冲突-->
<mvc:annotation-driven />

<!--使用一个配置语句,指定多种静态资源的访问 前面html等文件的的访问路径也需要改变
 加上static-->
<!--<mvc:resources mapping="/static/**" location="/static/" />-->

base路径

在这里插入图片描述
以后就用下面的base解决路径问题:
在这里插入图片描述

第3章 SSM 整合开发

SSM 编程,即 SpringMVC + Spring + MyBatis 整合,是当前最为流行的 JavaEE 开发技术架构。其实 SSM 整合的实质,仅仅就是将 MyBatis整合入 Spring。因为 SpringMVC原本就是 Spring的一部分,不用专门整合。

1.maven pom.xml

<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <!--servlet依赖-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    <!-- jsp依赖 -->
    <dependency>
      <groupId>javax.servlet.jsp</groupId>
      <artifactId>jsp-api</artifactId>
      <version>2.2.1-b03</version>
      <scope>provided</scope>
    </dependency>
    <!-- spring依赖 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依赖   服务ajax-->
    <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>
    <!--    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>
  </dependencies>

  <build>
    <resources>
      <resource>
        <directory>src/main/java</directory><!--所在的目录-->
        <includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
          <include>**/*.properties</include>
          <include>**/*.xml</include>
        </includes>
        <filtering>false</filtering>
      </resource>
    </resources>
    </build>
    </project>

2.配置 web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">
    <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:springmvc.xml</param-value>
        </init-param>


<!--        创建servlet对象,tomact刚开始启动 就会创建servlet对象-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>

<!--最后添加的 当我们要运行程序的时候,我们发现 并没有创建service对象  因为还没有配置视图解析器
 通知spring的配置文件 对service对象进行创建
-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring.xml</param-value>
    </context-param>
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <!--    注册字符过滤器-->
    <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>
            <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>

注册 ServletContext 监听器的实现类 ContextLoaderListener,用于创建 Spring 容器及将创建好的 Spring 容器对象放入到 ServletContext 的作用域中。
DispatcherServlet是为了创建controller对象
监听器是为了创建dao service对象 实体类也可(但是很少用)
注册字符集过滤器,用于解决请求参数中携带中文时产生乱码问题。

定义包,组织程序的结构。

在这里插入图片描述
spring.xml一般写为applicationContext.xml

3.编写配置文件

首先是jdbc.properties

jdbc.url=jdbc:mysql://localhost:3306/数据库名
jdbc.username=root
jdbc.password=密码

spring配置文件

<?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配置文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--声明数据源,连接数据库-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>
    <!--SqlSessionFactoryBean创建SqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource" />
        <property name="configLocation"  value="classpath:mybatis.xml" />
    </bean>

    <!--声明mybatis的扫描器,创建dao对象-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
        <property name="basePackage" value="com.bjpowernode.dao" />
    </bean>

<!--    写到controller的时候  用到service注解对象  所以在此添加service注解-->
    <context:component-scan base-package="com.bjpowernode.service"/>

</beans>

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"
       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">

    
    
<!--    扫描controller包-->
    <context:component-scan base-package="com.bjpowernode.controller"/>

<!--    视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>

<!--    注解驱动 解决对象转化成json格式的数据-->
    <mvc:annotation-driven/>
</beans>

mybatis配置文件

<?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>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <mappers>
        <package name="com.bjpowernode.dao"/>
    </mappers>
</configuration>

然后就写你自己的业务逻辑了,这里我只写了ajax
在这里插入图片描述

<%--
  Created by IntelliJ IDEA.
  User: z'x'x
  Date: 2020/10/21
  Time: 12:51
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%
    String basePath = request.getScheme() + "://" +
            request.getServerName() + ":" + request.getServerPort() +
            request.getContextPath() + "/";
%>
<html>
<head>
    <title>查询学生界面</title>
    <base href="<%=basePath%>" />
    <script type="text/javascript" src="js/jquery-3.4.1.js"></script>
    <script type="text/javascript">
        $(function () {
            $("#btn").click(function () {
                $.ajax({
                    url:"select.do",
                    type:"get",
                    dataType:"json",
                    success:function (data) {
                        $("#info").empty()
                        $.each(data,function (i,n) {
                            $("#info").append("<tr>")
                                .append("<td>"+n.name+"</td>")
                            .append("<td>"+n.age+"</td>").append("</tr>")
                        })
                    }
                })
            })
        })
    </script>
</head>
<body>
<form action="select.do">
    <table>
        <thead>
        <tr>
            <td>姓名:</td>
            <td>年龄:</td>
        </tr>
        </thead>
        <tbody id="info">

        </tbody>
    </table>
    <input type="button" id="btn" value="查询">
</form>
</body>
</html>

第4章 SpringMVC 核心技术

请求重定向和转发

首先为什么要用重定向和转发呢》咋们不都有视图解析器了吗?
回答:当我们使用视图解析器的时候,我们会把我们想要转发到指定的页面放在我们在视图解析器里面设置的指定目录之下。而不能跳转到设定的目录路径之外的页面,因此我们在使用视图解析器的时候,会把我们想要跳转到指定的页面都放在我们设置好的目录中。
而重定向和转发不和视图解析器一块工作,使用重定向和转发的时候,可以直接在controller业务方法中直接指定想要跳转的文件路径信息。这样的范围更广,但是容易乱。

当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发与重
定向。而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器。
注意,对于请求转发的页面,可以是WEB-INF中页面;而重定向的页面,是不能为WEB-INF
中页的。因为重定向相当于用户再次发出一次请求,而用户是不能直接访问 WEB-INF 中资
源的
在这里插入图片描述
SpringMVC 框架把原来 Servlet 中的请求转发和重定向操作进行了封装。现在可以使用简
单的方式实现转发和重定向。
forward:表示转发,实现 request.getRequestDispatcher(“xx.jsp”).forward()
redirect:表示重定向,实现 response.sendRedirect(“xxx.jsp”)

请求转发(视图完整路径)

处理器方法返回 ModelAndView 时,需在 setViewName()指定的视图前添加 forward:,且
此时的视图不再与视图解析器一同工作,这样可以在配置了解析器时指定不同位置的视图。
视图页面必须写出相对于项目根的路径。forward 操作不需要视图解析器。
处理器方法返回 String,在视图路径前面加入 forward: 视图完整路径。
在这里插入图片描述
在这里插入图片描述

请求重定向

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

异常在springmvc中的使用

利用aop功能,新建一个控制异常处理类,将程序中所有的异常统一集中在一起,然后利用这个控制异常处理类对这些异常进行详细的说明,就像是给用户说明为什么会出现这样的异常,然后我们应该怎么去处理这个异常。如果我们在代码中写这样的大量的信息,会使得程序看起来很臃肿,我们只会在程序中throw 这个异常,然后交给控制异常处理类。

@ExceptionHandler 注解

使用注解@ExceptionHandler 可以将一个方法指定为异常处理方法。该注解只有一个可
选属性 value,为一个 Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹
配的异常。而被注解的方法,其返回值可以是 ModelAndView、String,或 void,方法名随意,方法
参数可以是 Exception 及其子类对象、HttpServletRequest、HttpServletResponse 等。系统会自动为这些方法参数赋值。
对于异常处理注解的用法,也可以直接将异常处理方法注解于 Controller 之中。

下图是我们自定义的异常,myUserexcetion继承Exception异常,并继承它的两个方法,接着Nameexcetion和Ageexcetion继承MyUserexcetion。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

还有就是注解@ExceptionHandler 可以单独使用,如果程序只有很少的异常,我们就不用小题大做了,直接在controller类中就可以写一个有注解@ExceptionHandler 的方法了,用来写详细的异常信息。
如下图:
在这里插入图片描述

不过,一般不这样使用。而是将异常处理方法专门定义在一个类中,作为全局的异常处理类。需要使用注解@ControllerAdvice,字面理解就是“控制器增强”,是给控制器对象增强功能的。使用@ControllerAdvice 修饰的类中可以使用@ExceptionHandler。当使用@RequestMapping 注解修饰的方法抛出异常时,会执行@ControllerAdvice 修饰的类中的异常处理方法。@ControllerAdvice 是使用@Component 注解修饰的,可以context:component-scan扫描到@ControllerAdvice 所在的类路径(包名),创建对象。

controller代码

package com.bjpowernode.controller;
import com.bjpowernode.excetion.AgeExcetion;
import com.bjpowernode.excetion.NameExcetion;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class myController {

    @RequestMapping("/Excetion.do")
    public ModelAndView doSome(String name,Integer age) throws NameExcetion, AgeExcetion {
        ModelAndView mv = new ModelAndView();
        if ("zs"!=name){
            throw new NameExcetion("用户名与当前注册不符合");
        }else {

        mv.addObject("myname",name);}

        if(age>=88){
            throw new AgeExcetion("用户年龄过大,请下线");
        }else {
            mv.addObject("myage", age);
        }
        return  mv;
    }
}

handler控制器处理类
在这里插入图片描述

package com.bjpowernode.handler;


import com.bjpowernode.exception.AgeException;
import com.bjpowernode.exception.NameException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

/**
 * @ControllerAdvice : 控制器增强(也就是说给控制器类增加功能--异常处理功能)
 *           位置:在类的上面。
 *  特点:必须让框架知道这个注解所在的包名,需要在springmvc配置文件声明组件扫描器。
 *  指定@ControllerAdvice所在的包名
 */
@ControllerAdvice//控制器增强  也就是说给@Controller增加功能-异常处理
public class GlobalExceptionHandler {
    //定义方法,处理发生的异常
    /*
        处理异常的方法和控制器方法的定义一样, 可以有多个参数,可以有ModelAndView,
        String, void,对象类型的返回值

        形参:Exception,表示Controller中抛出的异常对象。
        通过形参可以获取发生的异常信息。

        @ExceptionHandler(异常的class):表示异常的类型,当发生此类型异常时,
        由当前方法处理
     */
    @ExceptionHandler(value = NameException.class)
    public ModelAndView doNameException(Exception exception){
        //处理NameException的异常。
        /*
           异常发生处理逻辑:
           1.需要把异常记录下来, 记录到数据库,日志文件。
             记录日志发生的时间,哪个方法发生的,异常错误内容。
           2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。
           3.给用户友好的提示。
         */
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","姓名必须是zs,其它用户不能访问");
        mv.addObject("ex",exception);
        mv.setViewName("nameError");
        return mv;

    }
    //处理AgeException
    @ExceptionHandler(value = AgeException.class)
    public ModelAndView doAgeException(Exception exception){
        //处理AgeException的异常。
        /*
           异常发生处理逻辑:
           1.需要把异常记录下来, 记录到数据库,日志文件。
             记录日志发生的时间,哪个方法发生的,异常错误内容。
           2.发送通知,把异常的信息通过邮件,短信,微信发送给相关人员。
           3.给用户友好的提示。
         */
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","你的年龄不能大于80");
        mv.addObject("ex",exception);
        mv.setViewName("ageError");
        return mv;
    }
    //处理其它异常, NameException, AgeException以外,不知类型的异常
    @ExceptionHandler
    public ModelAndView doOtherException(Exception exception){
        //处理其它异常
        ModelAndView mv = new ModelAndView();
        mv.addObject("msg","你的年龄不能大于80");
        mv.addObject("ex",exception);
        mv.setViewName("defaultError");
        return mv;
    }

}

配置文件 由于只用了controller 所以只用扫描controller和控制器处理类就行
在这里插入图片描述
web配置文件 注册中央处理器 字符处理器 由于只用了springmvc的controller对象,没有用service和dao所以就不用配置spring监听器了

<?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>

        <!--自定义springmvc读取的配置文件的位置-->
        <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>

页面:返回的页面
在这里插入图片描述

运行:输出年龄大于80 的时候

在这里插入图片描述
上面的msg是在handler控制异常处理类 处理的结果,我们可以在该类的处理指定的异常方法中将详细的异常信息打印出来,方便用户查看。下面的ex信息 是controller类中的提示信息,一般都是简单的提示,复杂的提示会在handler控制异常处理类的指定的异常方法中。
怎么指定呢?在该类的处理指定的异常方法的上面加入注解@ExceptionHandler 。

重要的是,在控制异常处理类的最上面必须加入注解@ControllerAdvice控制器增强类

拦截器

1)拦截器是springmvc中的一种,需要实现HandlerInterceptor接口。
2)拦截器和过滤器类似,功能方向侧重点不同。 过滤器是用来过滤器请求参数,设置编码字符集等工作。
拦截器是拦截用户的请求,做请求做判断处理的。
3)拦截器是全局的,可以对多个Controller做拦截。
一个项目中可以有0个或多个拦截器, 他们在一起拦截用户的请求。
拦截器常用在:用户登录处理,权限检查, 记录日志

拦截器的使用步骤:
1.定义一个handler类实现HandlerInterceptor接口
2.在springmvc配置文件中,声明拦截器,也就是扫描handler类, 让框架知道拦截器的存在。

在这里插入图片描述
自定义拦截器,需要实现 HandlerInterceptor 接口。而该接口中含有三个方法:

➢ preHandle(request,response, Object handler):(主要)

该方法在处理器方法执行之前执行。其返回值为 boolean,若为 true,则紧接着会执行处理器方法,且会将 afterCompletion()方法放入到一个专门的方法栈中等待执行。

➢ postHandle(request,response, Object handler,modelAndView):
该方法在处理器方法执行之后执行。处理器方法若最终未被执行,则该方法不会执行。
由于该方法是在处理器方法执行完后执行,且该方法参数中包含 ModelAndView,所以该方法可以修改处理器方法的处理结果数据,且可以修改跳转方向。

➢ afterCompletion(request,response, Object handler, Exception ex): 当 preHandle()方法返回 true (当preHandle()方法返回 true ,此时该方法一定会被执行)时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有
工作完成之后才执行该方法。即该方法是在中央调度器渲染(数据填充)了响应页面之后执行的,此时对 ModelAndView 再操作也对响应无济于事。

handler类代码以及三个方法

package com.bjpowernode.handler;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;

//拦截器类:拦截用户的请求。
public class MyInterceptor implements HandlerInterceptor {

    private long btime = 0;
    /*
     * preHandle叫做预处理方法。
     *   重要:是整个项目的入口,门户。 当preHandle返回true 请求可以被处理。
     *        preHandle返回false,请求到此方法就截止。
     *
     * 参数:
     *  Object handler : 被拦截的控制器对象
     * 返回值boolean
     *   true:请求是通过了拦截器的验证,可以执行处理器方法。
         *   拦截器的MyInterceptor的preHandle()
             =====执行MyController中的doSome方法=====
             拦截器的MyInterceptor的postHandle()
             拦截器的MyInterceptor的afterCompletion()
         *
     *   false:请求没有通过拦截器的验证,请求到达拦截器就截止了。 请求没有被处理
     *      拦截器的MyInterceptor的preHandle()
     *
     *
     *  特点:
     *   1.方法在控制器方法(MyController的doSome)之前先执行的。
     *     用户的请求首先到达此方法
     *
     *   2.在这个 方法中可以获取请求的信息, 验证请求是否符合要求。
     *     可以验证用户是否登录, 验证用户是否有权限访问某个连接地址(url)。
     *      如果验证失败,可以截断请求,请求不能被处理。
     *      如果验证成功,可以放行请求,此时控制器方法才能执行。
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        btime = System.currentTimeMillis();
        System.out.println("拦截器的MyInterceptor的preHandle()");
        //计算的业务逻辑,根据计算结果,返回true或者false
        //给浏览器一个返回结果
        //request.getRequestDispatcher("/tips.jsp").forward(request,response);
        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 mv) throws Exception {
        System.out.println("拦截器的MyInterceptor的postHandle()");
        //对原来的doSome执行结果,需要调整。
        if( mv != null){
            //修改数据
            mv.addObject("mydate",new Date());
            //修改视图
            mv.setViewName("other");
        }
    }

    /*
      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()");

        long etime = System.currentTimeMillis();
        System.out.println("计算从preHandle到请求处理完成的时间:"+(etime - btime ));
    }
}

preHandle叫做预处理方法。重要:是整个项目的入口,门户。 当preHandle返回true 请求可以被处理。preHandle返回false,请求到此方法就截止。

三级目录如果是true:true:请求是通过了拦截器的验证,可以执行处理器方法。此时执行方法的顺序是:

     *   拦截器的MyInterceptor的preHandle()
         =====执行MyController中的doSome方法=====  也叫做处理器方法
         拦截器的MyInterceptor的postHandle()
         拦截器的MyInterceptor的afterCompletion()
     *

false:请求没有通过拦截器的验证,请求到达拦截器就截止了。 请求没有被处理。

  • 特点:
    • 1.方法在控制器方法(MyController的doSome)之前先执行的。
    • 用户的请求首先到达此方法
    • 2.在这个 方法中可以获取请求的信息, 验证请求是否符合要求。
    • 可以验证用户是否登录, 验证用户是否有权限访问某个连接地址(url)。
    • 如果验证失败,可以截断请求,请求不能被处理。
    • 如果验证成功,可以放行请求,此时控制器方法才能执行。

/*
postHandle:后处理方法。
参数:
Object handler:被拦截的处理器对象MyController
ModelAndView mv:处理器方法的返回值

    特点:
     1.在处理器方法之后执行的(MyController.doSome())
     2.能够获取到处理器方法的返回值ModelAndView,可以修改ModelAndView中的
     数据和视图,可以影响到最后的执行结果。
     3.主要是对原来的执行结果做二次修正,

     ModelAndView mv = MyController.doSome();
     postHandle(request,response,handler,mv);

具体在上面的程序中就有体现。

声明拦截器,配置文件中扫描拦截器所在的包

在这里插入图片描述
<mvc:mapping/>用于指定当前所注册的拦截器可以拦截的请求路径,而/**表示拦截所有请求。/是从项目的名称开始的 也就是说项目/ 后面两个星星 表示拦截所有的请求。

(preHandler返回true)拦截器中方法与处理器方法的执行顺序如下图:

在这里插入图片描述

多个拦截器

再定义一个拦截器
在这里插入图片描述
多个拦截器的注册与执行
在这里插入图片描述
控制台执行结果
在这里插入图片描述
当有多个拦截器时,形成拦截器链。拦截器链的执行顺序,与其注册顺序一致。需要再
次强调一点的是,当某一个拦截器的 preHandle()方法返回 true 并被执行到时,会向一个专
门的方法栈中放入该拦截器的 afterCompletion()方法。

多个拦截器中方法与处理器方法的执行顺序如下图:
在这里插入图片描述
从图中可以看出,只要有一个 preHandle()方法返回 false,则上部的执行链将被断开,
其后续的处理器方法与 postHandle()方法将无法执行。但,无论执行链执行情况怎样,只要
方法栈中有方法,即执行链中只要有 preHandle()方法返回 true,就会执行方法栈中的
afterCompletion()方法。最终都会给出响应。

执行顺序

多个拦截器:
第一个拦截器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()

第二个拦截器为false时,执行链中断。
当preHandle()方法返回 true时,它的 afterCompletion()方法一定会被执行。
当preHandle()方法返回 false时,它的 afterCompletion()方法不会被执行。


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

111111-拦截器的MyInterceptor的preHandle()

从上面三个例子我们可以得出,不管有几个拦截器,只要任何一个拦截器的preHandle=false,那么都不会再去执行postHandler方法。多个拦截器在项目中会有不同的功能体现,比如拦截器1可以实现用户登录验证,拦截器2可以对用户权限访问进行控制,拦截器3可以对用户访问日志进行统计,这样就可以让不同的拦截器做不同的功能。

拦截器和过滤器的区别

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

SpringMvc执行流程 理解

在这里插入图片描述

流程分析:

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

处理器映射器:springmvc框架中的一种对象,框架把实现了HandlerMapping接口的类都叫做映射器(多个)

处理器映射器的作用:根据请求,从springmvc容器中获取处理器对象(MyController controller = ctx.getBean(“some.do”)),框架把找到的处理器对象放到一个叫做处理器执行链(HandlerExceptionChain)的类保存。

HandlerExceptionChain:类中保存着1.处理器对象 2.项目中所有的拦截器`List<HandlerInterceptor>`

方法调用:HandlerExceptionChain mappdeHandler=getHandler(processedRequest);

3.DispatcherServlet把2中的HandlerExceptionChain中的处理器对象交给了处理器适配器对象(多个)

处理器适配器:springmvc框架中的对象,需要实现HandlerAdapter接口。
处理器适配器的作用:执行处理器方法(调用MyController.dosome()) 得到返回值ModelAndView)

中央调度器调用适配器:
HandlerAdapter ha=getHandlerAdapter(mappdeHandler.getHandler());
执行处理器方法:mv=ha.handler(processedRequest,response,mappdeHandler.getHandler());

4.DispatcherServlet把3获取ModelAndView交给了视图解析器对象。

视图解析器:springmvc中的对象,需要实现ViewResoler接口(可以有多个)
视图解析器作用:组成视图完整路径,使用前缀,后缀。并创建View对象。
   View是一个接口,表示视图的,在框架中jsp,html不是string表示,而是使用View和他的实现类表示视图。
  InternalResourceView:视图类,表示jsp文件,视图解析器会创建InternalResourceView类对象。
   这个对象里面,有一个属性url=/WEB-INF/view/show.jsp

5.DispatcherServlet把4步骤中创建的View对象获取到,调用View类自己的方法,把Model数据放入到request作用域。执行对象视图的forward。请求结束。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值