Tomcat源码分析 (九)----- HTTP请求处理过程(二)
我们接着上一篇文章的容器处理来讲,当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")); } }
接下来我们看看standardenginevalve
的invoke()
方法。该方法主要是选择合适的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); }
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()); } }
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(); }
接下来我们看看standardwrappervalve
的invoke()
方法。
@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:这儿调用wrapper的allocate()方法分配一个servlet实例
- 关键点2,创建过滤器链,类似于pipeline的功能
- 关键点3,调用过滤器链的dofilter,最终会调用到servlet的service方法
- 关键点4,释放掉过滤器链及其相关资源
- 关键点5,释放掉servlet及相关资源
- 关键点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(); } }
总结下来,注意以下几点即可:
- 卸载过程中,不能分配servlet
- 如果不是单线程模式,则每次都会返回同一个servlet(默认servlet实现方式)
-
servlet
实例为null
或者servlet
实例还未初始化
,使用synchronized来保证并发时的原子性 - 如果是单线程模式,则使用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; }
关键的地方有两个:
- 通过实例管理器,创建servlet实例,而实例管理器是通过特殊的类加载器来加载给定的类
- 调用servlet的init方法
关键点2 - 创建过滤器链
创建过滤器链是调用的org.apache.catalina.core.applicationfilterfactory
的createfilterchain()
方法。我们来分析一下这个方法。该方法需要注意的地方已经在代码的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)方法。