Tomcat源码分析—设计模式

Tomcat源码分析—设计模式

这篇文章是我在2011年时写的,现转到我自己的博客上

 

责任链模式(Chain of Responsibility)

责任链模式是一种对象的行为模式。
在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链。请求在这个链上传递,知道链上的某一个对象决定处理此请求。
发出这个请求的客户端并不知道链上的哪一个对象最终处理这个请求,这使得系统可以在不影响客户端的情况下动态地重新组织链和分配责任。
责任链模式的简略类图如下所示:


Tomcat中责任链模式结构如下:
实际上这里包含了两种结构,一个是责任链模式,一个是管道。

责任链模式是在图左边一块部分,Tomcat中各种Valve实现都继承自ValveBase,Valve继承自Valve。ValveBase中只有 setNext()和getNext(),并没有对所有的value进行组织分配调用。调用这些valve的工作是由StandardPipeline去 做的,StandardPipeline负责管理每个valve之间的关系,在增加value的时候,就设置了每个valve的下一个valve,这就好 像是单向链表的结构,每个valve就相当于一个节点,在这个链表中找到任意一个节点都能知道它的下一个节点是什么,所以当一个valve处理完了,就调 用getNext().invoke()这样就会将请求交给下一个valve去处理了。
tomcat将引擎,主机,上下文,包装当做一个管道,每个管道里面可能有很多很多的valve,所以在一个管理里面的请求处理过程就是责任链模式了,
而管道和管道之间的交互,比如从引擎到主机这是由tomcat提供的标准的StandardXXXValve去完成的,比如在StandardEngineValve的invoke()
方法中有这么一段:
Host host = request.getHost();
host.getPipeline().getFirst().invoke(request, response);
首先通过请求获取Host(或者Context,或者Wrapper),当得到主机后,通过getPipeline()获取和这个主机关联的pipeline,也就是StandardPipeline
它是在容器的父类ContainerBase里面定义的,当找到了StandardPipeline,也就找到了和当前管道相关联的所有valve,此时所有的valve就像单向
链表一样,是有顺序的,那么就调用第一个节点,拿到第一个valve,然后执行invoke就可以了。
因为自定义的valve始终是放在标准valve的前面,所以StandardXXXValve就是valve链里面的最后一个vavle,当前面的valve执行完了,标准valve就负责将请求转给下一个管道,也就是调用 host.getPipeline().getFirst().invoke(request, response);   这样就可以了。

门面模式(Facade)模式

门面模式是对象的结构模式。外部与一个子系统的通信必须经过一个统一的门面(Facade)对象进行,这就是门面模式。
门面模式提供一个高层次的接口,使得子系统更易于使用。门面模式的简略图如下:


Tomcat中的门面模式类图如下:

这里有五组关系,上面两个是请求和响应的类,Request和Response是对规范HttpServletRequest和规范HttpServletResponse的真正实现,所以理论上将这两个实现类提供给用户就可以了,那么为什么还需要一个门面呢?因为Request和Response除了实现规范接口中的方法以外,内部还有一些对容器,比如它的内部还有清空资源,完成响应等方法,这些方法都不属于规范里定义的方法,用户通过接口强行向下转换就可以得到容器内部的Request和Response的实现了。所以门面模式的好处就将这些多余的内部方法隐藏,它继承了规范的类,然后有一个成员变Request,门面类里面所有的方法实现都是简单调用Request里面的方法,这样,用户就算强行向下转换也只能得到门面类,不会对危害到系统了。
当然,如果用反射的方式可能就能破坏这个系统了,可以得到容器的内部实现Request和Response,所以tomcat需要用安全文件定义的方式不允许访问org.apche.tomcat开头的包,这样就没问题了。

session和context也是同样的道理,真正的实现类除了规范的方法之外还有一些内部实现,所以需要用门面包装一下,再提供给用户。
wrapper相当于对一个servlet的包装,所以它可以获取ServletConfig,实际上它也继承了ServletConfig,这是一个规范的接口,而StandardWrapper内部做了很多其他的动作,比如加载servlet,这个方法当然不能暴露给客户了,所以它也有一个对应的门面。

Tomcat还有一些对规范的实现,比如
org.apache.catalina.core.ApplicationDispatcher
ApplicationFilterChain
这些并没有对应的门面,因为它们没有多余的内部实现,很安全,所以可以直接提供给用户。

观察者(Observer)模式

观察者模式是对象的行为模式,又叫做发布–订阅(Publish/Subscribe)模式、模型–视图(Model/View)模式、源–监听器(Source/Listener)模式或者从属者(Dependents)模式。
观察者模式定义了一种一对多的依赖关系,让多个观察者对象同事监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使它们能够自动更新自己。
观察者模式的类图如下:

定义了一个发布的事件类,一个观察事件的类,它们分别继承了发布事件的抽象类和观察事件的抽象类,当发布对象状态变化时,就会调用父类的更新方法,而父类里保持了一个观察者对象集合的列表,由父类去调用所有的观察者更新它们的变化。

Tomcat的观察者模式如下:


Tomcat用来处理观察者模式的一共有4个相关类
LifecycleListener: 这个表示需要监听的类
Lifecycle:这个表示会要发布事件的类
LifecycleEvent:这个类是对事件的一个封装
LifecycleSupport:这个类是用来触发事件的

ContainerBase,也就是所有容器的父类,包含了一个LifecycleSupport,当某个具体容器,比如主机,或者上下文启动的时候,就会调用这么一段:
lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);
这里的lifecycle就是继承自父类ContainerBase。
然后后呢,会调用到LifecycleSupport#fireLifecycleEvent(),它的内容如下:

    public void fireLifecycleEvent(String type, Object data) {
        if (Lifecycle.INIT_EVENT.equals(type)) {
            state = "INITIALIZED";
        } else if (Lifecycle.BEFORE_START_EVENT.equals(type)) {
            state = "STARTING_PREP";
        } else if (Lifecycle.START_EVENT.equals(type)) {
            state = "STARTING";
        } else if (Lifecycle.AFTER_START_EVENT.equals(type)) {
            state = "STARTED";
        } else if (Lifecycle.BEFORE_STOP_EVENT.equals(type)) {
            state = "STOPPING_PREP";
        } else if (Lifecycle.STOP_EVENT.equals(type)) {
            state = "STOPPING";
        } else if (Lifecycle.AFTER_STOP_EVENT.equals(type)) {
            state = "STOPPED";
        } else if (Lifecycle.DESTROY_EVENT.equals(type)) {
            state = "DESTROYED";
        }
        LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
        LifecycleListener interested[] = listeners;
        for (int i = 0; i < interested.length; i++)
            interested[i].lifecycleEvent(event);
    }
                                                                                 

fireLifecycleEvent()方法将事件类型,这个事件是由哪个具体对象发出的,以及对象绑定的数据都封装到LifecycleEvent中,然后得到当前容器关联的
所有监听事件类,依次遍历调用它们的lifecycleEvent()方法。
当具体的事件监听类,比如HostConfig,ContextConfig被调用时,就根据LifecycleEvent的事件类型去调用自身相应的方法,比如收到的是start类型的事件,就调用自己的start()方法。

从这里可以看出,Tomcat将标准的观察者模式给增强了,负责调用的工作都由LifecycleSupport完成,当我们定义发布事件的类,以及监听事件的类,它们之间的耦合性就很低了。具体工作都是由上层代码完成的。

3 次阅读

发表评论

电子邮件地址不会被公开。