SpringMVC学习笔记

本文详细介绍了SpringMVC的学习过程,包括Web和Servlet的基础知识,如HTTP特征、GET/POST请求的区别,以及Web容器和Servlet的工作原理。接着深入探讨了SpringMVC的使用,从基本开发步骤到核心概念如DispatcherServlet和注解开发。同时,文章提到了Ajax技术以及SpringMVC中处理静态资源的方式。此外,还讨论了SSM整合开发和SpringMVC的执行流程,以及重要特性如拦截器和异常处理。
摘要由CSDN通过智能技术生成

最近结合书本和b站教程学习完了SpringMVC的课程,记录一下

Web和Servlet

HTTP基本特征

  1. 基于请求/响应模型的通信协议,架构在TCP/IP之上的应用层
  2. 无状态

GET、POST使用时机

GET请求

向服务器取得指定的资源,发出GET请求时必须一并告诉服务器所请求资源的URL以及一些标头Header信息

在这里插入图片描述

  • GET请求可以发送的请求参数长度有限,对于**太大量的数据不适合使用GET方式来进行请求**
  • 请求参数会直接出现在地址栏中

POST请求

在请求时发布信息给服务器,对于大量或复杂的信息发送(如文件上传),基本会采用POST来进行发送。

<img src="D:\截图\image-20210511163627326.png在这里插入图片描述

  • POST将请求参数移至信息体,信息体的内容长度不受限制,所以大量数据的发送会使用POST方法
  • 请求参数不会出现在地址栏,所以对于一些敏感信息,即使长度不长通常也会改用POST方式发送

GET和POST的选择

  • POST请求

    • 过长的请求参数
    • 文件上传
    • 请求参数包含敏感信息
    • 希望浏览器能从缓存中获取旧的资料
  • GET请求

    • 希望用户可以设定书签便于日后浏览(POST请求的参数不会出现在地址栏,就无法家兔浏览器的书签中)
  • 非功能层面上的考虑:根据是否为等幂操作(是否改变服务器状态,同一个操作重复多次是否返回相同的结果)选择GET或POST。

    • GET-等幂操作(不改变服务器上的数据或状态,如查询数据库)
    • POST-非等幂操作(可能会影响服务器上的数据或状态,如增删改数据库)

Web容器

与JVM是java程序唯一认识的操作系统,其可执行文件为.class文件的概念类似,Web容器(Container)是Servlet/JSP唯一认识的HTTP服务器

  • Web容器不仅持有对象,还负责对象的生命周期与相关服务的连接
    • 具体层面上,容器其实就是一个用java写的程序,运行于JVM之上(服务器中的JVM)。HTTP文字性的通信协议变成Servlet/JSP中可用的Java对象,就是靠容器剖析和转换
    • 抽象层面上,Web容器就是运行Servlet/JSP的HTTP服务器
    • Web容器可能会使用同一个Servlet实例来服务多个请求,需要注意线程安全

Servlet/JSP

静态网页:服务器根据请求读取文件后不做处理,直接当作响应传给浏览器
动态网页:服务器在响应前根据请求参数、标头或实际服务器上的状态,动态产生响应内容,再传回给用户
  • Servlet/JSP是服务器上的一个技术,客户端通过HTTP协议和网络,传送请求给服务器上的Servlet/JSP,后者经过运算处理后再将响应传回客户端,一切处理都是在服务器上发生的。而JavaScript运行于客户端浏览器中。

  • JSP是便于用户编辑的,会被Web容器转译为Servlet的可执行文件,然后加载在容器之中,所以最后提供服务的还是Servlet

  • 一个Servlet类必须继承HttpServlet,Servlet主要用于Java程序逻辑的定义,JSP主要方便网页的编辑和设计

Servlet容器及Servlet的初始化

Servlet和Servlet容器通过标准化接口来相互协作

  • Tomcat容器模型

在这里插入图片描述

真正管理Servlet的是Context容器,一个Context对应一个Web工程,Context直接管理Servlet在容器中的包装类Wrapper

Servlet容器的启动

添加一个Web应用时会创建一个StandardContext容器,并且给这个容器设置必要的参数(url、path等),其中最重要的一个配置是ContextConfig,这个类负责整个Web应用配置的解析工作,然后将这个Context容器加到父容器Host中

ContextCofig的init方法主要完成以下工作:

  1. 创建用于解析XML配置文件的contextDigester对象
  2. 读取默认的context.xml、Host、Context配置文件,如果它们存在则解析它们
  3. 设置Context的DocBase

完成后Context容器会执行startInternal方法,这个方法主要完成以下工作:

创建读取资源文件的对象、ClassLoader对象,设置应用的工作目录,启动相关的辅助类(logger、realm、resources等),修改启动状态通知观察者,子容器初始化,获取ServletContext并设置必要参数,初始化Servlet

Tomcat的启动逻辑基于观察者模式,所有容器都继承Lifecycle接口,所有容器的修改和状态改变都会通知已经注册的观察者(Listener)

Servlet的创建过程

Servlet从被web.xml解析到完成初始化时序图(省略了部分细节)

在这里插入图片描述

Servlet体系结构

在这里插入图片描述

Java Web应用基于Servlet规范运转,Servlet规范就是基于以上几个顶层类运转的。

ServletRequest和ServletResponse用于处理请求,ServletConfig用于获取Servlet运行时可能用到的配置属性,ServletContext用于准备交易场景(Servlet的运行模式时典型的“握手型交互式”)。

Servlet如何工作(被调用)

用户从浏览器向服务器发起的一个请求通常会包含http://hostname:port/contextpath/servletpath

hostname和port用来建立与服务器的TCP连接,服务器根据后面的URL来选择子容器服务请求

Tomcat7中通过一个映射类org.apache.tomcat.util.http.mapper保存Tomcat的Container容器中所有子容器的信息。
org.apache.catalina.connector.Request类进入Container容器之前,Mapper就会根据请求将host和context容器设置到Request的mappingData属性中,此时要访问哪个子容器就已经确定。

在这里插入图片描述

接下来就执行Servlet中的service方法。

现在Web应用很少直接将交互的全部页面用Servlet来实现,而是采用更高效的MVC框架来实现,这些MVC框架的基本原理是将所有的请求都映射到一个Servlet,然后去实现service方法(MVC框架的入口)。

MVC/Model 2

对于Web应用,MVC指的是修正桌面应用MVC模式(去掉了模型通知视图,即HTTP服务器不可能主动对浏览器发出响应)后的架构。

在这里插入图片描述

控制器:获取、验证请求参数,转发请求给模型,转发请求给视图

模型:负责处理业务、数据存取逻辑

视图:根据需求呈现画面

view controller model

​ service、DAO

MVC的缺点

1、有时会导致级联的修改。这种修改尤其体现在自上而下的方向。如果在表示层中需要增加一个功能,为保证其设计符合分层式结构,可能需要在相应的业务逻辑层和数据访问层中都增加相应的代码。
2、降低了系统的性能。这是不言而喻的。如果不采用分层式结构,很多业务可以直接造访数据库,以此获取相应的数据,如今却必须通过中间层来完成。

3、由于它没有明确的定义,所以完全理解MVC并不是很容易。使用MVC需要精心的计划,由于它的内部原理比较复杂,所以需要花费一些时间去思考。

4、MVC并不适合小型甚至中等规模的应用程序,花费大量时间将MVC应用到规模并不是很大的应用程序通常会得不偿失。

Ajax

概念

Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)用于实现局部刷新,是多个技术的集合(JavaScript, html , dom , json ,css),主要是 JavaScript , XML,且需要配合服务器端的数据。

局部刷新:在浏览器内部发起请求,获取数据,改变页面中的部分内容

1. JavaScript
   负责创建异步对象,发送请求,更新页面的DOM对象
2. Json(之前是XML)
   网络中传送的数据格式

异步对象XmlHttpRequest

局部刷新使用的核心对象。存在浏览器内存中,代替浏览器在后台对服务器发起请求并接收处理结果,实现局部刷新的效果,可以创建多个XmlHttpRequest。

三个重要属性

1. onreadystatechange
为其制定一个 js 函数名 或 直接定义函数
每当readyState属性改变即当异步对象发起请求、获取了数据,都会触发这个事件调用这个函数

2. readyState
存有 XMLHttpRequest 的状态。从 0 到 4 发生变化。
• 0: 请求未初始化,创建异步请求对象 var xmlHttp = new XMLHttpRequest()
• 1: 初始化异步请求对象,xmlHttp.open(请求方式,请求地址,true)
• 2: 异步对象发送请求,xmlHttp.send()
• 3: 异步对象从服务端接收应答数据,获取了原始的数据。XMLHttpRequest内部处理。
•【常用】 4: 异步请求对象已经将数据解析完毕。此时开发者才可以读取、处理数据。

3. status
200: 网络请求成功
404: 未找到页面

Ajax中XMLHttpRequest对象的使用

  1. 创建异步对象

js语法进行创建:var xmlhttp=new XMLHttpRequest();

  1. 给异步对象绑定事件

onreadystatechange:当异步对象发起请求、获取了数据都会触发这个事件,为这个事件制定一个函数来处理状态变化。即每次readyState的值发生变化时,异步对象都会回调该事件绑定的函数

xmlHttp.onreadystatechange = function(){
  处理请求的状态变化
  if(xmlHttp.readyState == 4){
     		//可以处理服务端返回的数据,更新当前页面
    		var data = xmlHttp.responseText;
    		document.getElementById("name").value = data;
     }
}
  1. 初始化异步请求对象
xmlHttp.open((method, url, async)
method:get|post方式
url:服务器端的访问地址
async:同步|异步请求,默认为ture,异步请求
  1. 发送请求
xmlHttp.send()
  1. 获取服务器端返回的数据
使用异步对象的属性responseText
xmlHttp.responseText

SpringMVC概述

SpringMVC也叫Spring Web MVC,是基于Spring的一个框架,实际上就是Spring的一个模块,专门用于做Web开发,Servlet升级版。能够使用Spring的IOC和AOP,强化注解的使用。

# MVC框架的基本原理是将所有的请求都映射到一个Servlet,然后去实现service方法(MVC框架的入口)。框架在Servlet基础上加入一些功能,做web开发更为方便。
  1. SpringMVC能够创建对象,放入到容器中(SpringMVC容器),SpringMVC容器中放的是控制器对象。

  2. 我们要做的是,使用@Controller创建控制器对象并放入到SpringMVC中,这个控制器对象能够接收用户请求,显示处理结果,就当作一个Servlet使用。但它是一个普通类的对象,不是Servlet,SpringMVC为其增加了一些额外功能,才能像Servlet一样使用

  3. SpringMVC中有一个对象是Servlet——DispatcherServlet(中央调度器)。这个对象负责接受用户的所有请求,再转发请求给Controller对象来进行最后的处理。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-05GObKpY-1621756551743)(D:\截图\image-20210513091547915.png)]

基本开发步骤

新建web maven工程

默认生成的web.xml版本过低,替换为web-app_4_0.xsd

在这里插入图片描述

先“-”将原来的低版本xml文件删除,然后“+”选择4.0,但是直接这样改文件内容不会刷新,修改web.xml的文件名,内容才会刷新,然后再把名字改回去。

添加依赖

spring-mvc依赖,会间接地把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>

注册中央调度器

在web.xml中注册springmvc框架的核心对象DispatcherServlet		DispatcherServlet叫做中央调度器,也叫前端控制器,是一个Servlet,继承HttpServlet。负责接受用户提交的请求,调用其他的控制器对象,并把请求的处理结果显示给用户。<servlet>    <servlet-name>springmvc</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启动后就创建Servlet对象        load-on-startup:表示tomcat启动后创建对象的顺序,它的值是大于等于0的整数,                        数值越小,tomcat创建对象的时间越早    -->    <load-on-startup>1</load-on-startup></servlet><!-- servlet映射,指定哪些请求交给哪个servlet处理 --><servlet-mapping>    <servlet-name>springmvc</servlet-name>    <!--        使用框架的时候,url-pattern可以使用两种值        1. 使用扩展名方式,语法为*.xxx,xxx是自定义的扩展名。 常用方式为*.do,*.action,*.mvc等          表示http://locahost:8080/部署的项目名称/xxx.do都交给这个servlet来处理        2.使用斜杠 "/"    -->    <url-pattern>*.do</url-pattern></servlet-mapping>

创建一个发起请求的页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head>    <title>springmvc-helloworld</title></head><body>    <p>第一个springmvc项目</p>    <p><a href="some.do">发起some.do请求</a></p></body></html>

创建控制器类

1. 在类的上面加入@Controller注解,创建对象,并放入到springmvc容器中2. 在类中的方法上加入@RequestMapping注解  @Controller:创建处理器对象,放在springmvc容器中,类似@Service@Component能处理请求的都是控制器(处理器),MyController叫做后端处理器@Controllerpublic class MyController {  /*    * springmvc中使用自定义方法来处理用户提交的请求    *    * 自定义doSome方法处理Some.do请求    * @RequestMapping注解:请求映射    *     属性    *       value:String,表示请求的uri地址,唯一的,不能重复。 使用时推荐地址以”/“(表示根地址)开头    *     使用位置    *       1.方法上    *       2.类上    *   说明:使用@RequestMapping修饰的方法叫做控制器(处理器)方法    *        控制器方法用于处理请求,类似于Servlet中的doGet、doPost    *    * 返回值ModelAndView:表示本次请求的处理结果    *   Model:数据,请求处理完成后,要显示给用户的数据    *   View:视图    * */  @RequestMapping(value = "/some.do")//也可以指定多个 value = {"/some.do", "/first.do"}  public ModelAndView doSome(){ //doGet调用Service      //处理some.do请求      ModelAndView mv = new ModelAndView();      //添加数据,框架在请求的最后把数据放入到request作用域中,相当于request.setAttribute()      mv.addObject("msg", "欢迎使用springmvc进行web开发");      mv.addObject("fun", "执行的是doSome方法");      /*      * 指定视图的完整路径      * 框架对视图执行forward操作,request.getRequestDispatcher("/show.jsp").forward(...)      * */      mv.setViewName("/show.jsp");      return mv;  }}

创建一个结果页面

<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head>    <title>Title</title></head><body>    <h3>show.jsp从request作用域获取数据</h3><br/>    <h3>msg数据:${msg}</h3>    <h3>fun数据:${fun}</h3></body></html>

创建SpringMVC配置文件

1. 声明组件扫描器,指定@Controller注解所在的包名2. 声明视图解析器,帮助处理视图<?xml version="1.0" encoding="UTF-8"?><beans ...>    <context:component-scan base-package="com.liu.controller"/>    <!--声明springmvc框架中的视图解析器,帮助开发人员设置视图文件路径-->    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">        <!--前缀:视图文件的路径-->        <property name="prefix" value="/WEB-INF/view/" />        <!--后缀:视图文件的扩展名-->        <property name="suffix" value=".jsp"/>    </bean></beans>

部署项目到Tomcat中

在这里插入图片描述

分析

请求处理流程分析

  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 ==> DispathcerServlet ==> MyController

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-owqprDeL-1621756551745)(D:\截图\image-20210514093107156.png)]

执行过程源码分析

  1. tomcat启动,创建容器的过程

通过load-on-start标签指定的1,创建DispatcherServlet对象,在被创建时执行init()方法

1. 创建容器,读取配置文件创建@Controller注解所在的类的对象MyController,并将其放入springmvc容器中,容器使用map,类似map.put("myController", MyController对象)WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");2. 把容器对象放入到ServletContext中getServletContext().setAttribute(key,ctx);
  1. 请求的处理过程
1. 执行Servlet的Service方法protected void service(HttpServletRequest request, HttpServletResponese response)protected void doService(HttpServletRequest request, HttpServletResponese response)  DispatcherServlet.doDispatch(request, response){	调用MyCotroller的doSome()方法}

优点

之前有一个请求就需要写一个Servlet,现在只需要在控制器Controller中写一个方法就可以处理一个请求。

SpringMVC注解开发

@RequestMapping注解

使用位置

  1. 放在类上

    ​ 在类上添加@RequestMapping,其中指定vale。相当于给方法上的xxx.do加前缀,分模块

  2. 放在方法上

    ​ 将请求和方法绑定

属性

  1. value

    表示请求的uri地址,唯一的,不能重复。可以是String,指定多个时value = {"", “”}

  2. method

    表示请求的方式,值是RequestMethod类的枚举值。

    get方式——RequestMethod.GET,post方式——RequestMethod.POST

处理器方法

处理器的方法可以包含以下四类参数,这些参数会在系统调用时自动赋值,开发者可以直接在方法内使用

  • HttpServletRequest

  • HttpServletResponse

  • HttpSession

  • 用户提交的请求参数

接收用户提交的参数的方式:逐个接收、对象接收

参数

逐个接收请求参数

要求处理器(控制器)方法的形参名和请求中传过来的参数名一致

# 框架接收请求参数的过程1. 使用request对象接收请求参数参数的原始接收值都是String类型String strName = request.getParameter("name");String strAge = request.getParameter("age");2. SpringMVC通过DispatcherServlet,调用MyController的doSome方法调用方法时,按照名称对应,把接收的参数赋值给形参框架提供类型转换功能,能把String转换为int,long.float,double等doSome(strName, Integer.valueOf(strAge))使用springmvc框架可以直接将参数作为方法的形参,不用再手动写request.getParameter。在方法中可以直接使用参数。public ModelAndView doSome(String name, int age){}
  • 解决post提交中文参数乱码

使用过滤器,可以自定义,也可以使用框架提供的CharacterEncodingFilter,这个类封装了原本需要在每个处理方法中加上的request.serCharacterEncoding(“utf-8”)

在这里插入图片描述

在web.xml文件中配置<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. 属性value:请求中参数的名称required:boolean,默认为ture,表示请求中必须包含此参数2. 位置在处理器方法定义的形参前面public ModelAndView receiveParam(@RequestParam("pname") String name, @RequestParam("page") Integer age){
对象接收参数

在参数数量很多的情况下,逐个接收显然不现实。

创建一个对象,然后将处理器方法的参数定义为对象,对象的属性名与请求中的参数名一致即可。方法体中可以直接使用对象的属性、getset方法等。

​ 框架会根据形参的java对象,给属性赋值。如请求中的参数是name,那么框架就会调用setName()

在这里插入图片描述

返回值

ModelAndView

适用于处理器方法处理完请求后需要跳转到其他资源又要在跳转的资源间传递数据的时候

String

如果只需要字符的跳转,可以返回String指定逻辑视图名,然后通过视图解析器将其转换为物理视图地址

@Controllerpublic class MyController {    /*    * 处理器方法返回String,表示逻辑视图名称,需要配置视图解析器    * */    @RequestMapping(value = "/returnString.do")    public String doReturnView(String name, Integer age){        System.out.println("name = " + name + "   age = " + age);        return "show";    }}springmvc配置文件<!--声明springmvc框架中的视图解析器,帮助开发人员设置视图文件路径--><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">    <!--前缀:视图文件的路径-->    <property name="prefix" value="/WEB-INF/view/" />    <!--后缀:视图文件的扩展名-->    <property name="suffix" value=".jsp"/></bean>
void(了解)

不能表示数据,也不能表示视图。

在处理Ajax时可以使用void返回值,通过HttpServletResponse输出数据,响应Ajax请求。ajax请求服务器端返回的就是数据,与视图无关。	PrintWriter pw = response.getWriter();	pw.println();	1. 在项目中添加jQueary.js文件,并在jsp中引入,定义ajax<script type="text/javascript" src="js/jquery-3.4.1.js"></script><script type="text/javascript">    $(function(){        $("button").click(function () {            //alert("button click!");            $.ajax({                url:"returnVoid-ajax.do",                data:{                    name:"liushi",                    age:20                },                type:"post",                dataType:"json",                success:function (resp) {                    alert(resp);                }            })        })    })</script><p>发起ajax请求</p>    <button id="btn">发起ajax请求</button>2. 提供处理方法//处理器返回void,响应ajax请求@RequestMapping(value = "/returnVoid-ajax.do")public void doReturnVoidAjax(HttpServletResponse response, String name, Integer age) throws IOException {    System.out.println("----doReturnVoidAjax----, name = " + name + "   age = " + age);    //Service调用完成了,使用Student表示处理结果    //处理ajax,使用json数据格式    Student student = new Student();    student.setName(name);    student.setAge(age);  	//把结果中的对象转换为json格式    String json = "";    if (student != null){        ObjectMapper om = new ObjectMapper();        json = om.writeValueAsString(student);        System.out.println("student转换的json ==>" + json);    }    //输出数据,响应ajax请求,指定为json格式    response.setContentType("application/json;charset=utf-8");    PrintWriter pw = response.getWriter();    pw.println(json);    pw.flush();    pw.close();}
Object

处理器方法返回的Object可以是Integer、String、Map、List、自定义对象等,作为数据直接在页面进行显示,与视图无关

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

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

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

    ​ 完成的是将对象转换为json格式数据

    ​ String json = “”;

    ​ ObjectMapper om = new ObjectMapper();

    ​ json = om.writeValueAsString(student);这一步

  3. 在处理器方法上面加入@RequestBody注解

    ​ 完成的是 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、二进制等数据格式的转换		HttpMessageConvert接口:消息转换器。这个接口有很多实现类,用于完成java对象转换为不同格式。		在springmvc配置文件中加入该注解后,会自动创建HttpMessageConverter接口的8个实现类对象,包括MappingJackson2HttpMessageConverter(使用jackson工具库中的ObjectMapper实现java对象转为json字符串)2. @RequestBody把处理器方法返回的对象转为json后,通过HttpServletResponse输出给浏览器。加在方法的定义上面,和其他注解没有顺序要求1)框架会把返回的Student类型,调用框架的ArrayList<HttpMessageConverter>中的每个类的canWrite方法,检查哪个HttpMessageConvert的实现类能处理Student类型的数据——MappingJackson2HttpMessageConvert2)框架会调用实现类的write方法,调用Jackson中的ObjectMapper,把student对象转换为json3)框架调用@ResponseBody把第2步的结果数据输出到浏览器,ajax请求完成==开发者只需要专注与业务逻辑和功能==
List和String
1. List处理器方法中返回多个对象时,返回值使用List,框架将其转换为json array2. String处理器方法返回的是String,且有@ResponseBody注解,此时返回的String表示的是数据,不是视图。	如果没有@ResponseBody注解,返回的是视图此时框架调用的是StringHttpMessageConverter的write方法,默认字符集是text/plain;charset=ISO-8859-1,中文会乱码,需要自定义@RequestMapping(value = "/returnStringData.do", produces = "text/plain;charset=utf-8")# 过滤器服务的是客户端(浏览器)对服务器端发起的请求,此时是服务器端返回给浏览器的ajax中文乱码,不经过过滤器

静态资源相关

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

1. tomcat处理http://localhost:8080/ch05_urlpattern/index.jsp                                     /js/jsquery-3.1.4.js                                     /images/1.png                                     /html/test.html2. DispatcherServlet处理(SpringMVC框架处理)http://localhost:8080/ch05_urlpattern/some.do

Tomcat的DefaultServlet

tomcat本身能够处理静态资源的访问,如html、图像、js文件等

Tomcat安装目录下conf文件夹下的web.xml中指定了tomcat服务器启动时使用的默认Servlet:<servlet>    <servlet-name>default</servlet-name>    <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>    <init-param>        <param-name>debug</param-name>        <param-value>0</param-value>    </init-param>    <init-param>        <param-name>listings</param-name>        <param-value>false</param-value>    </init-param>    <load-on-startup>1</load-on-startup></servlet><!-- The mapping for the default servlet --><servlet-mapping>    <servlet-name>default</servlet-name>    <url-pattern>/</url-pattern></servlet-mapping>DefaultServlet上的注释The default servlet for all web applications, that serves staticresources.  It processes all requests that are not mapped to otherervlets with servlet mappings (defined either here or in your ownweb.xml file).

即Tomcat中这个DefaultServlet的作用:处理静态资源、处理未进行对servlet映射的请求

​ servlet-mapping标签中url-pattern设置的是"/",表示的就是静态资源和未进行对servlet映射的请求

静态资源的两种访问方式

如果自己的项目中也将url-pattern设置为"/",那么会代替tomcat的DefaultServlet,此时如果想处理静态资源、未进行对servlet映射的请求,需要自己进行相关的配置

1.使用<mvc:default-servlet-handler />

依赖服务器默认的Servletspringmvc配置文件中声明了这个标签后,框架会在容器中创建DefaultServletHttpRequestHandler处理器对象(类似于我们自己创建的MyController)它会对进入我们定义的DispatcherSevlet的URL进行筛查,如果是静态资源请求,转发给Web应用服务器默认的Servlet处理。# <mvc:default-servlet-handler />要和<mvc:annotaion-driven />一起使用不然的话对于动态资源处理(some.do)会失败,handler会和@RequestMapping有冲突	我们把url-pattern设置为"/",又添加了<mvc:default-servlet-handler />,就是将全部请求交给tomcat的DefaultServlet处理,此时some.do的处理不知道交给谁,需要<mvc:annotaion-driven />来进行处理器映射器、适配器的注册。		即浏览器请求some.do时,有了<mvc:annotaion-driven />注册的处理器映射、适配器,就能找到@RequestMapping("/some.do")这个处理器方法,从而进行处理

2.使用<mvc:resources />

不依赖于服务器Spring3.0之后,定义了专门用于处理静态资源访问请求的处理器ResourceHttpRequestHandlerspringmvc配置文件中声明了这个标签后,框架会在容器中创建ResourceHttpRequestHandler处理器对象<mvc:resources mapping="/images/**" location="/images/" /><mvc:resources mapping="/html/**" location="/html/" /><mvc:resources mapping="/js/**" location="/js/" />	mapping:表示对该资源的请求	location:表示静态资源所在目录	**:代表文件及其子目录同样需要配合<mvc:annotaion-driven />一起使用

思考:本来Web服务器默认有对静态资源进行处理的相关Servlet,把url-pattern设置为"/",使用标签后要么完全交给服务器原来的默认Servlet,要么完全交给框架提供的Servlet,交出去之后对于自己写的xxx.do又处理不了,又需要<mvc:annotation-driven />…

​ 不过此时所有的action就不用写成xxx.do,直接写some即可,浏览器地址栏相应地显示. …/some,比较清晰

绝对路径和相对路径

@RequestMapping("/some.do")<form action  = "some.do" /><img src="static/images/1.png" /><a href="some.do">在jsp、html中使用的地址都是在前端页面中的地址,都是相对地址
  • 绝对地址

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

  • 相对地址

没有协议开头,不能单独使用,必须有一个参考地址,通过参考地址+相对地址才能找到资源。uesr/some.do

例如当前页面是http://localhost:8080/cha05_path/index.jsp那么当前	路径:http://localhost:8080/cha05_path/	资源:index.jsp1. 不以斜杠开头当前在index.jsp页面点击链接<a href="some.do">,此时访问的地址是当前页面的路径加上链接	http://localhost:8080/cha05_path/some.do2. 以斜杠开头当前在index.jsp页面点击链接<a href="/some.do">,此时访问的地址为	http://localhost:8080/some.do以斜杠开头,斜杠表示的是项目的根目录即http://localhost:8080/两种解决方式1. <a href="/cha05_return/some.do">  不够灵活2. <a href="${pageContext.request.contextPath}/some.do">  EL表达式# 对于不以"/"开头的请求,还可以使用html的base标签(在head标签中使用),页面中所有不以"/"开头的请求都会以base标签中指定的作为基地址<base href="http://localhost:8080/cha06_path/" />同样可以写为<base href = "${pageContext.request.contextPath}/">或<% String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/"; %>

SSM整合开发

SSM—Spring + SpringMVC + MyBatis整合开发思路

SpringMVC:视图(界面)层,负责接收和处理请求,并显示处理结果 view、controller

Spring:业务层,负责管理service、dao和工具类对象

MyBatis:持久层,负责访问数据库

用户发起请求 ==> SpringMVC接收请求 ==> 调用Spring中的Service(访问DAO—Data Access Object数据库操作对象) ==> MyBatis处理数据

整合中涉及两个容器

SpringMVC容器:负责管理Controller控制器对象

Spring容器:负责管理Service、DAO、工具类对象

开发者做的就是把使用的对象,交给合适的容器创建和管理。把Controller还有Web开发的相关对象交给SpringMVC容器,配置在SpringMVC的配置文件中。把Service、DAO对象配置在Spring的配置文件中。

​ SpringMVC容器是Spring容器的子容器,类似继承,子容器中的Controller才能够访问父容器中的Service和DAO

整合核心

  • 工厂
既要创建工厂,又要保证其唯一、能被共用。1. ServletContextListener(只会被创建一次)中创建工厂保证唯一2. ServletContext.setAttribute("xxx", context)保证了工厂能被共用Spring将上述两步封装为ContextLoaderListener1. 创建工厂2. 把工厂存储在ServletContext中ContextLoaderListener使用方式在web.xml中<listener>	<listener-class>org.springfamework.web.context.ContextLoaderListener</listener-class></listener><!--配置文件的路径--><context-param>	<param-name>contextConfigLocation</param-name>  <param-value>classpath:applicationContext.xml</param-value></context-param>
  • 控制器

把Service通过依赖注入到Controller中

开发步骤

  1. 数据库建表
CREATE TABLE `student` (  `id` int NOT NULL AUTO_INCREMENT,  `name` varchar(50) DEFAULT NULL,  `age` int DEFAULT NULL,  PRIMARY KEY (`id`));

新建maven web项目(使用骨架)

在这里插入图片描述

pom.xml文件

添加SpringMVC、Spring、MyBatis、jackson、mysql驱动、druid连接池、jsp/servlet依赖

build标签中配置

<dependencies>  <dependency>    <groupId>junit</groupId>    <artifactId>junit</artifactId>    <version>4.11</version>    <scope>test</scope>  </dependency>  <dependency>    <groupId>javax.servlet</groupId>    <artifactId>javax.servlet-api</artifactId>    <version>3.1.0</version>    <scope>provided</scope>  </dependency>  <dependency>    <groupId>javax.servlet</groupId>    <artifactId>jsp-api</artifactId>    <version>2.0</version>    <scope>provided</scope>  </dependency>  <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.1.14.RELEASE</version>  </dependency>  <dependency>    <groupId>org.springframework</groupId>    <artifactId>spring-jdbc</artifactId>    <version>5.1.14.RELEASE</version>  </dependency>  <dependency>    <groupId>com.fasterxml.jackson.core</groupId>    <artifactId>jackson-databind</artifactId>    <version>2.9.0</version>  </dependency>  <dependency>    <groupId>com.fasterxml.jackson.core</groupId>    <artifactId>jackson-core</artifactId>    <version>2.9.0</version>  </dependency>  <dependency>    <groupId>org.mybatis</groupId>    <artifactId>mybatis-spring</artifactId>    <version>1.3.1</version>  </dependency>  <dependency>    <groupId>org.mybatis</groupId>    <artifactId>mybatis</artifactId>    <version>3.5.1</version>  </dependency>  <dependency>    <groupId>mysql</groupId>    <artifactId>mysql-connector-java</artifactId>    <version>5.1.9</version>  </dependency>  <dependency>    <groupId>com.alibaba</groupId>    <artifactId>druid</artifactId>    <version>1.1.18</version>  </dependency></dependencies><build>  <resources>    <resource>      <directory>src/main/resources</directory>      <includes>        <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>

web.xml文件

1)注册中央调度器DispatcherServlet	目的:创建SpringMVC容器对象进而创建Controller类对象、作为Servlet接收用户请求2)注册Spring的监听器	目的:创建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">  <!--注册中央调度器-->  <servlet>    <servlet-name>myweb</servlet-name>    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>    <init-param>      <param-name>contextConfigLocation</param-name>      <param-value>classpath:conf/dispatcherServlet.xml</param-value>    </init-param>    <load-on-startup>1</load-on-startup>  </servlet>  <servlet-mapping>    <servlet-name>myweb</servlet-name>    <url-pattern>*.do</url-pattern>  </servlet-mapping>  <!--注册监听器(Spring部分)-->  <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>  <!--注册字符集过滤器-->  <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>

创建包

实体类的包——Controller、Service、DAO

在这里插入图片描述

配置文件

配置SpringMVC、Spring、MyBatis、数据库属性的配置文件

  • SpringMVC配置文件
<?xml version="1.0" encoding="UTF-8"?><beans ...>    <!--SpringMVC配置文件:声明Controller和其他Web相关的对象-->    <context:component-scan base-package="com.liu.controller" />    <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>
  • 数据库属性配置文件
jdbc.driverClassName = com.mysql.jdbc.Driverjdbc.url = jdbc:mysql://localhost:3306/springdb?useSSL=false&amp;allowPublicKeyRetrieval=truejdbc.username = rootjdbc.password = 123456
  • Spring配置文件
<?xml version="1.0" encoding="UTF-8"?><beans ...>    <!--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="close">        <property name="driverClassName" value="${jdbc.driverClassName}"/>        <property name="url" value="${jdbc.url}"/>        <property name="username" value="${jdbc.username}"/>        <property name="password" value="${jdbc.password}"/>    </bean>    <!--SqlSessionFactoryBean-->    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">        <property name="dataSource" ref="dataSource"/>        <property name="configLocation" value="classpath:conf/mybatis.xml"/>        <property name="typeAliasesPackage" value="com.liu.entity"/><!--        <property name="mapperLocations">--><!--            <list>--><!--                <value>classpath:com.liu.dao/*Mapper.xml</value>--><!--            </list>--><!--        </property>-->    </bean>    <!--创建Scanner  创建DAO对象-->    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>        <property name="basePackage" value="com.liu.dao"/>    </bean>    <!--声明service的注解@Service所在的包-->    <context:component-scan base-package="com.liu.service" />    <!--事务配置--></beans>

编写实体类和页面

  • 实体类、DAO接口、Mapper文件、Service及实现类、Controller
1. 实体类public class Student {    private Integer id;    private String name;    private Integer age;        getter setter}2. DAO接口public interface StudentDAO {    int insertStudent(Student student);    List<Student> selectStudents();}3. Mapper.xml文件<?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.liu.dao.StudentDAO">    <select id="selectStudents" resultType="Student">        select id,name,age from student order by id desc    </select>    <insert id="insertStudent">        insert into student(name, age) values(#{name}, #{age})    </insert></mapper>查询不写 “select *”,1.如果表结构变化了,直接写*可能会与实体类不对应2.节约网络流量4. Service接口及其实现类public interface StudentService {    int addStudent(Student student);    List<Student> findAllStudents();}@Servicepublic class StudentServiceImpl implements StudentService {    @Autowired    private StudentDAO studentDAO;    @Override    public int addStudent(Student student) {        int nums = studentDAO.insertStudent(student);        return nums;    }    @Override    public List<Student> findAllStudents() {        return studentDAO.selectAllStudents();    }}5. Controller@Controller@RequestMapping("/student")public class StudentController {    @Autowired    private StudentService service;    //注册学生功能    @RequestMapping("/addStudent.do")    public ModelAndView addStudent(Student student) throws SQLException {        String tips = "注册失败";        ModelAndView mv = new ModelAndView();        //调用Service        int result = service.addStudent(student);        if (result > 0){            //注册成功            tips = "学生[" + student.getName() + "]注册成功";        }        //添加数据        mv.addObject("tips", tips);        //指定结果页面        mv.setViewName("result");        return mv;    }    //处理查询请求,响应ajax    @RequestMapping("/queryStudent.do")    @ResponseBody    public List<Student> queryStudent(){        /*        参数检查、简单的数据处理        */        List<Student> students = service.findAllStudents();        return students;    }}
  • 页面
1. index.jsp<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head>    <title>功能入口</title>    <base href = "${pageContext.request.contextPath}/"></head><body>    <div align="center">        <p>SSM整合实例</p>        <img src="images/2.png" height="300px" width="400px" />        <table>            <tr>                <td><a href="addStudent.jsp">注册学生</a></td>            </tr>            <tr>                <td><a href="listStudent.jsp">浏览学生</a></td>            </tr>        </table>    </div></body></html>2. addStudent.jsp<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head>    <title>注册学生</title>    <base href = "${pageContext.request.contextPath}/"></head><body>    <div align="center">        <form action="student/addStudent.do" method="post">            <table>                <tr>                    <td>姓名</td>                    <td><input type="text" name="name"></td>                </tr>                <tr>                    <td>年龄</td>                    <td><input type="text" name="age"></td>                </tr>                <tr>                    <td>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>                    <td><input type="submit" value="注册"></td>                </tr>            </table>        </form>    </div></body></html>3. result.jsp<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head>    <title>注册结果</title></head><body>    result.jsp结果页面 注册结果: ${tips}</body></html>4. listStudent.jsp<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head>    <title>查询学生ajax</title>    <base href = "${pageContext.request.contextPath}/">    <script type="text/javascript" src="js/jquery-3.4.1.js"></script>    <script type="text/javascript">        $(function () {            //在当前页面dom对象加载后,执行loadStudentData()            loadStudentData();            $("#btnLoader").click(function () {                loadStudentData();            })        })        function loadStudentData(){            $.ajax({                url:"student/queryStudent.do",                // url:"student/findStudentByName.do",                type:"get",                dataType:"json",                success:function (data) {                    //alert("data: " + data);                    //清除旧的数据                    $("#info").html("");                    $.each(data, function (i, n) {                        $("#info").append("<tr>")                            .append("<td>" + n.id + "</td>")                            .append("<td>" + n.name + "</td>")                            .append("<td>" + n.age + "</td>")                            .append("</tr>")                    })                }            })        }    </script></head><body>    <div align="center">        <table>            <thead>                <tr>                    <td>学号</td>                    <td>姓名</td>                    <td>年龄</td>                </tr>            </thead>            <tbody id="info">            </tbody>        </table>        <input type="button" id="btnLoader" value="查询"><%--        <form action="student/findStudentByName.do" method="get">--%><%--            姓名:<input type="text" name="name"><br/>--%><%--            <input type="submit" value="查询">--%><%--        </form>--%>    </div></body></html>

SpringMVC核心技术

请求转发和重定向

当处理器对完成对请求的处理后,向其他资源跳转时有两种跳转方式:请求转发和重定向

  • Servlet中的转发和重定向
    • 转发:request.getRequestDispatcher(“xxx.jsp”).forward()
    • 重定向:response.sendRedirect(“xxx.jsp”)

在这里插入图片描述

forward和redirect都是关键字,它们都不和视图解析器一同工作

  • forward
语法:ModelAndView.setViewName("forward:视图文件完整路径")

视图解析器只能解析配置的固定路径下的页面,需要访问不在该路径中,就需要使用forward

  • redirect

目的是在两次请求之间传递参数

语法:setViewName("redirect:视图文件完整路径")框架会把Model中简单类型的数据转为String使用,作为hello.jsp的请求参数使用目的是在doRedirect.do和hello.jsp两次请求之间传递参数@RequestMapping(value = "/doRedirect.do")public ModelAndView doWithRedirect(){    ModelAndView mv = new ModelAndView();    //数据放入Request作用域    mv.addObject("msg", "转发forward");    mv.addObject("fun", "执行doSome方法");    //重定向    mv.setViewName("redirect:/hello.jsp");    return mv;}此时如果使用${msg}、${fun}是取不到数据的,应为重定向是两次请求,第二个request中没有mv.addObject("msg", "转发forward")、mv.addObject("fun", "执行doSome方法")这两句但是浏览器请求地址栏中有参数http://localhost:8080/ch07_forward_redirect/hello.jsp?msg=转发forward&fun=执行doSome方法# 此时使用${param.msg}、${param.fun}能够取到参数,相当于request.getParameter()# 重定向不能访问受保护的资源(/WEB-INF/..)

统一全局异常处理

之前处理异常需要在方法中加入try catch,代码量多时会有大量冗余。

应用AOP思想,将处理异常的代码分离出来作为额外功能,方法中只保留业务方法

SpringMVC采用统一、全局的异常处理,使用两个注解@ExceptionHandler、@ControllerAdvice

开发步骤

  • 创建一个普通类,作为全局异常处理类,其中的方法和Controller中的方法用法完全一致
  1. 在类的上面加上@ControllerAdvice
  2. 在类中定义方法,加上@ExceptionHandler
@ControllerAdvicepublic class GlobalExceptionHandler {    /*    * 定义方法来处理异常    *    * 方法的定义和属性和Controller中的doSome方法一样    * 形参:Controller抛出的异常对象    */     //处理其他异常    @ExceptionHandler()    public ModelAndView doOtherException(Exception exception){        ModelAndView mv = new ModelAndView();        mv.addObject("msg", "年龄不能大于80");        mv.addObject("ex", exception);        mv.setViewName("defaultError");        return mv;    }}
  • 在配置文件中配置组件扫描器对@ControllerAdvice所在包进行扫描,加上注解驱动
<context:component-scan base-package="com.liu.handler"/><mvc:annotation-driven/>

拦截器

SpringMVC中的Interceptor拦截器对象非常重要,需要实现HandlerInterceptor接口,和过滤器类似但功能侧重点不同,拦截器主要作用是拦截指定的用户请求,进行相应的预处理和后处理

  • 过滤器主要用来过滤请求参数、设置编码字符集等工作
  • 拦截器是全局的,可以对多个Controller做拦截
  • 常用于登录处理、权限检查、记录日志

实质上就是多个Controller共用的功能,集中到拦截器进行统一处理,是AOP的思想

执行时间

  1. 请求处理之前,即Controller类中的方法执行前先执行拦截器
  2. 在控制器方法执行之后也会执行拦截器
  3. 在请求处理完成后也会执行拦截器

开发步骤

  1. 定义一个实现HandlerInterceptor接口的类,在其中对请求进行处理

最重要的是preHandler()方法,它是整个项目的入口、门户

public class MyInterceptor implements HandlerInterceptor {    /*    * preHandle 预处理方法    *    * 参数:Object handler——被拦截的控制器对象(MyController)    * 返回值:boolean    *   true:请求通过拦截器的验证,继续执行往下执行    *   false:请求没有通过拦截器的验证,停止执行    *    * 1. 在MyController的doSome方法前执行    * 2. 可以验证用户登录、是否有权限访问某个链接地址,验证失败可以截断请求    * */    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        System.out.println("拦截器MyInterceptor的preHandler方法");        //给浏览器一个返回结果        request.getRequestDispatcher("/tips.jsp").forward(request, response);        return false;    }    /*    * postHandler 后处理方法    * 参数:    *   Object handler:被拦截的控制器对象(MyController)    *   ModelAndView modelAndView:处理器方法的返回值    *    * 1. 在MyController的doSome方法后执行    * 2. 能够获取到处理器方法返回的modelAndView,可以修改其中的数据和视图,对原来的结果进行二次修正    * */    @Override    public void postHandle(HttpServletRequest request,                           HttpServletResponse response,                           Object handler,                           ModelAndView modelAndView) throws Exception {        System.out.println("拦截器MyInterceptor的postHandler方法");    }    /*    * afterCompletion 最后执行的方法    *    * 1. 在请求处理完成后执行——当视图处理完成后,即对视图执行了forward后    * 2. 一般做资源回收,删除程序在请求过程中创建的一些对象    * */    @Override    public void afterCompletion(HttpServletRequest request,                                HttpServletResponse response,                                Object handler,                                Exception ex) throws Exception {        System.out.println("拦截器MyInterceptor的afterCompletion方法");    }}
  1. 在SpringMVC配置文件中声明拦截器
 <!--声明拦截器,可以有0个或多个--><mvc:interceptors>    <mvc:interceptor>      	<!--拦截器的拦截地址路径-->        <mvc:mapping path="/**"/>        <!--声明拦截器对象-->        <bean class="com.liu.handler.MyInterceptor"/>    </mvc:interceptor></mvc:interceptors>

多个拦截器

可以把不同的验证写在不同的拦截器里,登录验证、请求权限验证等

有多个拦截器时,框架中通过ArrayList来保存,按照配置文件中声明拦截器的先后顺序放入到ArrayList中

<mvc:interceptors>    <mvc:interceptor>        <mvc:mapping path="/**"/>        <!--声明拦截器对象-->        <bean class="com.liu.handler.MyInterceptor"/>    </mvc:interceptor>    <!--声明第二个拦截器-->    <mvc:interceptor>        <mvc:mapping path="/**"/>        <bean class="com.liu.handler.MyInterceptor2"/>    </mvc:interceptor></mvc:interceptors>

执行结果

在这里插入图片描述

执行链

在这里插入图片描述

拦截器和过滤器的区别

1. 过滤器是Servlet规范中的对象,拦截器是SpringMVC框架中的对象   即过滤器是在Tomcat服务器中的对象,拦截器是在SpringMVC容器中的对象   2. 过滤器实现Filter接口,拦截器实现HandlerInterceptor接口3. 过滤器用来设置request、response的参数、属性,侧重于对数据的过滤   拦截器是用来验证请求的,能够截断请求   4. 过滤器只有一个执行时间点——在拦截器之前先执行   拦截器有三个执行时间点5. 过滤器可以处理jsp、js、html等   拦截器侧重拦截Controller对象,如果请求不能被DispatcherServlet接收,这个请求不会执行拦截器的内容(因为拦截器是组合执行链后返回给中央调度器进行处理)

用户登录验证例子

模拟登陆——将用户数据放入到session中

模拟退出——将用户数据从session中删除

1. index.jsp<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head>    <title>用户登录权限验证</title>    <base href="${pageContext.request.contextPath}/"></head><body>    <p>拦截器</p>    <form action="some.do" method="post">        姓名:<input type="text" name="name"><br/>        年龄:<input type="text" name="age"><br/>        <input type="submit" value="提交请求">    </form></body></html>  login.jsp<%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head>    <title>Title</title></head><body>    模拟登录,zhangsan登录系统    <%        session.setAttribute("name", "zhangsan");    %></body></html>2. MyInterceptor.javapublic class MyInterceptor implements HandlerInterceptor {    //验证登录的用户信息    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        System.out.println("拦截器MyInterceptor的preHandler方法");        String loginName = "";        Object attr = request.getSession().getAttribute("name");        if (attr != null){            loginName = (String) attr;        }        if (!"zs".equals(loginName)){            //不能访问系统            request.getRequestDispatcher("/tips.jsp").forward(request, response);            return false;        }        return true;    }}3. SpringMVC配置文件中<mvc:interceptors>    <mvc:interceptor>        <mvc:mapping path="/**"/>        <!--声明拦截器对象-->        <bean class="com.liu.handler.MyInterceptor"/>    </mvc:interceptor></mvc:interceptors>

SpringMVC执行流程

在这里插入图片描述

  1. 用户发起请求some.do

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

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

    ​ 作用:根据请求从SpringMVC容器对象中获取处理器对象

    ​ MyController controller = context.getBean(“some.do”)

  3. 框架把找到的处理器对象、项目中所有的拦截器对象,放到HandlerExecutionChain(处理器执行链)这个类中,返回给DispatcherServlet

  4. DispatcherServlet把HandlerExecutionChain中的处理器对象交给处理器适配器HandleAdaptor(多个)

    ​ 处理器适配器:SpringMVC框架中的一种对象,实现了HandlerAdaptor接口的类都叫做适配器(多个)

    ​ 作用:执行处理器方法(调用doSome方法 返回ModelAndView)

  5. HandleAdaptor将方法执行后返回的ModelAndView返回给DispatcherServlet

  6. DispatchServlet把获取的ModelAndView交给视图解析器对象ViewResolver

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

    ​ View是一个接口,表示视图,在框架中jsp、html是使用View及其实现类来表示的

    ​ 对于jsp,视图解析器创建InternalResourceView对象,这个对象中包含一个属性url=/WEB-INF/view/show.jsp

  7. DispatcherServlet获取到View对象,调用View中的方法,把Model数据放入到Request作用域,并执行对象视图的forward,请求结束

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值