Spring学习笔记——SpringMVC
SpringMVC
SpringMVC是一种基于Java的实现MVC设计模型的请求驱动类型的轻量级Web框架
SpringMVC已经成为目前最主流的MVC框架之一,伴随着Spring3.0的发布,已经完全超越Struts2,成为最优秀的MVC框架。它通过一套注解让一个简单的Java类称为处理请求的控制器,而无需实现任何接口
Spring集成Web环境
应用上下文是通过new ClasspathXmlApplicationContext()
方式获取的,但是每当从容器中获得Bean时都需要加载一次配置文件,弊端是配置文件和应用上下文会被多次加载和创建
在Web项目中,可以通过ServletContextListener
监听器来监听Web应用的启动,当Web应用启动时,加载配置文件,并创建应用上下文对象,并将其保存到servletContext
域中,这样就可以在任意位置从域中获取应用上下文对象
监听器ServletContextListener
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
public class ContextLoaderListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
// 将应用上下文存储到servletContext域中
ServletContext servletContext = servletContextEvent.getServletContext();
servletContext.setAttribute("app",app);
}
@Override
public void contextDestroyed(ServletContextEvent servletContextEvent) {
}
}
使用应用上下文对象
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
public class UserServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app");
System.out.println(app);
}
}
在Spring中,提供了一个监听器ContextLoaderListrner
就是对上述内容的封装,该监听器内部会加载Spring配置文件,创建应用上下文对象,并存储到ServletContext
域中,并提供了一个WebApplicationContextUtils
类来获取应用上下文对象
首先,需要在web.xml
中配置ContextLoaderListener
监听器(需要导入spring-web坐标)
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
使用WebApplicationContextUtils
获取ApplicationContext
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
System.out.println(app);
}
SpringMVC开发步骤
-
导入SpringMVC坐标
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.9.RELEASE</version> </dependency>
-
在
web.xml
中配置SpringMVC核心控制器DispathcerServlet
<!-- 配置Servler --> <servlet> <servlet-name>DispatcherServlet</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-mvc.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <!-- 配置Servlet地址映射 --> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <!-- 拦截所有请求 --> <url-pattern>/</url-pattern> </servlet-mapping>
-
创建Controller类和视图页面
-
将Controller使用注解配置到Spring容器中
@Controller // 将Controller导入Spring容器 public class UserController { @RequestMapping("/quick") public String save(){ System.out.println("Controller save running...."); return "success.jsp"; } }
-
配置SpringMVC核心文件
spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 配置注解扫描 --> <context:component-scan base-package="cn.hhxy.controller"/> </beans>
SpringMVC的执行流程
- 客户发送请求到前端控制器
DispatcherServlet
- 前端控制器
DispatcherServlet
收到请求后调用处理器映射器HandlerMapping
- 处理器映射器通过xml或注解寻找到具体的处理器,生成处理器对象和处理器拦截器,并一同返回给前端控制器
DispatcherServlet
- 前端控制器调用处理器适配器
HandlerAdapter
- 处理器适配器
HandlerAdapter
经过适配调用具体的后端控制器Controller
- 后端控制器
Controller
执行并返回ModelAndView
- 处理器适配器
HandlerAdapter
将后端控制器Controller
返回的ModelAndView
返回给前端控制器DispatcherServlet
- 前端控制器
DispatcherServlet
将ModelAndView
传给视图解析器ViewReslover
- 视图解析器
ViewReslover
解析后返回具体的视图View
- 前端控制器
DispatcherServlet
根据View
渲染视图,响应用户
SpringMVC注解解析
@RequestMapping
用于建立请求URL和处理请求方法之间的对应关系
位置
在类上使用,为请求URL的第一级访问目录。若不写,则相当于应用的根目录
在方法上使用,为请求URL的第二级访问目录,与类上的@RequestMapping
标注的一级目录一同组成访问虚拟路径
@Controller
@RequestMapping("/user")
public class UserController {
// 请求地址 http://localhost:8080/user/quick
@RequestMapping("/quick")
public String save(){
System.out.println("Controller save running....");
return "/success.jsp";
}
}
属性
① value:用于指定请求URL,和path的作用相同
② method:用于指定请求的方式
③ params:用于指定限制请求参数的条件,支持简单的表达式,要求请求参数的key
和value
必须和配置的一模一样
// 必须以GET方式请求,且请求参数中必须携带 username
@RequestMapping(value = "/quick",method = RequestMethod.GET,params = {"username"})
SpringMVC配置解析
组件扫描
除了使用<context:component-scan>
来直接配置组件扫描之外,还可以通过两个子标签进一步配置
<context:component-scan base-package="cn.hhxy.web">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<!--
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
-->
</context:component-scan>
其中两个子标签共有的type
属性为组件的类型,expression
为组件的全类名
<context:include-filter>
标签表示只扫描base-package
中对应的组件,以上例子中则是只扫描cn.hhxy.web
包下的@Controller
注解
<context:exclude-filter>
标签表示扫描除了对应组件之外的其他组件,以上例子中则是扫描cn.hhxy.web
包下除了@Service
注解的其他组件
SpringMVC的数据响应
SpringMVC的数据响应方式
页面跳转
直接返回字符串
将返回的字符串与视图解析器的前后缀拼接后跳转
配置内部视图解析器InternalResourceViewResolver
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/jsp/"/>
<!-- 后缀 -->
<property name="suffix" value=".jsp"/>
</bean>
@RequestMapping(value = "/quick")
public String save(){
System.out.println("Controller save running....");
return "success";
}
通过将返回字符串与前后缀拼接,得到的转发资源地址为/jsp/success.jsp
通过ModelAndView
对象返回
@RequestMapping(value = "/mv")
public ModelAndView MAV(){
// 创建 ModelAndView
ModelAndView modelAndView = new ModelAndView();
// 设置视图
modelAndView.setViewName("success");
// 设置模型数据
modelAndView.addObject("name","Melonico");
return modelAndView;
}
当在ModelAndView
中设置了数据后,可以在JSP中使用EL表达式来使用数据
<h1>Success! ${name}</h1>
除了在方法体内创建ModelAndView
对象,还可以将ModelAndView
添加到方法参数列表中
@RequestMapping(value = "/mv2")
public ModelAndView MAV2(ModelAndView modelAndView){
modelAndView.setViewName("success");
modelAndView.addObject("name","melonico");
return modelAndView;
}
还可以将模型Model
和视图View
拆开
@RequestMapping(value = "/mv3")
public String MAV3(Model model){
model.addAttribute("name","Model_Melonico");
return "success";
}
通过返回字符串,视图解析器解析对应的视图View
,同时在参数列表中加入模型Model
,并通过这个Model
来进行数据的设置
如果希望向域中存放数据,可以在方法参数列表中添加HttpServletRequest
@RequestMapping(value = "/mv4")
public String MAV4(HttpServletRequest request){
request.setAttribute("name","request_melonico");
return "success";
}
以上所有在参数列表内添加的对象,SpringMVC框架会自动注入对应的对象
回写数据
直接返回字符串
~~最简单的方式,可以直接通过respones.gerWriter().print()
方法来直接回写字符串~~
@RequestMapping(value = "/res1")
public void res1(HttpServletResponse response){
response.getWriter().print("Melonico");
}
可以将需要回写的字符串直接返回,但是此时需要通过@ResponseBody
注解告知SpringMVC,该方法返回的字符串不是逻辑视图名,而是直接在Http响应体中返回
@RequestMapping(value = "/res2")
@ResponseBody // 告知SpringMVC该方法不进行视图跳转,直接进行数据响应
public String res2() throws IOException {
return "Melonico";
}
~~返回JSON字符串~~
可以通过以上直接返回字符串的方式手动返回JSON字符串,如
return "{'name':'A','age':'20'}";
更高级的方法可以通过Jackson将对象转换为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>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
</dependencies>
在方法中创建对象,并使用ObjectMapper
对象将其转为Json字符串
@RequestMapping(value = "/res3")
@ResponseBody
public String res3() throws IOException {
User user = new User();
user.setUsername("Melonico");
user.setPassword("123456");
// 使用 Jackson 将 User 对象转换为 JSON 字符串
ObjectMapper objectMapper = new ObjectMapper();
String s = objectMapper.writeValueAsString(user);
return s;
}
返回对象、集合
在处理器适配器HandlerAdapter
的实现类RequestMappingHandlerAdapter
中,存在一个消息转换器MassageConverters
,可以配置这个消息转换器来让SpringMVC帮我们将对象或集合转为JSON字符串并返回
<!-- 配置处理器映射器 -->
<bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
<property name="messageConverters">
<list>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
</list>
</property>
</bean>
然后再方法中即可直接将对象返回,SpringMVC会将其转为Json字符串
@RequestMapping(value = "/res4")
@ResponseBody
public User res4(){
User user = new User("Melonico","123456");
return user;
}
以上的代码配置起来比较麻烦,可以通过使用mvc的注解驱动代替以上配置
<!-- 命名空间 -->
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd"
<!-- mvc注解驱动 -->
<mvc:annotation-driven/>
==处理器映射器,处理器适配器,视图解析器==被称为SpringMVC的三大组件,在使用<mvc:annotation-driven>
注解时会自动加载处理器映射器RequestMappingHandlerMapping
和处理器适配器RequestMappingHandlerAdapter
,可以在SpringMVC的配置文件中代替注解处理器和适配器的配置,同时,使用<mvc:annotation-driven>
后默认底层就会继承Jackson进行对对象或集合的Json字符串格式的转变
SpringMVC数据响应总结
SpringMVC的数据响应方式有
- 页面跳转
- 回写数据
在页面跳转中,可以直接返回字符串,或者返回一个ModelAndView
在回写数据中,可以直接回写字符串,需要使用@ResponseBody
告知SpringMVC对应方法的返回值不进行页面跳转;
返回对象或集合,可以通过<mvc:annotation-driven>
来使SpringMVC进行对象/集合与Json字符串之间的转变
SpringMVC获得请求数据
客户端的请求参数一般会以键值对方式拼接在请求url之后,格式为name=nameValue&password=passwordValue
服务器端要获得请求参数,有时需要对数据进行封装,SpringMVC可以接收的参数类型有
基本类型
Controller中的业务方法的参数名要与请求参数的name
一致,参数值会自动映射匹配
http://localhost:8080/SpringMVC/Controller?username=Melonico&sex=M
@RequestMapping(value = "/req1")
@ResponseBody // 不进行页面跳转
public void req1(String username,String sex){
System.out.println(username);
System.out.println(sex);
}
POJO(Plain Ordinary Java Object 简单的Java对象)
Controller中的业务方法的POJO参数的属性名与请求参数的name
一致,参数值会自动映射匹配
http://localhost:8080/SpringMVC/Controller?username=Melonico&password=123456
@RequestMapping(value = "/req2")
@ResponseBody // 不进行页面跳转
public void req2(User user){
System.out.println(user);
}
User类中应有与参数name
相同的属性名以及对应的Getter
和Setter
public class User {
private String username;
private String password;
Getter/Setter
}
数组
Controller中的业务方法数组名称与请求参数的name
一致,参数值会自动映射匹配
http://localhost:8080/SpringMVC/Controller?hobbies=swim&hobbies=sing
@RequestMapping(value = "/req3")
@ResponseBody // 不进行页面跳转
public void req3(String[] hobbies){
System.out.println(Arrays.asList(hobbies));
}
集合
获取集合参数时,需要将集合参数包装到一个POJO中
import java.util.List;
public class UserList {
private List<User> userList;
public List<User> getUserList() {
return userList;
}
public void setUserList(List<User> userList) {
this.userList = userList;
}
@Override
public String toString() {
return "UserList{" +
"userList=" + userList +
'}';
}
}
@RequestMapping(value = "/req4")
@ResponseBody // 不进行页面跳转
public void req4(UserList userList){
System.out.println(userList);
}
当使用Ajax
提交数据时,可以指定contentType
为Json格式,这样在方法参数位置使用@RequestBody
注解就可以直接接受集合数据,无需再使用POJO包装
@RequestMapping(value = "/req5")
@ResponseBody // 不进行页面跳转
public void req5(@RequestBody List<User> userList){
System.out.println(userList);
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="${pageContext.request.contextPath}/js/jquery-3.5.1.js"></script>
<script>
var userList = new Array();
userList.push({username:"AAA",age:20});
userList.push({username:"BBB",age:22});
$.ajax({
type:"POST",
url:"${pageContext.request.contextPath}/jsp/req5",
data:JSON.stringify(userList),
contentType:"application/json;charset=utf-8"
})
</script>
</head>
<body>
</body>
</html>
在上述代码执行时,会出现错误,查询网页控制台后可以发现错误原因是找不到$
的定义,而造成这个错误的原因是因为找不对对应的jQuery
文件,这时,我们需要开启SpringMVC的静态资源访问
通过使用<mvc:resources>
标签开启静态资源访问
<mvc:resources mapping="/js/**" location="/js/">
属性mapping
是请求映射地址,location
表示资源具体存在的目录
也可以通过<mvc:default-servlet-handler>
实现,当SpringMVC在@RequsetMapping
注解中找不到对应地址时,会交给原始容器(Tomcat)寻找资源
请求数据乱码解决
当POST请求参数中存在中文数据时,会出现乱码问题
(Tomcat服务器会解决GET请求方式的乱码问题)
除了通过request.setCharacterEncoding()
方法解决,也可以设置过滤器来进行编码过滤
<!-- 配置全局过滤器 -->
<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>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
参数绑定注解 @RequestParam
当页面传送的请求参数名与Controller内业务方法中的参数名不同时,如
http://localhost:8080/SpringMVC/Controller?name=123
@RequestMapping(value = "/Controller")
@ResponseBody
public void Controller(String username){
System.out.println(username);
}
这时控制台的输出为null
,这种情况下需要使用@RequsetParam
注解来绑定参数
@RequestMapping(value = "/Controller")
@ResponseBody
public void Controller(@RequestParam("name") String username){
System.out.println(username);
}
除了绑定参数之外,@RequestParam
的属性和功能有
- value:设置请求参数名称
- requierd:指定此参数是否必须包括,默认为
ture
,即请求时不包含此参数会报错 - defaultValue:当没有指定该请求参数时,使用默认值给请求参数赋值
获取Restful风格参数
Restful
是一种软件架构/设计风格,它提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件
Restful
风格的请求是使用url + 请求方式
表示一次请求的目的,HTTP协议中四个操作方式为
- GET:获取资源
- POST:新建资源
- PUT:更新资源
- DELETE:删除资源
如:/Controller/Param GET:获取 id 为 Param 的 Controller
其中,Param是需要获取的请求参数,在SpringMVC中,可以使用占位符 {}
进行参数绑定,并在业务方法中使用@PathVariable
进行占位符的匹配和获取
http://localhost:8080/SpringMVC/Restful/Melonico
@RequestMapping("/Restful/{username}")
@ResponseBody
public void Restful(@PathVariable(value = "username",required = true) String username){
System.out.println(username);
}
自定义类型转换器
步骤
① 定义类型转换器类——实现Converter
接口
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
// Converter<转换前对象,转换后对象>
public class DateConverter implements Converter<String, Date> {
@Override
public Date convert(String s) {
// 将日期字符串转换为Date对象
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date parse = null;
try {
parse = simpleDateFormat.parse(s);
} catch (ParseException e) {
e.printStackTrace();
}
return parse;
}
}
② 在SpringMVC配置文件中声明转换器
<!-- 声明日期转换器 -->
<bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="serviceFactoryBean">
<property name="converters">
<list>
<bean class="cn.hhxy.converter.DateConverter" id="dateConverter"></bean>
</list>
</property>
</bean>
③ 在<mvc:annotation-driven>
中引用转换器
<mvc:annotation-driven conversion-service="serviceFactoryBean"/>
获取Servlet的原生API
若希望在Controller的业务方法中使用Servlet的原生API,如request
,response
,在方法参数列表中直接声明即可
同时可以通过request
获取到session
,进而获取到ServletContext
@RequestMapping("/servlet")
@ResponseBody
public void Servlet_api(HttpServletRequest request,HttpServletResponse response){
System.out.println(request);
System.out.println(response);
// 获取session
HttpSession session = request.getSession();
System.out.println(session);
// 获取ServletContext
ServletContext servletContext = session.getServletContext();
System.out.println(servletContext);
}
获取请求头
@RequestHeader
@RequestHeader
可以获得请求头信息,相当于
request.getHeader(name);
其中的属性有
- value:请求头名称
- required:是否必须携带该请求头
@RequestMapping("/header")
@ResponseBody
public void Header(@RequestHeader(value = "cookie",required = true) String cookies){
System.out.println(cookies);
}
@CookieValue
@CookieValue
可以直接获取指定Cookie的值
其中的属性有
- value:Cookie名称
- required:是否必须携带该Cookie
@RequestMapping("/header")
@ResponseBody
public void Header(@CookieValue(value = "JSESSIONID",required = true) String cookies){
System.out.println(cookies);
}
文件上传
文件上传的三个要点
- 表单项的
type
为file
- 表单的提交方式
method
为POST
- 表单的
entype
为multipart/form-data
<form action="${pageContext.request.contextPath}/jsp/file" method="post" enctype="multipart/form-data">
<input type="text" name="name">
<input type="file" name="file">
<input type="submit" value="submit">
</form>
当form表单type
修改为multipart/form-data
时,request.getParameter()
等方法会失效,请求体正文为
-----------------------------7e46dca0510
Content-Disposition: form-data; name="name"
123
-----------------------------7e46dca0510
Content-Disposition: form-data; name="file"; filename="C:\Users\Melonico\Desktop\a.txt"
Content-Type: text/plain
-----------------------------7e46dca0510--
单文件上传步骤
① 导入fileupload
和io
坐标
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
② 配置文件上传解析器CommonsMultipartResolver
<!-- 配置文件上传解析器 -->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
<property name="defaultEncoding" value="utf-8"/>
<property name="maxUploadSize" value="548000"/>
</bean>
③ 编写上传代码
业务方法参数列表中String name
对应上传名称,MultipartFile file
对应上传文件,两者的参数名必须同表单中的name
相同
@RequestMapping("/file")
@ResponseBody
public void file(String name, MultipartFile file) throws IOException {
System.out.println(name);
}
文件存储步骤
若希望将上传的文件保存到其他位置,可以使用MultipartFile
中的transferTo()
方法
public void transferTo(File file) throws IOException, IllegalStateException
@RequestMapping("/file")
@ResponseBody
public void file(String name, MultipartFile file) throws IOException {
System.out.println(name);
// 获得上传文件的名称
String filename = file.getOriginalFilename();
file.transferTo(new File("C:\\file\\" + filename));
}
多文件上传
可以添加多个表单项来上传多个文件,同时在业务方法中可以使用MultipartFile
数组来获取文件
@RequestMapping("/file")
@ResponseBody
public void file(String name, MultipartFile[] file) throws IOException {
System.out.println(name);
for (MultipartFile uploadFile: file) {
System.out.println(uploadFile);
}
}
SpringMVC拦截器
SpringMVC的拦截器(interceptor)类似于Servlet
中的Filter
,用来对处理器进行预处理
和后处理
,拦截器也是AOP
思想的具体实现
将拦截器按一定的顺序连接成一条链,这条链称为拦截器链(Interceptor Chain)
,在访问被拦截的方法或字段时,拦截器链中的拦截器会按照之前定义的顺序被调用
拦截器 Interceptor 与 过滤器 Filter 的区别
过滤器 Filter | 拦截器 Interceptor | |
---|---|---|
使用范围 | 在任何Java Web工程中都可以使用 | 只有使用SpringMVC框架的工程才可以使用 |
拦截范围 | 根据url-pattern 中配置的路径拦截访问资源 |
只会拦截访问的控制器方法,不会拦截访问的JSP,HTML,JS等 |
使用步骤
① 创建拦截器类 实现HandlerInterceptor
接口
import org.omg.PortableInterceptor.Interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
// 在目标方法执行之前执行
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle....");
return true;
}
// 在目标方法执行之后 视图返回之前执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle....");
}
// 在所有流程执行完毕后执行
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion....");
}
}
② 配置拦截器
<!-- 配置拦截器 -->
<mvc:interceptors>
<mvc:interceptor>
<!-- 对哪些资源执行拦截 -->
<mvc:mapping path="/**"/>
<bean class="cn.hhxy.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
③ 编写代码测试
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class TargetController {
@RequestMapping("/target")
public ModelAndView show(){
System.out.println("目标方法执行....");
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("name","Melonico");
modelAndView.setViewName("success");
return modelAndView;
}
}
控制台输出
preHandle.... // preHandle方法执行
target.... // 目标方法执行
postHandle.... // postHandler方法执行
View.... // 视图显示
afterCompletion.... // afterCompletion方法执行
方法详解
在自定义拦截器继承HandlerInterceptor
后,有三个方法需要重写
preHandle
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
该方法在目标方法执行之前执行,当返回值为
true
时,表示放行,false
表示不放行同时可以通过其参数中的
request
和response
获取一些请求内容
例如,通过request
进行请求参数判断
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle....");
String name = request.getParameter("name");
if (name != null){
return true;
} else {
// 错误转发页面
request.getRequestDispatcher("/error.jsp").forward(request,response);
return false;
}
}
postHandle
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception
该方法在目标方法执行之后,视图返回之前执行
其参数中的
modelAndView
可以获取或更改模型和视图
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
modelAndView.addObject("name","melonico252");
System.out.println("postHandle....");
}
由于postHandle
方法在目标方法之后,视图返回之前执行,所以其中对modelAndView
的修改会覆盖目标方法之前的内容
afterCompletion
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception
该方法在整个流程之后执行
可以进行一些收尾工作
拦截器链
当存在多个拦截器时,根据拦截器在配置文件中的配置顺序,执行顺序为
如配置顺序为:拦截器1 拦截器2
执行顺序为
拦截器1 perHandle...
拦截器2 perHandle...
目标方法
拦截器2 postHandle...
拦截器1 postHandle...
拦截器2 afterCompletion...
拦截器1 afterCompletion...
SpringMVC拦截器总结
使用步骤
- 创建拦截器实现
HandlerInterceptor
接口 - 在SpringMVC配置文件中配置拦截器
<mvc:interceptors>
- 测试拦截效果
方法
方法名 | 说明 |
---|---|
preHandle() | 方法将在请求处理之前调用,该方法的返回值为布尔类型,当返回为false 时,表示请求结束,后续的Interceptor 和Controller 都不会继续执行;当返回值为true 时,会继续调用下一个Interceptor 的perHandle 方法(若存在)或Controller 中的目标方法 |
postHandle() | 该方法在当前请求进行处理之后被调用,前提是preHandle 方法的返回值为true ,且会在DispatcherServlet 进行视图返回渲染之前被调用,可以在该方法中对Controller 处理之后的ModelAndView 对象进行进一步操作 |
afterCompletion() | 该方法在整个请求结束之后,即DispatcherServlet 渲染对应视图之后执行,前提是perHandle 的返回值为true 时才能被调用 |
SpringMVC异常处理
系统中包括两类异常,预期异常
和运行期异常
,预期异常
通过捕获获取异常信息,运行期异常
通过规范代码开发,测试等手段减少运行期异常
的发生
系统的Dao
,Service
,Controller
出现异常时通过throw
向上抛出异常,最后由SpringMVC前端控制器交由异常处理器进行异常处理
异常处理的方式
简单异常处理器SimpleMappingExceptionResolver
由SpringMVC提供的,在使用时可以根据具体情况进行相应异常与视图的映射配置
<!-- 配置异常处理器 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 默认异常视图 -->
<property name="defaultErrorView" value="error"/>
<!-- 具体异常试图 -->
<property name="exceptionMappings">
<map>
<entry key="java.lang.ClassCastException" value="error1"/>
</map>
</property>
</bean>
SimpleMappingExceptionResolver
中有一些属性可以注入
defaultErrorView
用来配置默认的错误视图,value
为跳转需要到的页面
exceptionMappings
用来配置具体的错误视图,其中需要注入一个Map,用来配置多个异常视图,其中key
是异常类型,value
为该异常要跳转到的页面
自定义异常处理器
通过实现HandlerExceptionResolver
接口,可以自定义异常处理器
public class MyExceptionResolver implements HandlerExceptionResolver {
/**
* @param e 异常对象
* @return 跳转到错误视图信息
*/
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
ModelAndView modelAndView = new ModelAndView();
if (e instanceof ClassCastException){
modelAndView.addObject("error","ClassCastException");
} else if (e instanceof NullPointerException) {
modelAndView.addObject("error","NullPointerException");
}
modelAndView.setViewName("error");
return null;
}
}
创建自定义异常处理器后,需要在SpringMVC配置文件中配置
<!-- 配置自定义异常处理器 -->
<bean class="cn.hhxy.resolver.MyExceptionResolver"/>
评论