透过源码学习设计模式1—Servlet Filter和责任链模式

Posted java达人

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了透过源码学习设计模式1—Servlet Filter和责任链模式相关的知识,希望对你有一定的参考价值。


Servlet filter 一瞥

相信大家都熟悉Servlet中Filter过滤器,我们可以在servlet和servlet容器之间插入Filter过滤器来包装、预处理请求,或者包装、处理响应。过滤器本身不知道自己的顺序。而是由FilterChain按照web.xml中的配置顺序执行的,确切的说,按照<filter-mapping> 的顺序执行的。而调用完chain.doFilter()方法后,即当filter链中的filter都按顺序执行完毕,像堆栈一样,filter又从最后的一个开始执行。

Filter实现类的示例如下:

 
   
   
 
  1. import javax.servlet.*;


  2. import java.util.*;


  3. //实现 Filter 类


  4. @Slf4j


  5. public class LogFilter implements Filter {


  6. public void init(FilterConfig config) throws ServletException {


  7. // 获取初始化参数


  8. String preInfo = config.getInitParameter(“preInfo");


  9. // 输出初始化参数


  10. log.info("preInfo: " + preInfo);


  11. }


  12. public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws java.io.IOException, ServletException {


  13. // 取得根目录所对应的绝对路径:


  14. String currentURL = request.getRequestURI();


  15. currentURL = currentURL.replaceAll("//+","/");



  16. // 把请求传回过滤链


  17. chain.doFilter(request,response);


  18. }


  19. public void destroy( ){


  20. /* 在 Filter 实例被 Web 容器从服务移除之前调用 */


  21. }


  22. }

每个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. 通过改变链内成员的或者调整它们的次序,允许你动态地新增或删除责任。

细看FilterChain

我们以tomcat中的ApplicationFilterFactory和ApplicationFilterChain为例,看看他做了什么,首先,我们看看它是怎么样动态编排一条责任链的呢:

ApplicationFilterFactory 的createFilterChain方法:

 
   
   
 
  1. public static ApplicationFilterChain createFilterChain(ServletRequest request,

  2. Wrapper wrapper, Servlet servlet) {


  3. // 如果没有相关servlet,返回null

  4. if (servlet == null)

  5. return null;


  6. // 创建、初始化一个FilterChain对象

  7. ApplicationFilterChain filterChain = null;

  8. if (request instanceof Request) {

  9. Request req = (Request) request;

  10. if (Globals.IS_SECURITY_ENABLED) {

  11. // Security: Do not recycle

  12. filterChain = new ApplicationFilterChain();

  13. } else {

  14. filterChain = (ApplicationFilterChain) req.getFilterChain();

  15. if (filterChain == null) {

  16. filterChain = new ApplicationFilterChain();

  17. req.setFilterChain(filterChain);

  18. }

  19. }

  20. } else {

  21. // Request dispatcher in use

  22. filterChain = new ApplicationFilterChain();

  23. }


  24. filterChain.setServlet(servlet);

  25. filterChain.setServletSupportsAsync(wrapper.isAsyncSupported());


  26. // 获取上下文中的filterMapping

  27. StandardContext context = (StandardContext) wrapper.getParent();

  28. FilterMap filterMaps[] = context.findFilterMaps();


  29. // 如果没有 filter mappings, 完成。

  30. if ((filterMaps == null) || (filterMaps.length == 0))

  31. return (filterChain);


  32. // Acquire the information we will need to match filter mappings

  33. DispatcherType dispatcher =

  34. (DispatcherType) request.getAttribute(Globals.DISPATCHER_TYPE_ATTR);


  35. String requestPath = null;

  36. Object attribute = request.getAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR);

  37. if (attribute != null){

  38. requestPath = attribute.toString();

  39. }


  40. String servletName = wrapper.getName();


  41. // 添加相关path-mapped的filter到filterChain

  42. for (int i = 0; i < filterMaps.length; i++) {

  43. if (!matchDispatcher(filterMaps[i] ,dispatcher)) {

  44. continue;

  45. }

  46. if (!matchFiltersURL(filterMaps[i], requestPath))

  47. continue;

  48. ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)

  49. context.findFilterConfig(filterMaps[i].getFilterName());

  50. if (filterConfig == null) {

  51. // FIXME - log configuration problem

  52. continue;

  53. }

  54. filterChain.addFilter(filterConfig);

  55. }


  56. // Add filters that match on servlet name second

  57. for (int i = 0; i < filterMaps.length; i++) {

  58. if (!matchDispatcher(filterMaps[i] ,dispatcher)) {

  59. continue;

  60. }

  61. if (!matchFiltersServlet(filterMaps[i], servletName))

  62. continue;

  63. ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)

  64. context.findFilterConfig(filterMaps[i].getFilterName());

  65. if (filterConfig == null) {

  66. // FIXME - log configuration problem

  67. continue;

  68. }

  69. filterChain.addFilter(filterConfig);

  70. }


  71. // Return the completed filter chain

  72. return filterChain;

  73. }

 
   
   
 
  1. if (!matchFiltersURL(filterMaps[i], requestPath))

  2. continue;

  3. ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)

  4. context.findFilterConfig(filterMaps[i].getFilterName());

  5. if (filterConfig == null) {

  6. // FIXME - log configuration problem

  7. continue;

  8. }

  9. filterChain.addFilter(filterConfig);

后面截取的这段中,它会按路径(url path)匹配规则筛选Filter,匹配成功,则返回true,将该filter添加到filterChain,否则就不添加,这样针对不同的请求URL,形成了相应的filterChain,达到动态编排的效果。而在链具体执行过程中,FilterChain发挥了巨大的作用,看ApplicationFilterChain部分源码:

ApplicationFilterChain的dofilter方法和internalDoFilter()方法:

 
   
   
 
  1. /**

  2. *触发下一个filter,传递request,response,如果链中没有filter,就触发service()

  3. *

  4. * @param request The servlet request we are processing

  5. * @param response The servlet response we are creating

  6. *

  7. * @exception IOException if an input/output error occurs

  8. * @exception ServletException if a servlet exception occurs

  9. */

  10. @Override

  11. public void doFilter(ServletRequest request, ServletResponse response)

  12. throws IOException, ServletException {


  13. if( Globals.IS_SECURITY_ENABLED ) {

  14. final ServletRequest req = request;

  15. final ServletResponse res = response;

  16. try {

  17. java.security.AccessController.doPrivileged(

  18. new java.security.PrivilegedExceptionAction<Void>() {

  19. @Override

  20. public Void run()

  21. throws ServletException, IOException {

  22. internalDoFilter(req,res);

  23. return null;

  24. }

  25. }

  26. );

  27. } catch( PrivilegedActionException pe) {

  28. Exception e = pe.getException();

  29. if (e instanceof ServletException)

  30. throw (ServletException) e;

  31. else if (e instanceof IOException)

  32. throw (IOException) e;

  33. else if (e instanceof RuntimeException)

  34. throw (RuntimeException) e;

  35. else

  36. throw new ServletException(e.getMessage(), e);

  37. }

  38. } else {

  39. internalDoFilter(request,response);

  40. }

  41. }

 
   
   
 
  1. private void internalDoFilter(ServletRequest request,

  2. ServletResponse response)

  3. throws IOException, ServletException {


  4. // 如果有,获取下一个过滤器

  5. if (pos < n) {

  6. ApplicationFilterConfig filterConfig = filters[pos++];

  7. try {

  8. Filter filter = filterConfig.getFilter();


  9. if (request.isAsyncSupported() && "false".equalsIgnoreCase(

  10. filterConfig.getFilterDef().getAsyncSupported())) {

  11. request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR, Boolean.FALSE);

  12. }

  13. if( Globals.IS_SECURITY_ENABLED ) {

  14. final ServletRequest req = request;

  15. final ServletResponse res = response;

  16. Principal principal =

  17. ((HttpServletRequest) req).getUserPrincipal();


  18. Object[] args = new Object[]{req, res, this};

  19. SecurityUtil.doAsPrivilege ("doFilter", filter, classType, args, principal);

  20. } else {

  21. filter.doFilter(request, response, this);

  22. }

  23. } catch (IOException | ServletException | RuntimeException e) {

  24. throw e;

  25. } catch (Throwable e) {

  26. e = ExceptionUtils.unwrapInvocationTargetException(e);

  27. ExceptionUtils.handleThrowable(e);

  28. throw new ServletException(sm.getString("filterChain.filter"), e);

  29. }

  30. return;

  31. }


  32. // We fell off the end of the chain -- call the servlet instance

  33. try {

  34. if (ApplicationDispatcher.WRAP_SAME_OBJECT) {

  35. lastServicedRequest.set(request);

  36. lastServicedResponse.set(response);

  37. }


  38. if (request.isAsyncSupported() && !servletSupportsAsync) {

  39. request.setAttribute(Globals.ASYNC_SUPPORTED_ATTR,

  40. Boolean.FALSE);

  41. }

  42. // Use potentially wrapped request from this point

  43. if ((request instanceof HttpServletRequest) &&

  44. (response instanceof HttpServletResponse) &&

  45. Globals.IS_SECURITY_ENABLED ) {

  46. final ServletRequest req = request;

  47. final ServletResponse res = response;

  48. Principal principal =

  49. ((HttpServletRequest) req).getUserPrincipal();

  50. Object[] args = new Object[]{req, res};

  51. SecurityUtil.doAsPrivilege("service",

  52. servlet,

  53. classTypeUsedInService,

  54. args,

  55. principal);

  56. } else {

  57. servlet.service(request, response);

  58. }

  59. } catch (IOException | ServletException | RuntimeException e) {

  60. throw e;

  61. } catch (Throwable e) {

  62. e = ExceptionUtils.unwrapInvocationTargetException(e);

  63. ExceptionUtils.handleThrowable(e);

  64. throw new ServletException(sm.getString("filterChain.servlet"), e);

  65. } finally {

  66. if (ApplicationDispatcher.WRAP_SAME_OBJECT) {

  67. lastServicedRequest.set(null);

  68. lastServicedResponse.set(null);

  69. }

  70. }

  71. }

ApplicationFilterChain 会调用doFilter(request, response)方法,在doFilter里面会调用internalDoFilter(request,response)方法,该方法拿到每个过滤器,然后过滤器调用自身的doFilter(request, response, filterChain)方法(实现了Filter接口)。

Filter接口doFilter定义如下:

 
   
   
 
  1. public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)

过滤器链里面的filter在调用dofilter完成后,会继续调用chain.doFilter(request,response)方法,而这个chain其实就是ApplicationFilterChain,所以调用过程又回到了上面调用dofilter和调用internalDoFilter方法,以此类推直到里面的过滤器全部执行。通过FilterChain,每个过滤器相当于间接拥有了下一个Filter的引用,将请求在链中传递,接受者处理相应请求或者将请求传递给下一个接受者。


java达人

ID:drjava

(长按或扫码识别)


以上是关于透过源码学习设计模式1—Servlet Filter和责任链模式的主要内容,如果未能解决你的问题,请参考以下文章

学习笔记——Servlet底层源码分析;Servlet接口;ServletConfig接口;

SpringBoot源码学习系列之嵌入式Servlet容器

SpringBoot源码学习系列之嵌入式Servlet容器

RocketMQ源码学习- 1. 入门

Servlet开发

Dubbo 3 深度剖析 - 透过源码认识你