相信大家都熟悉Servlet中Filter过滤器,我们可以在servlet和servlet容器之间插入Filter过滤器来包装、预处理请求,或者包装、处理响应。过滤器本身不知道自己的顺序。而是由FilterChain按照web.xml中的配置顺序执行的,确切的说,按照<filter-mapping>的顺序执行的。而调用完chain.doFilter()方法后,即当filter链中的filter都按顺序执行完毕,像堆栈一样,filter又从最后的一个开始执行。
Filter实现类的示例如下:
import javax.servlet.*;
import java.util.*;
//实现 Filter 类
@Slf4j
public class LogFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
// 获取初始化参数
String preInfo = config.getInitParameter(“preInfo");
// 输出初始化参数
log.info("preInfo: " + preInfo);
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {
// 取得根目录所对应的绝对路径:
String currentURL = request.getRequestURI();
currentURL = currentURL.replaceAll("//+","/");
log.info("请求地址:{}",currentURL);
// 把请求传回过滤链
chain.doFilter(request,response);
}
public void destroy( ){
/* 在 Filter 实例被 Web 容器从服务移除之前调用 */
}
}
每个Filter过滤器实现javax.servlet.Filter接口,其中包含一个doFilter()方法,该方法接受一个request,resonse对以及filterChain作为参数输入,filterChain实现javax.servlet.FilterChain接口(由servlet容器提供),当请求到来时,它将会管理与该请求相关的一系列过滤器的执行,当过滤器执行完毕,doFilter接下来就会调用servlet的service()方法。
想象一下,如果没有filter机制,我们可能会把所有的逻辑写在一处,里面堆积着一堆if….else,这样是违反“单一职责原则”的,就算我们把这些逻辑拆分为多个类,假如后期增加或删除某些节点,或者调整节点顺序,就要修改源码,进行严格测试。而servlet filter使用了责任链模式,很好地解决了这个设计问题。
责任链模式包含一份请求对象和一系列执行对象,每个执行对象都定义了可以执行哪些请求对象,剩下的请求对象就传递给链中的下一个执行对象。也可以添加新的执行对象到链尾,这样责任链和if ... else if ... else if ....... else … endif 在语义上比较类似。但他的条件模块可以在运行时动态编排和配置。
责任链模式解决的问题:
1. 请求者和接受者松散耦合
在责任链模式中,请求者并不知道接受者是谁,也不知道具体如何处理。请求者只负责向责任链发出请求就可以了,该模式下可以有多个接受者处理对象,每个接受者只负责处理自己的部分,其他的就交给其他的接受者去处理。请求在链中传递,接受者处理该请求,或者传递给链中下一个接受者。请求者不再和特定接受者紧密耦合。
2. 通过改变链内成员的或者调整它们的次序,允许你动态地新增或删除责任。
我们以tomcat中的ApplicationFilterFactory和ApplicationFilterChain为例,看看他做了什么,首先,我们看看它是怎么样动态编排一条责任链的呢:
ApplicationFilterFactory 的createFilterChain方法:
public static ApplicationFilterChain createFilterChain(ServletRequest request, Wrapper wrapper, Servlet servlet) {
// 如果没有相关servlet,返回null if (servlet == null) return null;
// 创建、初始化一个FilterChain对象 ApplicationFilterChain filterChain = null; if (request instanceof Request) { Request req = (Request) request; if (Globals.IS_SECURITY_ENABLED) { // Security: Do not recycle filterChain = new ApplicationFilterChain(); } else { filterChain = (ApplicationFilterChain) req.getFilterChain(); if (filterChain == null) { filterChain = new ApplicationFilterChain(); req.setFilterChain(filterChain); } } } else { // Request dispatcher in use filterChain = new ApplicationFilterChain(); }
filterChain.setServlet(servlet); filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());
// 获取上下文中的filterMapping StandardContext context = (StandardContext) wrapper.getParent(); FilterMap filterMaps[] = context.findFilterMaps();
// 如果没有 filter mappings, 完成。 if ((filterMaps == null) || (filterMaps.length == 0)) return (filterChain);
// Acquire the information we will need to match filter mappings DispatcherType dispatcher = (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);
String requestPath = null; Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR); if (attribute != null){ requestPath = attribute.toString(); }
String servletName = wrapper.getName();
// 添加相关path-mapped的filter到filterChain for (int i = 0; i < filterMaps.length; i++) { if (!matchDispatcher(filterMaps[i] ,dispatcher)) { continue; } if (!matchFiltersURL(filterMaps[i], requestPath)) continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig == null) { // FIXME - log configuration problem continue; } filterChain.addFilter(filterConfig); }
// Add filters that match on servlet name second for (int i = 0; i < filterMaps.length; i++) { if (!matchDispatcher(filterMaps[i] ,dispatcher)) { continue; } if (!matchFiltersServlet(filterMaps[i], servletName)) continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig == null) { // FIXME - log configuration problem continue; } filterChain.addFilter(filterConfig); }
// Return the completed filter chain return filterChain; }
if (!matchFiltersURL(filterMaps[i], requestPath)) continue; ApplicationFilterConfig filterConfig = (ApplicationFilterConfig) context.findFilterConfig(filterMaps[i].getFilterName()); if (filterConfig == null) { // FIXME - log configuration problem continue; } filterChain.addFilter(filterConfig);
后面截取的这段中,它会按路径(url path)匹配规则筛选Filter,匹配成功,则返回true,将该filter添加到filterChain,否则就不添加,这样针对不同的请求URL,形成了相应的filterChain,达到动态编排的效果。而在链具体执行过程中,FilterChain发挥了巨大的作用,看ApplicationFilterChain部分源码:
ApplicationFilterChain的dofilter方法和internalDoFilter()方法:
/** *触发下一个filter,传递request,response,如果链中没有filter,就触发service() * * @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 */ @Override 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( new java.security.PrivilegedExceptionAction<Void>() { @Override public Void run() throws ServletException, IOException { 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 {
// 如果有,获取下一个过滤器 if (pos < n) { 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); } } }
ApplicationFilterChain 会调用doFilter(request, response)方法,在doFilter里面会调用internalDoFilter(request,response)方法,该方法拿到每个过滤器,然后过滤器调用自身的doFilter(request, response, filterChain)方法(实现了Filter接口)。
Filter接口doFilter定义如下:
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
过滤器链里面的filter在调用dofilter完成后,会继续调用chain.doFilter(request,response)方法,而这个chain其实就是ApplicationFilterChain,所以调用过程又回到了上面调用dofilter和调用internalDoFilter方法,以此类推直到里面的过滤器全部执行。通过FilterChain,每个过滤器相当于间接拥有了下一个Filter的引用,将请求在链中传递,接受者处理相应请求或者将请求传递给下一个接受者。