一、SpringMVC概述
1、SpringMVC简介
SpringMVC:是基于Spring的一个框架。实际上就是Spring的一个模块,专门是做web开发的,可以理解为是Servlet的一个升级。
Spring的使用原理:
Spring中IOC管理对象,AOP动态代理。SpringMVC也能够创建对象,放入到SpringMVC容器中,区别就是SpringMVC容器中放的是控制器对象。
在SpringMVC中我们使用@Controller注解创建控制器对象,把对象放入到SpringMVC容器中,把创建好的对象当作Servlet使用,即能够接收用户的请求,显示处理结果。
然而我们知道@Controller控制器创建的是普通的控制器对象,并不是Servlet,所以SpringMV赋予了控制器对象一些额外的功能,这样我们才能把它当作Servlet使用。
Web开发的底层是servlet。 在springmvc中有一个对象是servlet:DispatcherServlet(中央调度器)。
- DispatcherServlet叫做中央调度器,是一个servlet,它的父类继承的是HttpServlet
- DispatcherServlet也叫做前端控制器(front controller)
- DispatcherServlet负责接收用户提交的所有请求。用户把请求给了DispatcherServlet之后,DispatcherServlet会把请求转发给我们的Controller对象,最后是Controller对象处理请求。处理完请求后会将结果返回给DispatcherServlet,DispatcherServlet再将结果返回到请求端。
2、SpringMVC优点
-
基于 MVC 架构
基于 MVC 架构,功能分工明确。解耦合,
-
容易理解,上手快;使用简单。
就可以开发一个注解的 SpringMVC 项目,SpringMVC 也是轻量级的,jar 很小。不依赖的 特定的接口和类。
-
作 为 Spring 框 架 一 部 分 , 能 够 使 用 Spring 的 IoC 和 Aop 。 方便整合Strtus,MyBatis,Hiberate,JPA 等其他框架。
-
SpringMVC 强化注解的使用,在控制器,Service,Dao 都可以使用注解。方便灵活。
使用@Controller 创建处理器对象,@Service 创建业务对象,@Autowired 或者@Resource 在控制器类中注入 Service, Service 类中注入 Dao。
3、第一个SpringMVC程序
1、实现步骤
1、加入依赖
SpringMVC依赖
使用SpringMVC时,不需要我们手动加入Spring的依赖,因为SpringMVC本身依赖于Spring,所以根据Maven的依赖传递性会自动帮我们加入Spring的依赖。
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
Servlet、Jsp依赖
<!-- servlet的依赖 -->
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- jsp的依赖 -->
<!-- https://mvnrepository.com/artifact/javax.servlet.jsp/jsp-api -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.1</version>
<scope>provided</scope>
</dependency>
2、在web.xml中注册SpringMVC框架核心对象DispatcherServlet
- 配置Servlet程序:声明注册DispatcherServlet
- 指定自定义SpringMVC读取配置文件的位置
- 指定创建DispatcherServlet的实例
- 配置Servlet访问地址
<?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 -->
<servlet>
<!-- 给Servlet取别名 -->
<servlet-name>springmvc</servlet-name>
<!-- Servlet全类名 -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 自定义springmvc读取配置文件的位置 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 在tomcat启动后创建DispatcherServlet对象 load-on-startup:表示tomcat启动后创建对象的顺序。 值为大于0的整数,值越小tomcat创建对象的时间越早。 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 给Servlet配置访问地址 -->
<servlet-mapping>
<!-- Servlet名称,一般使用别名 -->
<servlet-name>springmvc</servlet-name>
<!-- 配置访问地址 使用框架的时候,url-pattern可以使用两种值 1. 使用*。 *.do、*.action、*.mvc 2. 使用/。 -->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
为什么要创建DispatcherServlet对象的实例呢?
因为DispatcherServlet在创建过程中,会同时创建SpringMVC容器对象读取SpringMVC的配置文件,把配置文件中的对象都创建好,当用户发起请求时就可以直接使用对象了。
DispatcherServlet的初始化会执行init()方法:
init(...){
// 创建容器,读取配置文件
WebApplicationContext ctx = new ClassPathXmlApplicationContext("springmvc.xml");
//把容器对象放入到ServletContext中
getServletContext().setAttribute(key,ctx);
}
创建DispatcherServlet的过程中,创建容器对象时默认读取的配置文件是**/WEB-INF/<servlet-name>-servlet.xml
**
例如:
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
这里的默认配置文件名为:springmvc-servlet.xml
我们可以使用<init-param>
标签自定义springmvc读取配置文件的位置。
3、创建页面发起请求、接收结果
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>请求</title>
</head>
<body>
<p><a href="show.do">访问/WEB-INF/view/下的show.jsp页面</a></p>
</body>
</html>
show.jsp 在/WEB-INF/view/目录下创建
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>受保护的页面</title>
</head>
<body>
<h1>您目前访问量的是/WEB-INF/view/show.jsp页面</h1>
${msg}
</body>
</html>
4、创建控制器类
- 在类的上面加上@Controller注解,创建对象并放到SpringMVC容器中
- 在类的上面加上@RequestMapping注解
package com.study.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class TestController {
/** * 自定义方法处理用户的请求。 * @RequestMapping:请求映射,作用是把一个请求地址和方法绑定在一起。一个请求一个方法处理。 * 被@RequestMapping修饰的方法叫做处理器方法或控制器方法。类似于Servlet的doGet、doPost方法。 * 属性: * value:请求的uri地址,值唯一,不能重复。 * 可以是一个字符串;也可以是一个数组,这样该控制类就有多个访问入口了。 * */
@RequestMapping(value = "/show.do")
public ModelAndView doSome(){
/** * ModelAndView:表示请求的处理结果。 * Model:数据,请求处理完后,要显示给用户的数据 * View:视图,如jsp等。 */
ModelAndView mv = new ModelAndView();
// 添加数据。框架最后会把数据放入到request作用域
mv.addObject("msg","Hello,SpringMVC!");
/** * 指定视图。 * 1. 如果访问的不是WEB-INF里的文件,则指定视图的完整路径 * 2. 如果访问的是WEB-INF里的文件,则指定文件名即可,后缀都不需要。 * 因为springmvc.xml文件中的视图解析器会处理: * 前缀 + 传过来的文件名 + 后缀 * 即 /WEB-INF/view/ + show + .jsp */
mv.setViewName("show");
return mv;
}
}
5、创建SpringMVC配置文件
SpringMVC的配置文件和Spring的配置文件是一样的。
-
声明组件扫描器,指定@contorller注解所在的包名
-
声明视图解析器。用于指定视图文件的路径。
什么是视图?就是页面。当我们点击跳转的时候,是跳转到了另外一个页面,可是如果我们直接访问跳转目标地址呢?当然我们也会访问到相应的页面,可这是属于非法访问,也就是没有按照我们正常的规矩来,所以为了防止这样的事情发生,我们一般会将需要保护的页面放到项目的WEB-INF目录下:
/WEB-INF/view/
。因为WEB-INF目录下的文件默认是受保护的。
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" 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:component-scan base-package="com.study.controller"/>
<!-- 声明框架的中的视图解析器,帮助开发人员设置视图的开发路径 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀:试图文件的路径 -->
<property name="prefix" value="/WEB-INF/view/"/>
<!-- 后缀:试图文件的扩展名 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
2、SpringMVC请求处理流程
组件 | 说明 |
---|---|
DispatcherServlet中央调度器 | web.xml中配置到的访问地址都会交由中央调度器处理转发 |
HandleMapping处理器映射器 | springmvc框架中的一种对象,框架把实现了HandlerMapping接口的类都叫做映射器(多个); 映射器会根据请求,从springmvc容器对象中获取处理器对象,即Conteoller类对象。获取到的对象会放到处理器执行链中保存。 |
处理器执行链 | 框架中的一个类,叫做HandlerExecutionChain。它里面会存储 Controller对象 和 项目中所有的拦截器 |
HandleAdaptor处理器适配器 | springmvc框架中的对象,需要实现HandlerAdapter接口。 作用:执行处理器方法,并将结果返回给中央调度器(调用Controller类中的方法,得到返回值ModelAndview) |
ViewResolver视图解析器 | springmvc框架中的对象,需要实现ViewResolver接口。 作用:组成视图完整的路径(前缀+传过来的值+后缀),并创建View对象返回给中央调度器。 View是一个接口,表示视图的,在框架中jsp、html不是String表示的,而是使用View和它的实现类表示视图的。例如:jsp对应的就是InternalResourceView实现类。 |
- index.jsp发起请求。请求地址:some.do
- tomcat(根据web.xml中的url-pattern知道*.do的请求给DispatcherServlet)
- DispatcherServlet(根据springmvc.xml配置知道some.do请求由doSome()方法处理)
- DispatcherServlet把some.do转发给TestController类中的doSome()方法
- 框架执行doSome()把得到的ModelAndView进行处理,转发到index.jsp
二、SpringMVC注解式开发
前面第一个SpringMVC程序我们已经使用到了@RequestMapping和@Controller两个注解,接下来我们将详细介绍SpringMVC中常用的几种注解。
1、@RequestMapping
1、使用在方法上
@RequestMapping:请求映射,可以定义处理器对于请求的映射规则,如把一个请求地址和方法绑定在一起。一个请求一个方法处理。被@RequestMapping修饰的方法叫做处理器方法或控制器方法。类似于Servlet的doGet、doPost方法。
-
属性:
-
value:请求的uri地址,值唯一,不能重复。
可以是一个字符串,常以“/”开始;也可以是一个数组,这样该控制类就有多个访问入口了。
-
method:指定请求方式。值为RequestMethod类枚举值。
get:RequestMethod.GET
post:RequestMethod.POST
-
@RequestMapping(value = "/show.do", method = RequestMethod.GET)
public ModelAndView doSome(){
...}
@RequestMapping(value = {
"/show.do","/other.do"})
public ModelAndView doSome(){
...}
2、使用在类上
@RequestMapping:请求映射,当这里和使用在方法上不一样,这里是声明请求地址的公共部分的。
例如:类中的方法的访问地址为:/test/some.do、/test/other.do等等,他们都有/test/部分,那么这一部分就可以使用@RequestMapping进行统一声明、定义。
- 属性:
- value:所有请求地址的公共部分
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping(value = "/show.do")
public ModelAndView doSome(){
ModelAndView mv = new ModelAndView();
mv.setViewName("show");
return mv;
}
}
这样doSome方法的访问地址为:/test/show.do
2、处理器方法的参数
处理器方法可以包含以下四类参数,这些参数会在系统调用时由系统自动赋值,即程序员可在方法内直接使用。
- HttpServletRequest
- HttpServletResponse
- HttpSession
- 请求中所携带的请求参数
HttpServletRequest、HttpServletResponse和HttpSession三个和Servlet中的使用方法是一样的,不做赘述。
请求中所携带的请求参数分为两类情况:逐个参数接收、对象参数接收。
1、逐个参数接收
使用这种方式的前提是:请求中的参数名和处理器方法的形参名一样。
使用示例
页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>请求</title>
</head>
<body>
<form action="form.do" method="post">
姓名:<input type="text" name="name"><br>
年龄:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
处理器方法
@RequestMapping(value = "/form.do")
public ModelAndView doForm(String name, int age) {
// 参数名和表单中的属性名一样
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("name", name);
modelAndView.addObject("age", age);
modelAndView.setViewName("show");
return modelAndView;
}
原理
-
框架使用request对象接收请求参数
String strName = request.getParameter("name"); String strAge = request.getParameter("age");
-
SpringMVC框架通过DispatcherServlet调用Controller类中的doForm方法,调用时按名称对应把接收的参数赋值给形参
doForm(strName, Integer.valueOf(strAge);
2、@RequestParam
如果逐个接收参数时,请求中参数和处理器方法形参名不一样,可以使用@RequestParam进行配置,使其一一对应。
属性:
-
value:请求参数名。配上后会和形参一一对应接收。
-
required:boolean类型,默认是true。
true:请求中必须包含此参数;
false:请求中可以不包含此参数。
@RequestMapping(value = "/form.do")
public ModelAndView doForm(@RequestParam(value = "userName")String name,
@RequestParam(value = "userAge")int age) {
...}
3、对象参数接收
将处理器方法的参数定义为一个对象,只要保证请求参数名与这个对象的属性同名即可。
框架会创建形参的Java对象,然后给属性赋值。例如,请求中的参数是name,则会调用setName方法赋值。
注意:这种放方式接收值,是不能使用@RequestParam注解的。
4、解决编码问题
当使用GET请求时,中文不会出现编码问题;而使用POST请求时,则会出现编码问题。我们需要使用过滤器处理乱码的问题。
我们可以使用自定义的过滤器,也可以使用框架中的过滤器。当然我们选择框架中的过滤器,因为方便。
框架中的过滤器:CharacterEncodingFilter
在web.xml文件中声明使用:
<!-- 注册字符集过滤器:解决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>
3、处理器方法的返回值
- ModelAndView:返回数据 + 视图;
- String:返回视图;
- void:Ajax;
- Object:返回数据。
1、返回ModelAndView:视图+数据
返回数据和视图。
若处理器方法处理完后,需要跳转到其它资源,且又要在跳转的资源间传递数据,此时处理器方法返回ModelAndView 比较好。当然,若要返回ModelAndView,则处理器方法中需要定义 ModelAndView 对象。
在使用时,若该处理器方法只是进行跳转而不传递数据,或只是传递数据而并不向任何资源跳转(如对页面的 Ajax 异步响应),此时若返回 ModelAndView,则将总是有一部分多余:要么 Model 多余,要么 View 多余。即此时返回 ModelAndView 将不合适。
@RequestMapping(value = "/form.do")
public ModelAndView doForm(String name, int age) {
// 创建ModelAndView对象
ModelAndView modelAndView = new ModelAndView();
// 传递的数据
modelAndView.addObject("name", name);
modelAndView.addObject("age", age);
// 跳转页面(类似重定向)
modelAndView.setViewName("show");
return modelAndView;
}
2、返回String:视图
只返回视图。
处理器方法返回的字符串可以是:
- 逻辑视图名,通过视图解析器解析可以将其转换为物理视图地址;
- 也可以是完整视图路径,使用完整路径则springmvc配置文件中就不能配置视图解析器。
@RequestMapping(value = "/form.do")
public String doForm(String name, int age) {
return "show";
}
如果想要在这种情况下,处理数据并返回,则需要在处理器方法中加入HttpServletRequest参数:
@RequestMapping(value = "/form.do")
public String doForm(HttpServletRequest request, String name, int age) {
request.setAttribute("name",name);
request.setAttribute("age",age);
return "show";
}
3、返回void:null(了解)
对于处理器方法返回 void 的应用场景,AJAX 响应。若处理器对请求处理后,无需跳转到其它任何资源,此时可以让处理器方法返回 void。
1、加入jQuery库文件
2、加入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>
3、请求页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>请求</title>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
</head>
<body>
姓名:<input id="name" type="text" name="name"><br>
年龄:<input id="age" type="text" name="age"><br>
<button id="ajax" type="button">异步请求姓名和年龄</button>
</body>
<script type="text/javascript">
$(function () {
$("#ajax").click(function () {
$.ajax({
async: true, // 是否异步处理
contentType: "application/json", // 前端发送请求的数据格式
dataType: "json", // 希望后端传回来的数据格式
error: function () { // 请求失败时运行的函数
console.log("请求出错!")
},
success: function (data) { // 请求成功时运行的函数,data是返回来的数据
$("#name").val(data.name);
$("#age").val(data.age);
},
type: "get", // 请求的类型
url: "form.do" // 发送请求的 URL
})
})
})
</script>
</html>
4、处理器方法
@RequestMapping(value = "/form.do")
public void doForm(HttpServletResponse response) throws IOException {
// 准备数据
Data data = new Data();
data.setName("张三");
data.setAge(19);
// 转为json
ObjectMapper om = new ObjectMapper();
String s = om.writeValueAsString(data);
// 输出数据
response.setContentType("application/json;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print(s);
writer.flush();
writer.close();
}
4、返回对象Object:数据
用于响应Ajax请求。
1、实现步骤
-
加入处理json的工具库的依赖,SpringMVC默认使用的jackson;
-
在SpringMVC配置文件中加入
<mvc:annotation-driven/>
标签:注解驱动,用于将对象转为json字符串;<mvc:annotation-driven/> 选http://www.springframework.org/schema/mvc这个的
原理:
<mvc:annotation-driven/>
在加入到springmvc配置文件后,会自动创建HttpMessageConverter接口的7个实现类对象,包括MappingJackson2HttpMessageConverter(用jacksonL具库中的objectMapper实现java对象转为json字符串的)HttpMessageConverter 接口实现类 作用 ByteArrayHttpMessageConverter 负责读取二进制格式的数据和写出二进制格 式的数据 StringHttpMessageConverter 负责读取字符串格式的数据和写出字符串格 式的数据 ResourceHttpMessageConverter 负责读取资源文件和写出资源文件数据 SourceHttpMessageConverter 能够读 / 写来自 HTTP 的请求与响应的 javax.xml.transform.Source ,支持DOMSource, SAXSource, 和StreamSource 的 XML 格式 AllEncompassingFormHttpMessageConverter 负责处理表单(form)数据 Jaxb2RootElementHttpMessageConverter 使用 JAXB 负责读取和写入 xml 标签格式的数 据 MappingJackson2HttpMessageConverter 负责读取和写入 json 格式的数据。利用 Jackson 的 ObjectMapper 读写 json 数据,操作 Object 类型数据,可读取 application/json,响应媒体类型为 application/json HttpMessageConverter接口:消息转换器。里面定义了Java对象转为json、xml、二进制等数据格式的方法。
boolean canWrite(Class<?> var1, @Nullable MediaType var2); void write(T var1, @Nullable MediaType var2, HttpOutputMessage var3)
1)canwrite作用:检查处理器方法的返回值,能不能转为var2表示的数据格式。如果检查能转为var2表示的数据格式,canwrite返回true。MediaType:表示数格式的,例如json,xml等等。
2)write:把处理器方法的返回值对象,调用jackson中的objectMapper转为json字符串。
-
在处理器方法上加入@ResponseBody注解,用于将数据返回到前端。通过HttpServletResponse输出数据,响应Ajax请求。
2、返回值是自定义对象
页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>请求</title>
<script type="text/javascript" src="js/jquery-3.4.1.min.js"></script>
</head>
<body>
姓名:<input id="name" type="text" name="name"><br>
年龄:<input id="age" type="text" name="age"><br>
<button id="ajax" type="button">异步请求姓名和年龄</button>
</body>
<script type="text/javascript">
$(function () {
$("#ajax").click(function () {
$.ajax({
async: true, // 是否异步处理
contentType: "application/json", // 前端发送请求的数据格式
dataType: "json", // 希望后端传回来的数据格式
error: function () { // 请求失败时运行的函数
console.log("请求出错!")
},
success: function (data) { // 请求成功时运行的函数,data是返回来的数据
$("#name").val(data.name);
$("#age").val(data.age);
},
type: "get", // 请求的类型
url: "form.do" // 发送请求的 URL
})
})
})
</script>
</html>
处理器方法
@ResponseBody // 使用该注解说明返回值当作数据传给前端,而不是视图
@RequestMapping(value = "/form.do")
public Data doForm() {
// 准备数据
Data data = new Data();
data.setName("张三");
data.setAge(19);
// 直接return
return data;
}
/** * 1. 框架会把返回Data类型,调用框架的中ArrayList<HttpMessageConverter>中每个类的canWrite()方法 * 检查哪个HttpMessageConverter接口的实现类能处理Data类型的数据, * 最后找到了MappingJackson2HttpMessageConverter; * 2. 框架会调用实现类的write(),即MappingJackson2HttpMessageConverter的write()方法 * 把data对象转为json,调用Jackson的objectMapper实现转为json; * 3. 框架会调用@ResponseBody把2的结果数据输出到浏览器,ajax请求处理完成。 */
3、返回值是List
页面中得到的数据就会是,json数组,每一个元素都是对象,在页面中循环遍历即可使用
4、返回值是String
这里返回String表示的是数据,不是视图。
怎么区分?就看你的处理器方法上是否有@ResponseBody注解,如果有就返回的是数据;没有返回的就是视图。
注意:返回String时默认使用的是"text/plain;charset=ISO-8859-1"作为contentType(数据类型/编码),导致中文有乱码
解决方法:在@RequestMapping注解中添加一个produce属性即可
@RequestMapping(value = "/form.do", produce="text/plain;charset=utf-8")
4、解读url-patten——使用/
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">
<!-- 声明注册SpringMVC的核心对象DispatcherServlet -->
<servlet>
<!-- 给Servlet取别名 -->
<servlet-name>springmvc</servlet-name>
<!-- Servlet全类名 -->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 自定义springmvc读取配置文件的位置 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 在tomcat启动后创建DispatcherServlet对象 load-on-startup:表示tomcat启动后创建对象的顺序。 值为大于0的整数,值越小tomcat创建对象的时间越早。 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 给Servlet配置访问地址 -->
<servlet-mapping>
<!-- Servlet名称,一般使用别名 -->
<servlet-name>springmvc</servlet-name>
<!-- 配置访问地址 使用框架的时候,url-pattern可以使用两种值 1. 使用*。 *.do、*.action、*.mvc 2. 使用/。 -->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
前面在web.xml中给控制器类配置访问地址时使用的是*.do
的方式,我们还可以使用/
的方式,但是会和tomcat中的一个默认的servlet冲突:
静态资源:html、js、css、图片等等
静态资源请求:比如我们前端显示的页面,都是从服务端请求过来的,也是url请求得到的
TomCat中有一个默认的Servlet,叫default,在 Tomcat 安装目录/conf/web.xml。除了由我们自定义Servlet(处理器方法)处理的请求,其余的一切静态资源请求 以及 未映射到其他Servlet的请求都由default Servlet处理。
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>
如果我们这里也使用了/
,则tomcat中的servlet就被覆盖了,所有的静态资源请求就会走我们配置的servlet了。最终会导致静态资源显示不了。
解决方式:
1、在SpringMVC配置文件中加入<mvc:default-servlet-handler/>
+注解驱动。
原理:声明了<mvc:default-servlet-handler/>
后 , springmvc 框 架 会 在 容 器 中 创 建 DefaultServletHttpRequestHandler 处理器对象。它会像一个检查员,对进入 DispatcherServlet 的 URL 进行筛查,如果发现是静态资源的请求,就将该请求转由 Web 应用服务器默认的 Servlet 处理。一般的服务器都有默认的 Servlet。
注意:这里也需要加上注解驱动:<mvc:annotation-driven/>
,因为default-servlet-handler和@RequestMapping注解有冲突,如果没有注解驱动,则所有的请求都会给tomcat中的default Servlet了。即加上注解驱动后,注解的优先级就高于default-servlet-handler了。
2、使用<mvc:resources/>
如果我们的Web应用服务器没有默认的Servlet呢?或者我们不想依赖Web应用服务器默认的Servlet?
我们就可以使用mvc:resources标签,在 Spring3.0 版本后,Spring 定义了专门用于处理静态资源访问请求的处理器ResourceHttpRequestHandler,它不会依赖于Web应用服务器默认的Servlet。并且添加了标签,专门用于解决静态资源无法访问问题。需要在springmvc 配置文件中添加如下形式的配置:
<!-- mapping:访问静态资源的地址;location:项目中静态资源的位置 -->
<mvc:resources mapping="/images/**" location="/images/"/>
<mvc:resources mapping="/html/**" location="/html/"/>
<!-- 注解驱动 -->
<mvc:annotation-driven/>
虽然这种方式很好,但是如果有很多静态资源的话,则需要使用多次mvc:resources标签,很麻烦。
我们常用的解决方式是:在webapp下创建一个static目录,里面专门存放我们的静态资源。
<mvc:resources mapping="/static/**" location="/static/"/>
5、相对路径和绝对路径
绝对地址:带有协议名称的是绝对地址,http://www.baidu.com,ftp://202.122.23.12。
相对地址:没有协议开头的,例如user/some.do,/user/some.do相对地址不能独立使用,必须有一个参考地址。通过参考地址+相对地址本身才能指定资源。
访问路径加/
和不加/
的区别:
首先加/
和不加/
的访问地址都是相对地址。
<!--不加 / -->
<a href="show.do"></a>
<!--加 / -->
<a href="/show.do"></a>
-
不加
/
:访问地址为:当前页面地址+访问路径,即:http://localhost:8080/study_ssm/ + show.do
当前页面地址理解:
eg1:访问show.do时url:http://localhost:8080/study_ssm/show.do
当前页面地址为:http://localhost:8080/study_ssm/
eg2:访问user/show.do时url:http://localhost:8080/study_ssm/user/show.do
当前页面地址为:http://localhost:8080/study_ssm/user/
总结:当前页面地址为,删除当前url最后一个 / 后面的路径,取剩下的url,即为当前页面地址。
bug:当两次访问eg2中的路径时会报错:
- 第一次访问url:http://localhost:8080/study_ssm/user/show.do
- 第二次访问url:http://localhost:8080/study_ssm/user/user/show.do
解决方法:
-
使用EL表达式
<a href="${pageContext.request.contextPath}/user/show.do"></a>
-
使用html中的
<bean>
标签:表示当前页面中访问地址的基地址。即页面中所有没有/
开头的访问地址,都是以base标签中的地址为参考地址<% String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + request.getContextPath() + "/"; %> <html> <head> <title></title> <base href="<%=basePath%>" /> </head>
-
加
/
:访问地址为:服务器地址+访问路径,即:http://localhost:8080 + /show.do
很明显,如果加
/
的话,我们的访问地址是错误的,所以我们不能机械的加上/
。解决方法:
-
设置访问地址为:项目名+访问地址
<a href="/study_ssm/show.do"></a>
-
使用EL表达式
<a href="${pageContext.request.contextPath}/show.do"></a>
-
三、SSM整合开发
1、分析
- Spring——业务层
- SpringMVC——视图层
- MyBatis——持久层
基本流程:用户发起请求——SpringMVC接收——Spring中的Service对象——MyBatis处理数据(处理完后按源路径返回结果)
容器:
- SpringMVC容器:管理Controller控制器对象
- Spring容器:管理Service、Dao(dao接口中定义每张表/实体类对应的操作)、工具类对象
虽然两个容器都是独立的,但是SpringMVC容器是Spring容器的子容器,是继承关系,即SpringMVC中的Controller可以访问Spring中的Service对象。
2、整合步骤
详细步骤
1、依赖:SpringMVC、Spring、MyBatis、jackson、mysql、druid、jsp、servlet、Lombok
2、web.xml:
- 注册中央调度器DispatcherServlet。作用:
- 创建SpringMVC容器对象;
- 所有的请求交给DispatcherServlet转发。我们默认使用的是
/
的访问路径。
- 注册Spring***ContextLoaderListener。作用:创建Spring容器对象。
- 注册字符集过滤器CharacterEncodingFilter。作用:解决Post请求乱码问题。
3、SpringMVC配置文件
- 声明组件扫描器。作用:使用注解就得使用。
- 声明视图解析器。作用:帮助开发人员设置视图的访问路径。
- 声明注解驱动。作用:Ajax、访问静态资源都需要注解驱动。
- 声明静态资源处理方式。作用:解决静态资源访问的问题。
4、Spring配置文件
-
声明数据源。作用:连接数据库的配置信息。
-
声明SqlSessionFactoryBean。作用:创建SqlSessionFactory对象
-
声明MyBatis扫描器。作用:创建dao对象
-
声明注解扫描器。作用:让spring自动扫描base-package对应的路径或者该路径的子包下面的java文件,如果扫描到文件中带有@Service,@Component,@Repository,@Controller等这些注解的类,则把这些类注册为bean。这里目的是让spring帮我们创建service实现类的对象。
注意:这里我们不使配置文件配置自动注入。而是使用@Service + @Resouce注解进行自动注入的。因为配置文件写多了,可读性会很差,程序员也看的眼花。
-
配置事务、配置注解、配置aspectj等等…
5、MyBatis配置文件(由于使用了数据库连接池,这里就不需要配置数据库配置信息了)
- MyBatis自带的日志(可选)
- 统一给实体类取别名
- 声明mapper文件
6、数据库属性配置文件:url、username、password
7、创建包
- controller:里面存放控制器方法,就是Servlet。
- dao:存放dao接口和mapper文件(mapper文件名和dao接口文件名一样),dao接口中定义表操作相关的方法,mapper文件中定义每个dao接口中定义的方法对应的sql语句。
- entity:实体类,每个实体类对应数据库中的一个表。
- service:存放service接口和思想类,实现类利用spring+mybatis帮我们创建出来的dao接口对象实现业务方法。这里利用到的dao接口对象我们使用注解的方式自动注入:@Service + @Resource。
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.1</version>
<scope>provided</scope>
</dependency>
<!-- spring-mvc依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- spring的依赖 -->
<!-- Spring IOC依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- Spring AspectJ依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- Spring事务依赖 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<!--spring的***-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- MyBatis依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>
<!-- MySQL依赖 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.18</version>
</dependency>
<!-- MyBatis和Spring集成的依赖 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.2</version>
</dependency>
<!-- 阿里数据库连接池druid的依赖 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.8</version>
</dependency>
<!-- jackson依赖:json-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<!-- 自动生成get、set方法Lombok的依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
<!-- MyBatis分页插件依赖 -->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>4.2.1</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<!--资源文件(非Java文件)所在的目录-->
<directory>src/main/java</directory>
<includes>
<!--包括目录下的.properties和.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!--filtering 选项 false 不启用过滤器, *.property 已经起到过滤的作用了 -->
<filtering>false</filtering>
</resource>
</resources>
</build>
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">
<!-- 1、注册中央调度器:DispatcherServlet -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 2、注册Spring*** -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<!-- contextConfigLocation表示配置文件的路径 -->
<param-name>contextConfigLocation</param-name>
<!-- 自定义配置文件的路径 -->
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!-- 3、注册字符集过滤器:解决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>
</web-app>
SpringMVC配置文件
dispatcherServlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:mvc="http://www.springframework.org/schema/mvc" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 1、声明组件扫描器 -->
<context:component-scan base-package="com.study.controller"/>
<!-- 2、声明框架的中的视图解析器,帮助开发人员设置视图的开发路径 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀:试图文件的路径 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!-- 后缀:试图文件的扩展名 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 3、声明注解驱动 -->
<mvc:annotation-driven/>
<!-- 4、解决静态资源 / 冲突 -->
<!-- 方式一:使用服务器默认servlet -->
<mvc:default-servlet-handler/>
<!-- 方式二:彻底摆脱服务器servlet,使用框架中的servlet -->
<!-- mapping:访问静态资源的地址;location:项目中静态资源的位置 -->
<!-- <mvc:resources mapping="/static/**" location="/static/"/> -->
</beans>
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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 添加外部配置文件 -->
<context:property-placeholder location="classpath:db.properties" file-encoding="utf-8"/>
<!-- 1、数据源DataSource配置:即配置数据库 -->
<!-- init-method:初始方法;destroy-method:销毁方法 -->
<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}"/>
<property name="filters" value="stat"/>
<!-- 设置连接池最大容量 -->
<property name="maxActive" value="${jdbc.maxActive}"/>
<!-- 设置初始创建连接数 -->
<property name="initialSize" value="1"/>
<property name="maxWait" value="6000"/>
<property name="minIdle" value="1"/>
<property name="timeBetweenEvictionRunsMillis" value="60000"/>
<property name="minEvictableIdleTimeMillis" value="300000"/>
<property name="testWhileIdle" value="true"/>
<property name="testOnBorrow" value="false"/>
<property name="testOnReturn" value="false"/>
<property name="poolPreparedStatements" value="true"/>
<property name="maxOpenPreparedStatements" value="20"/>
<property name="asyncInit" value="true"/>
</bean>
<!-- 2、SqlSessionFactory -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 配置数据库连接池的信息,引用类型注入,值用ref给 -->
<property name="dataSource" ref="dataSource"/>
<!-- 配置MyBatis主配置文件的信息 -->
<property name="configLocation" value="classpath:SqlMapConfig.xml"/>
</bean>
<!-- 3、声明MyBatis扫描器,创建dao对象 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 指定sqlSessionFactory -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 指定mapper接口,mapper接口中是实体类对应的操作方法 -->
<property name="basePackage" value="com.study.dao"/>
</bean>
<!-- 4、声明注解扫描器,让spring帮我们创建service实现类的对象 -->
<context:component-scan base-package="com.study.service"/>
</beans>
MyBatis配置文件
mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 1、MyBatis自带的日志 -->
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!-- 2、统一给实体类取别名 -->
<typeAliases>
<package name="com.study.entity"/>
</typeAliases>
<!-- 3、映射器:声明mapper文件 -->
<mappers>
<mapper resource="mapper_employees.xml"/>
</mappers>
</configuration>
数据库属性配置文件
jdbc.properties
jdbc.url=jdbc:mysql://127.0.0.1:3306/mysqldemo?serverTimezone=UTC&characterEncoding=UTF-8
jdbc.username=root
jdbc.password=password
jdbc.maxActive=20
四、SpringMVC核心技术
1、请求重定向和转发
请求转发:前端请求次数为 1 次,转发是在服务器内部进行的。
重定向:前端请求次数为 2 次。
SpringMVC 框架把原来 Servlet 中的请求转发和重定向操作进行了封装。现在可以使用简单的方式实现转发和重定向。
-
forward:表示转发,实现 request.getRequestDispatcher(“xx.jsp”).forward()
虽然视图解析器默认也是使用的转发,但是这种方式使用转发可以访问到视图解析器以外的页面。
-
redirect:表示重定向,实现 response.sendRedirect(“xxx.jsp”),注意:重定向是不能访问WEB-INF目录下受保护的页面的。
框架中对于重定向会把Model中的简单类型的数据,转为string使用,作为新请求的get请求参数。目的就是为了在两次请求之间传递数据。
在第二次请求中怎么拿到第一次请求传过来的数据(就是get请求后的参数)呢?
http://localhost:8080/study_ssm/show.jsp?name=lisi&age=19 在页面中使用参数集合对象param获取请求参数值 <p> ${param.name} </p> <p> ${param.age} </p>
// 使用转发
@RequestMapping(value = "/show.do", method = RequestMethod.GET)
public ModelAndView doSome() {
ModelAndView mv = new ModelAndView();
// 添加数据。框架最后会把数据放入到request作用域
mv.addObject("msg", "Hello,SpringMVC!");
// 指定视图,使用请求转发的方式,路径为视图的完整路径
mv.setViewName("forward:/WEB-INF/jsp/show.jsp");
return mv;
}
// 使用重定向
// 访问 /show.do 是一个request作用域
// 访问 /WEB-INF/jsp/show.jsp 就是另一个request作用域了
@RequestMapping(value = "/show.do", method = RequestMethod.GET)
public ModelAndView doSome() {
ModelAndView mv = new ModelAndView();
// 添加数据。框架最后会把数据放入到request作用域
mv.addObject("msg", "Hello,SpringMVC!");
mv.setViewName("redirect:/WEB-INF/jsp/show.jsp");
return mv;
}
2、异常处理
SpringMVC中采用的是统一的、全局的异常处理。
它采用的是AOP的思想,把controller中所有的异常全部集中到一个地方进行统一编写,再使用注解使其与业务逻辑代码结合起来。编写时却是分开写的,解耦合。
- @ExceptionHandler
- @ControllerAdvice
使用步骤:
背景:目前我有一个类运行起来会抛异常,这个异常可以是Java中的异常,也可以是我们自己定义的异常:继承Exception实现方法即可。
-
自定义异常类。指定可能会出现的异常的信息。
-
定义一个类专门处理异常。当对应的异常出现时会自动走这里面的方法。
注意:这里需要再springmvc的主配置文件中配置组件扫描器,让springmvc知道我们使用@ControllerAdvice注解的位置 + 注解驱动。
/** * @ControllerAdvice:控制器增强注解,作用是给控制器增加一些功能——异常处理功能 * 使用位置:必须再类的上面 * 使用时必须再springmvc配置文件中声明组件扫描器,让springmvc知道知道这里使用了@ControllerAdvice注解 */
@ControllerAdvice
public class MyExceptionHandler {
/** * 自定义方法处理处理发生的异常 * @ExceptionHandler(异常的class):表示异常的类型,当发生此类型的异常时,由当前方法来处理 * * @param exception 表示controller中抛出的异常对象,可以通过它获取发生的异常信息 * @return 返回值是多样的,可以是ModelAndView、String、void等等 */
@ExceptionHandler(Exception.class)
public ModelAndView handlerException(Exception exception) {
/** * 异常发生时,一般处理方法: * 1. 记录异常,发生的额时间、谁发生的、异常内容。记录到数据库 或者 日志文件等等 * 2. 发送通知。把异常信息,通过邮件、短信、微信发送给相关人员。 * 3. 给用户友好的提示。 */
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg","不好意思,程序出现了一点状况,请稍等,我们的程序员正在处理!");
modelAndView.addObject("exception",exception);
modelAndView.setViewName("exceptionErro");
return modelAndView;
}
// 如果没有找到对应的异常,就会走这里。一般这种处理只能有一个,即其他所有未处理异常都会走这里。
@ExceptionHandler
public ModelAndView handlerDefaultException(Exception exception) {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg","不好意思,程序出现了一点状况,请稍等,我们的程序员正在处理!");
modelAndView.addObject("exception",exception);
modelAndView.setViewName("defaultErro");
return modelAndView;
}
}
3、拦截器
SpringMVC 中的 Interceptor 拦截器是非常重要和相当有用的,它的**主要作用是拦截指定 Java 框架 SpringMVC的用户请求,并进行相应的预处理与后处理**。
拦截器执行时间点:
-
在请求处理之前,也就是controller类中的方法执行之前先被拦截。
-
在控制器方法执行之后也会执行拦截器。
-
在请求处理完成后也会执行拦截器。
请求处理完:视图处理完成后,对试图执行了forward,就认为请求处理完成。
多个拦截器执行的顺序:
-
先声明先执行
-
在框架中保存拦截器实际上使用的是一个ArrayList,按照声明的现后顺序存储到ArrayList中
拦截器和过滤器的区别:
-
过滤器是servlet中的对象,由tomcat服务器创建;拦截器是框架中的对象,在springmvc容器中创建;
-
过滤器实现Filter接口的对象,拦截器是实现HandlerInterceptor
-
过滤器是用来设置request,response的参数,属性,编码字符集的,侧重对数据过滤;
拦截器是用来验证请求的,能截断请求,一般用在用户登陆处理、权限检查、记录日志等方面; -
过滤器在拦截器之前先执行;
-
过滤器是一个执行时间点;拦截器有三个执行时间点;
-
过滤器可以处理jsp,js,html等等;
拦截器是侧重拦截对Controller的对象。如果你的请求不能被DispatcherServlet接收,这个请求不会执行拦截器内容; -
拦截器拦截普通类方法执行,过滤器过滤servlet请求响应。
拦截器的使用步骤:
- 定义类实现HandlerInterceptor接口,实现接口中的三个方法
- 在springmvc配置文件中,声明拦截器,让框架知道拦截器的存在。
public class MyInterceptor implements HandlerInterceptor {
/** * 预处理方法。在控制器方法执行前先执行,用户的请求首先到达此发方 * 我们可以在里面验证请求是否符合要求、验证用户是否登陆、验证用户是否有权限访问某个链接(url) * * @param request * @param response * @param handler 被拦截的控制器对象 * @return true:验证成功,放行请求;false:验证失败,截断请求 * @throws Exception */
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("【拦截器】预处理方法preHandle");
return true;
}
/** * 后处理方法。在控制器方法执行后执行 * 可以获取到控制器方法的返回值ModelAndView,可以修改其中的数据和视图,影响到最终的执行结果 * 主要是对原来的执行结果进行二次修正 * * @param request * @param response * @param handler 被拦截的控制器对象 * @param modelAndView 控制器方法的返回值 * @throws Exception */
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("【拦截器】后处理方法preHandle");
}
/** * 最后执行的方法。在请求处理完成后执行。 * 一般做资源回收工作的 * * @param request * @param response * @param handler 被拦截的控制器对象 * @param ex 程序中发生的异常 * @throws Exception */
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("【拦截器】最后执行方法afterCompletion");
}
}
<!-- 声明拦截器,拦截器可以有0或多个 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 声明访问拦截器的uri地址,即该uri路径下的所有资源都会被当前拦截器拦截 -->
<mvc:mapping path="/interceptor/**"/>
<!-- 声明拦截器对象 -->
<bean class="com.study.handler.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>