看透SpringMVC源代码分析与实践(二)

一、Tomcat的顶层结构及启动过程

1.Tomcat的顶层结构

       Tomcat中最顶层的容器叫Server,代表整个服务器,Server至少包含一个Service用于具体的服务。Service主要包含两部分,Connector和Container。Connector用于处理连接相关的事情,并提供Socket与request、response的转换,Container用于封装和管理Servlet,以及具体处理request的请求。一个Tomcat中只有一个Server,一个Server可以包含多个Service,一个Service只有一个Container,但可以包含多个Connector(因为一个服务可以有多个连接,如同时提供http和https的连接,也可以提供相同协议但不同端口的连接)。结构如下图所示

       Tomcat里的Server由org.apache.catalina.startup.Catalina来管理,Catalina是整个Tomcat的管理类,它里面的三个方法load、start、stop分别来管理整个服务器的生命周期,load方法用于根据conf/server.xml文件创建Server并调用Server的init方法进行初始化,start方法用于启动服务器,stop方法用于停止服务器。

不过Tomcat的入口main方法并不在Catalina类里,而是在org.apache.catalina.startup.Bootstrap中。

2.Bootstrap的启动过程

       Bootstrap是Tomcat的入口,正常情况下启动Tomcat就是调用的Bootstrap的main方法。

       Bootstrap的main方法非常简单,只有两部分内容:首先新建了Bootstrap,并执行init方法初始化;然后处理main方法传入的命令,如果args参数为空,默认执行start。

       在init方法里初始化了ClassLoader,并用ClassLoader创建了Catalina实例,然后赋给catalinaDaemon变量,后面对命令的操作都要使用catalinaDaemen来具体执行。

       对start命令的处理调用了三个方法:setAwait(true)、load(args)和start()。这三个方法内部都调用了Catalina的相应方法进行具体执行,只不过是用反射来调用的。

3.Catalina的启动过程

Catalina的启动主要是调用setAwait、load和start方法来完成的。setAwait方法用于设置Server启动完成后是否进入等待状态的标志,如果为true则进入,否则不进入;load方法用于加载配置文件,创建并初始化Server;start方法用于启动服务器。

4.Server的启动过程

         server接口中提供addService(Service service)、removeService(Service service)来添加和删除Service,Service的init方法和start方法分别循环调用了每个service的init方法和start方法来启动所有service。

         Server的默认实现是org.apache.catalina.core.StandardServer,StandardSever继承自LifecycleMBeanBase,LifecycleMBeanBase又继承自LifecycleBase,init和start方法就定义在LifecycleBase中,LifecycleBase里的init方法和start方法又调用initInternal方法和startInternal方法,这两个方法都是模板方法,由子类具体实现,所以调用StandardServer的init和start方法时会执行StandardServer自己的initInternal和startInternal方法,这就是tomcat生命周期的管理方式。StandardServer中的initInternal和startInternal方法分别循环调用了每一个service的start和init方法。

5.Service的启动过程

        Service的默认实现是org.apache.catalina.core.StandardService,StandardService也继承自LifeCycleMBeanBase类,所以init和start方法最终也会调用initInternal和startInternal方法。

       StandardService中的initInternal和startInternal方法主要调用container、executors、mapperListener、connectors的init和start方法。这里的container和connectors前面已经介绍过了。mapperListener是Mapper的监听器,可以监听container容器的变化,executors是在connectors中管理线程的线程池,在server.xml配置文件中有参考用法。

       这样Connector就配置了一个叫tomcatThreadPool的线程池,最多可以同时启动150个线程,至少要有4个可用的线程。

       整个Tomcat的启动流程如下图所示

二、Tomcat的生命周期管理

1.Lifecycle接口

Tomcat通过org.apache.catalina.Lifecycle接口统一管理生命周期,所有有生命周期的组件都要事先Lifecycle接口。Lifecycle接口一共做了4件事:

  • 定义了13个String类型常量,用于LifecycleEvent事件的type属性中,作用是区分组件发出的LifecycleEvent事件时的状态(如初始化前、启动前、启动中等)。这种设计方式可以让多种状态都发送同一种类型的事件(LifecycleEvent)然后用其中的一个属性来区分状态而不用定义多种事件。
  • 定义了三个管理监听器的方法addLifecycleListener、findLifecycleListeners和removeLifecycleListener,分别用来添加、查找和删除LifecycleListener类型的监听器。
  • 定义了4个生命周期的方法:init、start、stop和destroy,用于执行生命周期的各个阶段的操作。
  • 定义了获取当前状态的两个方法getState和getStateName,用来获取当前的状态,getState的返回值LifecycleState是枚举类型,里边列举了生命周期的各个节点,getStateName方法返回String类型的状态的名字,主要用于JMX中。

2.LifecycleBase

Lifecycle的默认实现是org.apache.catalina.util.LifecycleBase,所有实现了生命周期的组件都直接或间接继承自LifecycleBase,LifecycleBase为Lifecycle里的接口方法提供了默认实现:监听器管理是专门使用了一个LifecycleSupport类来完成的,LifecycleSupport中定义了一个LifecycleListener数组类型的属性来保存所有的监听器,然后定义了添加、删除、查找和执行监听器的方法;生命周期方法中设置了相应的状态并调用了相应的模板方法,ini、start、stop和destroy所对应的模板方法分别是initInternal、startInternal、stopInternal和destroyInternal方法,这四个方法由子类具体实现,所以对于子类来说,执行生命周期处理的方法就是initInternal、startInternal、stopInternal和destroyInternl。

三、Container分析

1.ContainerBase的结构

Container是Tomcat中容器的接口,通常使用的Servlet就封装在其子接口Wrapper中。

Container一共有四个子接口Engine、Host、Context、Wrapper和一个默认实现类ContainerBase,每个子接口都是一个容器,这四个子容器都有一个对应的StandardXXX实现类,并且这些实现类都继承ContainerBase类。另外Container还继承Lifecycle接口,而且ContainerBase间接继承了LifecycleBase,所以Engine、Host、Context、Wrapper四个子容器都符合前面讲过的Tomcat生命周期管理模式。结构图如下所示:

2.Container的四个子容器

Container的子容器Engine、Host、Context、Wrapper是逐层包含的关系,其中Engine是最顶层,每个Service最多只能有一个Engine,Engine里面可以有多个Host,每个Host下可以有多个Context,每个Context下可以有多个Wrapper,它们的关系如下图所示:

四个容器的作用分别是:

Engine:引擎,用来管理多个站点,一个Service最多只能有一个Engine。

Host:代表一个站点,也可以叫虚拟主机,通过配置Host就可以添加站点。

Context:代表一个应用程序,对应着平时开发的一套程序,或者一个WEB-INF目录以及下面的web.xml文件。

Wrapper:每个Wrapper封装着一个Servlet。

Context和Host的区别是Context表示一个应用。例如,默认配置下webapps下的每个目录都是一个应用,其中ROOT目录中存放着主应用,其他目录存放着别的子应用,而整个webapps是一个站点。假如www.excelib.com域名对应着webapps目录所代表的的站点,其中的ROOT目录里的应用就是主应用,访问时直接使用域名就可以,而webapps/test目录存放的是test子应用,访问时需要用www.excelib.com/test,每一个应用对应一个Context,所有webapps下的应用都属于www.excelib.com站点,而blog.excelib.com则是另外一个站点。属于另外一个Host。

3.Container的启动

Container的启动是通过init和start方法来完成的,这两个方法会在Tomcat启动时被Service调用。Container也是按照Tomcat的生命周期来管理的,init和start方法也会调用initInternal和startInternal方法来具体处理,不过Container和Tomcat整体结构启动的过程稍微有点不一样,主要有三点区别:

  • Container的四个子容器有一个共同的父类ContainerBase,这里定义了Container容器的initInternal和startInternal方法通用处理内容,具体容器还可以添加自己的内容;
  • 除了最顶层容器的init是被Service调用的,子容器的init方法并不是在容器中逐层循环调用的,而是在执行start方法的时候通过状态判断还没有初始化才调用;
  • start方法除了在父容器的startInternal方法中调用,还会在父容器的添加子容器的addChild方法中调用,这主要是因为Context和Wrapper是动态添加的,我们在站点目录下放一个应用的文件夹或者war包就可以添加一个Context,在web.xml文件中配置一个Servlet就可以添加一个Wrapper,所以Context和Wrapper是在容器启动的过程中才动态查找出来添加到相应的父容器中。

四、Pipeline-Value管道

Container处理请求是使用Pipeline-Value管道来处理的。

1.Pipeline-Value处理模式

Pipeline-Value是责任链模式,责任链模式是指在一个请求处理的过程中有多个处理这一次对请求进行处理,每个处理者负责做自己相应的处理,处理完成后将处理后的请求返回,再让下一个处理者继续处理。

Pipeline-Value的管道模型和普通的责任链模式有些不同,主要区别有两点:

  • 每个Pipeline都有特定的Value,而且是在管道的最后一个执行,这个Value叫BaseValue,BaseValue是不可删除的:
  • 在上层容器的管道的BaseValue中会调用下层容器的管道。

四个容器的BaseValue分别是StandardEngineValue、StandardHostValue、StandardContextValue和StandardWrapperValue,整个处理的流程如下图所示:

在Engine的管道中依次执行Engine的各个Value,最后执行StandEngineValue用于调用Host的管道,然后执行Host的Value,这样依次类推最后执行Wrapper管道中的StandardWrapperValue。

在Filter中用到的FilterChain其实就是这种模式,FilterChain相当于Pipeline,每个Filter都相当于一个Value,Servlet相当于最后的BaseValue。

五、Connector分析

Connector用于接收请求并将请求封装成Request和Response来具体处理,最底层是使用Socket来进行连接的,Request和Response是按照HTTP协议来封装的,所以Connector同时实现了TCP/IP协议和HTTP协议,Request和Response封装完之后交给Container进行处理,Container就是Servlet的容器,Container处理完之后返回给Connector,最后Connector使用Socket将处理结果返回给客户端。

1.Connector的结构

Connector中具体使用ProtocolHandler来处理请求的,不同的ProtocolHandler代表不同的连接类型,比如,HttpllProtocol使用的是普通Socket来连接的,HttpllNioProtocol使用的是NioSocket来连接的。

ProtocolHandler里面有三个非常重要的组件:Endpoint、Processor和Adapter。Endpoint用于处理底层Socket的网络连接,Processor用于将Endpoint接收到的Socket封装成Request,Adapter用于将封装好的Request交给Container进行具体处理。也就是说Endpoint用来实现TCP/IP协议,Processor用来实现HTTP协议,Adapter将请求适配到Servlet容器进行具体处理。

Endpoint的抽象实现AbstractEndPoint里面定义的Acceptor和AsyncTimeout两个内部类和一个Handler接口。Acceptor用于监听请求,AsyncTimeout用于检查异步request的超时,Handler用于处理接收到的Socket,在内部调用了Processor进行处理。

Connector的结构如下图所示:

猜你喜欢

转载自blog.csdn.net/linjiaen20/article/details/85221813