Tomcat(七)设计模式总结

前言

分析一下Tomcat中所涉及到设计模式,本文我们将主要来分析外观模式,观察者模式,责任链模式,模板方法模式,命令模式。
在开始本文之前,先说明一下对于设计模式的一点看法。曾经经常看到网上有人讨论设计模式,也偶尔会遇到有人非要严格按照GOF设计模式的类图以及其中的角色去套用别人的设计,只要类图不一样,或者角色多了或者少了就会觉得怎么和官方定义的模式不一样,我个人认为这是对设计模式的误解。设计模式其实不仅仅存在软件行业,各行各业其实都有模式,它是所在行业对一些通用问题解决方案的总结和抽象,是一种对常见问题的抽象的解决方案,不是一种具体的实现,所以我觉得在讨论设计模式的时候,千万别一个劲的套用GOF设计模式中的类图以及其中所涉及到的角色,而是要理解设计模式的思维,理解设计模式的使用场景,在代码层面上有无数种实现,无需照搬(或者copy)代码。

外观模式

在Tomcat中对于RequestResponseStandardSessionApplicationContextStandardWrapper都采用外观模式,它的类图如下。

通过上图,可以看到RequestFacade包装了Request,它们都实现了HttpServletRequest,当传递Request对象给应用的时候,其实是返回了RequestFacade对象,而RequestFacade内部可以根据是否自定义了安全管理器来进行相应的操作。
对于ResponseStandardSession等处理是类似的,这里就不写了。

观察者模式

Tomcat中需要对很多组件进行生命周期管理,为此Tomcat抽象了统一的生命周期管理骨架,通过这个骨架将所有需要进行生命周期管理的类都纳入进来管理,而这里的骨架的类图如下。

通过上图可以看出Tomcat抽象了一个LifecycleSupport的类,而所有需要生命周期管理的组件通过LifecycleSupport类通知对某个生命周期事件感兴趣的观察者,而所有的观察者都需要实现LifecycleListener
关注一下EventObject对象,它里面定义了一个事件源对象,所谓事件源就是事件发生的地方,而在Tomcat的设计中,事件源就是实现了LifeCycle接口的各个需要管理生命周期的组件,这里LifecycleSupportLifeCycleBase之间是双向的关联,LifeCycleSupport关联LifeCycle对象就是为了实现事件源的传递,这样在LifeCycleSupport触发事件的时候,可以通过事件源构建EventObject。这样以来LifecycleListener就可以通过事件对象获取到事件源,从而做一些与事件源相关的操作。

责任链模式

Tomcat中请求的处理流程其实就是采用了责任链模式,关于Tomcat请求处理,可以参考下Tomcat请求处理流程,Tomcat中责任链模式的实现的类图如下图所示:

从上图中,可以看到每一个容器都会有一个Pipeline,而一个Pipeline又会具有多个Valve阀门,其中StandardEngine对应的阀门是StandardEngineValveStandardHost对应的阀门是StandardHostValveStandardContext对应的阀门是StandardContextValveStandardWrapper对应的阀门是StandardWrapperValve。这里每一Pipeline就好比一个管道,而每一Valve就相当于一个阀门,一个管道可以有多个阀门,而对于阀门来说有两种,一种阀门在处理完自己的事情以后,只需要将工作委托给下一个和自己在同一管道的阀门即可,第二种阀门是负责衔接各个管道的,它负责将请求传递给下个管道的第一个阀门处理,而这种阀门叫Basic阀门,它是每个管道中最后一个阀门,上面的Standard*Valve都属于第二种阀门。
可以形象的通过下图来描述上面的过程。

通过上图,可以很清楚的了解到Tomcat的请求处理流程。当用户请求服务器的时候,Connector会接受请求,从Socket连接中根据http协议解析出对应的数据,构造Request和Response对象,然后传递给后面的容器处理,顶层容器是StandardEngineStandardEngine处理请求其实是通过容器的Pipeline进行的,而Pipeline其实最终是通过管道上的各个阀门进行的,当请求到达StandardEngineValve的时候,此阀门会将请求转发给对应StandardHostPipeline的第一个阀门处理,然后以此最终到达StandardHostValve阀门,它又会将请求转发给StandardContextPipeline的第一个阀门,这样以此类推,最后到达StandardWrapperValve,此阀门会根据Request来构建对应的Servlet,并将请求转发给对应的HttpServlet处理。从这里我们可以看出其实Tomcat核心处理流程就是通过责任链一步步的组装起来的。

模版方法模式

Tomcat中关于生命周期管理的地方很好应用了模板方法模式,在一个组件的生命周期中都会涉及到init()初始化start()启动stop()停止destory()销毁,而对于每一个生命周期阶段其实都有固定一些事情要做,比如判断前置状态,设置后置状态,以及通知状态变更事件的监听者等,而这些工作其实是可以固化的,所以Tomcat中就将每个生命周期阶段公共的部分固化,然后通过initInternalstartInternalstopInternaldestoryInternal这几个钩子方法开放给子类去实现具体的逻辑。Tomcat中关于模板方法模式的实现如下图所示。

命令模式

命令模式在Tomcat中主要是应用在对请求的处理过程中,Tomcat的实现中,根据它支持两种协议AJP和Http,而在具体的IO实现中,又分为Java同步阻赛IO,Java同步非祖塞IO,以及采用APRApache Portable Runtime支持库,因此Tomcat统一了org.apache.coyote.Processor接口,根据协议和IO实现的不同通过不同的Process子类去实现,Connector作为客户端每次只需要根据具体的协议和IO实现创建对应的Process执行即可。
下面来看一下命令模式在Tomcat中实现的相关类图。

通过上图可以清楚的看到,Tomcat首先根据协议的不同将Processor分为了Ajp和Http两组,然后又根据具体的IO实现方式的不同,将每一组都会实现同步祖塞IO,同步非祖塞IO,以及APR的Processor
再来看一个类图,就可以更加清楚的看到Tomcat中是如何利用命令模式来根据不同的协议以及IO实现方式来处理请求的。
看一下Tomcat中关于ProtocolHandler的类图。

通过上图可以看到针对每一种协议和IO实现方式的组合,都会有相应的协议处理类,而每个协议处理类都会有一个Handler,而每一个Handler在运行的时候就会创建出对应的Processor,比如AjpProtocol.AjpConnectionHandler创建AjpProcessor处理器,其它的类似。
通过上面的描述,可以看出Tomcat接受请求的处理流程如下。
Connector通过对应的Endpint监听Socket连接,当对应的端口有连接进来的时候,对应的Endpoint就会通过对应的Handler类处理,而Handler处理的时候,又会创建对应的Processor处理,而对应的Processor命令对象会解析Socket流的数据,然后生成Request和Response对象,最终通过上面说的责任链模式一步步的通过各个容器。

评论

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×