当前位置: 代码网 > 服务器>服务器>Tomcat > 详解Tomcat中Filter的执行流程

详解Tomcat中Filter的执行流程

2024年05月18日 Tomcat 我要评论
前言filter是什么?filter是servlet规范中定义的java web组件, 在所有支持java web的容器中都可以使用 它是位于前端请求到servlet之间的一系列过滤器,也可以称之为中

前言

filter是什么?filter是servlet规范中定义的java web组件, 在所有支持java web的容器中都可以使用 它是位于前端请求到servlet之间的一系列过滤器,也可以称之为中间件,它主要是对请求到达servlet之前做一些额外的动作:

  • 1、权限控制
  • 2、监控
  • 3、日志管理
  • 4、等等

这里涉及到两个接口:filter和filterchain

filter和filterchain密不可分, filter可以实现依次调用正是因为有了filterchain。

1、filter接口

public interface filter {
   // 容器创建的时候调用, 即启动tomcat的时候调用
   public void init(filterconfig filterconfig) throws servletexception;
   // 由filterchain调用, 并且传入filterchain本身, 最后回调filterchain的dofilter()方法
   public void dofilter(servletrequest request, servletresponse response,
       filterchain chain) throws ioexception, servletexception;
   // 容器销毁的时候调用, 即关闭tomcat的时候调用
   public void destroy();
 }

2、filterchain接口

public interface filterchain {
   // 由filter.dofilter()中的chain.dofilter调用
   public void dofilter(servletrequest request, servletresponse response)
     throws ioexception, servletexception;
}

执行流程

在前面的文章中,我们知道,tomcat启动会执行standardwrappervalve.java类的invoke方法:

public final void invoke(request request, response response){
  ......
  messagebytes requestpathmb = request.getrequestpathmb();
  dispatchertype dispatchertype = dispatchertype.request;
  if (request.getdispatchertype()==dispatchertype.async) dispatchertype = dispatchertype.async;
  request.setattribute(globals.dispatcher_type_attr,dispatchertype);
  request.setattribute(globals.dispatcher_request_path_attr,
      requestpathmb);
  // create the filter chain for this request
  applicationfilterchain filterchain =
      applicationfilterfactory.createfilterchain(request, wrapper, servlet);
  // call the filter chain for this request
  // note: this also calls the servlet's service() method
  container container = this.container;
  try {
    if ((servlet != null) && (filterchain != null)) {
      // swallow output if needed
      if (context.getswallowoutput()) {
        try {
          systemloghandler.startcapture();
          if (request.isasyncdispatching()) {
            request.getasynccontextinternal().dointernaldispatch();
          } else {
            filterchain.dofilter(request.getrequest(),
                response.getresponse());
          }
        } finally {
          string log = systemloghandler.stopcapture();
          if (log != null && log.length() > 0) {
            context.getlogger().info(log);
          }
        }
      } else {
        if (request.isasyncdispatching()) {
          request.getasynccontextinternal().dointernaldispatch();
        } else {
          filterchain.dofilter
            (request.getrequest(), response.getresponse());
        }
      }
    }
  } catch (clientabortexception | closenowexception e) {
  }
  ......
}

上面的代码做了如下一些动作:

  • 1、每次请求过来都会创建一个过滤器链(filterchain),并把待执行的servlet对象存放到过滤器链中。对于每个url,对应的filter个数都是不固定的,filterchain需要保存每个请求所对应的一个filter数组,以及调用到的filter的position,以便继续向下调用filter。
  • 2、创建了filterchain之后,就开始执行dofilter进行请求的链式处理。

1、创建filterchain

下面我们具体来看看filterchain是怎么创建的

public static applicationfilterchain createfilterchain(servletrequest request,
            wrapper wrapper, servlet servlet) {
  // if there is no servlet to execute, return null
  if (servlet == null)
    return null;
  // create and initialize a filter chain object
  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());
  // acquire the filter mappings for this context
  standardcontext context = (standardcontext) wrapper.getparent();
  filtermap filtermaps[] = context.findfiltermaps();
  // if there are no filter mappings, we are done
  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();
  // add the relevant path-mapped filters to this filter chain
  for (filtermap filtermap : filtermaps) {
    if (!matchdispatcher(filtermap, dispatcher)) {
      continue;
    }
    if (!matchfiltersurl(filtermap, requestpath))
      continue;
    applicationfilterconfig filterconfig = (applicationfilterconfig)
        context.findfilterconfig(filtermap.getfiltername());
    if (filterconfig == null) {
      // fixme - log configuration problem
      continue;
    }
    filterchain.addfilter(filterconfig);
  }
  // add filters that match on servlet name second
  for (filtermap filtermap : filtermaps) {
    if (!matchdispatcher(filtermap, dispatcher)) {
      continue;
    }
    if (!matchfiltersservlet(filtermap, servletname))
      continue;
    applicationfilterconfig filterconfig = (applicationfilterconfig)
        context.findfilterconfig(filtermap.getfiltername());
    if (filterconfig == null) {
      // fixme - log configuration problem
      continue;
    }
    filterchain.addfilter(filterconfig);
  }
  // return the completed filter chain
  return filterchain;
}

上面的代码做了一下几件事:

  • 1、把要执行的servlet存放到过滤器链中。
  • 2、如果没有配置过滤器则return一个空的过滤器链(只包含上面设置的servlet)。
  • 3、如果配置url-pattern过滤器,则把匹配的过滤器加入到过滤器链中
  • 4、如果配置servlet-name过滤器,则把匹配的过滤器加入到过滤器链中

注意: filterchain.addfilter()顺序与web.xml中定义的filter顺序一致,所以过滤器的执行顺序是按定义的上下顺序决定的。

2、执行dofilter

创建了chain之后,就开始执行链式请求了,具体的逻辑如下:

private void internaldofilter(servletrequest request,
                                  servletresponse response)
        throws ioexception, servletexception {
  // call the next filter if there is one
  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);
    }
  }
}

上面的代码逻辑如下:

  • 1、通过position索引判断是否执行完了所有的filter
  • 2、如果没有,取出当前待执行的索引filter,调用其dofilter方法,在上面的接口说明中,我们看到,所有的filter类都继承了filter接口,都实现了dofilter方法;我们也注意到,该方法接收一个filterchain对象。在这段代码中,filter.dofilter(request, response, this);可以看到,将自身引用传递进去了,那么各个filter在dofilter的方法中,可以根据自身业务需要,来判断是否需要继续进行下面的filter链式执行,如果需要,就执行filterchain.dofilter方法,此时就又回到了此代码中。如果反复
  • 3、如果执行完了所有的filter,则开始执行servlet业务模块servlet.service(request, response);

以上就是详解tomcat中filter是怎样执行的的详细内容,更多关于tomcat filter执行的资料请关注代码网其它相关文章!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com