拦截器与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>
拦截器路径匹配规则
Wildcard | Description |
---|---|
? | 匹配任何单字符 |
* | 匹配0个或任意数量的字符 |
** | 匹配0个或更多的目录 |
Demo
Path | Description |
---|---|
/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);
}
}
}