> 文档中心 > Tomcat组件化设计

Tomcat组件化设计


1 组件化及可配置

Tomcat整体架构基于组件,可通过XML或代码配置组件。如server.xml配置Tomcat的连接器及容器组件。

Tomcat提供一堆积木,怎么搭建这些积木你决定,你可根据需要灵活选择组件搭建你的Web容器,并且可自定义组件。

2 Web容器如何实现这种组件化设计?

2.1 面向接口编程

要对系统功能按“高内聚、低耦合”原则拆分,每个组件都有相应接口,组件间通过接口通信,就可方便替换组件。如选择不同连接器类型,只要这些连接器组件实现同一接口即可。

2.2 组装

Web容器提供一个载体把组件组装在一起工作。组件的工作无非就是处理请求,因此容器通过责任链模式把请求依次交给组件去处理。

对用户,只需告诉Web容器由哪些组件处理请求。把组件组织起来需要一个“管理者”,所以Tomcat有Server,即组件的载体,Server包含连接器组件、容器组件。容器还要把请求交给各子容器组件处理,Tomcat和Jetty都是责任链模式。

用户通过配置组装组件,类似Spring中Bean的DI。Spring的用户可通过配置文件或注解方式组装Bean,Bean与Bean的依赖关系完全由用户自定义。这和Web容器不同,Web容器中组件与组件之间的关系是固定的,比如Tomcat中Engine组件下有Host组件、Host组件下有Context组件等,但你不能在Host组件里“注入”一个Wrapper组件,这是由于Web容器本身的功能来决定的。

3 创建组件

由于组件可配置,Web容器在启动前并不知道要创建哪些组件,即不能通过硬编码实例化这些组件,而要通过反射。即Web容器通过Class.forName创建组件。
无论哪种方式,在实例化类前,Web容器需把组件类加载到JVM,这涉及类加载,Web容器设计自己的类加载器。

Spring也是通过反射机制来动态地实例化Bean,那么它用到的类加载器哪来的?
Web容器给每个Web应用创建了一个类加载器,Spring用到的类加载器是Web容器传给它!

4 组件的生命周期管理

不同类型的组件具有父子层次关系,父组件处理请求后再把请求传递给某个子组件。
Tomcat通过容器,把小容器放到大容器以实现父子关系。

Tomcat采用类似办法管理组件的生命周期:

  • 父组件负责子组件的创建、启停和销毁
    只要启动最上层组件,整个Web容器就被启动,即一键启停
  • Tomcat定义了组件的生命周期状态,并把组件状态的转变定义成事件
    一个组件的状态变化会触发子组件变化,比如Host容器的启动事件里会触发Web应用的扫描和加载,最终会在Host容器下创建相应的Context容器,而Context组件的启动事件又会触发Servlet的扫描,进而创建Wrapper组件。
    如何实现这种联动?
    观察者模式,创建监听器去监听容器的状态变化,在监听器的方法里去实现相应的动作,这些监听器其实是组件生命周期过程中的“扩展点”。

Spring也采用类似设计,Spring给Bean生命周期状态提供“扩展点”。这些扩展点定义成一个个接口,只要Bean实现这些接口,Spring就会负责调用这些接口,这样当Bean的创建、初始化和销毁这些控制权交给Spring后,Spring让你有机会在Bean的整个生命周期中执行你的逻辑。Tomcat组件化设计

5 模板模式

Tomcat大量采用骨架抽象类和模板模式。如Tomcat#ProtocolHandler接口,ProtocolHandler的抽象基类AbstractProtocol实现协议处理层的骨架和通用逻辑。

具体协议也有抽象基类,如HttpProtocol和AjpProtocol。这些抽象骨架类实现通用逻辑,并定义一些抽象方法,这些抽象方法由子类实现,抽象骨架类调用抽象方法实现骨架逻辑。

JDK也有这种设计,如Java集合的AbstractSet、AbstractMap。
Java 8开始允许接口有default方法,开始可以把抽象骨架类的通用逻辑放到接口。

6 总结

Web容器为了支持这种组件化设计,遵循了一些规范,比如面向接口编程,用“管理者”去组装这些组件,用反射的方式动态的创建组件、统一管理组件的生命周期,并且给组件生命状态的变化提供了扩展点,组件的具体实现一般遵循骨架抽象类和模板模式。