SpringMVC-过滤器,拦截器,监听器
参考文献
过滤器(Filter
)
- 作用:过滤器是处于客户端和服务器资源文件之间的一道过滤网,帮助我们过滤掉一些不符合要求的请求,通常用作
Session
校验,判断用户权限,如果不符合设定条件,则会被拦截到特殊的地址或者基于特殊的响应。
过滤器的使用
1 | package com.holelin.sundry.filter; |
拦截器 (Interceptor
)
- Java中的拦截器是动态拦截 action 调用的对象,然后提供了可以在 action 执行前后增加一些操作,也可以在 action 执行前停止操作,功能与过滤器类似,但是标准和实现方式不同。
- 登录认证:在一些应用中,可能会通过拦截器来验证用户的登录状态,如果没有登录或者登录失败,就会给用户一个友好的提示或者返回登录页面,当然大型项目中都不采用这种方式,都是调单点登录系统接口来验证用户。
- 记录系统日志:我们在常见应用中,通常要记录用户的请求信息,比如请求 ip,方法执行时间等,通过这些记录可以监控系统的状况,以便于对系统进行信息监控、信息统计、计算 PV、性能调优等。
- 通用处理:在应用程序中可能存在所有方法都要返回的信息,这是可以利用拦截器来实现,省去每个方法冗余重复的代码实现。
拦截器的使用
- 需要实现
HandlerInterceptor
类,并且重写三个方法preHandle
:在Controoler
处理请求之前被调用,返回值是boolean
类型,如果是true
就进行下一步操作;若返回false
,则证明不符合拦截条件,在失败的时候不会包含任何响应,此时需要调用对应的response
返回对应响应。postHandler
:在Controoler
处理请求执行完成后、生成视图前执行,可以通过ModelAndView
对视图进行处理,当然ModelAndView
也可以设置为 null。afterCompletion
:在DispatcherServlet
完全处理请求后被调用,通常用于记录消耗时间,也可以对一些资源进行处理。
1 | package com.holelin.sundry.interceptor; |
- 将自定义好的拦截器处理类进行注册,并通过
addPathPatterns
、excludePathPatterns
等属性设置需要拦截或需要排除URL
。
监听器(Listener
)
-
监听器通常用于监听 Web 应用程序中对象的创建、销毁等动作的发送,同时对监听的情况作出相应的处理,最常用于统计网站的在线人数、访问量等。
监听器大概分为以下几种:
ServletContextListener
:用来监听ServletContext
属性的操作,比如新增、修改、删除。HttpSessionListener
:用来监听Web
应用种的Session
对象,通常用于统计在线情况。ServletRequestListener
:用来监听Request
对象的属性操作。
1 | package com.holelin.sundry.listener; |
配置类
1 | package com.holelin.sundry.config; |
拦截器与过滤器的区别
实现原理不同
-
过滤器和拦截器底层实现方式大不相同,过滤器 是基于函数回调的,拦截器则是基于Java的反射机制(动态代理)实现的。
-
自定义的过滤器中都会实现一个
doFilter()
方法,这个方法有一个FilterChain
参数,而实际上它是一个回调接口。ApplicationFilterChain
是它的实现类, 这个实现类内部也有一个doFilter()
方法就是回调方法。 -
而每个
xxxFilter
会先执行自身的doFilter()
过滤逻辑,最后在执行结束前会执行filterChain.doFilter(servletRequest, servletResponse)
,也就是回调ApplicationFilterChain
的doFilter()
方法,以此循环执行实现函数回调。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134package org.apache.catalina.core;
// import 略
/**
* Implementation of <code>javax.servlet.FilterChain</code> used to manage
* the execution of a set of filters for a particular request. When the
* set of defined filters has all been executed, the next call to
* <code>doFilter()</code> will execute the servlet's <code>service()</code>
* method itself.
*
* @author Craig R. McClanahan
*/
public final class ApplicationFilterChain implements FilterChain {
/**
* Invoke the next filter in this chain, passing the specified request
* and response. If there are no more filters in this chain, invoke
* the <code>service()</code> method of the servlet itself.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet exception occurs
*/
public void doFilter(ServletRequest request, ServletResponse response)
throws IOException, ServletException {
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
try {
java.security.AccessController.doPrivileged(
(java.security.PrivilegedExceptionAction<Void>) () -> {
internalDoFilter(req,res);
return null;
}
);
} catch( PrivilegedActionException pe) {
Exception e = pe.getException();
if (e instanceof ServletException) {
throw (ServletException) e;
} else if (e instanceof IOException) {
throw (IOException) e;
} else if (e instanceof RuntimeException) {
throw (RuntimeException) e;
} else {
throw new ServletException(e.getMessage(), e);
}
}
} else {
internalDoFilter(request,response);
}
}
private void internalDoFilter(ServletRequest request,
ServletResponse response)
throws IOException, ServletException {
// Call the next filter if there is one
if (pos < n) {
// 获取pos位置的filter
ApplicationFilterConfig filterConfig = filters[pos++];
try {
Filter filter = filterConfig.getFilter();
if (request.isAsyncSupported() && "false".equalsIgnoreCase(
filterConfig.getFilterDef().getAsyncSupported())) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);
}
if( Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res, this};
SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);
} else {
filter.doFilter(request, response, this);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.filter"), e);
}
return;
}
// We fell off the end of the chain -- call the servlet instance
try {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(request);
lastServicedResponse.set(response);
}
if (request.isAsyncSupported() && !servletSupportsAsync) {
request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,
Boolean.FALSE);
}
// Use potentially wrapped request from this point
if ((request instanceof HttpServletRequest) &&
(response instanceof HttpServletResponse) &&
Globals.IS_SECURITY_ENABLED ) {
final ServletRequest req = request;
final ServletResponse res = response;
Principal principal =
((HttpServletRequest) req).getUserPrincipal();
Object[] args = new Object[]{req, res};
SecurityUtil.doAsPrivilege("service",
servlet,
classTypeUsedInService,
args,
principal);
} else {
servlet.service(request, response);
}
} catch (IOException | ServletException | RuntimeException e) {
throw e;
} catch (Throwable e) {
e = ExceptionUtils.unwrapInvocationTargetException(e);
ExceptionUtils.handleThrowable(e);
throw new ServletException(sm.getString("filterChain.servlet"), e);
} finally {
if (ApplicationDispatcher.WRAP_SAME_OBJECT) {
lastServicedRequest.set(null);
lastServicedResponse.set(null);
}
}
}
}
使用范围不同
- 过滤器 实现的是
javax.servlet.Filter
接口,而这个接口是在Servlet
规范中定义的,也就是说过滤器Filter
的使用要依赖于Tomcat
等容器,导致它只能在web
程序中使用。 - 而拦截器(
Interceptor
) 它是一个Spring
组件,并由Spring
容器管理,并不依赖Tomcat
等容器,是可以单独使用的。不仅能应用在web
程序中,也可以用于Application
、Swing
等程序中。
触发时机不同
1 | /* |
- 过滤器
Filter
是在请求进入容器后,但在进入servlet
之前进行预处理,请求结束是在servlet
处理完以后。 - 拦截器
Interceptor
是在请求进入servlet
后,在进入Controller
之前进行预处理的,Controller
中渲染了对应的视图之后请求结束。
拦截的请求范围不同
-
执行顺序 :
Filter 处理中
->Interceptor 前置
->我是controller
->Interceptor 处理中
->Interceptor 处理后
1
2
3
4
5Filter 处理中
Interceptor 前置
Interceptor 处理中
Interceptor 后置
Filter 处理中 -
过滤器
Filter
执行了两次,拦截器Interceptor
只执行了一次。这是因为过滤器几乎可以对所有进入容器的请求起作用,而拦截器只会对Controller
中请求或访问static
目录下的资源请求起作用。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 HoleLin's Blog!