过滤器(Filter)
过滤器有三个基础方法:init
、doFilter
、destroy
。init
过滤器 Bean 实例化时调用。doFilter
在 Action 请求到达时进入,可以实现过滤逻辑,通过 FilterChain 可以继续处理请求。destroy
在 Bean 销毁时调用。
拦截器是基于函数回调,由 Servlet 容器实现的。可以对几乎所有请求进行过滤。过滤器的执行在拦截器之前,不能访问 Action 上下文信息。
使用过滤器可以完成一些过滤操作,对传入的 request、response 过滤或者设置一些参数,然后再传入 Servlet 或者 Controller 进行业务逻辑操作。
使用
实现过滤器
实现 javax.servlet.Filter
接口,添加 @WebFilter
注解,设置过滤器生效路径以及一些参数
@Order
注解指定过滤器应用顺序(数值小的先执行)
@Component
@Order(1)
@WebFilter(urlPatterns = "/**")
public class FilterX implements Filter {
@Override
public void init(final FilterConfig filterConfig) throws ServletException {
// Filter Bean 实例化时调用
}
@Override
public void doFilter(final ServletRequest request, final ServletResponse response,
final FilterChain chain)
throws IOException, ServletException {
// 进入 Filter 方法,实现 Filter 逻辑
// 通过 FilterChain 继续处理请求,多个 Filter 链式调用
chain.doFilter(request, response);
// 请求处理结束后,退出 Filter 方法
}
@Override
public void destroy() {
// Filter 销毁时调用
}
}
复制代码
拦截器(Interceptor)
拦截器有三个基础方法:preHandle
、postHandle
、afterCompletion
。preHandle
在进入 Action 处理前调用,如果返回 true,继续处理,返回 false 则中断。postHandle
在 Action 处理完成后调用,如果 Action 抛出异常则不会调用。afterCompletion
在 postHandle
完成后或 Action 抛出异常时调用。
拦截器是基于 Java 的反射机制,由 Web 框架实现的,不依赖 Servlet 容器,对 Action 请求的生命周期内会多次调用,可以访问 Action 的上下文信息。只能对 Controller 请求进行拦截,对其他的一些比如直接访问静态资源的请求则没办法进行拦截处理。
应用场景
拦截器本质上是面向切面编程(AOP),符合横切关注点的功能都可以放在拦截器中来实现,主要的应用场景包括:
登录验证,判断用户是否登录。
权限验证,判断用户是否有权限访问资源。
后端埋点,统计 PV/UV,监控处理时长。
日志记录,记录请求操作日志,审计。
处理 Cookie、本地化、国际化、主题等。
使用
实现拦截器
实现 org.springframework.web.servlet.HandlerInterceptor
接口
通常会实现 org.springframework.web.servlet.handler.HandlerInterceptorAdapter
接口
preHandle()
方法返回 true,请求会继续处理。
如果使用 Feign,拦截 Feign 调用可以实现 feign.RequestInterceptor
如果使用 RestTemplate,拦截 RestTemplate 请求可以实现 org.springframework.http.client.ClientHttpRequestInterceptor
@Component
public class InterceptorX implements HandlerInterceptor {
@Override
public boolean preHandle(final HttpServletRequest request, final HttpServletResponse response,
final Object handler) throws Exception {
// return true,继续处理请求
return true;
}
@Override
public void postHandle(final HttpServletRequest request, final HttpServletResponse response,
final Object handler, final ModelAndView modelAndView) throws Exception {
// 请求处理完成后进入
// 请求抛出异常不会进入
}
@Override
public void afterCompletion(final HttpServletRequest request, final HttpServletResponse response,
final Object handler, final Exception ex) throws Exception {
// 请求处理完成,并且所有拦截器的 postHandle 处理完成后进入
// 或者请求处理异常后进入
}
}
复制代码
注册拦截器
实现拦截器后需要将拦截器注册到容器中,可以通过实现 WebMvcConfigurer,重写 addInterceptors(InterceptorRegistry registry)
方法。
@Configuration
public class Application implements WebMvcConfigurer {
@Autowired
public InterceptorX interceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 先 add 的拦截器先生效,链式调用
registry.addInterceptor(interceptor).addPathPatterns("/**");
registry.addInterceptor(new InterceptorX()).addPathPatterns("/**");
}
}
复制代码
源代码
过滤器相关代码
使用职责链设计模式
class StandardWrapperValve extends ValveBase {
public final void invoke(Request request, Response response) {
// ...
// 创建 FilterChain
ApplicationFilterChain filterChain =
ApplicationFilterFactory.createFilterChain(request, wrapper, servlet);
// 进入 Filter 处理逻辑
filterChain.doFilter(request.getRequest(), response.getResponse());
}
}
class ApplicationFilterChain implements FilterChain {
private int pos = 0;
private ApplicationFilterConfig[] filters;
public void doFilter(ServletRequest request, ServletResponse response) {
internalDoFilter(request,response);
}
private void internalDoFilter(ServletRequest request,
ServletResponse response) {
// ...
ApplicationFilterConfig filterConfig = filters[pos++];
Filter filter = filterConfig.getFilter();
filter.doFilter(request, response, this);
// ...
}
}
复制代码
FilterChain 的创建
class ApplicationFilterFactory {
public static ApplicationFilterChain createFilterChain(ServletRequest request,
Wrapper wrapper, Servlet servlet) {
ApplicationFilterChain filterChain = null;
filterChain = new ApplicationFilterChain();
Request req = (Request) request;
req.setFilterChain(filterChain);
// 这里 set servlet,后面会用到
filterChain.setServlet(servlet);
filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// 获取 FilterMap,找到提前注册好的 Filter
StandardContext context = (StandardContext) wrapper.getParent();
FilterMap filterMaps[] = context.findFilterMaps();
// 把跟本次请求相关的 Filter 加入 FilterChain
// URL 匹配,Servlet 匹配
for (int i = 0; i < filterMaps.length; i++) {
if (!matchDispatcher(filterMaps[i] ,dispatcher))
continue;
if (!matchFiltersURL(filterMaps[i], requestPath))
continue;
// 源码这里进行了两次 for 循环
// if (!matchFiltersServlet(filterMaps[i], servletName))
// continue;
ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
context.findFilterConfig(filterMaps[i].getFilterName());
// 加入 ApplicationFilterConfig[] filters
filterChain.addFilter(filterConfig);
}
return filterChain;
}
}
复制代码
拦截器相关代码
紧跟着过滤器的处理之后
class ApplicationFilterChain implements FilterChain {
// createFilterChain 时设置的
private Servlet servlet = null;
private void internalDoFilter(ServletRequest request,
ServletResponse response) {
// ...
filter.doFilter(request, response, this);
// 进入 Servlet 处理请求
servlet.service(request, response);
}
}
// 会进入到这里
class DispatcherServlet extends FrameworkServlet {
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) {
// ...
// 获取到 HandlerExecutionChain
HandlerExecutionChain mappedHandler = null;
mappedHandler = getHandler(processedRequest);
try {
// 执行 preHandle,返回 false 退出
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//... 请求处理,这里不详细介绍
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 执行 posthandle
mappedHandler.applyPostHandle(processedRequest, response, mv);
// 这里也会调用 triggerAfterCompletion
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Throwable t) {
// 异常会调用 triggerAfterCompletion
triggerAfterCompletion(processedRequest, response, mappedHandler, t);
}
}
}
复制代码
HandlerExecutionChain
以 applyPreHandle 举例,获取到 HandlerInterceptor 数组,顺序执行
class HandlerExecutionChain {
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
// 触发 afterCompletion
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
return true;
}
}
复制代码
处理流程图
总结
拦截器和过滤器的区别:
拦截器是基于 Java 的反射机制的,而过滤器是基于函数回调。
拦截器不依赖与 servlet 容器,过滤器依赖 servlet 容器。
拦截器只能对 action 请求起作用,而过滤器则可以对几乎所有的请求起作用。
拦截器可以访问 action 上下文、值栈里的对象,而过滤器不能访问。
在 action 的生命周期中,拦截器可以多次被调用,而过滤器只能在容器初始化时被调用一次。
拦截器基本可以实现过滤器的功能。
执行顺序 :
假设有 A、B 两个 Filter,C、D 两个 Interceptor,一次请求的处理顺序是:
进入 FilterA 代码 ->
进入 FilterB 代码 ->
执行 InterceptorC#preHandle ->
执行 InterceptorD#preHandle ->
处理 Action(进入 Controller) ->
执行 InterceptorD#postHandle ->
执行 InterceptorC#postHandle ->
执行 InterceptorD#afterCompletion ->
执行 InterceptorC#afterCompletion ->
退出 FilterB 代码 ->
退出 FilterA 代码
复制代码
评论