第1章 SpringMVC 概述

1.1 SpringMVC 简介

SpringMVC 也叫 Spring web mvc。是 Spring 框架的一部分,是在 Spring3.0 后发布的。

1.2 SpringMVC 优点

1.基于 MVC 架构
基于 MVC 架构,功能分工明确。解耦合,

MVC(Model–View–Controller)模式是软件工程中的一种软件架构模式,它把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。
模型(Model):程序员编写程序应有的功能(实现算法等)、数据库专家进行数据管理和数据库设计(可以实现具体的功能);
控制器(Controller):负责转发请求,对请求进行处理;
视图(View):界面设计人员进行图形界面设计。
MVC要实现的目标是将软件用户界面和业务逻辑分离以使代码可扩展性、可复用性、可维护性、灵活性加强。
比方说,有一个View会提交数据给Model进行处理以实现具体的行为,View通常不会直接提交数据给Model,它会先把数据提交给Controller,然后Controller再将数据转发给Model。假如此时程序业务逻辑的处理方式有变化,那么只需要在Controller中将原来的Model换成新实现的Model就可以了,控制器的作用就是这么简单, 用来将不同的View和不同的Model组织在一起,顺便替双方传递消息,仅此而已。
视图View
负责页面的显示;与用户的交互。包含各种表单。 实现视图用到的技术有html/css/jsp/js等前端技术。
用户交互:用户鼠标点击页面;填写页面中各种表单…等等
模型Model
模型负责各个功能的实现(如登录、增加、删除功能)。模型用JavaBean实现。
控制器Controller
控制器负责将视图与模型一一对应起来。相当于一个模型分发器。所谓分发就是:①接收请求,并将该请求跳转(转发,重定向)到模型进行处理。②模型处理完毕后,再通过控制器,返回给视图中的请求处。
三层架构:三层架构与MVC的目标一致:都是为了解耦和、提高代码复用。MVC是一种设计模式,而三层架构是一种软件架构。
三层架构分为:表现层(UI)(web层)、业务逻辑层(BLL)(service层)、数据访问层(DAL)(dao层) ,再加上实体类库(Model)

2.容易理解,上手快;使用简单。
就可以开发一个注解的 SpringMVC 项目,SpringMVC 也是轻量级的,jar 很小。不依赖的特定的接口和类。
3. 作 为 Spring 框 架 一 部 分 , 能 够 使 用 Spring 的 IoC 和 Aop 。 方 便 整 合Strtus,MyBatis,Hiberate,JPA 等其他框架。

4.SpringMVC 强化注解的使用,在控制器,Service,Dao 都可以使用注解。方便灵活。
使用@Controller 创建处理器对象,@Service 创建业务对象,@Autowired 或者@Resource
在控制器类中注入 Service, Service 类中注入 Dao。

1.3 第一个注解的 SpringMVC 程序

ch01-hello-springmvc: 第一个springmvc项目。
需求: 用户在页面发起一个请求, 请求交给springmvc的控制器对象,并显示请求的处理结果(在结果页面显示一个欢迎语句)。实现步骤:
1. 新建web maven工程
2. 加入依赖spring-webmvc依赖,间接把spring的依赖都加入到项目jsp,servlet依赖3.重点: 在web.xml中注册springmvc框架的核心对象DispatcherServlet1)DispatcherServlet叫做中央调度器, 是一个servlet, 它的父类是继承HttpServlet2)DispatcherServlet页叫做前端控制器(front controller)3)DispatcherServlet负责接收用户提交的请求, 调用其它的控制器对象,并把请求的处理结果显示给用户4.创建一个发起请求的页面 index.jsp5.创建控制器(处理器)类1)在类的上面加入@Controller注解,创建对象,并放入到springmvc容器中2)在类中的方法上面加入@RequestMapping注解。6.创建一个作为结果的jsp,显示请求的处理结果。7.创建springmvc的配置文件(spring的配置文件一样)1)声明组件扫描器, 指定@Contorller注解所在的包名2)声明视图解析器。帮助处理视图的。

1.3.1 新建 maven web 项目

1.3.2 pom.xml

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

  <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><!--springmvc依赖--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.5.RELEASE</version></dependency></dependencies>

1.3.3 注册中央调度器

    <!--声明,注册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)Could not open ServletContext resource [/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>

(1) 全限定性类名
该中央调度器为一个 Servlet,名称为 DispatcherServlet。中央调度器的全限定性类名在导入的 Jar 文件 spring-webmvc-5.2.5.RELEASE.jar 的第一个包 org.springframework.web.servlet下可找到。

(2) load-on-startup
<servlet/>中添加<load-on-startup/>的作用是,标记是否在Web服务器(这里是Tomcat)启动时会创建这个 Servlet 实例,即是否在 Web 服务器启动时调用执行该 Servlet 的 init()方法,而不是在真正访问时才创建。

它的值必须是一个整数。
当值大于等于 0 时,表示容器在启动时就加载并初始化这个 servlet,数值越小,该 Servlet的优先级就越高,其被创建的也就越早;
当值小于 0 或者没有指定时,则表示该 Servlet 在真正被使用时才会去创建。
当值相同时,容器会自己选择创建顺序。

(3) <url-pattern/>
对于<url-pattern/>,可以写为 / ,建议写为*.do 的形式。

(4) 配置文件位置与名称
注册完毕后,可直接在服务器上发布运行。此时,访问浏览器页面,控制台均会抛出FileNotFoundException 异常。即默认要从项目根下的 WEB-INF 目录下找名称为 Servlet 名称-servlet.xml 的配置文件。这里的“Servlet 名称”指的是注册中央调度器<servlet-name/>标签中指定的 Servlet 的 name 值。本例配置文件名为 myweb-servlet.xml。
而一般情况下,配置文件是放在类路径下,即 resources 目录下。所以,在注册中央调度器时,还需要为中央调度器设置查找 SpringMVC 配置文件路径,及文件名。

        <!--自定义springmvc读取的配置文件的位置--><init-param><!--springmvc的配置文件的位置的属性--><param-name>contextConfigLocation</param-name><!--指定自定义文件的位置--><param-value>classpath:springmvc.xml</param-value></init-param>

打开 DispatcherServlet 的源码,其继承自 FrameworkServlet,而该类中有一个属性contextConfigLocation,用于设置 SpringMVC 配置文件的路径及文件名。该初始化参数的属性就来自于这里。

1.3.4 创建 SpringMVC 配置文件

在工程的类路径即 src 目录下创建 SpringMVC 的配置文件 springmvc.xml。该文件名可以任意命名。

1.3.5 创建处理器

在类上与方法上添加相应注解即可。
@Controller:表示当前类为处理器
@RequestMapping:表示当前方法为处理器方法。该方法要对 value 属性所指定的 URI进行处理与响应。被注解的方法的方法名可以随意。

/***  @Controller:创建处理器对象,对象放在springmvc容器中。*  位置:在类的上面*  和Spring中讲的@Service ,@Component**  能处理请求的都是控制器(处理器): MyController能处理请求,*                         叫做后端控制器(back controller)**  没有注解之前,需要实现各种不同的接口才能做控制器使用*/
@Controller
public class MyController {/*处理用户提交的请求,springmvc中是使用方法来处理的。方法是自定义的, 可以有多种返回值, 多种参数,方法名称自定义*//*** 准备使用doSome方法处理some.do请求。* @RequestMapping: 请求映射,作用是把一个请求地址和一个方法绑定在一起。*                  一个请求指定一个方法处理。*       属性: 1. value 是一个String,表示请求的uri地址的(some.do)。*                value的值必须是唯一的, 不能重复。 在使用时,推荐地址以“/”*       位置:1.在方法的上面,常用的。*            2.在类的上面*  说明: 使用RequestMapping修饰的方法叫做处理器方法或者控制器方法。*  使用@RequestMapping修饰的方法可以处理请求的,类似Servlet中的doGet, doPost**  返回值:ModelAndView 表示本次请求的处理结果*   Model: 数据,请求处理完成后,要显示给用户的数据*   View: 视图, 比如jsp等等。*/@RequestMapping(value = "/some.do")public ModelAndView doSome(){// doGet()--service请求处理//处理some.do请求了。 相当于service调用处理完成了。  以后讲ModelAndView mv  = new ModelAndView();//添加数据, 框架在请求的最后把数据放入到request作用域。//request.setAttribute("msg","欢迎使用springmvc做web开发");mv.addObject("msg","欢迎使用springmvc做web开发");mv.addObject("fun","执行的是doSome方法");//指定视图, 指定视图的完整路径//框架在最后对视图执行的forward操作, request.getRequestDispather("/show.jsp).forward(...)//mv.setViewName("/show.jsp");mv.setViewName("/WEB-INF/show.jsp");return mv;}

若有多个请求路径均可匹配该处理器方法的执行,则@RequestMapping 的 value 属性中可以写上一个数组。

ModelAndView 类中的 addObject()方法用于向其 Model 中添加数据。Model 的底层为一个 HashMap。
Model 中的数据存储在 request 作用域中,SringMVC 默认采用转发的方式跳转到视图,本次请求结束,模型中的数据被销毁。

1.3.6 声明组件扫描器

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

1.3.7 定义目标页面

在 webapp 目录下新建一个子目录 jsp,在其中新建一个 jsp 页面show.jsp。

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

1.3.8 修改视图解析器的注册

SpringMVC 框架为了避免对于请求资源路径与扩展名上的冗余,在视图解析器InternalResouceViewResolver 中引入了请求的前辍与后辍。而 ModelAndView 中只需给出要跳转页面的文件名即可,对于具体的文件路径与文件扩展名,视图解析器会自动完成拼接。

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

1.3.9 修改处理器

使用逻辑视图名称,show 是逻辑视图名称。

1.3.10 使用 SpringMVC 框架 web 请求处理顺序

1.4 SpringMVC 的 MVC 组件

1.5 SpringMVC 执行流程(理解)

1.5.1 流程图

1.5.2 执行流程简单分析

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

第2章 SpringMVC 注解式开发

2.1 @RequestMapping 定义请求规则

2.1.1 指定模块名称

通过@RequestMapping 注解可以定义处理器对于请求的映射规则。该注解可以注解在方法上,也可以注解在类上,但意义是不同的。value 属性值常以“/”开始。

@RequestMapping 的 value 属性用于定义所匹配请求的 URI。但对于注解在方法上与类上,其 value 属性所指定的 URI,意义是不同的。

一个@Controller 所注解的类中,可以定义多个处理器方法。当然,不同的处理器方法所匹配的 URI 是不同的。这些不同的 URI 被指定在注解于方法之上的@RequestMapping 的value 属性中。但若这些请求具有相同的 URI 部分,则这些相同的 URI,可以被抽取到注解在类之上的@RequestMapping 的 value 属性中。此时的这个 URI 表示模块的名称。URI 的请求是相对于 Web 的根目录。

换个角度说,要访问处理器的指定方法,必须要在方法指定 URI 之前加上处理器类前定义的模块名称

@Controller
@RequestMapping(value = "/user")
public class MyController {@RequestMapping(value = "/some.do",method = RequestMethod.GET)public ModelAndView doSome(){// doGet()--service请求处理//处理some.do请求了。 相当于service调用处理完成了。  以后讲ModelAndView mv  = new ModelAndView();//添加数据, 框架在请求的最后把数据放入到request作用域。//request.setAttribute("msg","欢迎使用springmvc做web开发");mv.addObject("msg","欢迎使用springmvc做web开发");mv.addObject("fun","执行的是doSome方法");//指定视图, 指定视图的完整路径//框架在最后对视图执行的forward操作, request.getRequestDispather("/show.jsp).forward(...)mv.setViewName("show");//mv.setViewName("/WEB-INF/show.jsp");return mv;}

2.1.2 对请求提交方式的定义

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

以上处理器方法只能处理 POST 方式提交的请求。客户端浏览器常用的请求方式,及其提交方式有以下几种:

2.2 处理器方法的参数

处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用。
➢ HttpServletRequest
➢ HttpServletResponse
➢ HttpSession
➢ 请求中所携带的请求参数

2.2.1 逐个参数接收

只要保证请求参数名与该请求处理方法的参数名相同即可。

Step1:修改 index 页面

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

Step2:修改处理器类 MyController

    @RequestMapping(value = "/receiveproperty.do")public ModelAndView doSome(String name, Integer age){System.out.println("doSome, name="+name+"   age="+age);//可以在方法中直接使用 name , age//处理some.do请求了。 相当于service调用处理完成了。ModelAndView mv  = new ModelAndView();mv.addObject("myname",name);mv.addObject("myage",Integer.valueOf(age));//show是视图文件的逻辑名称(文件名称)mv.setViewName("show");return mv;}

Step3:添加 show 页面

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

2.2.2 请求参数中文乱码问题

对于前面所接收的请求参数,若含有中文,则会出现中文乱码问题。Spring 对于请求参数中的中文乱码问题,给出了专门的字符集过滤器:spring-web-5.2.5.RELEASE.jar 的org.springframework.web.filter 包下CharacterEncodingFilter 类。
(1) 解决方案
在 web.xml 中注册字符集过滤器,即可解决 Spring 的请求参数的中文乱码问题。不过,最好将该过滤器注册在其它过滤器之前。因为过滤器的执行是按照其注册顺序进行的。直接在项目 receiveParameters-property 上进行修改。

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

(2) 源码分析

2.2.3 校正请求参数名@RequestParam

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

        <form action="user/receiveproperty2.do" method="post">姓名:<input type="text" name="rname"> <br/>年龄:<input type="text" name="rage"> <br/><input type="submit" value="提交参数"></form>
    @RequestMapping(value = "/receiveproperty2.do")public ModelAndView doSome2(@RequestParam(value = "rname",required = false) String name,@RequestParam(value = "rage",required = false) Integer age){System.out.println("doSome, name="+name+"   age="+age);//可以在方法中直接使用 name , age//处理some.do请求了。 相当于service调用处理完成了。ModelAndView mv  = new ModelAndView();mv.addObject("rmyname",name);mv.addObject("rmyage",Integer.valueOf(age));//show是视图文件的逻辑名称(文件名称)mv.setViewName("show");return mv;}

2.2.4 对象参数接收-vo

将处理器方法的参数定义为一个对象,只要保证请求参数名与这个对象的属性同名即可。项目:receiveParameters-object。在 receiveParameters-property 基础上修改。

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

Step1:定义类 Student

public class Student {private String name;private int age;public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}

Step2:修改处理器类 MyController

    @RequestMapping(value = "/receiveobject.do")public ModelAndView doSome3(Student student){System.out.println("doSome, name="+student.getName()+"   age="+student.getAge());//可以在方法中直接使用 name , age//处理some.do请求了。 相当于service调用处理完成了。ModelAndView mv  = new ModelAndView();mv.addObject("xmyname",student.getName());mv.addObject("xmyage",student.getAge());mv.addObject("mystudent",student.toString());//show是视图文件的逻辑名称(文件名称)mv.setViewName("show");return mv;}

2.3 处理器方法的返回值

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

2.3.1 返回 ModelAndView

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

2.3.2 返回 String

处理器方法返回的字符串可以指定逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址
返回内部资源逻辑视图名
若要跳转的资源为内部资源,则视图解析器可以使用 InternalResourceViewResolver 内部资源视图解析器。此时处理器方法返回的字符串就是要跳转页面的文件名去掉文件扩展名后的部分。这个字符串与视图解析器中的 prefix、suffix 相结合,即可形成要访问的 URI。

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

直接修改处理器类 MyController

/*** 处理器方法返回String--表示逻辑视图名称,需要配置视图解析器*/
@RequestMapping(value = "/returnString-view.do")
public String doReturnView(HttpServletRequest request,String name, Integer age){System.out.println("doReturnView, name="+name+"   age="+age);//可以自己手工添加数据到request作用域request.setAttribute("myname",name);request.setAttribute("myage",age);// show : 逻辑视图名称,项目中配置了视图解析器// 框架对视图执行forward转发操作return "show";
}

当然,也可以直接返回资源的物理视图名。不过,此时就不需要再在视图解析器中再配置前辍与后辍了。

   //处理器方法返回String,表示完整视图路径, 此时不能配置视图解析器@RequestMapping(value = "/returnString-view2.do")public String doReturnView2(HttpServletRequest request,String name, Integer age){System.out.println("===doReturnView2====, name="+name+"   age="+age);//可以自己手工添加数据到request作用域request.setAttribute("myname",name);request.setAttribute("myage",age);// 完整视图路径,项目中不能配置视图解析器// 框架对视图执行forward转发操作// /WEB-INF/view//WEB-INF/view/show.jsp.jsp// /WEB-INF/view/WEB-INF/view/show.jsp.jspreturn "/WEB-INF/view/show.jsp";}

2.3.3 返回 void(了解)

对于处理器方法返回 void 的应用场景,AJAX 响应.
若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回 void。

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

    <!--Jackson依赖--><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></dependencies>

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

当然,该 jQuery 库文件,需要在使用 ajax()方法的 index 页面中引入。

<script type="text/javascript" src="js/jquery-3.4.1.js"></script>

Step3:定义 index 页面
index 页面由两部分内容构成:一个是<button/>,用于提交 AJAX 请求;一个是<script/>,用于处理 AJAX 请求。

     <script type="text/javascript">$(function(){$("button").click(function(){//alert("button click");$.ajax({//url:"returnVoid-ajax.do",//url:"returnStudentJsonArray.do",url:"returnStringData.do",data:{name:"zhangsan",age:20},type:"post",dataType:"text",//dataType:"json",success:function(resp){//resp从服务器端返回的是json格式的字符串 {"name":"zhangsan","age":20}//jquery会把字符串转为json对象, 赋值给resp形参。// [{"name":"李四同学","age":20},{"name":"张三","age":28}]//alert(resp.name + "    "+resp.age);/*$.each(resp,function(i,n){alert(n.name+"   "+n.age)})*/alert("返回的是文本数据:"+resp);}})})})</script>

点击按钮发起请求

<button id="btn">发起ajax请求</button>

Step4: 定义对象 Student

public class Student {// 属性名和请求中参数名一样private String name;private Integer age;

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

    //处理器方法返回void, 响应ajax请求//手工实现ajax,json数据: 代码有重复的 1. java对象转为json; 2. 通过HttpServletResponse输出json数据@RequestMapping(value = "/returnVoid-ajax.do")public void doReturnVoidAjax(HttpServletResponse response, String name, Integer age) throws IOException {System.out.println("===doReturnVoidAjax====, name="+name+"   age="+age);//处理ajax, 使用json做数据的格式//service调用完成了, 使用Student表示处理结果Student student  = new Student();student.setName("张飞同学");student.setAge(28);String json = "";//把结果的对象转为json格式的数据if( student != null){ObjectMapper om  = new ObjectMapper();json  = om.writeValueAsString(student);System.out.println("student转换的json===="+json);}//输出数据,响应ajax的请求response.setContentType("application/json;charset=utf-8");PrintWriter pw  = response.getWriter();pw.println(json);pw.flush();pw.close();}

2.3.4 返回对象 Object

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

(1) 环境搭建

A、 maven pom.xml
由于返回 Object 数据,一般都是将数据转化为了 JSON 对象后传递给浏览器页面的。而这个由 Object 转换为 JSON,是由 Jackson 工具完成的。所以需要导入 Jackson 的相关 Jar 包。

B、 声明注解驱动
将 Object 数据转化为 JSON 数据,需要由消息转换器 HttpMessageConverter 完成。而转换器的开启,需要由<mvc:annotation-driven/>来完成。
SpringMVC 使用消息转换器实现请求数据和对象,处理器方法返回对象和响应输出之间的自动转换

当 Spring 容器进行初始化过程中,在<mvc:annotation-driven/>处创建注解驱动时,默认创建了七个 HttpMessageConverter 对象。也就是说,我们注册<mvc:annotation-driven/>,就是为了让容器为我们创建 HttpMessageConverter 对象。

<mvc:annotation-driven/>

HttpMessageConverter 接口 : HttpMessageConverter是 Spring3.0 新添加的一个接口,负责将请求信息转换为一个对象(类型为 T),将对象(类型为 T)输出为响应信息


public interface HttpMessageConverter<T> {boolean canRead(Class<?> var1, @Nullable MediaType var2);boolean canWrite(Class<?> var1, @Nullable MediaType var2);List<MediaType> getSupportedMediaTypes();T read(Class<? extends T> var1, HttpInputMessage var2) throws IOException, HttpMessageNotReadableException;void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3) throws IOException, HttpMessageNotWritableException;
}

HttpMessageConverter接口定义的方法:

boolean canRead(Class<?> clazz,MediaType mediaType): 指定转换器可以读取的对象类型,即转 换 器 是 否 可 将 请 求 信 息 转 换 为 clazz 类 型 的 对 象 , 同 时 指 定 支 持 MIME 类 型(text/html,applaiction/json 等)

boolean canWrite(Class<?> clazz,MediaType mediaType):指定转换器是否可将 clazz 类型的对象写到响应流中,响应流支持的媒体类型在 MediaType 中定义。

LIst<MediaType> getSupportMediaTypes():该转换器支持的媒体类型。

T read(Class<? extends T> clazz,HttpInputMessage inputMessage):将请求信息流转换为 T 类型的对象。

void write(T t,MediaType contnetType,HttpOutputMessgae outputMessage):将 T 类型的对象写到响应流中,同时指定相应的媒体类型为 contentType

加入注解驱动<mvc:annotation-driven/>后适配器类的messageConverters 属性值

(2) 返回自定义类型对象
返回自定义类型对象时,不能以对象的形式直接返回给客户端浏览器,而是将对象转换为 JSON 格式的数据发送给浏览器的。
由于转换器底层使用了Jackson转换方式将对象转换为JSON数据,所以需要导入Jackson的相关 Jar 包。
Step1:定义数据类

public class Student {private String name;private int age;

Step2:修改处理器 MyController

Step3:修改 index 页面

(3) 返回 List 集合

Step2:修改 index 页面

(4) 返回字符串对象

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

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

2.4 解读<url-pattern/>

2.4.1 配置详解

(1) *.do
在没有特殊要求的情况下,SpringMVC 的中央调度器 DispatcherServlet 的<url-pattern/>常使用后辍匹配方式,如写为*.do 或者 *.action, *.mvc 等。

(2) /
可以写为/,因为 DispatcherServlet 会将向静态资源的获取请求,例如.css、.js、.jpg、.png等资源的获取请求,当作是一个普通的 Controller 请求。中央调度器会调用处理器映射器为其查找相应的处理器。当然也是找不到的,所以在这种情况下,所有的静态资源获取请求也均会报 404 错误。

2.4.2 静态资源访问

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

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

在 Tomcat 中,有一个专门用于处理静态资源访问的 Servlet 名叫 DefaultServlet。其<servlet-name/>为 default。可以处理各种静态资源访问请求。该 Servlet 注册在 Tomcat 服务器的 web.xml 中。在 Tomcat 安装目录/conf/web.xml。

只需要在 springmvc.xml 中添加<mvc:default-servlet-handler/>标签即可。

<mvc:default-servlet-handler/>表示使用DefaultServletHttpRequestHandler 处理器对象。而该处理器调用了 Tomcat 的 DefaultServlet 来处理静态资源的访问请求。
当然了,要想使用<mvc: …/>标签,需要引入 mvc 约束

(2) 使用<mvc:resources/>(掌握)

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

location 表示静态资源所在目录。当然,目录不要使用/WEB-INF/及其子目录。
mapping 表 示 对 该 资 源 的 请 求 ( 以 /images/ 开 始 的 请 求 , 如 /image/beauty.jpg , /images/car.png 等)。注意,后面是两个星号**。

(3) 声明注解驱动
解决动态资源和静态资源冲突的问题,在 springmvc 配置文件加入:

第3章 SSM 整合开发

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

SSM 整合的实现方式可分为两种:基于 XML 配置方式,基于注解方式。

ch07-ssm: SSM整合开发。
SSM: SpringMVC + Spring + MyBatis.SpringMVC:视图层,界面层,负责接收请求,显示处理结果的。
Spring:业务层,管理service,dao,工具类对象的。
MyBatis:持久层, 访问数据库的用户发起请求--SpringMVC接收--Spring中的Service对象--MyBatis处理数据SSM整合也叫做SSI (IBatis也就是mybatis的前身), 整合中有容器。
1.第一个容器SpringMVC容器, 管理Controller控制器对象的。
2.第二个容器Spring容器,管理Service,Dao,工具类对象的
我们要做的把使用的对象交给合适的容器创建,管理。 把Controller还有web开发的相关对象
交给springmvc容器, 这些web用的对象写在springmvc配置文件中service,dao对象定义在spring的配置文件中,让spring管理这些对象。springmvc容器和spring容器是有关系的,关系已经确定好了
springmvc容器是spring容器的子容器, 类似java中的继承。 子可以访问父的内容
在子容器中的Controller可以访问父容器中的Service对象, 就可以实现controller使用service对象实现步骤:
0.使用springdb的mysql库, 表使用student(id auto_increment, name, age)
1.新建maven web项目
2.加入依赖springmvc,spring,mybatis三个框架的依赖,jackson依赖,mysql驱动,druid连接池jsp,servlet依赖3.写web.xml1)注册DispatcherServlet ,目的:1.创建springmvc容器对象,才能创建Controller类对象。2.创建的是Servlet,才能接受用户的请求。2)注册spring的监听器:ContextLoaderListener,目的: 创建spring的容器对象,才能创建service,dao等对象。3)注册字符集过滤器,解决post请求乱码的问题4.创建包, Controller包, service ,dao,实体类包名创建好5.写springmvc,spring,mybatis的配置文件1)springmvc配置文件2)spring配置文件3)mybatis主配置文件4)数据库的属性配置文件6.写代码, dao接口和mapper文件, service和实现类,controller, 实体类。
7.写jsp页面

3.1 搭建 SSM 开发环境

3.1.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><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><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><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.12</version></dependency></dependencies>

3.1.2 配置 web.xml

(1) 配置中央调度器
配置中央调度器时需要注意,SpringMVC的配置文件名与其它 Spring配置文件名不相同。这样做的目的是 Spring 容器创建管理 Spring 配置文件中的 bean, SpringMVC 容器中负责视图层 bean 的初始。

<!--  注册中央调度器 springmvc容器创建--><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>

(2) 注册 ContextLoaderListener 监听器
注册 ServletContext 监听器的实现类 ContextLoaderListener,用于创建 Spring 容器及将创建好的 Spring 容器对象放入到 ServletContext 的作用域中。

<!--  注册spring的监听器-->
<!--  在服务器启动的时候,创建监听器,通过监听器创建spring容器,把创建号的容器放入servleteContext中 --><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>

(3) 注册字符集过滤器
注册字符集过滤器,用于解决请求参数中携带中文时产生乱码问题。

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

3.2 SSM 整合注解开发

3.2.1 建表 Student

3.2.2 新建 Web 工程

3.2.3 maven 依赖

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


3.2.5 编写配置文件

  • Jdbc 属性配置文件 jdbc.properties
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=123456

Spring 配置文件 applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><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="url" value="${jdbc.url}"/><property name="username" value="${jdbc.username}"/><property name="password" value="${jdbc.password}"/></bean><!--    SqlSessionFactoryBean创建SqlSessionFactory--><bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"><property name="dataSource" ref="dataSource"/><property name="configLocation" value="classpath:conf/mybatis.xml"/></bean><!--    声明mybatis扫描器,创建dao对象--><bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"><property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/><property name="basePackage" value="com.link.dao"/></bean><!--    声明service的注解@Service的包名位置--><context:component-scan base-package="com.link.service"/><!-- 事务的配置  注解配置  aspectj的配置   --></beans>
  • Springmvc 配置文件:springmvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xmlns:mvc="http://www.springframework.org/schema/mvc"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"><!--springmvc配置文件, 声明controller和其它web相关的对象--><context:component-scan base-package="com.link.controller" /><bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"><property name="prefix" value="/WEB-INF/jsp/" /><property name="suffix" value=".jsp" /></bean><mvc:annotation-driven /><!--1. 响应ajax请求,返回json2. 解决静态资源访问问题。-->
</beans>
  • mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--settings:控制mybatis全局行为--><settings>
<!-- 设置mybatis输出日志--><setting name="logImpl" value="STDOUT_LOGGING"/></settings><!--设置别名--><typeAliases><!--name:实体类所在的包名(不是实体类的包名也可以)--><package name="com.link.domain"/></typeAliases><!-- sql mapper(sql映射文件)的位置--><mappers><!--name:是包名, 这个包中的所有mapper.xml一次都能加载使用package的要求:1. mapper文件名称和dao接口名必须完全一样,包括大小写2. mapper文件和dao接口必须在同一目录--><package name="com.link.dao"/></mappers>100
</configuration>

3.2.6 定义 web.xml

1)注册 ContextLoaderListener
2)注册 DisatcherServlet
3)注册字符集过滤器
4)同时创建 Spring 的配置文件和 SpringMVC 的配置文件

3.2.7 实体类 Student

public class Student {private Integer id;private String name;private  Integer age;

3.2.8 Dao 接口和 sql 映射文件

public interface StudentDao {int insertStudent(Student student);List<Student> selectStudents();
}
<mapper namespace="com.link.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>

3.2.9 Service 接口和实现类

public interface StudentService {int addStudent(Student student);List<Student> findStudents();}
@Service
public class StudentServiceImpl implements StudentService {/*** 如果一个接口有多个实现类时,通过注解获取实例时怎么知道应该获取的是哪一个实现类serviceImpl呢?* 多个实现类的话可通过以下2种方式来指定具体要使用哪一个实现:* 1、 通过指定bean的名字来明确到底要实例哪一个类 @Autowired 需要结合@Qualifier来使用* @Resource可直接通过指定name属性的值即可,不过也可以使用@Qualifier(有点多此一举了...)* 2、 通过在实现类上添加@Primary注解来指定默认加载类**为什么非要调用接口来多此一举,而不直接调用实现类serviceImpl的bean来得简单明了呢?*1、 直接获取实现类serviceImpl的bean也是可以的;*2、 至于加一层接口的原因:一是AOP程序设置思想指导,给别人调用的接口,调用者只想知道方法和功能,* 而对于这个方法内部逻辑怎么实现的并不关心;* 二是可以降低各个模块间的关联,实现松耦合、程序分层、高扩展性,使程序更加灵活,* 他除了在规范上有卓越贡献外,最精髓的是在多态上的运用;继承只能单一继承,接口却可以多实现*3、 当业务逻辑简单,变更较少,项目自用时,省略掉接口直接使用实现类更简单明了;反之则推荐使用接口;*/@Resourceprivate StudentDao studentDao;@Overridepublic int addStudent(Student student) {int nums=studentDao.insertStudent(student);return nums;}@Overridepublic List<Student> findStudents() {return studentDao.selectStudents();}
}

3.2.10 处理器定义


@Controller
@RequestMapping(value = "/student")
public class StudentController {@Resourceprivate StudentService service;//注册学生@RequestMapping(value = "/addStudent.do")public ModelAndView addStudent(Student student){ModelAndView mv=new ModelAndView();String tips="注册失败!";//调用service处理studentint nums=service.addStudent(student);if(nums>0){//注册成功tips="学生【"+student.getName()+"】注册成功!";}mv.addObject("tips",tips);mv.setViewName("result");return mv;}//处理查询,响应ajax@RequestMapping(value = "/queryStudent.do")@ResponseBodypublic List<Student> queryStudent(){//参数检查,简单的数据处理List<Student> students=service.findStudents();return students;}}

3.2.11 定义视图-首页文件— index.jsp

3.2.12 注册学生页面 — addStudent.jsp

3.2.13 浏览学生页面 — listStudent.jsp

3.2.14 注册成功页面— success.jsp

3.2.15 注册失败页面— fail.jsp

第4章 SpringMVC 核心技术

4.1 请求重定向和转发

当处理器对请求处理完毕后,向其它资源进行跳转时,有两种跳转方式:请求转发与重定向。而根据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器。注意,对于请求转发的页面,可以是WEB-INF中页面;而重定向的页面,是不能为WEB-INF中页的。因为重定向相当于用户再次发出一次请求,而用户是不能直接访问 WEB-INF 中资源的。

SpringMVC 框架把原来 Servlet 中的请求转发和重定向操作进行了封装。现在可以使用单的方式实现转发和重定向。
forward:表示转发,实现 request.getRequestDispatcher(“xx.jsp”).forward()
redirect:表示重定向,实现 response.sendRedirect(“xxx.jsp”)

setViewName()默认是forward。

4.1.1 请求转发

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

处理器方法返回 String,在视图路径前面加入 forward: 视图完整路径

4.1.2 请求重定向

在处理器方法返回的视图字符串的前面添加 redirect:,则可实现重定向跳转。

forward:表示转发
redirect:表示重定向
forward和redirect都是关键字, 有一个共同的特点不和视图解析器一同工作

扩展:
forward和redirect他们都可以访问 视图文件,比如某个jsp ,html
forward:/hello.jsp forward:/main.html

forward和redirect他们都可以访问其它的controller
forward:/some.do , redirect:/other.do
处理器方法可以返回ModelAndView, String , void 都可以使用forward,redirect

4.2 异常处理

SpringMVC 框架处理异常的常用方式:使用@ExceptionHandler 注解处理异常。

springmvc框架采用的是统一,全局的异常处理。
把controller中的所有异常处理都集中到一个地方。 采用的是aop的思想。把业务逻辑和异常处理代码分开。解耦合。

使用两个注解
1.@ExceptionHandler
2.@ControllerAdvice

4.2.1 @ExceptionHandler 注解

使用注解@ExceptionHandler 可以将一个方法指定为异常处理方法。该注解只有一个可选属性 value,为一个 Class<?>数组,用于指定该注解的方法所要处理的异常类,即所要匹配的异常。

而被注解的方法,其返回值可以是 ModelAndView、String,或 void,方法名随意,方法参数可以是 Exception 及其子类对象、HttpServletRequest、HttpServletResponse 等。系统会自动为这些方法参数赋值。
对于异常处理注解的用法,也可以直接将异常处理方法注解于 Controller 之中。

(1) 自定义异常类
定义三个异常类:NameException、AgeException、MyUserException。其中 MyUserException是另外两个异常的父类。

public class MyUserException extends Exception {public MyUserException() {super();}public MyUserException(String message) {super(message);}
}
//当年龄有问题时,抛出的异常
public class AgeException extends MyUserException {public AgeException() {super();}public AgeException(String message) {super(message);}
}
//表示当用户的姓名有异常,抛出NameException
public class NameException extends MyUserException {public NameException() {super();}public NameException(String message) {super(message);}
}

(2) 修改 Controller 抛出异常

@Controller
public class MyController {@RequestMapping(value = "/some.do")public ModelAndView doSome(String name,Integer age) throws MyUserException {//处理some.do请求了。 相当于service调用处理完成了。ModelAndView mv  = new ModelAndView();//try {//根据请求参数抛出异常if (!"zs".equals(name)) {throw new NameException("姓名不正确!!!");}if (age == null || age > 80) {throw new AgeException("年龄比较大!!!");}//}catch(Exception e){//   e.printStackTrace();//}mv.addObject("myname",name);mv.addObject("myage",age);mv.setViewName("show");return mv;}
}

(3) 定义异常响应页面

定义三个异常响应页面。

不过,一般不这样使用。而是将异常处理方法专门定义在一个类中,作为全局的异常处理类。

需要使用注解@ControllerAdvice,字面理解就是“控制器增强”,是给控制器对象增强功能的。使用@ControllerAdvice 修饰的类中可以使用@ExceptionHandler。
当使用@RequestMapping 注解修饰的方法抛出异常时,会执行@ControllerAdvice 修饰的类中的异常处理方法。
@ControllerAdvice 是使用@Component 注解修饰的,可以<context:component-scan>扫描到@ControllerAdvice 所在的类路径(包名),创建对象。

(4) 定义全局异常处理类

/*** @ControllerAdvice : 控制器增强(也就是说给控制器类增加功能--异常处理功能)*           位置:在类的上面。*  特点:必须让框架知道这个注解所在的包名,需要在springmvc配置文件声明组件扫描器。*  指定@ControllerAdvice所在的包名*/
@ControllerAdvice
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以外,不知类型的异常@ExceptionHandlerpublic ModelAndView doOtherException(Exception exception){//处理其它异常ModelAndView mv = new ModelAndView();mv.addObject("msg","服务器超时!");mv.addObject("ex",exception);mv.setViewName("defaultError");return mv;}
}

(5) 定义 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"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"><!--声明组件扫描器--><context:component-scan base-package="com.link.controller" /><!--声明 springmvc框架中的视图解析器, 帮助开发人员设置视图文件的路径--><bean  class="org.springframework.web.servlet.view.InternalResourceViewResolver"><!--前缀:视图文件的路径--><property name="prefix" value="/WEB-INF/view/" /><!--后缀:视图文件的扩展名--><property name="suffix" value=".jsp" /></bean><!--处理需要的两步--><context:component-scan base-package="com.link.handler" /><!--注册注解驱动--><mvc:annotation-driven />
</beans>

4.3 拦截器

SpringMVC 中的 Interceptor 拦截器是非常重要和相当有用的,它的主要作用是拦截指定的用户请求,并进行相应的预处理与后处理。其拦截的时间点在“处理器映射器根据用户提交的请求映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,在处理器适配器执行处理器之前”。当然,在处理器映射器映射出所要执行的处理器类时,已经将拦截器与处理器组合为了一个处理器执行链,并返回给了中央调度器。

4.3.1 一个拦截器的执行

所有自定义拦截器都需要继承HandlerInterceptor

public interface HandlerInterceptor {default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {return true;}default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable ModelAndView modelAndView) throws Exception {}default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, @Nullable Exception ex) throws Exception {}
}

自定义拦截器,需要实现 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 时,会将该方法放到专门的方法栈中,等到对请求进行响应的所有工作完成之后才执行该方法。即该方法是在中央调度器渲染(数据填充)了响应页面之后执行的,此时对 ModelAndView 再操作也对响应无济于事。

afterCompletion 最后执行的方法,清除资源,例如在 Controller 方法中加入数据

自定义拦截器

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)。*      如果验证失败,可以截断请求,请求不能被处理。*      如果验证成功,可以放行请求,此时控制器方法才能执行。*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {btime = System.currentTimeMillis();System.out.println("拦截器的MyInterceptor的preHandle()");return true;}/***postHandle:后处理方法。参数:Object handler:被拦截的处理器对象MyControllerModelAndView mv:处理器方法的返回值特点:1.在处理器方法之后执行的(MyController.doSome())2.能够获取到处理器方法的返回值ModelAndView,可以修改ModelAndView中的数据和视图,可以影响到最后的执行结果。3.主要是对原来的执行结果做二次修正,ModelAndView mv = MyController.doSome();postHandle(request,response,handler,mv);*/@Overridepublic void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler,ModelAndView modelAndView) throws Exception {System.out.println("拦截器MyInterceptor的postHandle()");if(modelAndView!=null){//修改数据modelAndView.addObject("mydata",new Date());System.out.println(modelAndView);//修改视图//  modelAndView.setViewName("forward:/WEB-INF/view/other.jsp");modelAndView.setViewName("other");}}/*afterCompletion:最后执行的方法参数Object handler:被拦截器的处理器对象Exception ex:程序中发生的异常特点:1.在请求处理完成后执行的。框架中规定是当你的视图处理完成后,对视图执行了forward。就认为请求处理完成。2.一般做资源回收工作的, 程序请求过程中创建了一些对象,在这里可以删除,把占用的内存回收。*/@Overridepublic 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));}
}

(1) 注册拦截器
在springmvc.xml中注册拦截器

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

mvc:mapping/用于指定当前所注册的拦截器可以拦截的请求路径,而/**表示拦截所有请求。

(2) 修改 index 页面

<html>
<head><title>Title</title><base href="<%=basePath%>" />
</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>

(3) 修改处理器

@Controller
public class MyController {@RequestMapping(value = "/some.do")public ModelAndView doSome(String name,Integer age)  {System.out.println("=====执行MyController中的doSome方法=====");//处理some.do请求了。 相当于service调用处理完成了。ModelAndView mv  = new ModelAndView();mv.addObject("myname",name);mv.addObject("myage",age);mv.setViewName("show");return mv;}
}

(4) 修改 show 页面

/WEB-INF/view/show.jsp从request作用域获取数据

myname数据:${myname}

myage数据:${myage}

(5) 控制台输出结果 ![在这里插入图片描述](https://img-blog.csdnimg.cn/cbb8203eebee4e7c8fa01fd388659ffe.png)

4.3.2 多个拦截器的执行

(1) 定义多个拦截器

//拦截器类:拦截用户的请求。
public class MyInterceptor implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("111111-拦截器的MyInterceptor的preHandle()");return true;}@Overridepublic void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler, ModelAndView mv) throws Exception {System.out.println("111111-拦截器的MyInterceptor的postHandle()");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) throws Exception {System.out.println("111111-拦截器的MyInterceptor的afterCompletion()");}
}
//拦截器类:拦截用户的请求。
public class MyInterceptor2 implements HandlerInterceptor {@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("22222-拦截器的MyInterceptor的preHandle()");return true;}@Overridepublic void postHandle(HttpServletRequest request,HttpServletResponse response,Object handler, ModelAndView mv) throws Exception {System.out.println("22222-拦截器的MyInterceptor的postHandle()");}@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response,Object handler, Exception ex) throws Exception {System.out.println("22222-拦截器的MyInterceptor的afterCompletion()");}
}

(2) 多个拦截器的注册与执行

    <!--声明拦截器: 拦截器可以有0或多个在框架中保存多个拦截器是ArrayList,按照声明的先后顺序放入到ArrayList--><mvc:interceptors><!--声明第一个拦截器--><mvc:interceptor><mvc:mapping path="/**"/><!--声明拦截器对象--><bean class="com.link.handler.MyInterceptor" /></mvc:interceptor><!--声明第二个拦截器--><mvc:interceptor><mvc:mapping path="/**"/><bean class="com.link.handler.MyInterceptor2" /></mvc:interceptor></mvc:interceptors>

(3) 控制台执行结果

当有多个拦截器时,形成拦截器链。拦截器链的执行顺序,与其注册顺序一致。需要再次强调一点的是,当某一个拦截器的 preHandle()方法返回 true 并被执行到时,会向一个专门的方法栈中放入该拦截器的 afterCompletion()方法。


从图中可以看出,只要有一个 preHandle()方法返回 false,则上部的执行链将被断开,其后续的处理器方法与 postHandle()方法将无法执行。但,无论执行链执行情况怎样,只要方法栈中有方法,即执行链中只要有 preHandle()方法返回 true,就会执行方法栈中的afterCompletion()方法。最终都会给出响应。

4.3.3 权限拦截器举例

只有经过登录的用户方可访问处理器,否则,将返回“无权访问”提示。
本例的登录,由一个 JSP 页面完成。即在该页面里将用户信息放入 session 中。也就是说,只要访问过该页面,就说明登录了。没访问过,则为未登录用户。

(1) 修改 index 页面

<html>
<head><title>Title</title><base href="<%=basePath%>" />
</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>

(2) 定义 Controller

@Controller
public class MyController {@RequestMapping(value = "/some.do")public ModelAndView doSome(String name,Integer age)  {System.out.println("=====执行MyController中的doSome方法=====");//处理some.do请求了。 相当于service调用处理完成了。ModelAndView mv  = new ModelAndView();mv.addObject("myname",name);mv.addObject("myage",age);mv.setViewName("show");return mv;}
}

(3) 定义权限拦截器

/拦截器类:拦截用户的请求。
public class MyInterceptor implements HandlerInterceptor {//验证登录的用户信息, 正确return true,其它return false@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("111111-拦截器的MyInterceptor的preHandle()");String loginName = "";//从session中获取name的值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;}//zs登录return true;}
}

(4) 注册权限拦截器

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

(5) 定义 login 页面

<html>
<head><title>Title</title>
</head>
<body>模拟登录,zs登录系统<%session.setAttribute("name","zs");%></body>
</html>

(6) 定义 logout 页面

<html>
<head><title>Title</title>
</head>
<body>退出系统,从session中删除数据<%session.removeAttribute("name");%>
</body>
</html>

4.3.4拦截器和过滤器的区别

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

SpringMVC执行流程

SpringMVC-动力节点-王鹤相关推荐

  1. 动力节点王鹤Spring Boot笔记

    多年来,随着新功能的增加,spring变得越来越复杂.只需访问https://spring.io/projects页面,我们就会看到可以在我们的应用程序中使用的所有Spring项目的不同功能. 如果必 ...

  2. ssm整合开发 动力节点王鹤版本

    目录 介绍 整合 1. pom.xml 2. web.xml 3. 新建包.目录.文件 4. applicationContext.xml 5. dispatcherServlet.xml 6. jd ...

  3. SpringBoot学习笔记总结—动力节点王鹤

    先说说Spring Boot是什么? 多年来,随着新功能的增加,spring变得越来越复杂.只需访问https://spring.io/projects页面,我们就会看到可以在我们的应用程序中使用的所 ...

  4. SpringBoot集成Redis笔记-动力节点王鹤

    关于springboot集成redis,我做了笔记,分享给小伙伴们,视频看的动力节点王鹤老师讲的, 动力节点王鹤老师讲解的springboot教程,由浅入深,带你体验Spring Boot的极速开发过 ...

  5. Ajax学习笔记-动力节点-王鹤老师

    Ajax学习笔记-动力节点-王鹤老师 视频教程来自:https://www.bilibili.com/video/BV15k4y167XM?spm_id_from=333.999.0.0 第一节 全局 ...

  6. 史上最全SpringBoot学习笔记-动力节点王鹤(2021最新版)

    SpringBoot 资料官方下载地址 动力节点springboot资料 视频观看地址 https://www.bilibili.com/video/BV1XQ4y1m7ex 第一章 JavaConf ...

  7. 动力节点王鹤SpringBoot3学习笔记——第二章 掌握SpringBoot基础篇

    目录 二.掌控SpringBoot基础篇 2.1 Spring Boot ? 2.1.1 与Spring关系 2.1.2 与SpringCloud关系 2.1.3  最新的Spring Boot3 新 ...

  8. 整理动力节点王鹤老师ssm整合步骤

    1.SSM整合开发步骤 总体来说就是 SPringMVC接收用户请求-----Spring中的Service对象-----Mybatis处理数据 ssm整合也叫做ssi,整合中有容器 1.第一容器Sp ...

  9. CHY的Spring学习笔记---师从动力节点王鹤老师(B站白嫖)

    Spring学习笔记 核心技术:ioc和aop ioc:使用di(依赖注入)实现控制反转,底层使用的是反射机制 spring可以创建自己写的类的对象,也可以创建非自定义对象,只要知道类的全限定名即可. ...

  10. Spring Boot入门篇,动力节点学习笔记整理

    什么是Spring Boot? 多年来,随着新功能的增加,spring变得越来越复杂.只需访问https://spring.io/projects页面,我们就会看到可以在我们的应用程序中使用的所有Sp ...

最新文章

  1. AT NEW 用法和注意
  2. Java 判断操作系统类型(适用于各种操作系统)
  3. P1468 派对灯 Party Lamps(BIG 模拟)
  4. Go语言与数据库开发:01-09
  5. 链接(了解)---Linux
  6. CMake 常用命令和变量
  7. 阿里云使用js 实现OSS图片上传、获取OSS图片列表、获取图片外网访问地址(读写权限私有、读写权限公共);...
  8. C语言第五次作业--数据类型
  9. Hibernate原生SQL查询
  10. HDFS体系结构(NameNode、DataNode详解)
  11. 将所有.java文件修改为.jad文件格式
  12. android局域网怎么传文件,两手机同一局域网怎么传文件
  13. java文件内容比较_怎么用JAVA技术编写一个两文件内容比较的程序?
  14. linux 3g拨号,中兴MF637U 3G 联通 linux 拨号
  15. java vml_VML书签
  16. 不同颜色蔬菜代表什么营养?
  17. 关于tkinter.Canvas 不显示图片的问题
  18. 关于自动驾驶车辆控制
  19. uniapp最好用的二维码生成
  20. 基于多目标优化算法的 LCOE电力成本的敏感性分析(Matlab代码实现)

热门文章

  1. 深入理解SQL Server的规划和安装
  2. 悲观锁和乐观锁_浅谈数据库悲观锁和乐观锁
  3. 道路建立拓扑关系的规则_建立真正社区关系的5条规则
  4. 用开源代码如何建立网站_在开源上建立自举业务
  5. Linus Torvalds的安全性,Facebook的AI工具等
  6. (24)css3盒子阴影
  7. Bootstrap3 模态对话框的尺寸
  8. Bootstrap创建按钮工具栏
  9. 如何检测支付宝接口中notify_url.php有没有返回,微信小程序支付成功,但是notify_url接收不到回调如何排查此问题?...
  10. java wav合并_用Java串联WAV文件