开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第36天,点击查看活动详
一、概述
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。
- 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
- 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的
自定义拦截器需要实现org.springframework.web.servlet.HandlerInterceptor接口。
HandlerInterceptor接口中定义了3个方法:
-
preHandle():预处理方法,在目标方法执行前被执行。返回true表示拦截器放行;返回false表示不再继续执行后续的拦截器或目标方法,可以借助response参数给客户端返回应答,此后仅调用它之前拦截器的afterCompletion方法。
-
postHandle():后处理方法,在处理器方法之后,渲染视图之前被执行。可以通过modelAndView参数对模型数据或视图进行修改,modelAndView可能为null。
-
afterCompletion():整个请求处理完毕后即视图渲染完毕时回调,可以进行资源清理。只有拦截器中preHandle方法返回true的拦截器的此方法才会被执行。
拦截器的配置:
拦截器都是基于HandlerMapping进行拦截的,可以将拦截器bean注入到映射器的interceptors属性中
推荐以下方式:
<mvc:mapping path="/**"/>表示拦截所有请求,<mvc:exclude-mapping path=""/>表示排除特定的请求。
面试题:拦截器和过滤器区别
-
拦截器是基于java的反射机制的,而过滤器是基于函数回调。
-
拦截器不依赖于servlet容器,过滤器依赖于servlet容器。
-
拦截器可以获取IOC容器中的bean实现业务逻辑,过滤器不行,很重要。
触发时机不同:拦截器是被包裹在过滤器之中的,是spring容器的。过滤器是在请求进入容器后,进入servlet前进行预处理。
总结:
Spring的拦截器与Servlet的Filter二者都是AOP编程思想的体现,都能实现权限检查、日志记录等。不同的是:
-
使用范围不同: Filter 是Servlet规范规定的,只能用于Web程序中。而拦截器既可以用于Web程序,也可以用于Application、Swing 程序中。
-
规范不同: Filter是在Servlet 规范中定义的,是Servlet容器支持的。而拦截器是在Spring容器内的,是Spring框架支持的。
-
使用的资源不同: 拦截器是一个Spring 组件,归Spring管理,配置在Spring文件中,因此能使用Spring里的任何资源、对象,例如Service对象、数据源、事务管理等,通过loC注入到拦截器即可:而Filter则不能。
-
深度不同: Filter 在只在Servlet前后起作用。而拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。所以在Spring构架的程序中,要优先使用拦截器。
二、自定义拦截器
创建新的Web模块,搭建maven工程,添加项目所需依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
配置web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.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>
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
配置SpringMVC 全局配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 -->
<context:component-scan base-package="com.zhao.controller"/>
<mvc:annotation-driven/>
<!--静态资源过滤-->
<mvc:default-servlet-handler />
<!--解决json乱码问题-->
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
编写一个拦截器
public class MyInterceptor implements HandlerInterceptor {
//return true; 执行下一个拦截器 放行。
//return false; 不执行下一个拦截器
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("=========处理前==========");
return false;
}
//拦截日志
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("=========处理后==========");
}
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("=========清理==========");
}
}
在springmvc的配置文件中配置拦截器
<!--拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--包括这个请求下的所有请求-->
<mvc:mapping path="/**"/>
<bean class="com.zhao.config.MyInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
编写一个Controller,接收请求
@RestController
public class TestController {
@GetMapping("/t1")
public String test(){
System.out.println("Controller执行成功!");
return "ok";
}
}
前端 index文件
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<a href="${pageContext.request.contextPath}/t1">拦截器测试</a>
</body>
</html>
测试一下
从上图可以看出,拦截器中的方法都执行了,测试成功。
三、拦截器应用(登陆认证)
编写一个登陆页面 login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<%--web-INF下的所有资源或者页面只能通过controller 或者servlet进行访问--%>
<body>
<h1>登陆页面</h1>
<form action="${pageContext.request.contextPath}/user/login" method="post">
用户名:<input type="text" name="username" />
密码:<input type="password" name="password">
<input type="submit" value="提交">
</form>
</body>
编写一个Controller处理请求
@Controller
@RequestMapping("/user")
public class LoginController {
@RequestMapping("/main")
public String main(){
return "main";
}
@RequestMapping("/goLogin")
public String goLogin(){
return "login";
}
@RequestMapping("/login")
public String login(String username, String password, HttpSession session, Model model){
//把用户的信息存到session中
session.setAttribute("userLoginInfo",username);
model.addAttribute("username",username);
return "login";
}
@RequestMapping("/goOut")
public String goOut(String username, String password, HttpSession session, Model model){
//把用户的信息存到session中
session.removeAttribute("userLoginInfo");
return "main";
}
}
登陆成功进入首页main.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>首页</h1>
<span>${username}</span>
<p>
<a href="${pageContext.request.contextPath}/user/goOut">注销</a>
</p>
</body>
</html>
使用index页面完成跳转
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h1><a href="${pageContext.request.contextPath}/user/login">登录页面</a></h1>
<h1><a href="${pageContext.request.contextPath}/user/main">主页面</a> </h1>
</body>
</html>
编写用户登录拦截器
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request.getSession();
//放行
if (request.getRequestURI().contains("goLogin")){
return true;
}
if (request.getRequestURI().contains("login")){
return true;
}
if (session.getAttribute("userLoginInfo")!=null){
return true;
}
//判断什么情况没有登录
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response);
return false;
}
}
注册拦截器
<mvc:interceptor>
<!--包括这个请求下的所有请求-->
<mvc:mapping path="/user/**"/>
<bean class="com.zhao.config.LoginInterceptor" />
</mvc:interceptor>
测试一下,先进入主页面
发现是未登录状态,直接跳到登录页面
输入账号密码进行登录
登录成功进入首页,测试完成。