Springmvc的拦截器

拦截器与filter的区别

spring的拦截器与servlet的filter有相似之处,比如二者都是AOP编程思想的体现,都实现权限检验、日志记录等。不同的是:
使用范围不同:filter是servlet规范规定的,只能用在web程序中。而拦截器既可以用在web工程中,也可以用于applicable、swing、javase程序中。
规范不同:filter是在servlet规范中定义的,是servlet容器支持的。而拦截器是spring容器内的,是spring框架支持的。
使用的资源不同:同其他的代码块一样,拦截器也是一个spring的组件,归spring管理,配置在spring文件中,因此能使用spring里的任何资源、对象,例如:service对象、数据源、事务管理等,通过IOC注入到拦截器即可;filter则不能。
深度不同:filter在只在servlet前后起作用。而拦截器能够深入到方法前后、异常抛出前后等,因此拦截器的使用具有更大的弹性。所以在spring构架的程序中优先使用拦截器。

关系图

关系图

配置

在springmvc的配置文件中配置(比如dispatcher-servlet.xml),springmvc根据请求的路径匹配到对应的DispatcherServlet,只有在这个servlet对应的配置文件中配置拦截器才对这次请求有效。

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/login"/>	拦截/login路径的所有请求
            <mvc:exclude-mapping path="/admin/**"/>		排除/admin下的所有路径(可以写多条)
            <bean class="com.interceptors.FirstInterceptor"/>		定义拦截器类
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.interceptors.SecondInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

拦截器路径匹配规则

WildcardDescription
?匹配任何单字符
*匹配0个或任意数量的字符
**匹配0个或更多的目录

Demo

PathDescription
/app/*.x匹配所有在app路径下的.x文件
/app/p?ttern匹配 /app/pattern 和 /app/pXttern,但是不包括/app/pttern
/**/example匹配 /app/example, /app/foo/example, 和 /example
/app/**/dir/file.匹配 /app/dir/file.jsp, /app/foo/dir/file.html,/app/foo/bar/dir/file.pdf, 和 /app/dir/file.java
/**/*.jsp匹配任何的.jsp 文件

自定义拦截器

//实现HandlerInterceptor接口
public class FirstInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
        System.out.println("firstPreHandle");
        //返回true表示通过这个拦截器,拦截器链如果还有拦截器未执行,会继续执行剩下的拦截器;
        //返回false表示未通过这个拦截器,那么就不执行剩下的拦截器和Controller处理器。
        return true;
    }

	//Controller处理器的方法调用后执行,如果处理器抛出了异常就不执行
    @Override
    public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
        System.out.println("firstPostHandle");
    }
	
	//不论处理器是否抛出异常都会执行
    @Override
    public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
        System.out.println("firstAfterCompletion");
    }
}

正常情况下(preHandle方法都返回true),Spring会先从第一个拦截器开始进入前置方法,这样前置方法是按配置的顺序运行的,然后运行处理器的代码,最后运行后置方法。注意:后置方法和完成方法是按照配置的逆序运行的。有多个过滤器的情况下,当其中一个过滤器的preHandle方法返回false后,按配置顺序,后面过滤器的preHandle方法都不执行,Controller控制器和所有的后置方法postHandle也不会执行。执行过preHandle方法且该方法返回为true的拦截器的完成方法afterCompletion会按照配置的逆序运行。

源码分析

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
		...

		try {
			ModelAndView mv = null;
			Exception dispatchException = null;

			try {
				processedRequest = checkMultipart(request);
				multipartRequestParsed = processedRequest != request;

				// Determine handler for the current request.
				//获取最终对应的Controller处理器的方法和所有符合条件的拦截器
				mappedHandler = getHandler(processedRequest);
				if (mappedHandler == null || mappedHandler.getHandler() == null) {
					//没有对应处理器时,抛出异常或转到404页面
					noHandlerFound(processedRequest, response);
					return;
				}
				...
				
				//执行拦截器链的前置方法,返回false时不会执行处理器方法、postHandle和afterCompletion方法
				if (!mappedHandler.applyPreHandle(processedRequest, response)) {
					return;
				}

				try {
					// Actually invoke the handler.执行处理器方法,处理器方法的实际参数也是这个方法里生成的,因此拦截器无法获取到方法执行时的实际参数
					mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
				}
				finally {
					if (asyncManager.isConcurrentHandlingStarted()) {
						return;
					}
				}

				applyDefaultViewName(request, mv);
				//前置方法、处理器方法抛出异常不执行后置方法
				mappedHandler.applyPostHandle(processedRequest, response, mv);
			}
			catch (Exception ex) {
				dispatchException = ex;
			}
			//前置方法、处理器方法、后置方法抛出异常都会执行完成后方法
			processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
		}
		catch (Exception ex) {
			//抛出异常后的完成后方法
			triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
		}
		catch (Error err) {
			triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
		}
		finally {
			if (asyncManager.isConcurrentHandlingStarted()) {
				// Instead of postHandle and afterCompletion
				mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
				return;
			}
			// Clean up any resources used by a multipart request.
			if (multipartRequestParsed) {
				cleanupMultipart(processedRequest);
			}
		}
	}
	boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
		if (getInterceptors() != null) {
			for (int i = 0; i < getInterceptors().length; i++) {
				HandlerInterceptor interceptor = getInterceptors()[i];
				if (!interceptor.preHandle(request, response, this.handler)) {
					triggerAfterCompletion(request, response, null);
					return false;
				}
				this.interceptorIndex = i;
			}
		}
		return true;
	}
	
	void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
		if (getInterceptors() == null) {
			return;
		}
		for (int i = getInterceptors().length - 1; i >= 0; i--) {
			HandlerInterceptor interceptor = getInterceptors()[i];
			interceptor.postHandle(request, response, this.handler, mv);
		}
	}
	
	void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex)
			throws Exception {

		if (getInterceptors() == null) {
			return;
		}
		for (int i = this.interceptorIndex; i >= 0; i--) {
			HandlerInterceptor interceptor = getInterceptors()[i];
			try {
				interceptor.afterCompletion(request, response, this.handler, ex);
			}
			catch (Throwable ex2) {
				logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
			}
		}
	}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值