SpringMVC Interceptor 拦截器的配置与使用

一、Spring MVC 拦截器介绍

  Spring MVC 的拦截器(如无特殊说明,下文所说的拦截器即处理器拦截器)类似于过滤器 Filter,用于对处理器进行预处理和后处理。

二、拦截器常用应用场景

  拦截器的应用场景也很多,主要有以下几个 方面:

  1. 日志记录:请求信息的日志记录,以对系统进行监控、信息统计等。

  2. 权限检查:如登录校验、权限拦截;

  3. 性能监控:可以通过拦截器记录请求开始时间和结束时间,从而得到该请求的处理时间;

  4. 通用行为:cookie 、Locale 、Theme 信息的存取;

  5. 资源管理:Session 管理、资源清理。

三、编写自定义拦截器

  自定义拦截器,需要实现 HandlerInterceptor 接口:

package com.ssm.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;

public class HandlerInterceptor1 implements HandlerInterceptor {
	
	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		
		System.out.println("HandlerInterceptor1 ... preHandle");
		
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {

		System.out.println("HandlerInterceptor1 ... postHandle");
		
	}

	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("HandlerInterceptor1 ... afterCompletion");
		
	}

}

  实现 HandlerInterceptor 接口的拦截器中有三个方法,在这之中有三个方法,每个方法的调用时间都不一样:

preHandleController 执行前调用此方法,返回值为 true 表示继续流程(如调用下一个拦截器或处理器),为 false 表示该拦截器拦截了(如登录校验失败),不会继续调用其他两个方法,此时我们需要通过 response 来产生响应;

postHandleController 执行后,并且未返回 ModelAndView 前调用此方法,此时我们可以通过形参 ModelAndView 对象对模型数据或视图进行再处理,ModelAndView 对象可能为 null。这个方法必须所以的拦截器的 preHandle 方法都返回 true 才执行。

afterCompletionController 执行后且视图返回后调用此方法,此时可以进行性能监控和一些资源的清理。这个方法必须是 preHandle 返回 true 才执行。

注:如果想知道其原因,请看 org.springframework.web.servlet.DispatcherServlet 的 方法 doDispatch ,其中有详细说明。

四、拦截器配置

  1. 针对某一个特定的 Mapping 配置拦截器:

<bean
	class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
	<property name="interceptors">
		<list>
			<ref bean="handlerInterceptor1"/>
			<ref bean="handlerInterceptor2"/>
		</list>
	</property>
</bean>
<bean id="handlerInterceptor1" class="com.ssm.interceptor.HandlerInterceptor1"/>
<bean id="handlerInterceptor2" class="com.ssm.interceptor.HandlerInterceptor2"/>

  2. 全局配置拦截器:
  在 springmvc.xml 中配置,这里配置了两个全局拦截器 HandlerInterceptor1 和 HandlerInterceptor2。

<!-- 拦截器  -->
<mvc:interceptors>
	<!-- 配置拦截器方式一 -->
	<!-- <bean class="com.ssm.interceptor.HandlerInterceptor1"></bean> -->
	
	<!-- 配置拦截器方式二 -->
	<!--多个拦截器,顺序执行 -->
	<!-- "/**" 表示所有的url路径包括子url路径
	"/*" 表示所有的url路径
	 -->
	<mvc:interceptor>
		<!-- 拦截哪些URL -->
		<mvc:mapping path="/**"/>
		<!-- 哪些URL不拦截 -->
		<mvc:exclude-mapping path="/toLogin.action"/>
		<!-- 全局拦截器 -->
		<bean class="com.ssm.interceptor.HandlerInterceptor1"></bean>
	</mvc:interceptor>
	<mvc:interceptor>
		<mvc:mapping path="/**"/>
		<mvc:exclude-mapping path="/toLogin.action"/>
		<bean class="com.ssm.interceptor.HandlerInterceptor2"></bean>
	</mvc:interceptor>
</mvc:interceptors>

五、拦截器实现登陆验证

  1. 编写登陆 Controller

package com.ssm.controller;

import javax.servlet.http.HttpSession;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class LoginController {
	
	// 登陆页面
	@RequestMapping(value="/toLogin",method=RequestMethod.GET)
	public String login() throws Exception {

		//跳转登陆页面
		return "login";
	}
		
	// 登陆
	@RequestMapping(value="/login",method=RequestMethod.POST)
	public String login(HttpSession session, String username, String password)
			throws Exception {

		// 调用service进行用户身份验证
		
		// 在session中保存用户身份信息
		session.setAttribute("username", username);

		return "success";
	}

	// 退出
	@RequestMapping("/logout")
	public String logout(HttpSession session) throws Exception {

		// 清除session
		session.invalidate();

		return "error";
	}

}

  2. 在 HandlerInterceptor1 拦截器中的 preHandle 方法中编写登陆校验,其中对于请求 login.action 不进行拦截。

@Override
public boolean preHandle(HttpServletRequest request,
		HttpServletResponse response, Object handler) throws Exception {
	
	System.out.println("HandlerInterceptor1 ... preHandle");
	
	//获取请求的url
	String url = request.getRequestURI();
	//判断url是否是公开 地址(实际使用时将公开地址配置配置文件中)
	if(url.indexOf("login.action") >= 0) {
		//如果进行登陆提交,放行
		return true;
	}
	
	//判断session
	HttpSession session = request.getSession();
	
	//从session中取出用户身份信息
	String username = (String) session.getAttribute("username");
	
	if(username != null && !"".equals(username)){
		//身份存在,放行
		return true;
	}
	
	//执行这里表示用户身份需要认证,跳转登陆页面
	request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
	
	return false;

}

  3. 配置全局拦截器
  在 springmvc.xml 中配置:

<!-- 拦截器  -->
<mvc:interceptors>
	<mvc:interceptor>
		<!-- 拦截哪些URL -->
		<mvc:mapping path="/**"/>
		<!-- 哪些URL不拦截 -->
		<mvc:exclude-mapping path="/toLogin.action"/>
		<!-- 全局拦截器 -->
		<bean class="com.ssm.interceptor.HandlerInterceptor1"></bean>
	</mvc:interceptor>
</mvc:interceptors>

  使用上面的全局拦截器配置,其中对 toLogin.action 不进行拦截,因为这个进入登陆页面的请求 URL。

  4. JSP 页面
  login.jsp 页面在 WEB-INF 的 JSP 文件夹下,代码如下:

<form action="${pageContext.request.contextPath }/login.action" method="post">
	用户名:<input type="text" name="username">
  	密码:<input type="password" name="password">
  	<input type="submit" value="submit">
</form>
<a href="${pageContext.request.contextPath }/logout.action">注销</a>

  在 index.jsp 中添加链接 <a href="${pageContext.request.contextPath }/toLogin.action">登陆页面</a> 到登陆页面。

  5. 总结
  当没登陆时,在 index.jsp 页面点击链接到登陆页面时,拦截器不会拦截,因为在配置全局拦截器时将其设置为不拦截了,控制台打印:

DEBUG [http-bio-8080-exec-2] - DispatcherServlet with name 'SpringMVC' processing GET request for [/ssm/toLogin.action]
DEBUG [http-bio-8080-exec-2] - Looking up handler method for path /toLogin.action
DEBUG [http-bio-8080-exec-2] - Returning handler method [public java.lang.String com.ssm.controller.LoginController.login() throws java.lang.Exception]
DEBUG [http-bio-8080-exec-2] - Returning cached instance of singleton bean 'loginController'
DEBUG [http-bio-8080-exec-2] - Last-Modified value for [/ssm/toLogin.action] is: -1
DEBUG [http-bio-8080-exec-2] - Rendering view [org.springframework.web.servlet.view.JstlView: name 'login'; URL [/WEB-INF/jsp/login.jsp]] in DispatcherServlet with name 'SpringMVC'
DEBUG [http-bio-8080-exec-2] - Forwarding to resource [/WEB-INF/jsp/login.jsp] in InternalResourceView 'login'
DEBUG [http-bio-8080-exec-2] - Successfully completed request

  而请求其他链接时,如注销 /logout.action 是会进行拦截的,然后跳转到登陆页面。控制台打印:

DEBUG [http-bio-8080-exec-8] - DispatcherServlet with name 'SpringMVC' processing GET request for [/ssm/logout.action]
DEBUG [http-bio-8080-exec-8] - Looking up handler method for path /logout.action
DEBUG [http-bio-8080-exec-8] - Returning handler method [public java.lang.String com.ssm.controller.LoginController.logout(javax.servlet.http.HttpSession) throws java.lang.Exception]
DEBUG [http-bio-8080-exec-8] - Returning cached instance of singleton bean 'loginController'
DEBUG [http-bio-8080-exec-8] - Last-Modified value for [/ssm/logout.action] is: -1
HandlerInterceptor1 ... preHandle
DEBUG [http-bio-8080-exec-8] - Successfully completed request

  当登陆后,可正常访问 /logout.action ,是因为拦截器拦截到它后进行登陆校验,发现其已经登陆了,就对其放行,然后才能正常访问。

DEBUG [http-bio-8080-exec-9] - DispatcherServlet with name 'SpringMVC' processing POST request for [/ssm/login.action]
DEBUG [http-bio-8080-exec-9] - Looking up handler method for path /login.action
DEBUG [http-bio-8080-exec-9] - Returning handler method [public java.lang.String com.ssm.controller.LoginController.login(javax.servlet.http.HttpSession,java.lang.String,java.lang.String) throws java.lang.Exception]
DEBUG [http-bio-8080-exec-9] - Returning cached instance of singleton bean 'loginController'
HandlerInterceptor1 ... preHandle
DEBUG [http-bio-8080-exec-9] - Skip CORS processing, request is a same-origin one
HandlerInterceptor1 ... postHandle
DEBUG [http-bio-8080-exec-9] - Invoking afterPropertiesSet() on bean with name 'success'
DEBUG [http-bio-8080-exec-9] - Rendering view [org.springframework.web.servlet.view.JstlView: name 'success'; URL [/WEB-INF/jsp/success.jsp]] in DispatcherServlet with name 'SpringMVC'
DEBUG [http-bio-8080-exec-9] - Forwarding to resource [/WEB-INF/jsp/success.jsp] in InternalResourceView 'success'
HandlerInterceptor1 ... afterCompletion
DEBUG [http-bio-8080-exec-9] - Successfully completed request

六、研究拦截器执行顺序

  以两个拦截器为例,研究拦截器的执行顺序。因此,我们编写一个拦截器 2 。然后进行全局的配置。

package com.ssm.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class HandlerInterceptor2 implements HandlerInterceptor {

	@Override
	public boolean preHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler) throws Exception {
		System.out.println("HandlerInterceptor2 ... preHandle");
		return true;
	}

	@Override
	public void postHandle(HttpServletRequest request,
			HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		System.out.println("HandlerInterceptor2 ... postHandle");
		
	}

	@Override
	public void afterCompletion(HttpServletRequest request,
			HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		System.out.println("HandlerInterceptor2 ... afterCompletion");
		
	}

}

  增加拦截器 2 的配置:

<!-- 拦截器  -->
<mvc:interceptors>
	<mvc:interceptor>
		<!-- 拦截哪些URL -->
		<mvc:mapping path="/**"/>
		<!-- 哪些URL不拦截 -->
		<mvc:exclude-mapping path="/toLogin.action"/>
		<!-- 全局拦截器 -->
		<bean class="com.ssm.interceptor.HandlerInterceptor1"></bean>
	</mvc:interceptor>
	<mvc:interceptor>
		<mvc:mapping path="/**"/>
		<mvc:exclude-mapping path="/toLogin.action"/>
		<bean class="com.ssm.interceptor.HandlerInterceptor2"></bean>
	</mvc:interceptor>
</mvc:interceptors>

  说明:这里的两个拦截器的顺序是拦截器1在前,拦截器2在后。

  1. 拦截器1放行,拦截器2放行
  控制台打印出:

HandlerInterceptor1 ... preHandle
HandlerInterceptor2 ... preHandle

HandlerInterceptor2 ... postHandle
HandlerInterceptor1 ... postHandle

HandlerInterceptor2 ... afterCompletion
HandlerInterceptor1 ... afterCompletion

  结论:preHandle 按配置顺序正序执行;postHandle 按配置倒序执行;afterCompletion 按配置倒序执行。

  2. 拦截器1不放行,拦截器2放行
  控制台打印出:

HandlerInterceptor1 ... preHandle

  结论:拦截器1执行 preHandle,拦截器2不执行 preHandle;postHandle 都不执行;afterCompletion 都不执行。

  3. 拦截器1放行,拦截器2不放行
  控制台打印出:

HandlerInterceptor1 ... preHandle
HandlerInterceptor2 ... preHandle
HandlerInterceptor1 ... afterCompletion

  结论:preHandle 按配置顺序正序执行;postHandle 都不执行;拦截器1执行 afterCompletion,拦截器2不执行 afterCompletion。

  4. 拦截器1不放行,拦截器2不放行
  控制台打印出:

HandlerInterceptor1 ... preHandle

  结论:拦截器1执行 preHandle ,拦截器2不执行 preHandle ;postHandle 都不执行;afterCompletion 都不执行。

综上所述,如果是通过拦截器进行日志记录的话,需要配置成第一个拦截器,登陆校验配置成第二个拦截器,等等。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lytao123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值