前段时间家里有事忙,停更了好长一段时间,这里跟期待更新的小伙伴们说一声道歉,没能提前阐明一下,让小伙伴们等了这么久,真的不好意思!

后面说完了Tomcat的初始化和启动步骤,那么接下来就要进入重头戏了!在本篇文章中,我会跟后面一样,通过图文的形式来带着小伙伴们理解一个 Servlet是如何被tomcat解决的,具体的解决链路都有哪些。

一、申请剖析

在《Tomcat源码学习第2篇》中备注了各个组件的阐明。

当一个servlet申请到来的时候,首先通过的是connector组件,它是用来接管申请的。

该组件接管到申请之后,会把相干申请进行封装,而后传递到engine组件中。

紧跟着,engine组件会锁定对应的hostcontext以及wrapper,一层层的传递上来,找到最终解决申请的servlet实例。

二、深刻摸索

不晓得大家还有没有印象,在后面的文章中,咱们在NioEndpoint类中,启动Accepter线程的入口处上方还有着一个线程组在启动运行,然而却没有解说该线程是用来干嘛的~

NioEndpoint.startInternal()

点击跳转到该类过去,咱们能够看到他实现了Runnable接口,那么咱们间接查看他的run()办法,看看它的运行逻辑。

Poller.run()

通过正文咱们能够晓得,该线程次要用于轮询已连贯的套接字,查看是否触发了事件,并在事件产生时将关联的套接字移交给对应的处理器。在源码中咱们能够看到keyCount变量记录着待处理申请数,提供给前面做相应判断。

持续往下走,通过keyCount判断是否有申请须要进行解决,需要的话则通过selector.selectedKeys()拿到须要被解决的channel汇合,进行循环解决。在while循环中咱们看到,所有就绪的通道都调用的是processKey(sk, socketWrapper)办法进行解决。

点击跳转过来该办法,在这里能够看到他对该sk做了读写判断,既然是申请进来,那必定是做读操作,咱们先进读相干的办法看一下。

NioEndpoint.processKey()

进来之后咱们能够看到它首先在缓存池中尝试去获取一个解决线程,当缓存池中没有线程时,就创立一个新的线程,如果有的话就间接应用。

AbstractEndpoint.processSocket()

既然是线程了,那么咱们就关怀线程的外围办法即可。点击SocketProcessorBase跳转查看run()办法。

SocketProcessorBase.run()

doRun()处打上断点,单击下一步,跳转到NioEndpoint.doRun()办法中。Poller线程移交到这边的线程进行解决,在该线程中须要失去以后的socket,做进一步的解决。

进入该办法之后,咱们能够看到它首先对wrapper进行判断,不为空再取出socket,而后尝试着在connections中去获取对应的processor,如果获取不到,再尝试获取曾经解决过连贯,然而尚未销毁的processor中去获取,还获取不到才进行创立。这样能够防止频繁的创立和销毁对象。

AbstractProtocol.process()

失去processor之后,调用process办法对报文进行解析。

进入该办法之后,咱们能够看到这外面是对socketEvent的状态进行判断,咱们以后申请次要是读状态,在此处打上断点,跳到该办法进来看一下。

AbstractProcessorLight.process()

这里咱们能够看到是进入到了 http11类中,在该类外面对报文进行解析,封装原生的requestresponse对象。这里的response因为咱们还没有到返回的步骤,所以只是做个初步的参数设置。后续要传入Adapter进行下一步操作。

Http11Processor.service()

在这里对原生的requestresponse进行转换,失去HttpServletRequestHttpServletResponse。而后依据申请信息找到可能解决以后申请的hostcontextwrapper

CoyoteAdapter.service()

在这办法能够看到它会通过getMapper()办法去匹配可能解决以后申请的 host,context,wrapper。到这里可能有的小伙伴会奇怪,为什么是从mapper中去匹配呢?这个问题留给你们去摸索一下,等下篇再给你们解答。

CoyoteAdapter.postParseRequest()

上一办法中,通过connector获取service之后再获得对应的mapper,可是进来之后却没有看到对该mapper对象的构建,那该对象是哪里来的呢?

Mapper.map()

不晓得大家还记不记得在第二篇中,在StandardService类中initInternal()startInternal()办法中有mapperListener办法的初始化和启动。

在该办法中查找到对应的host, context, wrapper

Mapper.internalMap()

回到CoyoteAdapter.postParseRequest(),通过Evaluste咱们能够看到以后申请对应的host, context, wrapper以及实例的映射均已找到。

接下来要做的就是依据链路组件进行一级级的调用,直至最初取出servlet执行。

CoyoteAdapter.service()

先失去host,在通过host持续调用下一级组件

StandardEngineValve.invoke()

AbstractAccessLogValve.invoke()

ErrorReportValve.invoke()

这里拿到context,持续invoke()

StandardHostValve.invoke()

AuthenticatorBase.invoke()

StandardContextValve.invoke()

拿到wrapper之后,持续向下执行,从wrapper容器中失去servlet对象。

StandardWrapperValve.invoke()

紧接着,把失去的servlet退出过滤器链中(可能有其它的解决,这里不间接进行解决),留待上面调用过滤器链再对立进行解决。

ApplicationFilterChain.doFilter()

终于找到具体的实例了,太不容易了!!!

ApplicationFilterChain.internalDoFilter()

三、总结

我收集有泛滥的 计算机电子书籍,有须要的小伙伴自提哦~