SpringMVC的拦截器
1 什么是springMVC的拦截器
- SpringMVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
- 将拦截器按照一定的顺序联结成一条链,这条链称为拦截器链(InterceptorChain)。在访问被拦截的方法和字段时,拦截器就会按照其之前定义的顺序被调用。
- 拦截器是AOP思想的具体应用。
2 过滤器Filter和拦截器的区别
区别 | 过滤器Filter | 拦截器 |
---|---|---|
使用范围 | 是servlet规范中的一部分,任何java web工程都可以使用 | 是springmvc框架自己的,只有使用 了springmvc框架的工程才能用。 |
拦截范围 | 在url-pattern中配置了/*后,可以对所有要访问的资源进行拦截 | 只会拦截访问的控制器方法,如果访问的是jsp,html,css,image或者js是不会拦截的 |
- WEB-INF目录下的资源浏览器不能直接访问,需要访问controller方法,由方法通过视图解析器的解析才能访问web-inf下的资源
- 这里要与之前的前端控制器有所区分,在前端控制器配置那里,我们在web.xml里配置了拦截所有的客户端请求,统一交由前端控制器dispatcherServlet来处理,如果访问的是对应的controller方法,则交由controller方法处理,如果是静态资源,则交由tomcat默认的servlet来处理。
- 通过上面的图片可知webapp下面包括css,img等资源和web-inf目录。除了wen-inf下面的资源,其他的都可以在浏览器输入特定的地址来访问。web-inf则必须由controller经视图解析器解析后来访问。
3 快速入门:自定义一个验证用户登录的拦截器
实现思路:
- 有一个登陆页面,需要写一个controller访问页面。
- 登陆页面有一提交表单的动作。需要在controller中处理。判断用户名密码是否正确。如果正确, 向session中写入用户信息。返回登陆成功
- 拦截用户请求,判断用户是否登陆。如果用户已经登陆。放行, 如果用户未登陆,跳转到登陆页面
代码
- 编写一个登录界面login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>登录页面</h1>
<hr>
<form action="/user/login">
用户名:<input type="text" name="username"> <br>
密码: <input type="password" name="pwd"> <br>
<input type="submit" value="提交">
</form>
</body>
</html>
- 编写一个登录成功的界面success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ page isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>登录成功界面</h1>
<hr>
${user}
<a href="/user/logout">注销</a>
</body>
</html>
- 在默认的index.jsp上编写开始页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h1>首页</h1>
<hr>
<a href="user/jumpLogin">登录</a>
<a href="user/jumpSuccess">成功页面</a>
</body>
</html>
- 编写controller处理请求
package com.southwind.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.servlet.http.HttpSession;
/**
* Created with Intellij IDEA
* Description:
* user: CoderChen
* Date: 2021-05-05
* Time: 12:24
*/
@Controller
@RequestMapping("/user")
public class userController {
// 跳转到登录界面
@RequestMapping("/jumpLogin")
public String jumpLogin() {
return "login";
}
// 跳转到成功界面
@RequestMapping("/jumpSuccess")
public String jumpSuccess() {
return "success";
}
// 登录提交
@RequestMapping("/login")
public String login(HttpSession session, String username, String pwd) {
System.out.println("接收前端===" + username);
session.setAttribute("user", username);
return "success";
}
// 退出登录
@RequestMapping("/logout")
public String logout(HttpSession session) {
// session过期
//这里补充一下session的作用域是一个用户打开网站的那一刻直到浏览器关闭,这期间无论在该网站访问多少资源
//都属于同一个session
session.invalidate();
return "login";
}
}
- 配置springmvc.xml中的视图解析器和注解扫描
注意:到目前我们可以访问登录这个界面。(如图)
存在的问题:即使没有登录成功也可以从首页进入登录成功界面。实际开发中不能这样做。因此就需要进行拦截器的设置,来拦截未登录就成功进入登录成功界面的行为。
- 编写用户登录拦截请求
package com.southwind.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
/**
* Created with Intellij IDEA
* Description:
* user: CoderChen
* Date: 2021-05-05
* Time: 19:59
*/
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("uri: " + request.getRequestURI());
//如果是登录页面就放行
if (request.getRequestURI().contains("login")) {
return true;
}
//如果用户已经登录也放行
HttpSession session = request.getSession();
if (session.getAttribute("user") != null) {
return true;
}
//用户没有登录就跳转到登录界面
request.getRequestDispatcher("/login.jsp").forward(request,response);
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
- 在springmvc.xml里配置注册拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="loginInterceptor" class="com.southwind.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
如图:
- 根据springmvc.xml里关于拦截器的配置,对所有请求都进行了拦截。然后根据条件判断是否放行。
4 拦截器相应api和配置的说明:
自定义拦截器类的说明:
拦截器中方法的说明
方法名 | 说明 |
---|---|
preHandle() | 方法将在请求处理之前进行调用,如果返回true就执行下一个拦截器,如果为false就不执行下一个拦截器。 |
postHandle() | 在请求处理方法执行后才被调用,前提是preHandle()方法返回true。它会在DispatcherServlet进行视图返回渲染之前调用,所以我们可以在这个方法中对controller处理之后的ModelAndView对象进行操作。 |
afterCompletion() | 在整个请求结束后执行,做清理工作,前提是preHandle()返回值为true |
- 如果有多个拦截器,则在xml里配置在前的先执行,配置在后的后执行。
- 拦截器中方法执行顺序是:preHandle()—>目标资源—->postHandle()——>afterCompletion()
springMVC.xml里拦截器配置的说明:
<mvc:interceptors>
<mvc:interceptor>
<!--对哪些资源进行拦截操作-->
<!--/admin/** 拦截的是/admin/下的所有-->
<!--/** 包括路径及其子路径,即拦截所有的资源-->
<mvc:mapping path="/**"/>
<!--bean配置的就是拦截器-->
<bean id="loginInterceptor" class="com.southwind.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
5 拦截器的执行过程说明:
- 当请求从客户端传过来时,首先会经过dispatcherServlet前端控制器处理,然后将请求交由handlerMapping处理器映射器来找具体的handler,handlerMapping处理器映射器在处理过程中会根据handler和拦截器生成一个处理器执行链返回给前端控制器,前端控制器根据这个链来依次执行操作,将它们交由处理器适配器HandlerAdaptor,执行后边的操作。
- 这个过程中拦截器的定义点是HandlerMapping处理器映射器。拦截器有三个方法,其中preHandle()方法 在业务处理器处理请求之前被调用,SpringMVC 中的Interceptor是链式调用的,在一个应用中或者说是在一个请求中可以同时存在多个Interceptor。每个Interceptor 的调用会依据它的声明顺序依次执行,而且最先执行的都是Interceptor中的preHandle 方法,所以可以在这个方法中进行一些前置初始化操作或者是对当前请求的一个预处理,也可以在这个方法中进行一些判断来决定请求是否要继续进行下去。该方法的返回值是布尔值Boolean 类型的,当它返回为false 时,表示请求结束,后续的Interceptor 和Controller都不会再执行;当返回值为true 时就会继续调用下一个Interceptor 的preHandle 方法,如果已经是最后一个Interceptor 的时候就会是调用当前请求的Controller 方法。
- postHandle():这个方法在当前请求进行处理之后,也就是Controller方法调用之后执行,但是它会在DispatcherServlet 进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView 对象进行操作。postHandle 方法被调用的方向跟preHandle 是相反的,也就是说先声明的Interceptor 的postHandle 方法反而会后执行。
- afterCompletion():该方法也是需要当前对应的Interceptor 的preHandle 方法的返回值为true 时才会执行。顾名思义,该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行。这个方法的主要作用是用于进行资源清理工作的。afterCompletion方法被调用的方向和perHandle也是相反的,先声明的Interceptor的afterCompletion方法后执行。
- 参考文章:springMVC框架–springMVC拦截器原理(五)_愚人节第二天的博客-CSDN博客_spring拦截器实现原理