【声明】文章为本人学习时记录的笔记。 原课程地址:www.liaoxuefeng.com/wiki/154595…
一、前言
Filter是Servlet规范中的一个重要组件。 它的作用是在HTTP请求到达Servlet之前进行预处理。它可以被一个或多个Filter按照一定的顺序组成一个处理链(FilterChain),用来处理一些公共逻辑。
二、目标
实现Filter功能,让web Server有实现拦截功能。
关注点:
1、Filter和Servlet一样是重要组件,所以也放在ServletContext中进行管理。
2、调用时将哪些Filter组装成当前请求的FilterChain。
3、filter执行完毕后,回归到目标servlet。
三、设计
1、因为Filter和Servlet同属三大重要组件,所以在组成上有很多相似性。
Servlet在ServletContext中有相关的3个成员:servletRegistrations、nameToServlets、servletMappings
这里也同样为Filter增加3个成员:filterRegistrations、nameToFilters、filterMappings
相互对比的功能也是类似,filterRegistrations主要完成对Filter的注册,nameToFilters映射Filter的name和Filter,filterMappings缓存urlPattern和Filter之间的映射关系。
2、针对请求处理环节 根据请求path在servletMappings匹配到目标servelet后,还需要根据path在filterMappings匹配出目标Fileer组成FilterChain。 filter执行的终点,还得回归到servlet上。这里可以将匹配出来的servlet和filter都整合到FilterChain上。
四、实现
1、uml类图如下:
ServletContext:
FilterChain:
2、Servlet中新增对Filter的初始化方法initFilters
public void initFilters(List<Class<?>> filterClasses) {
for (Class<?> c : filterClasses) {
WebFilter wf = c.getAnnotation(WebFilter.class);
if (wf != null) {
logger.info("auto register @WebFilter: {}", c.getName());
@SuppressWarnings("unchecked")
Class<? extends Filter> clazz = (Class<? extends Filter>) c;
// 将Filter注册到 filterRegistrations中
FilterRegistration.Dynamic registration = this.addFilter(AnnoUtils.getFilterName(clazz), clazz);
// 这里读取@WebFilter(urlPatterns = "/xxx")urlPatterns信息
// 并添加到registration的urlPatterns中去
registration.addMappingForUrlPatterns(EnumSet.of(DispatcherType.REQUEST), true, AnnoUtils.getFilterUrlPatterns(clazz));
registration.setInitParameters(AnnoUtils.getFilterInitParams(clazz));
}
}
// init filters:
for (String name : this.filterRegistrations.keySet()) {
var registration = this.filterRegistrations.get(name);
try {
registration.filter.init(registration.getFilterConfig());
this.nameToFilters.put(name, registration.filter);
// Filter的urlPattern增加映射关系
for (String urlPattern : registration.getUrlPatternMappings()) {
this.filterMappings.add(new FilterMapping(urlPattern, registration.filter));
}
registration.initialized = true;
} catch (ServletException e) {
logger.error("init filter failed: " + name + " / " + registration.filter.getClass().getName(), e);
}
}
}
3、修改请求处理逻辑process方法
新增逻辑:根据请求path匹配filter、组装FilterChain,然后调用chain.doFilter(request, response);
public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
String path = request.getRequestURI();
// search servlet:
Servlet servlet = null;
for (ServletMapping mapping : this.servletMappings) {
if (mapping.matches(path)) {
servlet = mapping.servlet;
break;
}
}
if (servlet == null) {
// 404 Not Found:
PrintWriter pw = response.getWriter();
pw.write("<h1>404 Not Found</h1><p>No mapping for URL: " + path + "</p>");
pw.close();
return;
}
// search filter:
List<Filter> enabledFilters = new ArrayList<>();
for (FilterMapping mapping : this.filterMappings) {
if (mapping.matches(path)) {
enabledFilters.add(mapping.filter);
}
}
Filter[] filters = enabledFilters.toArray(Filter[]::new);
logger.atDebug().log("process {} by filter {}, servlet {}", path, Arrays.toString(filters), servlet);
FilterChain chain = new FilterChainImpl(filters, servlet);
try {
chain.doFilter(request, response);
} catch (ServletException e) {
logger.error(e.getMessage(), e);
throw new IOException(e);
} catch (IOException e) {
logger.error(e.getMessage(), e);
throw e;
}
}
FilterChain中重写的doFilter方法:
@Override
public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
if (index < total) {
int current = index;
index++;
filters[current].doFilter(request, response, this);
} else {
servlet.service(request, response);
}
}
total用于维护当前FilterChain中的filter总数。 index用于维护当前执行的filter。 如果当前filter都执行完了,则开始执行目标servlet。