门面设计模式

门面设计模式在 Tomcat 中有多处使用,在 Request 和 Response 对象封装中、Standard Wrapper 到 ServletConfig 封装中、ApplicationContext 到 ServletContext 封装中等都用到了这种设计模式。

门面设计模式的原理

这么多场合都用到了这种设计模式,那这种设计模式究竟能有什么作用呢?顾名思义,就是将一个东西封装成一个门面好与人家更容易进行交流,就像一个国家的外交部一样。

这种设计模式主要用在一个大的系统中有多个子系统组成时,这多个子系统肯定要涉及到相互通信,但是每个子系统又不能将自己的内部数据过多的暴露给其它系统,不然就没有必要划分子系统了。每个子系统都会设计一个门面,把别的系统感兴趣的数据封装起来,通过这个门面来进行访问。这就是门面设计模式存在的意义。

门面设计模式示意图如下:

图 1. 门面示意图

Client 只能访问到 Façade 中提供的数据是门面设计模式的关键,至于 Client 如何访问 Façade 和 Subsystem 如何提供 Façade 门面设计模式并没有规定死。

Tomcat 的门面设计模式示例

Tomcat 中门面设计模式使用的很多,因为 Tomcat 中有很多不同组件,每个组件要相互交互数据,用门面模式隔离数据是个很好的方法。

下面是 Request 上使用的门面设计模式:

图 2. Request 的门面设计模式类图

从图中可以看出 HttpRequestFacade 类封装了 HttpRequest 接口能够提供数据,通过 HttpRequestFacade 访问到的数据都被代理到 HttpRequest 中,通常被封装的对象都被设为 Private 或者 Protected 访问修饰,以防止在 Façade 中被直接访问。

观察者设计模式

这种设计模式也是常用的设计方法通常也叫发布 - 订阅模式,也就是事件监听机制,通常在某个事件发生的前后会触发一些操作。

观察者模式的原理

观察者模式原理也很简单,就是你在做事的时候旁边总有一个人在盯着你,当你做的事情是它感兴趣的时候,它就会跟着做另外一些事情。但是盯着你的人必须要到你那去登记,不然你无法通知它。观察者模式通常包含下面这几个角色:

  • Subject 就是抽象主题:它负责管理所有观察者的引用,同时定义主要的事件操作。
  • ConcreteSubject 具体主题:它实现了抽象主题的所有定义的接口,当自己发生变化时,会通知所有观察者。
  • Observer 观察者:监听主题发生变化相应的操作接口。

Tomcat 的观察者模式示例

Tomcat 中观察者模式也有多处使用,前面讲的控制组件生命周期的 Lifecycle 就是这种模式的体现,还有对 Servlet 实例的创建、Session 的管理、Container 等都是同样的原理。下面主要看一下 Lifecycle 的具体实现。

Lifecycle 的观察者模式结构图:

图 3. Lifecycle 的观察者模式结构图

上面的结构图中,LifecycleListener 代表的是抽象观察者,它定义一个 lifecycleEvent 方法,这个方法就是当主题变化时要执行的方法。 ServerLifecycleListener 代表的是具体的观察者,它实现了 LifecycleListener 接口的方法,就是这个具体的观察者具体的实现方式。Lifecycle 接口代表的是抽象主题,它定义了管理观察者的方法和它要所做的其它方法。而 StandardServer 代表的是具体主题,它实现了抽象主题的所有方法。这里 Tomcat 对观察者做了扩展,增加了另外两个类:LifecycleSupport、LifecycleEvent,它们作为辅助类扩展了观察者的功能。LifecycleEvent 使得可以定义事件类别,不同的事件可区别处理,更加灵活。LifecycleSupport 类代理了主题对多观察者的管理,将这个管理抽出来统一实现,以后如果修改只要修改 LifecycleSupport 类就可以了,不需要去修改所有具体主题,因为所有具体主题的对观察者的操作都被代理给 LifecycleSupport 类了。这可以认为是观察者模式的改进版。

LifecycleSupport 调用观察者的方法代码如下:

清单 1. LifecycleSupport 中的 fireLifecycleEvent 方法

1

2

3

4

5

6

7

8

9

public void fireLifecycleEvent(String type, Object data) {

    LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);

    LifecycleListener interested[] = null;

    synchronized (listeners) {

        interested = (LifecycleListener[]) listeners.clone();

    }

    for (int i = 0; i < interested.length; i++)

        interested[i].lifecycleEvent(event);

}

主题是怎么通知观察者呢?看下面代码:

清单 2. 容器中的 start 方法

1

2

3

4

5

6

7

8

9

10

11

12

public void start() throws LifecycleException {

    lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);

    lifecycle.fireLifecycleEvent(START_EVENT, null);

    started = true;

    synchronized (services) {

        for (int i = 0; i < services.length; i++) {

            if (services[i] instanceof Lifecycle)

                ((Lifecycle) services[i]).start();

            }

        }

    lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);

}

命令设计模式

前面把 Tomcat 中两个核心组件 Connector 和 Container,比作一对夫妻。男的将接受过来的请求以命令的方式交给女主人。对应到 Connector 和 Container,Connector 也是通过命令模式调用 Container 的。

命令模式的原理

命令模式主要作用就是封装命令,把发出命令的责任和执行命令的责任分开。也是一种功能的分工。不同的模块可以对同一个命令做出不同解释。

下面是命令模式通常包含下面几个角色:

  • Client:创建一个命令,并决定接受者
  • Command 命令:命令接口定义一个抽象方法
  • ConcreteCommand:具体命令,负责调用接受者的相应操作
  • Invoker 请求者:负责调用命令对象执行请求
  • Receiver 接受者:负责具体实施和执行一次请求

Tomcat 中的命令模式的示例

Tomcat 中命令模式在 Connector 和 Container 组件之间有体现,Tomcat 作为一个应用服务器,无疑会接受到很多请求,如何分配和执行这些请求是必须的功能。

下面看一下 Tomcat 是如何实现命令模式的,下面是 Tomcat 命令模式的结构图:

图 4. Tomcat 命令模式的结构图

Connector 作为抽象请求者,HttpConnector 作为具体请求者。HttpProcessor 作为命令。Container 作为命令的抽象接受者,ContainerBase 作为具体的接受者。客户端就是应用服务器 Server 组件了。Server 首先创建命令请求者 HttpConnector 对象,然后创建命令 HttpProcessor 命令对象。再把命令对象交给命令接受者 ContainerBase 容器来处理命令。命令的最终是被 Tomcat 的 Container 执行的。命令可以以队列的方式进来,Container 也可以以不同的方式来处理请求,如 HTTP1.0 协议和 HTTP1.1 的处理方式就会不同。

责任链模式

Tomcat 中一个最容易发现的设计模式就是责任链模式,这个设计模式也是 Tomcat 中 Container 设计的基础,整个容器的就是通过一个链连接在一起,这个链一直将请求正确的传递给最终处理请求的那个 Servlet。

责任链模式的原理

责任链模式,就是很多对象有每个对象对其下家的引用而连接起来形成一条链,请求在这条链上传递,直到链上的某个对象处理此请求,或者每个对象都可以处理请求,并传给下一家,直到最终链上每个对象都处理完。这样可以不影响客户端而能够在链上增加任意的处理节点。

通常责任链模式包含下面几个角色:

  • Handler(抽象处理者):定义一个处理请求的接口
  • ConcreteHandler(具体处理者):处理请求的具体类,或者传给下家

Tomcat 中责任链模式示例

在 tomcat 中这种设计模式几乎被完整的使用,tomcat 的容器设置就是责任链模式,从 Engine 到 Host 再到 Context 一直到 Wrapper 都是通过一个链传递请求。

Tomcat 中责任链模式的类结构图如下:

图 5. Tomcat 责任链模式的结构图

上图基本描述了四个子容器使用责任链模式的类结构图,对应的责任链模式的角色,Container 扮演抽象处理者角色,具体处理者由 StandardEngine 等子容器扮演。与标准的责任链不同的是,这里引入了 Pipeline 和 Valve 接口。他们有什么作用呢?

实际上 Pipeline 和 Valve 是扩展了这个链的功能,使得在链往下传递过程中,能够接受外界的干预。Pipeline 就是连接每个子容器的管子,里面传递的 Request 和 Response 对象好比管子里流的水,而 Valve 就是这个管子上开的一个个小口子,让你有机会能够接触到里面的水,做一些额外的事情。

为了防止水被引出来而不能流到下一个容器中,每一段管子最后总有一个节点保证它一定能流到下一个子容器,所以每个容器都有一个 StandardXXXValve。只要涉及到这种有链式是处理流程这是一个非常值得借鉴的模式。

微信公众号【Java技术江湖】一位阿里 Java 工程师的技术小站。(关注公众号后回复”Java“即可领取 Java基础、进阶、项目和架构师等免费学习资料,更有数据库、分布式、微服务等热门技术学习视频,内容丰富,兼顾原理和实践,另外也将赠送作者原创的Java学习指南、Java程序员面试指南等干货资源)

走进JavaWeb技术世界7:Tomcat中的设计模式的更多相关文章

  1. 走进JavaWeb技术世界7:Tomcat和其他WEB容器的区别

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  2. 走进JavaWeb技术世界1:JavaWeb的由来和基础知识

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  3. 走进JavaWeb技术世界3:JDBC的进化与连接池技术

    走进JavaWeb技术世界3:JDBC的进化与连接池技术 转载公众号[码农翻身] 网络访问 随着 Oracle, Sybase, SQL Server ,DB2,  Mysql 等人陆陆续续住进数据库 ...

  4. 走进JavaWeb技术世界14:Mybatis入门

    本系列文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查看 https://github.com/h2pl/Java-Tutorial 喜欢的话麻烦点下 ...

  5. 走进JavaWeb技术世界2:JSP与Servlet的曾经与现在

    转载自:码农翻身 转自: 刘欣 码农翻身 1周前 我是Servlet, 由于很多框架把我深深地隐藏了起来,我变得似乎无关紧要了,很多人也选择性的把我给遗忘了. 其实,我还活得好好的呢, 只不过是从前台 ...

  6. 走进JavaWeb技术世界开篇:JavaWeb技术汇总

    微信公众号[Java技术江湖]一位阿里 Java 工程师的技术小站.(关注公众号后回复”Java“即可领取 Java基础.进阶.项目和架构师等免费学习资料,更有数据库.分布式.微服务等热门技术学习视频 ...

  7. 走进JavaWeb技术世界16:极简配置的SpringBoot

    一位阿里 Java 工程师的技术小站.作者黄小斜,专注 Java 相关技术:SSM.SpringBoot.MySQL.分布式.中间件.集群.Linux.网络.多线程,偶尔讲点Docker.ELK,同时 ...

  8. 走进JavaWeb技术世界8:浅析Tomcat9请求处理流程与启动部署过程

    谈谈 Tomcat 请求处理流程 转自:https://github.com/c-rainstorm/blog/blob/tomcat-request-process/reading-notes &l ...

  9. 走进JavaWeb技术世界6:Tomcat5总体架构剖析

      本文以 Tomcat 5 为基础,也兼顾最新的 Tomcat 6 和 Tomcat 4.Tomcat 的基本设计思路和架构是具有一定连续性的. Tomcat 总体结构 Tomcat 的结构很复杂, ...

随机推荐

  1. javascript--清除表单缓存

    表单缓存是指当用户在表单输入之后再次回到该表单或者刷新页面的时候,浏览器会直接显示用户之前的输入,即表单缓存下来了.多数情况下这正是我们想要的,但也有些情况我们希望表单能够刷新,特别是根据后台的数据刷 ...

  2. RFC destination fails with error Incomplete Logon Data after system copy

    1. 问题现象 1.1在system copy后,提示RFC报错Unable to configure STMS 2.  重要的参考文件: 2.1RFC passwords not available ...

  3. 解决 React-Native: Android project not found. Maybe run react-native android first?

    在终端运行命令react-native run-android时报错Android project not found. Maybe run react-native android first? 解 ...

  4. centos7创建共享文件夹

    0.检查是否已经安装samba rpm -qi samba 1.未安装,安装samba, 如果已安装,请忽略: yum -y install samba samba-client 2.共享一个目录,使 ...

  5. SUSE CaaS Platform 4 - 使用 NFS 作为持久存储

    SUSE CaaS Platform 4 - 持久存储使用 NFS

  6. cuda实现向量相加

    cuda实现向量相加 博客最后附上整体代码 如果有说的不对的地方还请前辈指出, 因为cuda真的接触没几天 一些总结(建议看) cuda 并不纯GPU在运行程序, 而是 cpu 与 gpu 一起在运行 ...

  7. Linux网络编程综合运用之MiniFtp实现(六)

    间隔了一周时间没写了,由于今年的股势行情貌似不错的样子,对于对股市完全不懂的我也在蠢蠢欲动,所以最近一周业余时间在“不务正业”-----学习炒股.发现学习它其实挺费神的,满脑子都是走势图,而且是神经有 ...

  8. 常考JS题笔记

    ### 1. 原始类型有哪几种?null 是对象吗? 答: Null,undefined,Number,String,Blooean,symbol1)[理解和使用ES6中的Symbol][https: ...

  9. Django2-settings常用配置

    1. MySQL 数据库连接 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'bms', # 要连接 ...

  10. Django REST framework+Vue 打造生鲜电商项目(笔记十)

    (from:https://www.cnblogs.com/derek1184405959/p/8877643.html  有修改) 十三.首页.商品数量.缓存和限速功能开发 首先把pycharm环境 ...