你的位置:首页 > Java教程

[Java教程]How Tomcat works — 四、tomcat启动(3)


上一节说到StandardService负责启动其子组件:container和connector,不过注意,是有先后顺序的,先启动container,再启动connector,这一节先来看看container。

目录

  • Pipeline和Vavle
  • StandardEngine类和StandardHost类
  • StandardContext类
  • 总结

Pipeline和Vavle

在第二节(How Tomcat works — 二、tomcat启动(1))中没有介绍关于Pipeline和Vavle,因为前面侧重的是整个架构,但是在初始化的时候就不得不说了。

Pipeline,就是一根管道,用来连接两个容器,在一个容器流向下一个容器的时候使用。在tomcat中也是这个意思,很形象,Engine、Host等都是容器,在执行完上一个容器(比如Engine)的相关操作的时候要开始执行下一个容器(比如Host)的操作了,这个时候需要经过一根管道pipeline,那么我们可以在管道中执行一个其他必要的操作,这个时候可以在管道上面添加Vavle(阀),一根管道pipeline上可以有多个阀门(也很形象)。每根管道都有一个默认的阀门。

在tomcat实现中有一个实现了Pipeline接口的类StandardPipeline——是每两个容器之间的管道,每个容器都有一个默认的Valve实现StandardEnginevavle、StandardHostValve、StandardContextVavle、StandardWrapperVavle。

其实valve的作用和filter的作用类似。

StandardEngine类

StandardEngine作为整个容器的最顶层负责启动其子组件——StandardHost,对,他就这一个作用。

StandardEngine.initInternal

这个方法被超类LifecycleBase.init方法调用,主要作用就是调用超类LifecycleMBeanBase.initInternal方法注册MBean,并初始化一个startStopExecutor(ThreadPoolExecutor),后面用来使用线程启动子容器。

StandardEngine.startInternal

这个方法的主要作用就是调用父类的方法Container.startInternal——主要的操作就在这个方法里面:

protected synchronized void startInternal() throws LifecycleException {  // Start our subordinate components, if any  if ((loader != null) && (loader instanceof Lifecycle))    ((Lifecycle) loader).start();  logger = null;  getLogger();  if ((manager != null) && (manager instanceof Lifecycle))    ((Lifecycle) manager).start();  if ((cluster != null) && (cluster instanceof Lifecycle))    ((Lifecycle) cluster).start();  Realm realm = getRealmInternal();  if ((realm != null) && (realm instanceof Lifecycle))    ((Lifecycle) realm).start();  if ((resources != null) && (resources instanceof Lifecycle))    ((Lifecycle) resources).start();  // Start our child containers, if any  Container children[] = findChildren();  List<Future<Void>> results = new ArrayList<Future<Void>>();  for (int i = 0; i < children.length; i++) {    results.add(startStopExecutor.submit(new StartChild(children[i])));  }  boolean fail = false;  for (Future<Void> result : results) {    try {      result.get();    } catch (Exception e) {      log.error(sm.getString("containerBase.threadedStartFailed"), e);      fail = true;    }  }  if (fail) {    throw new LifecycleException(        sm.getString("containerBase.threadedStartFailed"));  }  // Start the Valves in our pipeline (including the basic), if any  if (pipeline instanceof Lifecycle)    ((Lifecycle) pipeline).start();  setState(LifecycleState.STARTING);  // Start our thread  threadStart();}

View Code
  • manager、cluster、realm、resource如果有的话全部启动(所以其实tomcat本身就是支持集群的)
  • 调用findChildren方法找到左右子容器——也就是所有的StandardHost(默认只有一个)
  • 对于每个子容器(StandardHost)使用线程启动,StartChild类实现了Callable接口,在call方法里面执行传入容器的start方法,将子容器启动作为一个task,然后使用ThreadPoolExecutor.submit提交task
  • 然后等待这些Future执行完(因为里面是各个子容器的初始化和启动工作,后面connector必须在容器准备好之后才能启动)
  • 启动pipeline(这里就是StandardPipeline),pipeline会启动所包含的所有vavle(这里就是只有StandardEngineValve)
  • setState会通知所有监听了StandardEngine事件的listener
  • threadStart,启动一个后台线程执行包含的所有阀门的backgroundProcess方法,并触发Lifecycle.PERIODIC_EVENT事件

StandardHost类

StandardHost的作用和StandardEngine类似,都是初始化启动子容器,不过这里是StandardContext。启动的方式也一样,所以就不再详述。

StandardContext类

这个类才是重中之重,解析web.

  • 调用init方法初始化StandardContext
  • 调用startInternal方法启动StandardContext

init

在该方法中主要进行了MBean的注册,再就是触发了Lifecycle.AFTER_INITEVENT事件,ContextConfig是StandardContext的listener,在发生该事件的时候执行ContextConfig.init方法,在该方法中主要是构造一个能解析web.

startInternal

由于LifecycleBase没有实现该方法,所以就是直接调用StandardContext.startInternal,在StandardContext.startInternal方法中主要进行了如下操作:

  • setResource:添加新的resource
  • 如果webappLoader为null则初始化
  • 初始化charset mapper
  • 设置webapp工作目录,比如%TOMCAT_HOME%/work//Catalina/localhost/_等
  • 触发Lifecycle.CONFIGURE_START_EVENT事件,ContextConfig监听了该事件,会进行解析web.
  • 启动所有的children(StandardWrapper,代表所有配置的servlet)
  • 初始化Standardmanager
  • 配置initParam
  • 配置启动filter、listener

 

总结

container部分终于启动完成了,不过还是有些部分略过了,比如StandardWrapper(这个还可以往深挖,不过也包含在StandardContext的启动过程中)初始化和webapp的发布。到现在发现,这些启动过程就是围绕生命周期的这几个方法展开:

  • start和init:LifecycleBase提供了默认实现
  • startInternal和initInternal:这两个方法每个类都是重载实现自己需要启动的东西