爱网站长尾,加强宣传阵地建设 高校 网站,贵阳网站备案,个人网站备案怎么做前面两篇#xff0c;我们重点分析了Tomcat的容器和连接器的基本设计#xff0c;今天我们来看一下两个机构如何在service的调度下进行协同工作的。
目录
1.模板模式与Tomcat的重用性设计
2.观察者模式与Tomcat可扩展性设计 1.模板模式与Tomcat的重用性设计
首先#xff0…前面两篇我们重点分析了Tomcat的容器和连接器的基本设计今天我们来看一下两个机构如何在service的调度下进行协同工作的。
目录
1.模板模式与Tomcat的重用性设计
2.观察者模式与Tomcat可扩展性设计 1.模板模式与Tomcat的重用性设计
首先我们将前两篇的结构放在一起就是这样的 从图中可以看到各种组件的层次关系图中的虚线表示一个请求在 Tomcat 中流转的过程。
上面这张图描述了组件之间的静态关系如果想让一个系统能够对外提供服务我们需要创建、组装并启动这些组件在服务停止的时候我们还需要释放资源销毁这些组件因此这是一个动态的过程。也就是说Tomcat 需要动态地管理这些组件的生命周期。
在我们实际的工作中如果你需要设计一个比较大的系统或者框架时你同样也需要考虑这几个问题如何统一管理组件的创建、初始化、启动、停止和销毁如何做到代码逻辑清晰如何方便地添加或者删除组件如何做到组件启动和停止不遗漏、不重复
今天我们就来解决上面的问题在这之前先来看看组件之间的关系。如果你仔细分析过这些组件可以发现它们具有两层关系。 第一层关系是组件有大有小大组件管理小组件比如 Server 管理 ServiceService 又管理连接器和容器。 第二层关系是组件有外有内外层组件控制内层组件比如连接器是外层组件负责对外交流外层组件调用内层组件完成业务功能。也就是说请求的处理过程是由外层组件来驱动的。
这两层关系决定了系统在创建组件时应该遵循一定的顺序。 第一个原则是先创建子组件再创建父组件子组件需要被“注入”到父组件中。 第二个原则是先创建内层组件再创建外层组件内层组建需要被“注入”到外层组件。
因此最直观的做法就是将图上所有的组件按照先小后大、先内后外的顺序创建出来然后组装在一起。不知道你注意到没有这个思路其实很有问题因为这样不仅会造成代码逻辑混乱和组件遗漏而且也不利于后期的功能扩展。
为了解决这个问题我们希望找到一种通用的、统一的方法来管理组件的生命周期就像电脑的“一键启动”那样的效果。
这个工作就是由LifeCycle 接口来统一定义的设计就是要找到系统的变化点和不变点。这里的不变点就是每个组件都要经历创建、初始化、启动这几个过程这些状态以及状态的转化是不变的。而变化点是每个具体组件的初始化方法也就是启动方法是不一样的。
因此我们把不变点抽象出来成为一个接口这个接口跟生命周期有关叫作 LifeCycle。LifeCycle 接口里应该定义这么几个方法init()、start()、stop() 和 destroy()每个具体的组件去实现这些方法。
理所当然在父组件的 init() 方法里需要创建子组件并调用子组件的 init() 方法。同样在父组件的 start() 方法里也需要调用子组件的 start() 方法因此调用者可以无差别的调用各组件的 init() 方法和 start() 方法这就是组合模式的使用并且只要调用最顶层组件也就是 Server 组件的 init() 和 start() 方法整个 Tomcat 就被启动起来了。下图就是 LifeCycle 接口的定义。 有了接口我们就要用类去实现接口。一般来说实现类不止一个不同的类在实现接口时往往会有一些相同的逻辑如果让各个子类都去实现一遍就会有重复代码。那子类如何重用这部分逻辑呢其实就是定义一个基类来实现共同的逻辑然后让各个子类去继承它就达到了重用的目的。
而基类中往往会定义一些抽象方法所谓的抽象方法就是说基类不会去实现这些方法而是调用这些方法来实现骨架逻辑。抽象方法是留给各个子类去实现的并且子类必须实现否则无法实例化。
比如宝马和荣威的底盘和骨架其实是一样的只是发动机和内饰等配套是不一样的。底盘和骨架就是基类宝马和荣威就是子类。仅仅有底盘和骨架还不是一辆真正意义上的车只能算是半成品因此在底盘和骨架上会留出一些安装接口比如安装发动机的接口、安装座椅的接口这些就是抽象方法。宝马或者荣威上安装的发动机和座椅是不一样的也就是具体子类对抽象方法有不同的实现。
回到 LifeCycle 接口Tomcat 定义一个基类 LifeCycleBase 来实现 LifeCycle 接口把一些公共的逻辑放到基类中去比如生命状态的转变与维护、生命事件的触发以及监听器的添加和删除等而子类就负责实现自己的初始化、启动和停止等方法。为了避免跟基类中的方法同名我们把具体子类的实现方法改个名字在后面加上 Internal叫 initInternal()、startInternal() 等。我们再来看引入了基类 LifeCycleBase 后的类图 在上面的方法中我们可以看到有两个方法是增加和删除Listener的这个是做什么的呢简单来说是为了提高系统的扩展性的。
从图上可以看到LifeCycleBase 实现了 LifeCycle 接口中所有的方法还定义了相应的抽象方法交给具体子类去实现这是典型的模板设计模式。
我们还是看一看代码加深理解下面是 LifeCycleBase 的 init() 方法实现。
Override
public final synchronized void init() throws LifecycleException {//1. 状态检查if (!state.equals(LifecycleState.NEW)) {invalidTransition(Lifecycle.BEFORE_INIT_EVENT);}try {//2. 触发 INITIALIZING 事件的监听器setStateInternal(LifecycleState.INITIALIZING, null, false);//3. 调用具体子类的初始化方法initInternal();//4. 触发 INITIALIZED 事件的监听器setStateInternal(LifecycleState.INITIALIZED, null, false);} catch (Throwable t) {...}这个方法逻辑比较清楚主要完成了四步
第一步检查状态的合法性比如当前状态必须是 NEW 然后才能进行初始化。
第二步触发 INITIALIZING 事件的监听器
setStateInternal(LifecycleState.INITIALIZING, null, false);在这个 setStateInternal 方法里会调用监听器的业务方法。监听的问题我们稍后再看。
第三步调用具体子类实现的抽象方法 initInternal() 方法。我在前面提到过为了实现一键式启动具体组件在实现 initInternal() 方法时又会调用它的子组件的 init() 方法。
第四步子组件初始化后触发 INITIALIZED 事件的监听器相应监听器的业务方法就会被调用。
setStateInternal(LifecycleState.INITIALIZED, null, false);2.观察者模式与Tomcat可扩展性设计
因为各个组件 init() 和 start() 方法的具体实现是复杂多变的比如在 Host 容器的启动方法里需要扫描 webapps 目录下的 Web 应用创建相应的 Context 容器如果将来需要增加新的逻辑直接修改 start() 方法这样会违反开闭原则那如何解决这个问题呢开闭原则说的是为了扩展系统的功能你不能直接修改系统中已有的类但是你可以定义新的类。
我们注意到组件的 init() 和 start() 调用是由它的父组件的状态变化触发的上层组件的初始化会触发子组件的初始化上层组件的启动会触发子组件的启动因此我们把组件的生命周期定义成一个个状态把状态的转变看作是一个事件。而事件是有监听器的在监听器里可以实现一些逻辑并且监听器也可以方便的添加和删除这就是典型的观察者模式。
具体来说就是在 LifeCycle 接口里加入两个方法添加监听器和删除监听器。除此之外我们还需要定义一个 Enum 来表示组件有哪些状态以及处在什么状态会触发什么样的事件。因此 LifeCycle 接口和 LifeCycleState 就定义成了下面这样。 可以看到组件的生命周期有 NEW、INITIALIZING、INITIALIZED、STARTING_PREP、STARTING、STARTED 等而一旦组件到达相应的状态就触发相应的事件比如 NEW 状态表示组件刚刚被实例化而当 init() 方法被调用时状态就变成 INITIALIZING 状态这个时候就会触发 BEFORE_INIT_EVENT 事件如果有监听器在监听这个事件它的方法就会被调用。
总之LifeCycleBase 调用了抽象方法来实现骨架逻辑。讲到这里我们再来看前面的LifeCycle里的问题LifeCycleBase 负责触发事件并调用监听器的方法那是什么时候、谁把监听器注册进来的呢
分为两种情况 Tomcat 自定义了一些监听器这些监听器是父组件在创建子组件的过程中注册到子组件的。比如 MemoryLeakTrackingListener 监听器用来检测 Context 容器中的内存泄漏这个监听器是 Host 容器在创建 Context 容器时注册到 Context 中的。 我们还可以在 server.xml 中定义自己的监听器Tomcat 在启动时会解析 server.xml创建监听器并注册到容器组件。