开头说两句
小刀博客: https://www.lixiang.red
小刀公众号: 程序员学习大本营
学习背景
在前面几篇文章,我们一起学习了 tomcat 中的 server.xml , 类加载器,组件默认值,digester 解析 server.xml 并初步初始化等基础知识点
https://www.lixiang.red/articles/2019/08/11/1565515601658.html
下面我们就要真正的走进源码,去看一看这些组件是如何实现的,今天我们一起学习 tomcat 中组件的设计
源码中的这些组件
通过下图我们可以看到,在我们直接使用的 Context,Service,Server 上面还有一层接口: Container 和 Lifecycle
我们说接口是有什么什么能力,现在我们就整理下这些组件的接口
以内层的 Context 为例,我们在 idea 中可以看到如下继承图:
同样,我们去对比其他的组件,也会发现他们都类似于继承这些接口,几大常用组件的继承关系如下所示:
我们可以通过 idea 的类图工具,查看类的继承以集接口相关的方法,对着类名点右键,然后可以看到相关的图
Lifecycle 接口
我们可以对上图中的接口做一个划分,如下所示
上面三个方法是 Listener 相关的
中间三个方法是自身生命周期相关的
下面两个方法是获取自身状态的
listener
监听器,每一组件,有一组监听器,在组件本身达到某一状态时,可以循环监听器 list , 然后这些注册监听器的组件,再根据监听到的状态进行相关的动作行为.
我们以 server 为例:
/**
* 循环监听器List , 去执行不同的动作
*
* @param type Event type
* @param data Data associated with event.
*/
protected void fireLifecycleEvent(String type, Object data) {
LifecycleEvent event = new LifecycleEvent(this, type, data);
for (LifecycleListener listener : lifecycleListeners) {
listener.lifecycleEvent(event);
}
}
当执行 server 的 start 时,就会去向所有的监听器传播 CONFIGURE_START_EVENT 这个事件.但是监听器对这个事件做不做响应,就是对应的实际监听器所做的决定,如下图所示,HostConfig ,在接收到事件时,要判断类型,对不同类型的事件,做不同的处理. 基本上 Config 都继承了 LifecycleListener 这个接口
组件本身的生命周期
中间四个方法,代表着组件的四种状态:init(),初始化, start() 启动,stop()停止,destory(销毁),这些通过字面意思就能猜出来.tomcat 官方给了一张生命周期和状态对应的流程图:
重点不在于上面的图,而在于下面话,组件通过调用不同的方法,使自身达到不同的状态,然后调用监听器 list , 去通知其他的组件/配置做相应的处理,以 Server 这个组件为例:,在前几篇中,我们讲 tomcat 启动流程的时候,分析到这里, 会调用 server.init(),实际上就是调用了生命周期的第一个阶段方法
我们可以看到, init 执行的 LifecycleBase 类中的 init 方法,最终,是执行的 StandardServer 中的 initInternal 方法,
// 初始化我们的service
for (int i = 0; i < services.length; i++) {
services[i].init();
}
同样,我们可以去看到,start 也是一个类似的过程,所以这四个方法, 是贯穿整个 tomcat 生命周期的,推动着 tomcat 的运行
本身状态
可以通过下图看使用方法:组件.getState().isAvailable(), 去判断组件是否在可用状态,通过 LifecycleState 源码可以看到,只在三种状态下 available 是可用的
STARTING(true, Lifecycle.START_EVENT),
STARTED(true, Lifecycle.AFTER_START_EVENT),
STOPPING_PREP(true, Lifecycle.BEFORE_STOP_EVENT),
Container 接口,pipeline,valve
通过其名字,我们可以大致猜测,是表示一个容器,那么做为一个容器,他有自己的名字,有子容器(Children), 但是这些容器本身并没有处理业务逻辑的功能,所以,一个容器还会绑定一个执行链.
Tomcat 中定义了 Pipeline, valve 来帮助 Container 来处理业务,每个 Container 组件通过执行一个 pipeline 里面的 valve 来执行业务.对于每个容器,都会有一个默认的 valve 在最底层,最后来执行.如果用户/使用者没有自定义的话,就会使用默认的.
我们可以看到在 valve 的方法中, invoke 方法,已经是和业务/具体处理是相关联的了
我们以 StandardEngineValve 为例,可以看到,对于同一个 request 和 response,他在 Invoke 方法中,又调用了 host 的 valve 的 inove
@Override
public final void invoke(Request request, Response response)
throws IOException, ServletException {
// Select the Host to be used for this Request
Host host = request.getHost();
if (host == null) {
response.sendError
(HttpServletResponse.SC_BAD_REQUEST,
sm.getString("standardEngine.noHost",
request.getServerName()));
return;
}
if (request.isAsyncSupported()) {
request.setAsyncSupported(host.getPipeline().isAsyncSupported());
}
// 调用host的valve去继续处理request,response
host.getPipeline().getFirst().invoke(request, response);
}
后面我们学习也就是以对 request, response 的处理为主
最后说两句
今天我们学习的 tomcat 组件的接口结构,是后面学习的基础,在学习过程中,小伙伴们有什么问题,可以和小刀一起交流:best396975802
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于