欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Tomcat源码分析 (九)----- HTTP请求处理过程(二)

程序员文章站 2023-11-13 09:31:52
我们接着上一篇文章的容器处理来讲,当postParseRequest方法返回true时,则由容器继续处理,在service方法中有connector.getService().getContainer().getPipeline().getFirst().invoke(request, respon ......

我们接着上一篇文章的容器处理来讲,当postparserequest方法返回true时,则由容器继续处理,在service方法中有connector.getservice().getcontainer().getpipeline().getfirst().invoke(request, response)这一行:

  • connector调用getservice()返回standardservice;
  • standardservice调用getcontainer返回standardengine;
  • standardengine调用getpipeline返回与其关联的standardpipeline;

engine处理请求

我们在前面的文章中讲过standardengine的构造函数为自己的pipeline添加了基本阀standardenginevalve,代码如下:

public standardengine() {
    super();
    pipeline.setbasic(new standardenginevalve());
    try {
        setjvmroute(system.getproperty("jvmroute"));
    } catch(exception ex) {
        log.warn(sm.getstring("standardengine.jvmroutefail"));
    }
}

接下来我们看看standardenginevalveinvoke()方法。该方法主要是选择合适的host,然后调用host中pipeline的第一个valve的invoke()方法。

public final void invoke(request request, response response)
    throws ioexception, servletexception {

    // select the host to be used for this request
    host host = request.gethost();
    if (host == null) {
        response.senderror
            (httpservletresponse.sc_bad_request,
             sm.getstring("standardengine.nohost",
                          request.getservername()));
        return;
    }
    if (request.isasyncsupported()) {
        request.setasyncsupported(host.getpipeline().isasyncsupported());
    }

    // ask this host to process this request
    host.getpipeline().getfirst().invoke(request, response);
}
该方法很简单,校验该engline 容器是否含有host容器,如果不存在,返回400错误,否则继续执行 host.getpipeline().getfirst().invoke(request, response),可以看到 host 容器先获取自己的管道,再获取第一个阀门,我们再看看该阀门的 invoke 方法。

host处理请求

分析host的时候,我们从host的构造函数入手,该方法主要是设置基础阀门。
public standardhost() {
    super();
    pipeline.setbasic(new standardhostvalve());
}

standardpipeline调用getfirst得到第一个阀去处理请求,由于基本阀是最后一个,所以最后会由基本阀去处理请求。

standardhost的pipeline里面一定有 errorreportvalve 与 standardhostvalve两个valve,errorreportvalve主要是检测 http 请求过程中是否出现过什么异常, 有异常的话, 直接拼装 html 页面, 输出到客户端。

我们看看errorreportvalve的invoke方法:

public void invoke(request request, response response)
    throws ioexception, servletexception {
    // perform the request
    // 1. 先将 请求转发给下一个 valve
    getnext().invoke(request, response);  
    // 2. 这里的 iscommitted 表明, 请求是正常处理结束    
    if (response.iscommitted()) {               
        return;
    }
    // 3. 判断请求过程中是否有异常发生
    throwable throwable = (throwable) request.getattribute(requestdispatcher.error_exception);
    if (request.isasyncstarted() && ((response.getstatus() < 400 &&
            throwable == null) || request.isasyncdispatching())) {
        return;
    }
    if (throwable != null) {
        // the response is an error
        response.seterror();
        // reset the response (if possible)
        try {
            // 4. 重置 response 里面的数据(此时 response 里面可能有些数据)
            response.reset();                  
        } catch (illegalstateexception e) {
            // ignore
        }
         // 5. 这就是我们常看到的 500 错误码
        response.senderror(httpservletresponse.sc_internal_server_error);
    }
    response.setsuspended(false);
    try {
        // 6. 这里就是将 异常的堆栈信息组合成 html 页面, 输出到前台        
        report(request, response, throwable);                                   
    } catch (throwable tt) {
        exceptionutils.handlethrowable(tt);
    }
    if (request.isasyncstarted()) {          
        // 7. 若是异步请求的话, 设置对应的 complete (对应的是 异步 servlet)                   
        request.getasynccontext().complete();
    }
}

该方法首先执行了下个阀门的 invoke 方法。然后根据返回的request 属性设置一些错误信息。那么下个阀门是谁呢?其实就是基础阀门了:standardhostvalve,该阀门的 invoke 的方法是如何实现的呢?

@override
public final void invoke(request request, response response)
    throws ioexception, servletexception {

    // select the context to be used for this request
    context context = request.getcontext();
    if (context == null) {
        response.senderror
            (httpservletresponse.sc_internal_server_error,
             sm.getstring("standardhost.nocontext"));
        return;
    }

    // bind the context cl to the current thread
    if( context.getloader() != null ) {
        // not started - it should check for availability first
        // this should eventually move to engine, it's generic.
        if (globals.is_security_enabled) {
            privilegedaction<void> pa = new privilegedsettccl(
                    context.getloader().getclassloader());
            accesscontroller.doprivileged(pa);                
        } else {
            thread.currentthread().setcontextclassloader
                    (context.getloader().getclassloader());
        }
    }
    if (request.isasyncsupported()) {
        request.setasyncsupported(context.getpipeline().isasyncsupported());
    }

    // don't fire listeners during async processing
    // if a request init listener throws an exception, the request is
    // aborted
    boolean asyncatstart = request.isasync(); 
    // an async error page may dispatch to another resource. this flag helps
    // ensure an infinite error handling loop is not entered
    boolean erroratstart = response.iserror();
    if (asyncatstart || context.firerequestinitevent(request)) {

        // ask this context to process this request
        try {
            context.getpipeline().getfirst().invoke(request, response);
        } catch (throwable t) {
            exceptionutils.handlethrowable(t);
            if (erroratstart) {
                container.getlogger().error("exception processing " +
                        request.getrequesturi(), t);
            } else {
                request.setattribute(requestdispatcher.error_exception, t);
                throwable(request, response, t);
            }
        }

        // if the request was async at the start and an error occurred then
        // the async error handling will kick-in and that will fire the
        // request destroyed event *after* the error handling has taken
        // place
        if (!(request.isasync() || (asyncatstart &&
                request.getattribute(
                        requestdispatcher.error_exception) != null))) {
            // protect against npes if context was destroyed during a
            // long running request.
            if (context.getstate().isavailable()) {
                if (!erroratstart) {
                    // error page processing
                    response.setsuspended(false);

                    throwable t = (throwable) request.getattribute(
                            requestdispatcher.error_exception);

                    if (t != null) {
                        throwable(request, response, t);
                    } else {
                        status(request, response);
                    }
                }

                context.firerequestdestroyevent(request);
            }
        }
    }

    // access a session (if present) to update last accessed time, based on a
    // strict interpretation of the specification
    if (access_session) {
        request.getsession(false);
    }

    // restore the context classloader
    if (globals.is_security_enabled) {
        privilegedaction<void> pa = new privilegedsettccl(
                standardhostvalve.class.getclassloader());
        accesscontroller.doprivileged(pa);                
    } else {
        thread.currentthread().setcontextclassloader
                (standardhostvalve.class.getclassloader());
    }
}
首先校验了request 是否存在 context,其实在执行 coyoteadapter.postparserequest 方法的时候就设置了,如果context 不存在,就返回500,接着还是老套路:context.getpipeline().getfirst().invoke,该管道获取的是基础阀门:standardcontextvalve,我们还是关注他的 invoke 方法。

context处理请求

接着context会去处理请求,同理,standardcontextvalve的invoke方法会被调用:

@override
public final void invoke(request request, response response)
    throws ioexception, servletexception {
    // disallow any direct access to resources under web-inf or meta-inf
    messagebytes requestpathmb = request.getrequestpathmb();
    if ((requestpathmb.startswithignorecase("/meta-inf/", 0))
            || (requestpathmb.equalsignorecase("/meta-inf"))
            || (requestpathmb.startswithignorecase("/web-inf/", 0))
            || (requestpathmb.equalsignorecase("/web-inf"))) {
        response.senderror(httpservletresponse.sc_not_found);
        return;
    }

    // select the wrapper to be used for this request
    wrapper wrapper = request.getwrapper();
    if (wrapper == null || wrapper.isunavailable()) {
        response.senderror(httpservletresponse.sc_not_found);
        return;
    }

    // acknowledge the request
    try {
        response.sendacknowledgement();
    } catch (ioexception ioe) {
        container.getlogger().error(sm.getstring(
                "standardcontextvalve.acknowledgeexception"), ioe);
        request.setattribute(requestdispatcher.error_exception, ioe);
        response.senderror(httpservletresponse.sc_internal_server_error);
        return;
    }

    if (request.isasyncsupported()) {
        request.setasyncsupported(wrapper.getpipeline().isasyncsupported());
    }
    wrapper.getpipeline().getfirst().invoke(request, response);
}

wrapper处理请求

wrapper是一个servlet的包装,我们先来看看构造方法。主要作用就是设置基础阀门standardwrappervalve

public standardwrapper() {
    super();
    swvalve=new standardwrappervalve();
    pipeline.setbasic(swvalve);
    broadcaster = new notificationbroadcastersupport();
}

接下来我们看看standardwrappervalveinvoke()方法。

@override
public final void invoke(request request, response response)
    throws ioexception, servletexception {

    // initialize local variables we may need
    boolean unavailable = false;
    throwable throwable = null;
    // this should be a request attribute...
    long t1=system.currenttimemillis();
    requestcount.incrementandget();
    standardwrapper wrapper = (standardwrapper) getcontainer();
    servlet servlet = null;
    context context = (context) wrapper.getparent();

    // check for the application being marked unavailable
    if (!context.getstate().isavailable()) {
        response.senderror(httpservletresponse.sc_service_unavailable,
                       sm.getstring("standardcontext.isunavailable"));
        unavailable = true;
    }

    // check for the servlet being marked unavailable
    if (!unavailable && wrapper.isunavailable()) {
        container.getlogger().info(sm.getstring("standardwrapper.isunavailable",
                wrapper.getname()));
        long available = wrapper.getavailable();
        if ((available > 0l) && (available < long.max_value)) {
            response.setdateheader("retry-after", available);
            response.senderror(httpservletresponse.sc_service_unavailable,
                    sm.getstring("standardwrapper.isunavailable",
                            wrapper.getname()));
        } else if (available == long.max_value) {
            response.senderror(httpservletresponse.sc_not_found,
                    sm.getstring("standardwrapper.notfound",
                            wrapper.getname()));
        }
        unavailable = true;
    }

    // allocate a servlet instance to process this request
    try {
        // 关键点1:这儿调用wrapper的allocate()方法分配一个servlet实例
        if (!unavailable) {
            servlet = wrapper.allocate();
        }
    } catch (unavailableexception e) {
        container.getlogger().error(
                sm.getstring("standardwrapper.allocateexception",
                        wrapper.getname()), e);
        long available = wrapper.getavailable();
        if ((available > 0l) && (available < long.max_value)) {
            response.setdateheader("retry-after", available);
            response.senderror(httpservletresponse.sc_service_unavailable,
                       sm.getstring("standardwrapper.isunavailable",
                                    wrapper.getname()));
        } else if (available == long.max_value) {
            response.senderror(httpservletresponse.sc_not_found,
                       sm.getstring("standardwrapper.notfound",
                                    wrapper.getname()));
        }
    } catch (servletexception e) {
        container.getlogger().error(sm.getstring("standardwrapper.allocateexception",
                         wrapper.getname()), standardwrapper.getrootcause(e));
        throwable = e;
        exception(request, response, e);
    } catch (throwable e) {
        exceptionutils.handlethrowable(e);
        container.getlogger().error(sm.getstring("standardwrapper.allocateexception",
                         wrapper.getname()), e);
        throwable = e;
        exception(request, response, e);
        servlet = null;
    }

    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
    // 关键点2,创建过滤器链,类似于pipeline的功能
    applicationfilterchain filterchain =
            applicationfilterfactory.createfilterchain(request, wrapper, servlet);

    // call the filter chain for this request
    // note: this also calls the servlet's service() method
    try {
        if ((servlet != null) && (filterchain != null)) {
            // swallow output if needed
            if (context.getswallowoutput()) {
                try {
                    systemloghandler.startcapture();
                    if (request.isasyncdispatching()) {
                        request.getasynccontextinternal().dointernaldispatch();
                    } else {
                        // 关键点3,调用过滤器链的dofilter,最终会调用到servlet的service方法
                        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 {
                    // 关键点3,调用过滤器链的dofilter,最终会调用到servlet的service方法
                    filterchain.dofilter
                        (request.getrequest(), response.getresponse());
                }
            }

        }
    } catch (clientabortexception e) {
        throwable = e;
        exception(request, response, e);
    } catch (ioexception e) {
        container.getlogger().error(sm.getstring(
                "standardwrapper.serviceexception", wrapper.getname(),
                context.getname()), e);
        throwable = e;
        exception(request, response, e);
    } catch (unavailableexception e) {
        container.getlogger().error(sm.getstring(
                "standardwrapper.serviceexception", wrapper.getname(),
                context.getname()), e);
        //            throwable = e;
        //            exception(request, response, e);
        wrapper.unavailable(e);
        long available = wrapper.getavailable();
        if ((available > 0l) && (available < long.max_value)) {
            response.setdateheader("retry-after", available);
            response.senderror(httpservletresponse.sc_service_unavailable,
                       sm.getstring("standardwrapper.isunavailable",
                                    wrapper.getname()));
        } else if (available == long.max_value) {
            response.senderror(httpservletresponse.sc_not_found,
                        sm.getstring("standardwrapper.notfound",
                                    wrapper.getname()));
        }
        // do not save exception in 'throwable', because we
        // do not want to do exception(request, response, e) processing
    } catch (servletexception e) {
        throwable rootcause = standardwrapper.getrootcause(e);
        if (!(rootcause instanceof clientabortexception)) {
            container.getlogger().error(sm.getstring(
                    "standardwrapper.serviceexceptionroot",
                    wrapper.getname(), context.getname(), e.getmessage()),
                    rootcause);
        }
        throwable = e;
        exception(request, response, e);
    } catch (throwable e) {
        exceptionutils.handlethrowable(e);
        container.getlogger().error(sm.getstring(
                "standardwrapper.serviceexception", wrapper.getname(),
                context.getname()), e);
        throwable = e;
        exception(request, response, e);
    }

    // release the filter chain (if any) for this request
    // 关键点4,释放掉过滤器链及其相关资源
    if (filterchain != null) {
        filterchain.release();
    }

    // 关键点5,释放掉servlet及相关资源
    // deallocate the allocated servlet instance
    try {
        if (servlet != null) {
            wrapper.deallocate(servlet);
        }
    } catch (throwable e) {
        exceptionutils.handlethrowable(e);
        container.getlogger().error(sm.getstring("standardwrapper.deallocateexception",
                         wrapper.getname()), e);
        if (throwable == null) {
            throwable = e;
            exception(request, response, e);
        }
    }

    // if this servlet has been marked permanently unavailable,
    // unload it and release this instance
    // 关键点6,如果servlet被标记为永远不可达,则需要卸载掉它,并释放这个servlet实例
    try {
        if ((servlet != null) &&
            (wrapper.getavailable() == long.max_value)) {
            wrapper.unload();
        }
    } catch (throwable e) {
        exceptionutils.handlethrowable(e);
        container.getlogger().error(sm.getstring("standardwrapper.unloadexception",
                         wrapper.getname()), e);
        if (throwable == null) {
            throwable = e;
            exception(request, response, e);
        }
    }
    long t2=system.currenttimemillis();

    long time=t2-t1;
    processingtime += time;
    if( time > maxtime) maxtime=time;
    if( time < mintime) mintime=time;
}

通过阅读源码,我们发现了几个关键点。现罗列如下,后面我们会逐一分析这些关键点相关的源码。

  1. 关键点1:这儿调用wrapper的allocate()方法分配一个servlet实例
  2. 关键点2,创建过滤器链,类似于pipeline的功能
  3. 关键点3,调用过滤器链的dofilter,最终会调用到servlet的service方法
  4. 关键点4,释放掉过滤器链及其相关资源
  5. 关键点5,释放掉servlet及相关资源
  6. 关键点6,如果servlet被标记为永远不可达,则需要卸载掉它,并释放这个servlet实例

关键点1 - wrapper分配servlet实例

我们来分析一下wrapper.allocate()方法

@override
public servlet allocate() throws servletexception {

    // if we are currently unloading this servlet, throw an exception
    // 卸载过程中,不能分配servlet
    if (unloading) {
        throw new servletexception(sm.getstring("standardwrapper.unloading", getname()));
    }

    boolean newinstance = false;

    // if not singlethreadedmodel, return the same instance every time
    // 如果wrapper没有实现singlethreadedmodel,则每次都会返回同一个servlet
    if (!singlethreadmodel) {
        // load and initialize our instance if necessary
        // 实例为null或者实例还未初始化,使用synchronized来保证并发时的原子性
        if (instance == null || !instanceinitialized) {
            synchronized (this) {
                if (instance == null) {
                    try {
                        if (log.isdebugenabled()) {
                            log.debug("allocating non-stm instance");
                        }

                        // note: we don't know if the servlet implements
                        // singlethreadmodel until we have loaded it.
                        // 加载servlet
                        instance = loadservlet();
                        newinstance = true;
                        if (!singlethreadmodel) {
                            // for non-stm, increment here to prevent a race
                            // condition with unload. bug 43683, test case
                            // #3
                            countallocated.incrementandget();
                        }
                    } catch (servletexception e) {
                        throw e;
                    } catch (throwable e) {
                        exceptionutils.handlethrowable(e);
                        throw new servletexception(sm.getstring("standardwrapper.allocate"), e);
                    }
                }
                // 初始化servlet
                if (!instanceinitialized) {
                    initservlet(instance);
                }
            }
        }

        if (singlethreadmodel) {
            if (newinstance) {
                // have to do this outside of the sync above to prevent a
                // possible deadlock
                synchronized (instancepool) {
                    instancepool.push(instance);
                    ninstances++;
                }
            }
        }
        // 非单线程模型,直接返回已经创建的servlet,也就是说,这种情况下只会创建一个servlet
        else {
            if (log.istraceenabled()) {
                log.trace("  returning non-stm instance");
            }
            // for new instances, count will have been incremented at the
            // time of creation
            if (!newinstance) {
                countallocated.incrementandget();
            }
            return instance;
        }
    }

    // 如果是单线程模式,则使用servlet对象池技术来加载多个servlet
    synchronized (instancepool) {
        while (countallocated.get() >= ninstances) {
            // allocate a new instance if possible, or else wait
            if (ninstances < maxinstances) {
                try {
                    instancepool.push(loadservlet());
                    ninstances++;
                } catch (servletexception e) {
                    throw e;
                } catch (throwable e) {
                    exceptionutils.handlethrowable(e);
                    throw new servletexception(sm.getstring("standardwrapper.allocate"), e);
                }
            } else {
                try {
                    instancepool.wait();
                } catch (interruptedexception e) {
                    // ignore
                }
            }
        }
        if (log.istraceenabled()) {
            log.trace("  returning allocated stm instance");
        }
        countallocated.incrementandget();
        return instancepool.pop();
    }
}

总结下来,注意以下几点即可:

  1. 卸载过程中,不能分配servlet
  2. 如果不是单线程模式,则每次都会返回同一个servlet(默认servlet实现方式)
  3. servlet实例为null或者servlet实例还未初始化,使用synchronized来保证并发时的原子性
  4. 如果是单线程模式,则使用servlet对象池技术来加载多个servlet

接下来我们看看loadservlet()方法

public synchronized servlet loadservlet() throws servletexception {

    // nothing to do if we already have an instance or an instance pool
    if (!singlethreadmodel && (instance != null))
        return instance;

    printstream out = system.out;
    if (swallowoutput) {
        systemloghandler.startcapture();
    }

    servlet servlet;
    try {
        long t1=system.currenttimemillis();
        // complain if no servlet class has been specified
        if (servletclass == null) {
            unavailable(null);
            throw new servletexception
                (sm.getstring("standardwrapper.notclass", getname()));
        }

        // 关键的地方,就是通过实例管理器,创建servlet实例,而实例管理器是通过特殊的类加载器来加载给定的类
        instancemanager instancemanager = ((standardcontext)getparent()).getinstancemanager();
        try {
            servlet = (servlet) instancemanager.newinstance(servletclass);
        } catch (classcastexception e) {
            unavailable(null);
            // restore the context classloader
            throw new servletexception
                (sm.getstring("standardwrapper.notservlet", servletclass), e);
        } catch (throwable e) {
            e = exceptionutils.unwrapinvocationtargetexception(e);
            exceptionutils.handlethrowable(e);
            unavailable(null);

            // added extra log statement for bugzilla 36630:
            // https://bz.apache.org/bugzilla/show_bug.cgi?id=36630
            if(log.isdebugenabled()) {
                log.debug(sm.getstring("standardwrapper.instantiate", servletclass), e);
            }

            // restore the context classloader
            throw new servletexception
                (sm.getstring("standardwrapper.instantiate", servletclass), e);
        }

        if (multipartconfigelement == null) {
            multipartconfig annotation =
                    servlet.getclass().getannotation(multipartconfig.class);
            if (annotation != null) {
                multipartconfigelement =
                        new multipartconfigelement(annotation);
            }
        }

        // special handling for containerservlet instances
        // note: the instancemanager checks if the application is permitted
        //       to load containerservlets
        if (servlet instanceof containerservlet) {
            ((containerservlet) servlet).setwrapper(this);
        }

        classloadtime=(int) (system.currenttimemillis() -t1);

        if (servlet instanceof singlethreadmodel) {
            if (instancepool == null) {
                instancepool = new stack<>();
            }
            singlethreadmodel = true;
        }

        // 调用servlet的init方法
        initservlet(servlet);

        firecontainerevent("load", this);

        loadtime=system.currenttimemillis() -t1;
    } finally {
        if (swallowoutput) {
            string log = systemloghandler.stopcapture();
            if (log != null && log.length() > 0) {
                if (getservletcontext() != null) {
                    getservletcontext().log(log);
                } else {
                    out.println(log);
                }
            }
        }
    }
    return servlet;
}

关键的地方有两个:

  1. 通过实例管理器,创建servlet实例,而实例管理器是通过特殊的类加载器来加载给定的类
  2. 调用servlet的init方法

关键点2 - 创建过滤器链

创建过滤器链是调用的org.apache.catalina.core.applicationfilterfactorycreatefilterchain()方法。我们来分析一下这个方法。该方法需要注意的地方已经在代码的comments里面说明了。

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
    // 1. 如果加密打开了,则可能会多次调用这个方法
    // 2. 为了避免重复生成filterchain对象,所以会将filterchain对象放在request里面进行缓存
    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();
    // 从这儿看出过滤器链对象里面的元素是根据context里面的filtermaps来生成的
    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
    // 类型和路径都匹配的情况下,将context.filterconfig放到过滤器链里面
    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
    // 类型和servlet名称都匹配的情况下,将context.filterconfig放到过滤器链里面
    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;
}

关键点3 - 调用过滤器链的dofilter

applicationfilterchain类的dofilter函数代码如下,它会将处理委托给internaldofilter函数。

@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);
    }
}

applicationfilterchain类的internaldofilter函数代码如下:

// 1. `internaldofilter`方法通过pos和n来调用过滤器链里面的每个过滤器。pos表示当前的过滤器下标,n表示总的过滤器数量
// 2. `internaldofilter`方法最终会调用servlet.service()方法
private void internaldofilter(servletrequest request,
                              servletresponse response)
    throws ioexception, servletexception {

    // call the next filter if there is one
    // 1. 当pos小于n时, 则执行filter
    if (pos < n) {
        // 2. 得到 过滤器 filter,执行一次post++
        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 {
                // 4. 这里的 filter 的执行 有点递归的感觉, 通过 pos 来控制从 filterchain 里面拿出那个 filter 来进行操作
                // 这里把this(filterchain)传到自定义filter里面,我们自定义的filter,会重写dofilter,在这里会被调用,dofilter里面会执行业务逻辑,如果执行业务逻辑成功,则会调用 filterchain.dofilter(servletrequest, servletresponse); ,filterchain就是这里传过去的this;如果业务逻辑执行失败,则return,filterchain终止,后面的servlet.service(request, response)也不会执行了
                // 所以在 filter 里面所调用 return, 则会终止 filter 的调用, 而下面的 servlet.service 更本就没有调用到
                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 {
            //当pos等于n时,过滤器都执行完毕,终于执行了熟悉的servlet.service(request, response)方法。
            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);
        }
    }
}

自定义filter

@webfilter(urlpatterns = "/*", filtername = "myfilter")
public class filetercontroller implements filter {

    @override
    public void init(filterconfig filterconfig) throws servletexception {
        system.out.println("filter初始化中");
    }

    @override
    public void dofilter(servletrequest servletrequest, servletresponse servletresponse, filterchain filterchain) throws ioexception, servletexception {

        system.out.println("登录逻辑");
        if("登录失败"){
            response.getwriter().write("登录失败");
            //后面的拦截器和servlet都不会执行了
            return;
        }
        //登录成功,执行下一个过滤器
        filterchain.dofilter(servletrequest, servletresponse);
    }

    @override
    public void destroy() {
        system.out.println("filter销毁中");
    }
}
  • pos和n是applicationfilterchain的成员变量,分别表示过滤器链的当前位置和过滤器总数,所以当pos小于n时,会不断执行applicationfilterchain的dofilter方法;
  • 当pos等于n时,过滤器都执行完毕,终于执行了熟悉的servlet.service(request, response)方法。