Tomcat-Container源码分析

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。是时候放出一张图了。
这里写图片描述


Container组成

Container容器的结构图
下图是一个非常常见的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实现类。
这里写图片描述


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的子容器。
这里写图片描述
这些都是在上文createStartDigester()方法中创建实例化的。

init()执行轨迹如下图:
这里写图片描述

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()执行轨迹如下图:
这里写图片描述

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,如下图:
这里写图片描述

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的关系
这里写图片描述

  • 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这个链路上的容器初始化已经完成。

猜你喜欢

转载自blog.csdn.net/it_freshman/article/details/81609998
今日推荐