SpringMVC 拦截器(HandlerInterceptor)是 Spring AOP 的应用。
过滤器 | 拦截器 | |
---|---|---|
说明 | Servlet 规范中的一部分 | SpringMVC 框架内置 |
可用工程 | 任何 Java Web 工程 | 使用了 SpringMVC 框架的工程 |
配置 | 在 web.xml 的 url-pattern 中配置 | 在 Spring 配置文件的 mvc:interceptor 中配置 |
拦截 | 要访问的资源,包括静态资源 | Controller 请求方法,不包括静态资源 |
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>characterEncoding</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>characterEncoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
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"
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="indi.jaywee.controller"/>
<mvc:default-servlet-handler/>
<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>
TestController
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/t1")
public String test1() {
String msg = "TestController test() has been executed";
System.out.println(msg);
return msg;
}
}
MyInterceptor
实现 HandlerInterceptor 接口,重写方法
public class MyInterceptor1 implements HandlerInterceptor {
/**
* @return true表示放行
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor1:预处理");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor1:后置处理");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor1:资源清理");
}
}
配置
在 Spring 配置文件中配置拦截器。
/
:拦截请求,不拦截页面;
/*
:拦截文件夹,不包括子文件夹;
/**
:拦截文件夹,包括子文件夹。
<mvc:interceptors>
<mvc:interceptor>
<!-- 要拦截的请求路径 -->
<mvc:mapping path="/test/**"/>
<!-- 拦截器 -->
<bean class="indi.jaywee.interceptor.MyInterceptor1"/>
</mvc:interceptor>
</mvc:interceptors>
/**
*
* @param request
* @param response
* @param handler
* @return
* @throws Exception
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
...
}
/**
*
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
...
}
/**
*
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
...
}
先测试,再得出结论。
对一组请求配置拦截器:A
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/test/**"/>
<bean class="indi.jaywee.interceptor.MyInterceptorA"/>
</mvc:interceptor>
</mvc:interceptors>
放行:预处理方法返回true
拦截:预处理方法返回 false
对同一组请求配置3个拦截器,声明顺序为 A、B、C。
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/test/**"/>
<bean class="indi.jaywee.interceptor.MyInterceptorA"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/test/**"/>
<bean class="indi.jaywee.interceptor.MyInterceptorB"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/test/**"/>
<bean class="indi.jaywee.interceptor.MyInterceptorC"/>
</mvc:interceptor>
</mvc:interceptors>
放行:三个预处理方法都返回 true
A拦截:A的预处理方法返回 false
A放行,B拦截:A的预处理方法返回 true,B的预处理方法返回 false
AB放行,C拦截:AB的预处理方法返回 true,C的预处理方法返回 false
拦截器在不同情况的执行顺序如下:
全部放行
按拦截器声明的顺序,执行每个拦截器的 preHandle
请求处理
按拦截器声明的相反顺序,执行每个拦截器的 postHandle
按拦截器声明的相反顺序,执行每个拦截器的 afterCompletion;
部分放行:假设执行到拦截器demo
时拦截请求
demo
的 preHandle);demo
的 afterCompletion);UserController
@Controller
@RequestMapping("/user")
public class UserController {
/**
* 跳转到首页
*/
@RequestMapping("/toHome")
public String toHome() {
return "home";
}
/**
* 跳转到登录页
*/
@RequestMapping("/toLogin")
public String toLogin() {
return "login";
}
/**
* 处理用户登录
*/
@RequestMapping("/login")
public String login(String username, String password, HttpSession session) {
// 判断用户名密码是否合法,此处省略
// 登录成功,设置session
session.setAttribute("userInfo", username);
return "success";
}
/**
* 处理用户注销
*/
@RequestMapping("/logout")
public String logout(HttpSession session) {
String userInfoSession = "userInfo";
if (session.getAttribute(userInfoSession) != null) {
session.removeAttribute(userInfoSession);
}
return "login";
}
}
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h3><a href="${pageContext.request.contextPath}/user/toHome">进入首页</a></h3>
<h3><a href="${pageContext.request.contextPath}/user/toLogin">进入登录页</a></h3>
</body>
</html>
home.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>用户首页</title>
</head>
<body>
<h1>用户首页</h1>
<span>这里是用户首页,用户登录才能访问</span>
</body>
</html>
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/user/login" method="post">
<label> 用户名<input type="text" name="username" required> </label>
<br>
<label> 密码<input type="password" name="password" required> </label>
<br>
<input type="submit">
</form>
</body>
</html>
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>登录成功</title>
</head>
<body>
<h1>登录成功:${userInfo}</h1>
<h3><a href="${pageContext.request.contextPath}/user/toHome">进入首页</a></h3>
<h3><a href="${pageContext.request.contextPath}/user/logOut">注销</a></h3>
</body>
</html>
Controller 和基本的页面都写好了,此时所有的页面都可以直接访问。
LoginInterceptor
通过 session 判断用户是否已登录。
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String userInfo = (String) request.getSession().getAttribute("userInfo");
System.out.println("loginInfo = " + userInfo);
if (userInfo != null) {
System.out.println("用户已登录,放行");
return true;
} else {
response.sendRedirect(request.getContextPath() + "/user/toLogin");
}
return false;
}
}
配置
不拦截地址
,否则 /user 下的所有请求,包括登录也会被拦截;<mvc:interceptor>
<mvc:mapping path="/user/**"/>
<!-- 不拦截地址 -->
<mvc:exclude-mapping path="/user/toLogin"/>
<mvc:exclude-mapping path="/user/login"/>
<bean class="indi.jaywee.interceptor.LoginInterceptor"/>
</mvc:interceptor>
再次测试
拦截器是 AOP 思想的一个体现,可以在不改变业务代码的情况下扩展业务。
原文:https://www.cnblogs.com/secretmrj/p/15171016.html