在死磕Tomcat系列(1)——总体架构中咱们简单介绍了容器的概念,而且说了在容器中全部子容器的父接口是Container
。在死磕Tomcat系列(2)——EndPoint源码解析中,咱们知道了链接器将请求过来的数据解析成Tomcat须要的ServletRequest
对象给容器。那么容器又是如何将这个对象准确的分到到对应的请求上去的呢?html
Container
是容器的父接口,全部子容器都须要实现此接口,咱们首先看一下Container
接口的设计。java
public interface Container extends Lifecycle { public void setName(String name); public Container getParent(); public void setParent(Container container); public void addChild(Container child); public void removeChild(Container child); public Container findChild(String name); }
Tomcat是如何管理这些容器的呢?咱们能够经过接口的设计能够了解到是经过设置父子关系,造成一个树形的结构(一父多子)、链式结构(一父一子)来管理的。一想到树形的结构咱们应该就立马可以联想到设计模式中的组合模式,而链式结构咱们应该可以想到设计模式中的责任链设计模式。不管这两种的哪种咱们都知道这种关系是上下层级的关系。用图来表示就是以下。web
既然是父子的结构,那么链接器是如何将转换好的ServletRequest
给到容器的呢?咱们能够看CoyoteAdapter
中的service
方法。由于在链接器中最后一环是将解析过的Request
给到Adapter运用适配器设计模式解析为ServletRequest
对象。在service
方法中咱们看到有这么一句。设计模式
connector.getService().getContainer().getPipeline().getFirst().invoke( request, response);
而其中的getContainer
方法,返回的是Engine
对象tomcat
public Engine getContainer();
这里看到了Pipeline
,Pipeline
应该你们有所熟悉,是管道的概念,那么管道里面装的是什么呢?咱们看其定义的方法架构
public interface Pipeline extends Contained { public void addValve(Valve valve); public Valve getBasic(); public void setBasic(Valve valve); public Valve getFirst(); }
能够看到Pipeline
管道里面装的是Valve
,那么Valve是如何组织起来的呢?咱们也能够看它的代码定义app
public interface Valve { public Valve getNext(); public void setNext(Valve valve); public void invoke(Request request, Response response) }
能够知道每一个Valve
都是一个处理点,它的invoke
就是相对应的处理逻辑。能够看到有setNext
的方法,所以咱们大概可以猜到是经过链表将Valve组织起来的。而后将此Valve装入Pipeline
中。所以每一个容器都有一个Pipeline
,里面装入系统定义或者自定义的一些拦截节点来作一些相应的处理。所以只要得到了容器中Pipeline
管道中的第一个Valve
对象,那么后面一系列链条都会执行到。框架
可是不一样容器之间Pipeline
之间是如何进行触发的呢?即例如Engine
的Pipeline
处理完了最后一个Valve,那么如何调用Host
的PipeLine
管道中的Valve呢?咱们能够看到每一个Pipeline
中还有一个方法。setBasic
这个方法设置的就是Valve链条的末端节点是什么,它负责调用底层容器的Pipeline
第一个Valve节点。用图表示就是这样的。webapp
Engine容器比较简单,只是定义了一些基本的关联关系。它的实现类是StandardEngine
。ide
@Override public void addChild(Container child) { if (!(child instanceof Host)) throw new IllegalArgumentException (sm.getString("standardEngine.notHost")); super.addChild(child); } @Override public void setParent(Container container) { throw new IllegalArgumentException (sm.getString("standardEngine.notParent")); }
须要注意Engine容器是没有父容器的。若是添加是会报错。添加子容器也只是能添加Host
容器。
Host容器是Engine的子容器,一个Host在Engine中表明一个虚拟主机,这个虚拟主机的做用就是运行多个应用,它负责安装和展开这个应用,而且标识这个应用以便可以区分它们。它的子容器一般是Context
容器。咱们能够看配置文件中也可以看出Host
文件的做用。
<Host name="localhost" appBase="webapps" unpackWARs="true" autoDeploy="true">
那么Host
容器在启动时具体干了什么呢?咱们看它的startInternal
方法看不出来什么,只是启动了相应的Valve
,是由于在Tomcat的设计中引入了生命周期的概念,即每一个模块都有本身相应的生命周期,模块的生命周期定义有NEW、INITIALIZING、INITIALIZED、SSTARTING_PREP、STARTING、STARTED
,每一个模块状态的变化都会引起一系列的动做,那么这些动做的执行是直接写在startInternal
中吗?这样会违反开闭原则,那么如何解决这个问题呢?开闭原则说的是为了扩展性系统的功能,你不能修改系统中现有的类,可是你能够定义新的类。
因而每一个模块状态的变化至关于一个事件的发生,而事件是有相应的监听器的。在监听器中实现具体的逻辑,监听器也能够方便的增长和删除。这就是典型的观察者模式。
那么Host容器在启动的时候须要扫描webapps目录下面的全部Web应用,建立相应的Context容器。那么Host的监听器就是HostConfig
,它实现了LifecycleListener
接口
public interface LifecycleListener { public void lifecycleEvent(LifecycleEvent event); }
接口中只定义了一个方法,即监听到相应事件的处理逻辑。能够看到在setState
方法中调用了监听器的触发。
protected void fireLifecycleEvent(String type, Object data) { LifecycleEvent event = new LifecycleEvent(this, type, data); for (LifecycleListener listener : lifecycleListeners) { listener.lifecycleEvent(event); } }
因此容器中各组件的具体处理逻辑是在监听器中实现的。
一个Context对应一个Web应用
Context表明的是Servlet的Context,它具有了Servlet的运行的基本环境。Context最重要的功能就是管理它里面的Servlet实例,Servlet实例在Context中是以Wrapper出现的。Context准备运行环境是在ContextConfig
中lifecycleEvent
方法准备的。
@Override public void lifecycleEvent(LifecycleEvent event) { // Identify the context we are associated with try { context = (Context) event.getLifecycle(); } catch (ClassCastException e) { log.error(sm.getString("contextConfig.cce", event.getLifecycle()), e); return; } // Process the event that has occurred if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) { configureStart(); } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) { beforeStart(); } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) { // Restore docBase for management tools if (originalDocBase != null) { context.setDocBase(originalDocBase); } } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) { configureStop(); } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) { init(); } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) { destroy(); } }
Wrapper容器表明一个Servlet,包括Servlet的装载、初始化、执行以及资源的回收。Wrapper是最底层的容器,它没有子容器。
Wrapper的实现类是StandardWrapper
,主要任务是载入Servlet类,并进行实例化。可是StandardWrapper
类并不会调用Servlet
的service
方法。而是StandardWrapperValue
类经过调用StandardWrpper
的allocate
方法得到相应的servlet,而后经过拦截器的过滤以后才会调用相应的Servlet的service方法
Tomcat的容器中有许多值得咱们学习的设计思想,例如将不变的抽取出来,而后变化的子类来实现的模板设计模式、维护一堆父子关系的组合设计模式、事件的发生伴随监听者的相应动做执行的观察者设计模式等等。
在学习框架的时候,有时不必深究里面一行一行的代码,而要学习它的思想。知道它是如何运行,随后若是查找问题,或者是对框架进行相应扩展。这时候再深刻学习里面的代码将会事半功倍。