Marco's Java【SpringMVC入门(五) 之 SpringMVC的拦截器的使用】

前言

我们知道在web开发中,一般有三大板块:Servlet(服务连接器)Listener(监听器)Filter(过滤器),而今天我们要学习的拦截器可以算是一个精致的过滤器"法宝","法宝"用的好不好,也间接关系到你的Servelt项目是否足够安全,是否足够稳定。

拦截器的概念

在学习Servlet的时候,我们曾使用自定义类实现不同Filter接口的方式,做过拦截的操作,比如说登录拦截,编码拦截等,在讲解SpringMVC的拦截器之前,我们来回顾下之前的一些拦截案例吧~
就以编码拦截为例子

public class EncodingFilter implements Filter{

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
				
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest httpRqst = (HttpServletRequest)request;
		HttpServletResponse httpResp = (HttpServletResponse)response;
		httpRqst.setCharacterEncoding("UTF-8");
		httpResp.setContentType("text/html;charset=UTF-8");
		chain.doFilter(request, response);
	}

	@Override
	public void destroy() {
		// TODO Auto-generated method stub
		
	}
}

大家可以看到,我们通过实现Filter接口对所有能够匹配上/*的url操作做了拦截处理后,再集中设置请求和响应的编码,最终通过doFilter()方法放行,xml配置如下

<filter>
    <filter-name>characterEncoding</filter-name>
    <filter-class>com.marco.filter.EncodingFilter</filter-class>
</filter>
 
<filter-mapping>
    <filter-name>characterEncoding</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

而在SpringMVC中,我们可以通过拦截器来实现拦截的功能,并且SpringMVC为我们提供了很多自带的拦截共呢个,比方说上述的编码拦截转换功能我们只需在web.xml中对CharacterEncodingFilter配置即可

<!-- 配置字符编码过滤器 -->
<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>

拦截器原理其实和过滤器差不多,只不过在过滤器的基础上更加的细化而已,但是需要注意的是SpringMVC的拦截器只能拦截经过前端控制器DispatcherServlet的请求

其实说白了拦截器就是过滤器的升级版,对所有被拦截内容加以修饰,然后放行,但是这里的拦截的内容仅限于控制器方法执行前后的操作,这种编程方式也贴合了我们之前讲到的AOP的编程思想了吧

SpringMVC拦截器的使用
登录拦截器

相信大家大致已经知道拦截器的作用和概念了,那么我们接下来演示一个登录拦截的栗子吧~
在SpringMVC的web jar中为我们提供了一个控制器拦截器的接口HandlerInterceptor,我们需要重写里面的三个方法,分别是afterCompletion()postHandle()preHandle(),那么这三个方法分别在什么时候被调用呢?
我们接下往下看。
首先我们编写一个Login控制器LoginController

package com.marco.controller;

import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.marco.domain.User;
import com.marco.service.UserService;
import com.marco.util.WebUtil;
import com.marco.vo.UserVo;

@Controller
@RequestMapping("login")
public class LoginController {
	
	@Autowired
	UserService userService;
	
	@RequestMapping("toLogin")
	public String toLogin() throws Exception {
		return "login";
	}
	
	@RequestMapping("login")
	public String login(UserVo userVo, Model model) throws Exception {
		User user = userService.login(userVo.getUloginname(),userVo.getUpsw());
		if(user!= null) {
			HttpSession httpSession = WebUtil.getHttpSession();
			httpSession.setAttribute("currentUser", user);
			return "index";
		} else {
			model.addAttribute("error", "用户名或者密码不正确");
			return "login";
		}
	}
}

接下来编写登录拦截器LoginInterceptor

package com.marco.interceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import com.marco.domain.User;

public class LoginInterceptor implements HandlerInterceptor{
	/**
	 * 在postHandle执行完成这这后回调的方法
	 * @param handler 和postHandle里面的handler一样
	 */
	@Override
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
		System.out.println("####@###############afterCompletion#################");
	}
	/**
	 * 当preHandle 返回true时会调用的方法
	 * @param handler就是用户调用的自定义controller的方法及方法所在的对象的封装 handler{Contoller bean,Method method}
	   @param modelAndView  就是自定义Controller的返回值最终封装的对象
	 * 
	 */
	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
		System.out.println("####@###############postHandle#################");
	}
	/**
	 * 确定是否放行的方法
	 * @return true 代表放行
	 * 		   false 代表拦截
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
		System.out.println("####@###############preHandle#################");
		User user = (User) request.getSession().getAttribute("currentUser");
		if(null != user) {
			return true;
		} else {
			response.sendRedirect(request.getContextPath() + "/index.jsp");
			return false;
		}
	}
}

通过上面的代码不难看出我们主要是在preHandle()这个方法中做文章,是不是和doFilter()很相似呢?
只不过在preHandle(),使用true和false来决定是否放行或者拦截。
配置springmvc.xml!         配置springmvc.xml !      配置springmvc.xml!
重要的事情说三遍,这里的配置和我们之前的配置有些出入,请看

<mvc:interceptors>
	 <mvc:interceptor>
		<!--代表拦截所有经过DispatcherServlet的请求-->
		<mvc:mapping path="/**"/>
		<!--做登录拦截要记得将login.action或者login.jsp放行,否则会造成登录不成功的问题-->
		<mvc:exclude-mapping path="/login/login*"/>
		<bean class="com.marco.interceptor.LoginInterceptor" ></bean>
	</mvc:interceptor> 
</mvc:interceptors>

那么我们测试看看这三个方法的执行顺序
在这里插入图片描述
可以看到这三个方法的执行先后顺序为preHandle()postHandle()afterCompletion()
准确上来说preHandle()是进行处理器拦截用的,在Controller处理之前进行调用,因为Interceptor拦截器本质上就是Filter,因此它也遵循链式规则,可以有多个Interceptor同时存在,然后SpringMVC会根据Interceptor声明的前后顺序来一一调用处理,所有的Interceptor中的preHandle方法都会在Controller方法调用之前调用,当返回值为false时,则不会继续请求下去

postHandle()只会在当前这个Interceptor的preHandle方法返回值为true的时候才会执行,反言之,结果为false时该方法时不执行的,因此他是在处理器进行处理之后进行调用的,但是它在时间轴上的位置会在DispatcherServlet视图的渲染之前,因此我们通过此方法是可以对ModelAndView进行操作的

afterCompletion()同理,被调用的条件是preHandle方法返回值为true,该方法是在DispatcherServlet视图的渲染之后执行的,它主要是用于清理资源的。

在这里插入图片描述

敏感词汇拦截器

如果说你对登录拦截不太熟悉,那么敏感词汇拦截你总该碰到过吧?
比如说你在玩LOL的时候,遇到一个菜鸡队友,想骂它两句,结果留言台下面就变成了
***** 你个 ******, 怎么这么**,*

这个时候你就更气了,但是为了平台的和谐文明,建议不要这样骂人,可以委婉一点。
咳咳… 我什么都没说!好了,那么我们来模拟一下这个"讨厌嫌"的拦截器

首先来个破旧的消息发送台

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<html lang="zh-cn">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <meta name="renderer" content="webkit">
    <title>LOL消息发送台</title>  
    <link rel="stylesheet" href="/dms/css/pintuer.css">
    <link rel="stylesheet" href="/dms/css/admin.css">
    <script src="/dms/js/jquery.js"></script>   
</head>
<body>
	<form action="test.action">
		<input type="text" name="message"/>
		<input type="submit" name="send"/>
	</form>
</body>
</html>

在编写我们的Controller

package com.marco.controller;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.marco.domain.User;
import com.marco.service.UserService;
import com.marco.util.WebUtil;
import com.marco.vo.UserVo;

@Controller
public class TestController {
	@RequestMapping("test")
	public String test(HttpServletRequest request) throws Exception {
		System.out.println("####@##############我被拦截了################");
		String message = request.getParameter("message");
		System.out.println("玩家输入:" + message);
		request.setAttribute("message", message);
		return "response";
	}
}

上面都没啥好说的,接着编写拦截器,大家注意看!这次我没有在preHandle()中做文章,因为需要方法被执行之后并在视图被解析之前对敏感文字做处理,因此,我们的操作应该写在postHandle()方法中!
我们这里通过替换的方式过滤掉敏感词汇。

package com.marco.interceptor;

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

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

import com.marco.domain.User;

public class SensitiveWordInterceptor implements HandlerInterceptor{
	/**
	 * 在postHandle执行完成这这后回调的方法
	 * @param handler 和postHandle里面的handler一样
	 */
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object object, Exception arg3)
			throws Exception {
		System.out.println("####@###############afterCompletion#################");
	}
	/**
	 * 当preHandle 返回true时会调用的方法
	 * @param handler就是用户调用的自定义controller的方法及方法所在的对象的封装 handler{Contoller bean,Method method}
	   @param modelAndView  就是自定义Controller的返回值最终封装的对象
	 * 
	 */
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object object, ModelAndView modelAndView)
			throws Exception {
		System.out.println("####@###############postHandle#################");
		String message = request.getAttribute("message").toString();
		if(message.contains("垃圾")) {
			message = message.replace("垃圾", "***");
		}
		request.setAttribute("message", message);
	}
	
	/**
	 * 确定是否放行的方法
	 * @return true 代表放行
	 * 		   false 代表拦截
	 */
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object object) throws Exception {
		System.out.println("####@###############preHandle#################");
		return true;
	}
}

接下来重中之重啦,记得在springmvc.xml中配置我们的静态放行文件

<mvc:interceptors>
	<mvc:interceptor>
		<!-- 代表拦截所有经过DispatcherServlet的请求 -->
		<mvc:mapping path="/**"/>
		<bean class="com.marco.interceptor.SensitiveWordInterceptor" ></bean>
	</mvc:interceptor>
</mvc:interceptors>

好了,咱们来看看效果,首先输入一串不文明的话,我们跟着debug走
在这里插入图片描述
首先debug小虫子进入preHandle(),执行打印语句
在这里插入图片描述
当preHandle()返回true时,不会被return中断,而是执行handle方法
在这里插入图片描述
跟着我们执行handle方法进入到我们的控制器中并执行test()方法,这里我返回的路径是一个return.jsp页面
在这里插入图片描述
上面的步骤执行完,我们跟着debug走,发现一个有点熟悉的名词PostHandle
在这里插入图片描述
果不出所料!我们进入了postHandle()方法中,接下来就是处理我们的敏感词汇了
在这里插入图片描述
等待postHandle()执行完后,返回结果给DispatcherServlet的路上,我们再执行afterCompletion()方法
在这里插入图片描述
在这里插入图片描述
好,接下来我们看看页面上显示的结果是不是被修改了
在这里插入图片描述
发现这句话变成了我们熟悉的样子,哈哈,那今天的SpringMVC的内容就讲到这里啦!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值