欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Tomcat-Container源码分析

程序员文章站 2022-07-14 10:47:48
...

Tomcat源码分析—-Container初始化与加载 https://yq.aliyun.com/articles/20172
Digester分析:http://blog.csdn.net/wgw335363240/article/details/5869660
《看透SpringMVC源码分析与实践》

TOMCAT启动-源码跟踪Bootstrap.load() 方法中,调用了createStartDigester();该方法通过解析conf/server.xml,创建Server,初始化Connector和Container,但是并未深究,在这里就研究下createStartDigester()中到底做了些什么。

Server.xml

首先来查看下conf/server.xml的内容:

<Server port="8005" shutdown="SHUTDOWN">
    <!-- listeners ,  GlobalNamingResources 省略-->
  <Service name="Catalina">
    <Connector port="8080" protocol="HTTP/1.1"
               connectionTimeout="20000"
               redirectPort="8443" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />

    <!-- defaultHost:默认域名  (url匹配优先级最低) -->
    <Engine name="Catalina" defaultHost="localhost">
      <Realm className="org.apache.catalina.realm.LockOutRealm">
        <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
               resourceName="UserDatabase"/>
      </Realm>

      <!-- 
        name: 表示站点域名 (url匹配优先级最高)
        appBase: 指定站点的位置
        unpackWARs: 是否自动解压war文件
        autoDeploy:是否自动部署,若为true,在tomcat运行过程中,在webapps目录下增加了应用将会自动部署。
        --> 
      <Host name="localhost"  appBase="webapps"
            unpackWARs="true" autoDeploy="true">

          <!-- 域名别名 (url匹配优先级第二高) -->  
          <alias>xxx</alias>

          <!-- 配置context.xml -->
          <Context path="/img" docBase="E:/data/ftpFiles/" debug="0"></Context>
          <Context path="/zz" docBase="/home/zzz.war" debug="0">
      </Host>
    </Engine>
  </Service>

</Server>

在这个xml文件中,Server下包含了Service,Service下包含了Connector和Engine,Engin下包含了Host。是时候放出一张图了。
Tomcat-Container源码分析


Container组成

Container容器的结构图
下图是一个非常常见的Container容器的结构图。
Tomcat-Container源码分析

  • Engine 表示一个Servlet引擎,它可以包含一个或多个子容器,比如多个Host或者Context容器;
  • Host 表示一台虚拟的主机,它可以包含一系列Context容器;
  • Context 表示一个唯一的ServletContext,一个 Context 对应一个 Web 工程,它可以包含一个
    或多个Wrapper容器;

Context配置方式

  • 1.文件配置
    • 将<context/>配置添加到conf/server.xml
    • conf/[enginename]/[hostname]/应用名.xml
    • 应用自身目录/META-INF/context.xml
    • conf/context.xml文件 ——整个tomcat共享
    • conf/[enginename]/[hostname]/context.xml.default 文件 —-整个站点(host)共享
  • 2.将war放置到Host配置的appBase目录下
  • 3.将应用文件夹放置到Host配置的appBase目录下
  • Wrapper 表示一个独立的Servlet定义,即Wrapper本质就是对Servlet进行了一层包装。

Container的类图
Container是Tomcat中容器的接口.Container有四个子接口Engine,Host,Context,Wrapper和一个默认abstract实现类ContainerBase。这四个子接口都有一个对应的StandradXXX实现类。
Tomcat-Container源码分析


Container实例化

Bootstrap.load() 执行过程:Bootstrap.load() --> catalina.load() --> createStartDigester() --> getServer().init() --> 遍历services[i].init() --> container.init() 和 遍历connectors[i].init();

  • 通过createStartDigester()方法创建实例化了Server,Container和Connector等相关组件。
  • container.init() 进行初始化

createStartDigester()创建

//org.apache.catalina.startup.Catalina
    protected Digester createStartDigester() {
        //省略.....

        //当解析xml文件时,遇到"Server"就初始化一个"org.apache.catalina.core.StandardServer”对象,并且把该对象压入栈顶
        digester.addObjectCreate("Server",
                                 "org.apache.catalina.core.StandardServer",
                                 "className");
        //从server.xml读取"Server"元素的所有{属性:值}配对,用对应的Setter方法将属性值设置到堆栈顶层元素(Server)。                      
        digester.addSetProperties("Server");

        //遇到"Server"结束符时,调用"次顶层元素(Catalina)"的"setServer"方法,
        digester.addSetNext("Server",
                            "setServer",
                            "org.apache.catalina.Server");

        /*
         * 调用Rule的begin 、body、end、finish方法来解析xml,入栈和出栈给对象赋值
         * ConnectorCreateRule是通过server.xml中配置的Connector的protocol类型,寻找对应的className
         */
        digester.addRule("Server/Service/Connector",
                         new ConnectorCreateRule());

        //判断connector是否使用线程池,即是否配置executor属性
        digester.addRule("Server/Service/Connector",
                         new SetAllPropertiesRule(new String[]{"executor"}));
        digester.addSetNext("Server/Service/Connector",
                            "addConnector",
                            "org.apache.catalina.connector.Connector");

        //调用RuleSetBase.addRuleInstances来解析xml标签,可以RuleSetBase认为是一组Rule。
        digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
        digester.addRuleSet(new ContextRuleSet("Server/Service/Engine/Host/"));
        digester.addRuleSet(new NamingRuleSet("Server/Service/Engine/Host/Context/"));

        /*
         * 当发现配置了engine,设置它的parentClassLoader为parentClassLoader
         * parentClassLoader = Catalina.class.getClassLoader();
         */ 
        digester.addRule("Server/Service/Engine",new SetParentClassLoaderRule(parentClassLoader));      

        return (digester);

    }

Container启动

Container的启动时通过init和start方法来完成的,这两个方法是在Tomcat启动过程时被Service调用。
Container的init和start方法也会调用initInternal和startInternal来具体处理,但是container和之前的tomcat整体结构启动过程稍微有点不一样,不同的是:

  • Container四个子容器有一个共同的父类ContainerBase,它定义了通用的initInternal和startInternal处理内容。
  • 除了最顶层容器(Engine)的init是被Service调用的,子容器(Host,Context,Wrapper)的init方法并不是此时在容器中循环调用的,而是在执行start方法时通过状态判断调用的。
  • start方法除了在父容器的startInternal方法中调用,还会在父容器的添加子容器的addChild方法中调用,这主要因为Context和Wrapper是动态添加的,在web.xml配置一个Servlet就可以添加一个Wrapper,所以Context和Wrapper是在容器启动的过程中cai动态查找出来添加到响应的父容器中的。

Container-init

container.init()
container.init()方法调用的入口是StandardService.initInternal()中调用container.init(),

//org.apache.catalina.core.StandardService
protected void initInternal() throws LifecycleException {
       if (container != null) {
           container.init();
       }
       //省略其他代码.......
 }

此时container的name="Catalina"的StandardEngine类型的容器,它有一个name="localhost"的StandardHost的子容器。
Tomcat-Container源码分析
这些都是在上文createStartDigester()方法中创建实例化的。

init()执行轨迹如下图:
Tomcat-Container源码分析

ContainerBase.initInternal()
ContainerBase.initInternal()方法主要初始化了ThreadPoolExecutor类型的属性startStopExecutor,用于管理启动和关闭的线程。

//org.apache.catalina.core.ContainerBase
@Override
protected void initInternal() throws LifecycleException {
    BlockingQueue<Runnable> startStopQueue =
        new LinkedBlockingQueue<Runnable>();
    startStopExecutor = new ThreadPoolExecutor(
            getStartStopThreadsInternal(),
            getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
            startStopQueue,
            new StartStopThreadFactory(getName() + "-startStop-"));
    startStopExecutor.allowCoreThreadTimeOut(true);
    super.initInternal();
}

这个方法并没有设置生命周期的相应状态,所以对于的具体容器也没有设置相应的状态,那么即使init方法进行了初始化,在start启动前也会再次调用init方法。

Container-start

container.start()
container.start()方法调用的入口是StandardService.startInternal()中调用container.start(),
start()执行轨迹如下图:
Tomcat-Container源码分析

ContainerBase.startInternal()
ContainerBase.startInternal()方法主要完成:

  • 如果有Cluster和Realm则调用其start方法。Cluster用于配置集群,用来同步Session;Realm是tomcat的安全域,可以用来管理资源的访问权限。
  • 调用所有子容器的start方法启动
  • 调用管道中Value的Start方法来启动管道
  • 启动完成后将生命周期状态设置为STARTING状态。
  • 启动后台线程处理一些事情:如session过期清理,jsp动态替换,动态部署资源包等。
//org.apache.catalina.core.ContainerBase
@Override
protected synchronized void startInternal() throws LifecycleException {
       //启动loader,manager,cluster,realm,resources 略.....

        //获取所有子容器
        Container children[] = findChildren();
        List<Future<Void>> results = new ArrayList<Future<Void>>();
        for (int i = 0; i < children.length; i++) {
            //调用执行StartChild.call() --> 内部调用 chilid[i].start()方法
            results.add(startStopExecutor.submit(new StartChild(children[i])));
        }

        //处理子容器启动线程的结果results
        boolean fail = false;
        for (Future<Void> result : results) {
          result.get();//获取结果,阻塞..
        }

        //如果有子容器启动失败,抛出异常 省略.....

        //启动管道
        if (pipeline instanceof Lifecycle)
            ((Lifecycle) pipeline).start();

        setState(LifecycleState.STARTING);

        //启动后台线程,定期调用容器的backgroundProcess()方法。
        threadStart();
    }

StartChild是一个实现了Callable的内部类,主要作用就是调用子容器的child.start(); 方法。

子容器start()执行完成之后,接着会启动容器的管道。tomcat-Pipeline-Value管道,管道的启动也是直接调用start方法来完成的。管道启动完成之后设置生命周期状态,最后调用threadStart方法来启动后台线程。

threadStart()
threadStart()方法设置一个thread为后台线程,并调用ContainerBackgroundProcessor()的run方法。

//org.apache.catalina.core.ContainerBase
protected void threadStart() {
  //省略部分判断代码...

  threadDone = false;
  String threadName = "ContainerBackgroundProcessor[" + toString() + "]";
  thread = new Thread(new ContainerBackgroundProcessor(), threadName);
  thread.setDaemon(true); //设置后台进程
  thread.start();
}

ContainerBackgroundProcessor

//org.apache.catalina.core.ContainerBase.ContainerBackgroundProcessor
 protected class ContainerBackgroundProcessor implements Runnable {

        @Override
        public void run() {
            while (!threadDone) {
              //每隔backgroundProcessorDelay定期执行本方法
              Thread.sleep(backgroundProcessorDelay * 1000L);
              if (!threadDone) {
                  Container parent = (Container) getMappingObject();
                  ClassLoader cl = 
                      Thread.currentThread().getContextClassLoader();
                  if (parent.getLoader() != null) {
                      cl = parent.getLoader().getClassLoader();
                  }
                  processChildren(parent, cl);
              }
            }
        }

        protected void processChildren(Container container, ClassLoader cl) {

            //执行backgroundProcess
            container.backgroundProcess();

            //递归执行子容器的processChildren --> backgroundProcess
            Container[] children = container.findChildren();
            for (int i = 0; i < children.length; i++) {
                if (children[i].getBackgroundProcessorDelay() <= 0) {
                    processChildren(children[i], cl);
                }
            }
        }
    }

上述代码的作用是每该线程每10s会调用一次processChildren,最终执行container.backgroundProcess()方法,ContainerBase中的默认backgroundProcess实现如下:

container.backgroundProcess()

  • ContainerBase.backgroundProcess():调用一系列组件的backgroundProcess方法。值得关注的是:loader.backgroundProcess();,通过此方法达到**重新加载**webapps,以实现热部署
//org.apache.catalina.core.ContainerBase
public void backgroundProcess() {

     if (!getState().isAvailable())
         return;

    cluster.backgroundProcess(); //SimpleTcpCluster:集群心跳测试
    loader.backgroundProcess();//WebappLoader.backgroundProcess()----reload()重新加载
    manager.backgroundProcess();//ManagerBase:废弃所有过期的session
    Realm realm = getRealmInternal();
    realm.backgroundProcess(); //默认UserDatabaseRealm,do nothing.

     Valve current = pipeline.getFirst();
     while (current != null) {
         current.backgroundProcess();
         current = current.getNext();
     }

     //触发PERIODIC_EVENT事件
     fireLifecycleEvent(Lifecycle.PERIODIC_EVENT, null);
 }
  • WebappLoader.backgroundProcess()--实现热部署
//org.apache.catalina.loader.WebappLoader
 public void backgroundProcess() {
     //reloadable即为是否开启热部署,modified()则是当前文件是否有修改的判断。
     //`通过<Context reloadable="true"></Context> 配置。`
     if (reloadable && modified()) {
         try {
             Thread.currentThread().setContextClassLoader
                 (WebappLoader.class.getClassLoader());
             if (container instanceof StandardContext) {
                 ((StandardContext) container).reload();
             }
         } finally {
             if (container.getLoader() != null) {
                 Thread.currentThread().setContextClassLoader
                     (container.getLoader().getClassLoader());
             }
         }
     } else {
         closeJARs(false);
     }
 }
  • StandardWrapper.backgroundProcess():方法首先调用super.backgroundProcess()方法,然后再执行Servlet.periodicEvent()来进行
//org.apache.catalina.core.StandardWrapper
public void backgroundProcess() {
      super.backgroundProcess();//调用ContainerBase.backgroundProcess()

      if (!getState().isAvailable())
          return;

      if (getServlet() != null && (getServlet() instanceof PeriodicEventListener)) {
          //调用Servlet.periodicEvent,实际上调用JspServlet.periodicEvent()
          ((PeriodicEventListener) getServlet()).periodicEvent();
      }
  }
//org.apache.jasper.servlet.JspServlet
public class JspServlet extends HttpServlet implements PeriodicEventListener {
 //省略其他代码.....

  private transient JspRuntimeContext rctxt;
  @Override
  public void periodicEvent() {
         //检测未加载的jsp
      rctxt.checkUnload();

      //检测未编译的jsp
      rctxt.checkCompile();
  }
}

Container容器组件启动链

容器启动的启动节点是在Service调用了,container.start(),然后会逐层的启动:
StandardEngine --> StandardHost --> StandradContext --> StandardWrapper,如下图:
Tomcat-Container源码分析

Engine
Engine的默认实现是StandradEngine,它默认实现了initInternal和startInternal,代码如下:

//org.apache.catalina.core.StandardEngine
public class StandardEngine extends ContainerBase implements Engine {
    @Override
    protected void initInternal() throws LifecycleException {
        getRealm();
        super.initInternal();
    }

    @Override
    protected synchronized void startInternal() throws LifecycleException {
        //直接调用父类的同名方法
        super.startInternal();
    }

    @Override
    public Realm getRealm() {
        //如果没有配置realm,使用一个默认的NullRealm
        Realm configured = super.getRealm();
        if (configured == null) {
            configured = new NullRealm();
            this.setRealm(configured);
        }
        return configured;
    }
}

Host
Host的默认实现类是StandradHost,没有重写initInternal方法,默认调用父类的initInternal方法,代码如下:

//org.apache.catalina.core.StandardHost
public class StandardHost extends ContainerBase implements Host {
    @Override
    protected synchronized void startInternal() throws LifecycleException {

        //errorValve  = “org.apache.catalina.valves.ErrorReportValve”
        String errorValve = getErrorReportValveClass();
        if ((errorValve != null) && (!errorValve.equals(""))) {
             boolean found = false;
             Valve[] valves = getPipeline().getValves();
             for (Valve valve : valves) {
                 if (errorValve.equals(valve.getClass().getName())) {
                     found = true;
                     break;
                 }
             }
            //如果管道中没有指定的errorValve类,则假如管道
             if(!found) {
                 Valve valve = (Valve) Class.forName(errorValve).newInstance();
                 getPipeline().addValve(valve);
             }
        }
        //调用父类即ContainerBase.startInternal()
        super.startInternal();
    }
}

Host启动调用自身的startInternal()的方法之后,会调用父类ContainerBase的同名方法,在该方法中会执行setState(LifecycleState.STARTING);,此主动触发Host的监听Listener的相关事件。
而在上文中,createStartDigester()创建 中,创建Host的过程中,给Host增加了监听类HostConfig

//1. org.apache.catalina.startup.Catalina.
protected Digester createStartDigester() {
    //....省略其他
    digester.addRuleSet(new HostRuleSet("Server/Service/Engine/"));
}

//2.org.apache.catalina.startup.HostRuleSet

  public void addRuleInstances(Digester digester) {
     //匹配xxx/Host 标签,创建org.apache.catalina.core.StandardHost类,如果存在className属性则创建该属性对应的类。
   digester.addObjectCreate(prefix + "Host",
                                "org.apache.catalina.core.StandardHost",
                                "className");
     digester.addSetProperties(prefix + "Host");
     digester.addRule(prefix + "Host",
                      new CopyParentClassLoaderRule());

  //使用LifecycleListenerRule规则,给当前Host添加Listener,类型为HostConfig
     digester.addRule(prefix + "Host",
                      new LifecycleListenerRule
                      ("org.apache.catalina.startup.HostConfig",
                       "hostConfigClass"));
    //省略其他......
}


//3.org.apache.catalina.startup.LifecycleListenerRule 
public void begin(String namespace, String name, Attributes attributes)
        throws Exception {
  //peek()表示获取当前栈顶数据,但是不从栈中移除它。此时栈顶是Host      
  Container c = (Container) digester.peek();

  //省略部分代码.....

  //反射生成listener
  Class<?> clazz = Class.forName(className);
  LifecycleListener listener = (LifecycleListener) clazz.newInstance();


  //给Host添加监听。
   c.addLifecycleListener(listener);
}

到这里我们可以认为那么在接着StandardHost启动过程中,之后触发执行HostConfig相关的事件的流程,代码如下:

//1.org.apache.catalina.core.ContainerBase
protected synchronized void startInternal() throws LifecycleException {
    //省略其他....
    setState(LifecycleState.STARTING);
}

//2.org.apache.catalina.util.LifecycleBase
protected synchronized void setState(LifecycleState state)
         throws LifecycleException {
     setStateInternal(state, null, true);
}

private synchronized void setStateInternal(LifecycleState state,
            Object data, boolean check) throws LifecycleException {
    //事件类型是:Lifecycle.START_EVENT即“start”
    String lifecycleEvent = state.getLifecycleEvent();
    if (lifecycleEvent != null) {
        fireLifecycleEvent(lifecycleEvent, data);
    }
}

protected void fireLifecycleEvent(String type, Object data) {
    lifecycle.fireLifecycleEvent(type, data);
}

//3.org.apache.catalina.util.LifecycleSupport
public void fireLifecycleEvent(String type, Object data) {
     LifecycleEvent event = new LifecycleEvent(lifecycle, type, data);
     LifecycleListener interested[] = listeners;
     for (int i = 0; i < interested.length; i++)
         interested[i].lifecycleEvent(event);
 }

最终调用HostConfig.lifecycleEvent()方法,最关键的逻辑:

  • lifecycleEvent():判断事件类型,调用start()方法
  • start()调用deployApps方法
  • deployApps()通过三种方式部署应用。
  • deployDescriptor()部署应用,并生成Context,且将Context添加监听ContextConfig,并将其添加至Host的Child列表中。
//org.apache.catalina.startup.HostConfig
public class HostConfig implements LifecycleListener {
public void lifecycleEvent(LifecycleEvent event) {

        // Identify the host we are associated with
        try {
            host = (Host) event.getLifecycle();
            if (host instanceof StandardHost) {
                setCopyXML(((StandardHost) host).isCopyXML());
                setDeployXML(((StandardHost) host).isDeployXML());
                setUnpackWARs(((StandardHost) host).isUnpackWARs());
                setContextClass(((StandardHost) host).getContextClass());
            }
        } catch (ClassCastException e) {
            log.error(sm.getString("hostConfig.cce", event.getLifecycle()), e);
            return;
        }


        if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
            check(); //监听PERIODIC_EVENT事件
        } else if (event.getType().equals(Lifecycle.START_EVENT)) {
            start(); //监听start事件 --- 
        } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
            stop();
        }
    }

    public void start() {
        //省略其他....

      //如果Host配置了 autoDeploy
      if (host.getDeployOnStartup())
            deployApps();

    }

    //在此可以看到:可以通过如下三种方式deployApps
    protected void deployApps() {

        File appBase = appBase();
        File configBase = configBase();
        String[] filteredAppPaths = filterAppPaths(appBase.list());
        //configBase = ~/[ENGINE-NAME]/[HOST-NAME]
        deployDescriptors(configBase, configBase.list());
        //部署war
        deployWARs(appBase, filteredAppPaths);

        //部署文件夹
        deployDirectories(appBase, filteredAppPaths);
    }

    //部署,生成Context并添加至host的child列表中
    protected void deployDirectory(ContextName cn, File dir) {
        //省略.....
        //contextClass = "org.apache.catalina.core.StandardContext"
         Context context = (Context) Class.forName(contextClass).newInstance();

        //configClass = "org.apache.catalina.startup.ContextConfig"
         Class<?> clazz = Class.forName(host.getConfigClass());
         LifecycleListener listener = (LifecycleListener) clazz.newInstance();

         //同理,给context添加listener监听ContextConfig   
         context.addLifecycleListener(listener);

        //省略.....
        context.setName(cn.getName());
        context.setPath(cn.getPath());
        context.setWebappVersion(cn.getVersion());
        context.setDocBase(cn.getBaseName());
        host.addChild(context);
    }
}    

到这里为止,Host启动完毕,在它启动的同时伴随着Cntext的部署,且Context创建过程中添加了ContextConfig监听,它会影响到后续的Wrapper的创建和启动


Context
Context的默认实现类StandardContext默认实现了initInternal和startInternal,在startInternal方法主要做了如下操作:

  • 创建WebappLoader,并启动
  • 触发configure_start事件,ContextConfig做出相应,生成并启动子容器(Wrapper)
  • 创建ServletContext、StandardContext --> ApplicationContext --> ServletContext并设置了ServletContext相关属性
  • 调用了web.xml中定义的Listener以及初始化了其中的Filter和Load-on-startup的Servlet,

代码如下:

//org.apache.catalina.core.StandardContext
public class StandardContext extends ContainerBase
        implements Context, NotificationEmitter {
 protected void initInternal() throws LifecycleException {
        super.initInternal();

        if (processTlds) {//是否允许解析tld:默认为true
            //添加、解析 tld标签
            this.addLifecycleListener(new TldConfig());
        }

        // Register the naming resources
        if (namingResources != null) {
            namingResources.init();
        }

        //发送通知,主要用具JMS
        if (this.getObjectName() != null) {
            Notification notification = new Notification("j2ee.object.created",
                    this.getObjectName(), sequenceNumber.getAndIncrement());
            broadcaster.sendNotification(notification);
        }
    }

    protected synchronized void startInternal() throws LifecycleException {
        //设置resources,在webapploader创建webappclassloader时,需要指定此信息为加载目录
        if (webappResources == null) {  
           if ((getDocBase() != null) && (getDocBase().endsWith(".war")) &&
                        (!(new File(getBasePath())).isDirectory()))
            setResources(new WARDirContext());
            else
                setResources(new FileDirContext());
        }

         if (getLoader() == null) {
            WebappLoader webappLoader = new WebappLoader(getParentClassLoader());
            webappLoader.setDelegate(getDelegate());
            //设置loader
            setLoader(webappLoader);
        }

        //getServletContext().setAttribute(xxx,xxx) 省略....
        if (ok)
            getServletContext().setAttribute
             (Globals.RESOURCES_ATTR, getResources());

          //触发configure_start事件,ContextConfig监听了此事件
         fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

         //启动子容器,Wrapper容器。
         for (Container child : findChildren()) {
             if (!child.getState().isAvailable()) {
                 child.start();
             }
         }

        if (ok) {      
            //执行webappLoader.start() 启动
            if ((loader != null) && (loader instanceof Lifecycle))
                 ((Lifecycle) loader).start();

             if (pipeline instanceof Lifecycle) {
                  ((Lifecycle) pipeline).start();
               }
        }

        //listener启动,调用contextInitialized方法
        if (ok) {
             if (!listenerStart()) {
                  log.error( "Error listenerStart");
                  ok = false;
              }
          }

          //filetr启动,调用init方法
         if (ok) {
            if (!filterStart()) {
                  log.error("Error filterStart");
                  ok = false;
              }
          }

          // Load-on-startup的Servlet启动,调用init
          if (ok) {
              loadOnStartup(findChildren());
          }

        //启动父类的后台进程,定时执行backgroundProcess,监听变化
         super.threadStart();
    }

    //StandardContext --> ApplicationContext --> ServletContext 
     public ServletContext getServletContext() {
        if (context == null) {
            context = new ApplicationContext(this);
            if (altDDName != null)
                context.setAttribute(Globals.ALT_DD_ATTR,altDDName);
        }
        return (context.getFacade());

    }
}

关于StandardContext,WebappLoader和WebappClassLoader的关系
Tomcat-Container源码分析

  • StandardContext启动时,创建了WebappLoader,并将其设为自身的loader属性,然后启动WebappLoader
  • WebappLoader启动时,创建了WebappClassLoader,并设置了WebappClassLoader的resources等属性,然后启动
  • WebappClassLoader启动,工作比较简单,仅设置了 encoding.

在Context启动时,执行了fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);
ContextConfig会监听到该事件,会调用configureStart()方法,加载配置servlet-wrapper**,代码如下:

//org.apache.catalina.startup.ContextConfig
public class ContextConfig implements LifecycleListener {
    @Override
    public void lifecycleEvent(LifecycleEvent event) {
        if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
            configureStart();//CONFIGURE_START_EVENT触发configureStart()方法
        } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
            beforeStart();
        } else if (event.getType().equals(Lifecycle.AFTER_START_EVENT)) {
            if (originalDocBase != null) {
                context.setDocBase(originalDocBase);
            }
        } else if (event.getType().equals(Lifecycle.CONFIGURE_STOP_EVENT)) {
            configureStop();
        } else if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
            init();
        } else if (event.getType().equals(Lifecycle.AFTER_DESTROY_EVENT)) {
            destroy();
        }
    }

    protected synchronized void configureStart() {
        //执行webConfig方法
        webConfig();

        //省略 一系列 角色,授权等校验.....
    }

    protected void webConfig() {
        //省略其他......

         WebXml webXml = createWebXml();

         //1.如果jars中包含了web-fragment.xml文件,将他们合并到一起
         Map<String,WebXml> fragments = processJarsForWebFragments();

        // Step 2. Order the fragments.
        Set<WebXml> orderedFragments = null;
        orderedFragments =
                WebXml.orderWebFragments(webXml, fragments, sContext);

       //省略其他.....

        // Step 8. Convert explicitly mentioned jsps to servlets
        if (ok) {
             convertJsps(webXml);
         }

       // Step 9. 将合并后的web.xml应用到context
       if (ok) {
            webXml.configureContext(context);
        }

       //省略其他.....
    }
}

重点关注Step 9,它调用了webXml.configureContext(context),其内部逻辑是:

//org.apache.catalina.deploy.WebXml
public void configureContext(Context context) {
    //context设置属性值,略...... 

    //filters
    for (FilterDef filter : filters.values()) {
       if (filter.getAsyncSupported() == null) {
            filter.setAsyncSupported("false");
        }
        context.addFilterDef(filter);
    }
    for (FilterMap filterMap : filterMaps) {
        context.addFilterMap(filterMap);
    }

    //listeners
    for (String listener : listeners) {
        context.addApplicationListener(
                new ApplicationListener(listener, false));
    }

    for (ServletDef servlet : servlets.values()) {
         Wrapper wrapper = context.createWrapper();

         // servlet转换为 wrapper,略.....

         wrapper.setOverridable(servlet.isOverridable());
         //添加到child子容器中
         context.addChild(wrapper);
    }

    //省略.........
}

webConfig主要完成如下操作:

  • 解析合并web.xml,生成WebXml对象
  • 通过Webxml对象的configureContext方法:生成servlet、filter、jsp2Servlt等信息,配置加载Wrapper,并将Wrapper添加至Context的子容器里面
  • 完成其他的初始化操作…

自此可以看到Wrapper的生成过程,同样的他们也会在后台进程周期执行过程中启动


Wrapper
Wrapper的默认实现类StandardWrapper没有重写initInternal方法,它的stratInternal方法代码如下:

//org.apache.catalina.core.StandardWrapper
@Override
protected synchronized void startInternal() throws LifecycleException {
    // broadcaster发送通知,省略.....

    // 调用服了startInternal方法
    super.startInternal();

    //设置servlet有效
    setAvailable(0L);
}

Wrapper没有XXXConfig样式的LifecycleListener监听器。


从Engine—Host—Contex—-Wrapper这个链路上的容器初始化已经完成。

相关标签: tomcat container