Tomcat源码解析:启动

文章目录

  • 1.Catalina
    • 开启start
  • 2.Server启动
  • 3.Service启动
    • 3.1 Engine启动
      • 3.2 Host
        • 3.2.1 Host的初始化
        • 3.2.2 Host的启动
        • 3.2.3Pipeline启动
        • 3.2.4 HostConfig
      • 3.3 Context
        • 3.3.1 Context的启动
        • 3.3.2 ContextConfig
          • 3.3.2.1 Wrapper的启动
          • 3.3.2.2 加载静态文件
          • 3.3.2.3 设置ServletContainerInitializer
        • 3.3.3 调用ServletContainerInitializer
        • 3.3.4 启动listener
        • 3.3.5 初始化filter
        • 3.3.6 加载Wrapper
      • 3.4MapperListener启动
      • 3.5 Connector启动
        • 3.5.1 Endpoint启动

启动阶段也开始于Bootstrap的main方法里的start,我们都知道,初始化时只是到容器Engine,所以start时主要对Engine的子容器以下这一部分进行操作。

而start方法反射调用了Catalina的start方法。

daemon.start();

public void start() throws Exception {
    if (catalinaDaemon == null) {
        init();
    }

    Method method = catalinaDaemon.getClass().getMethod("start", (Class [])null);
    method.invoke(catalinaDaemon, (Object [])null);
}

1.Catalina

开启start

public void start() {

    if (getServer() == null) {
        load();
    }
	//...
    long t1 = System.nanoTime();

    // Start the new server
    try {
        //执行start
        getServer().start();
    } catch (LifecycleException e) {
        //...
    }

    // Register shutdown hook
    if (useShutdownHook) {
        if (shutdownHook == null) {
            shutdownHook = new CatalinaShutdownHook();
        }
        Runtime.getRuntime().addShutdownHook(shutdownHook);
    }

    if (await) {
        await();
        stop();
    }
}
  1. 调用server的start,开始进行启动。
  2. 注册jvm关闭钩子,以便优雅停机。
  3. 开启shutdown端口的监听并阻塞,用于监听关闭指令。

这里的start同样会调用LifeCycleBase的start方法,start较init来说多了一种状态STARTING,对于正常流程来说,先由LifecycleBase改变当前状态值为STARTING_PREP,并发出事件通知,即执行当前事件的监听器,然后执行各个节点的start方法,在启动过程中,将状态值设置为STARTING,并执行事件监听器,启动结束后,将状态设置为初始化完成STARTED,并发出事件通知。

LifecycleBase#start

public final synchronized void start() throws LifecycleException {

    //...

    try {
        setStateInternal(LifecycleState.STARTING_PREP, null, false);
        startInternal();
        if (state.equals(LifecycleState.FAILED)) {
            // This is a 'controlled' failure. The component put itself into the
            // FAILED state so call stop() to complete the clean-up.
            stop();
        } else if (!state.equals(LifecycleState.STARTING)) {
            // Shouldn't be necessary but acts as a check that sub-classes are
            // doing what they are supposed to.
            invalidTransition(Lifecycle.AFTER_START_EVENT);
        } else {
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    } catch (Throwable t) {
        handleSubClassException(t, "lifecycleBase.startFail", toString());
    }
}

2.Server启动

StadardServer#startInternal

protected void startInternal() throws LifecycleException {

    fireLifecycleEvent(CONFIGURE_START_EVENT, null);
    setState(LifecycleState.STARTING);

    globalNamingResources.start();

    // Start our defined Services //启动services组件
    synchronized (servicesLock) {
        for (int i = 0; i < services.length; i++) {
            services[i].start();
        }
    }
	//执行生命周期事件
    if (periodicEventDelay > 0) {
        monitorFuture = getUtilityExecutor().scheduleWithFixedDelay(
            new Runnable() {
                @Override
                public void run() {
                    startPeriodicLifecycleEvent();
                }
            }, 0, 60, TimeUnit.SECONDS);
    }
}

这里的Listener主要执行了NamingContextListener,做了一些JNDI资源的初始化。然后就是执行service容器的start方法。

3.Service启动

StadardService#startInternal

protected void startInternal() throws LifecycleException {

    if(log.isInfoEnabled())
        log.info(sm.getString("standardService.start.name", this.name));
    setState(LifecycleState.STARTING);

     //engine启动
    if (engine != null) {
        synchronized (engine) {
            engine.start();
        }
    }
    // 启动Executor线程池
    synchronized (executors) {
        for (Executor executor: executors) {
            executor.start();
        }
    }
    // 启动MapperListener
    mapperListener.start();
    // 启动Connector
    synchronized (connectorsLock) {
        for (Connector connector: connectors) {
            // If it has already failed, don't try and start it
            if (connector.getState() != LifecycleState.FAILED) {
                connector.start();
            }
        }
    }
}
  1. 启动engine
  2. 启动线程池
  3. 启动mapperListener
  4. 启动Connector

3.1 Engine启动

engine的启动和初始化一样,会调用父类ContainerBasestartInternal方法,主要分为3个步骤

  1. 使用线程池启动子容器。
  2. 启动Pipeline,并将状态设置为STARTING,发出事件通知
  3. 如果backgroundProcessorDelay参数 >= 0,则开启ContainerBackgroundProcessor线程,用于调用子容器的backgroundProcess

ContainerBase#startInternal

protected synchronized void startInternal() throws LifecycleException {

    // ...

    // 把子容器的启动放在线程池中处理
    Container[] children = findChildren();
    List<Future<Void>> results = new ArrayList<>();
    for (int i = 0; i < children.length; i++) {
        results.add(startStopExecutor.submit(new StartChild(children[i])));
    }

    MultiThrowable multiThrowable = null;
    // 阻塞当前线程,直到子容器start完成
    for (Future<Void> result : results) {
        try {
            result.get();
        } catch (Throwable e) {
            log.error(sm.getString("containerBase.threadedStartFailed"), e);
            if (multiThrowable == null) {
                multiThrowable = new MultiThrowable();
            }
            multiThrowable.add(e);
        }

    }
    //...
    // 启用Pipeline
    if (pipeline instanceof Lifecycle) {
        ((Lifecycle) pipeline).start();
    }

    setState(LifecycleState.STARTING);

     // 开启ContainerBackgroundProcessor线程用于调用子容器的backgroundProcess方法,默认情况下backgroundProcessorDelay=-1。
    if (backgroundProcessorDelay > 0) {
        monitorFuture = Container.getService(ContainerBase.this).getServer()
            .getUtilityExecutor().scheduleWithFixedDelay(
            new ContainerBackgroundProcessorMonitor(), 0, 60, TimeUnit.SECONDS);
    }
}

启动子容器

startStopExecutor是在init阶段被创建的,默认情况下会创建线程池InlineExecutorService,这个线程池的coreSize=maxSize=1。而这个可以在server.xml里通过startStopThreads来配置,这里需要注意的是,startStopExecutor的创建是在ContainerBase的初始化方法调用时,意味着每个继承了ContainerBase的容器在启动子容器时都维护了一个自己的线程池,并可以分别修改线程池属性。继承了ContainerBase的如下:

Tomcat源码解析:启动_第1张图片

ContainerBase会把StartChild任务丢给线程池处理,得到Future,并且会遍历所有的Future进行阻塞result.get(),这个操作是将异步启动转同步,子容器启动完成才会继续运行。然后再来看看submit到线程池的StartChild任务,它实现了Callable接口,在call里面完成子容器的start动作,而start方法依然是调用LifecycleBase的start方法。

private static class StartChild implements Callable<Void> {

    private Container child;

    public StartChild(Container child) {
        this.child = child;
    }

    @Override
    public Void call() throws LifecycleException {
        child.start();
        return null;
    }
}

3.2 Host

由于Host在init阶段没有进行初始化,所以在调用start方法时,状态为NEW,需要先进行初始化。

3.2.1 Host的初始化

由于默认实现StandardHost内部没有实现initInternal,所以这里的初始化执行了ContainerBaseinitInternal方法,详见ContainerBase的initInternal

3.2.2 Host的启动

StandardHost#startInternal

 private String errorReportValveClass ="org.apache.catalina.valves.ErrorReportValve";

protected synchronized void startInternal() throws LifecycleException {

    // Set error report valve  // errorValve默认使用org.apache.catalina.valves.ErrorReportValve
    String errorValve = getErrorReportValveClass();
    if ((errorValve != null) && (!errorValve.equals(""))) {
        try {
            boolean found = false;
          
            Valve[] valves = getPipeline().getValves();
            for (Valve valve : valves) {
                if (errorValve.equals(valve.getClass().getName())) {
                    found = true;
                    break;
                }
            }
            if(!found) {
                // 将Valve添加到 Pipeline 中,注意是添加到 basic valve 的前面
                Valve valve =
                    (Valve) Class.forName(errorValve).getConstructor().newInstance();
                getPipeline().addValve(valve);
            }
        } catch (Throwable t) {
           //...
        }
    }
    // 调用父类 ContainerBase
    super.startInternal();
}

在Pipeline里寻找是否存在ErrorReportValve,如果不存在则实例化后添加到Pipeline。那么Pipline和Valve是什么呢

Pipeline是管道组件,用于封装了一组有序的Valve,便于Valve顺序地传递或者处理请求

Valve可以理解为请求拦截器。

Pipeline也是一个Lifecycle组件, 默认实现是StandardPipeline,这段被定义在ContainerBase中,也就意味着同上面的startStopExecutor一样,每个容器都维护了一个自己的拦截器链。

protected final Pipeline pipeline = new StandardPipeline(this);

而Valve的接口如下:

public interface Valve {
    public Valve getNext();
    public void setNext(Valve valve);
    public void backgroundProcess();
    public void invoke(Request request, Response response) throws IOException, ServletException;
    public boolean isAsyncSupported();
}

而对于Host来说,Valve在server.xml`配置了一个日志拦截器。

<Host name="localhost"  appBase="webapps"
      unpackWARs="true" autoDeploy="true" startStopThreads="4" >
    <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
           prefix="localhost_access_log" suffix=".txt"
           pattern="%h %l %u %t "%r" %s %b" />
Host>

另一个默认的Valve来自于StandardHost的构造方法。这里每个实现了ContainerBase的容器都会在构造方法里创建一个默认的Valve。

public StandardHost() {

    super();
    pipeline.setBasic(new StandardHostValve());

}

所以StandardHost Pipeline 包含的 Valve 组件:

  1. basic:org.apache.catalina.core.StandardHostValve
  2. first:org.apache.catalina.valves.AccessLogValve

而在添加ErrorReportValve时,会将ErrorReportValve添加到basic之前,first之后。

StandardPiepline#addValve

public void addValve(Valve valve) {

    // Validate that we can add this Valve
    if (valve instanceof Contained)
        ((Contained) valve).setContainer(this.container);
   //...

    // Add this Valve to the set associated with this Pipeline
    if (first == null) {
        first = valve;
        valve.setNext(basic);
    } else {
        Valve current = first;
        while (current != null) {
            if (current.getNext() == basic) {
                //设置到basic之前
                current.setNext(valve);
                valve.setNext(basic);
                break;
            }
            current = current.getNext();
        }
    }

    container.fireContainerEvent(Container.ADD_VALVE_EVENT, valve);
}

3.2.3Pipeline启动

ContainerBase#startInternal方法中,执行完子容器的启动后会启动pipeline。这里也会先执行pipeline的初始化在执行启动,不过初始化时除了状态的变更和事件通知之外,什么也没做,而在启动阶段,会遍历所有的Valve,如果当前Valve是 Lifecycle 的子类,则会调用其 start 方法启动 Valve 组件。

StandardPipeline#startInternal

protected synchronized void startInternal() throws LifecycleException {

    // Start the Valves in our pipeline (including the basic), if any
    Valve current = first;
    if (current == null) {
        current = basic;
    }
    //遍历 Valve 链表,如果 Valve 是 Lifecycle 的子类,则会调用其 start 方法启动 Valve 组件
    while (current != null) {
        if (current instanceof Lifecycle)
            ((Lifecycle) current).start();
        current = current.getNext();
    }

    setState(LifecycleState.STARTING);
}

StandardHost的启动到这里就结束了,从代码可以看出,并没有做过多的事情。而对于Host的真正操作是在监听器HostConfig里,即每次LifeCycle状态变更时发送的事件通知。HostConfig是在解析xml阶段配置的,主要是找到webapp目录,并解压war包。

3.2.4 HostConfig

HostConfig主要负责处理start和stop事件

HostConfig#lifecycleEvent

public void lifecycleEvent(LifecycleEvent event) {

    //判断事件是否由 Host 发出,并且为 HostConfig 设置属性
    //..

    // Process the event that has occurred
    if (event.getType().equals(Lifecycle.PERIODIC_EVENT)) {
        check();
    } else if (event.getType().equals(Lifecycle.BEFORE_START_EVENT)) {
        beforeStart();
    } else if (event.getType().equals(Lifecycle.START_EVENT)) {
        start();
    } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
        stop();
    }
}

public void start() {

    //...
    if (host.getDeployOnStartup())
        deployApps();
}
protected void deployApps() {

    File appBase = host.getAppBaseFile();
    File configBase = host.getConfigBaseFile();
    // 过滤掉hostConfig.ignorePath
    String[] filteredAppPaths = filterAppPaths(appBase.list());
    // 部署 xml 描述文件
    deployDescriptors(configBase, configBase.list());
    // 解压 war 包
    deployWARs(appBase, filteredAppPaths);
    // 处理扩展的文件
    deployDirectories(appBase, filteredAppPaths);

}

解压war包

protected void deployWARs(File appBase, String[] files) {

    //...
    for (int i = 0; i < files.length; i++) {
        File war = new File(appBase, files[i]);
        if (files[i].toLowerCase(Locale.ENGLISH).endsWith(".war") &&
            war.isFile() && !invalidWars.contains(files[i]) ) {
            //...
            results.add(es.submit(new DeployWar(this, cn, war)));
        }
    }

}

private static class DeployWar implements Runnable {

    private HostConfig config;
    private ContextName cn;
    private File war;

    @Override
    public void run() {
        config.deployWAR(cn, war);
    }
}

//部署Web应用程序时,默认情况下是否应将XML文件复制到 $CATALINA_BASE / conf /  / 
protected boolean copyXML = false;

protected String contextClass = "org.apache.catalina.core.StandardContext";

protected void deployWAR(ContextName cn, File war) {
    //...
    Context context = null;
    //实例化context
    if (deployThisXML && useXml && !copyXML) {
        //从扩展目录的xml解析并实例化 这个是有限制条件的
        context = (Context) digester.parse(xml);
    }else if (deployThisXML && xmlInWar) {
       	//从META-INF/context.xml目录下的jar包里解析并实例化
         context = (Context) digester.parse(istream);
    }else if (!deployThisXML && xmlInWar) {
        // ...
    } else {
        //一般情况下的实例化
        context = (Context) Class.forName(contextClass).getConstructor().newInstance();
    }
    //...
    //实例化ContextConfig,并将其作为监听器add到StandardContext中
     Class<?> clazz = Class.forName(host.getConfigClass());
    LifecycleListener listener = (LifecycleListener) clazz.getConstructor().newInstance();
    context.addLifecycleListener(listener);

    context.setName(cn.getName());
    context.setPath(cn.getPath());
    context.setWebappVersion(cn.getVersion());
    context.setDocBase(cn.getBaseName() + ".war");
    //添加并启动子容器
    host.addChild(context);
}

3.3 Context

Context的启动和Host类似。init阶段就不再赘述了。

3.3.1 Context的启动

StandardContext#startInternal

protected synchronized void startInternal() throws LifecycleException {
    //由于这个方法比较长,这里节选重点部分
    
    //调用ContextConfig 从web.xml 或者 Servlet3.0 的注解配置,读取 Servlet 相关的配置信息,比如 Filter、Servlet、Listener 等
    fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);

    // Call ServletContainerInitializers 执行ServletContainerInitializer的SPI实现类
    for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :initializers.entrySet()) {
        entry.getKey().onStartup(entry.getValue(),getServletContext());
    }

    //  初始化filter
    if (ok) {
        if (!filterStart()) {
            log.error(sm.getString("standardContext.filterFail"));
            ok = false;
        }
    }

    // StandardWrapper 实例化并且启动 Servlet,由于 Servlet 存在 loadOnStartup 属性
    // 因此使用了 TreeMap,根据 loadOnStartup 值 对 Wrapper 容器进行排序,然后依次启动 Servlet
    // Load and initialize all "load on startup" servlets
    if (ok) {
        if (!loadOnStartup(findChildren())){
            log.error(sm.getString("standardContext.servletFail"));
            ok = false;
        }
    }
}

由于这段代码比较复杂,这里说一下大概的内容。

  1. 实例化 WebResourceRoot,默认实现类是 StandardRoot,用于读取 webapp 的文件资源
  2. 实例化loader对象,这里默认是WebappLoader,并执行loader的start方法,将所有class缓存起来。
  3. 发布CONFIGURE_START_EVENT事件,该事件主要由ContextConfig处理。
  4. 实例化 Sesssion 管理器,默认使用 StandardManager。
  5. 初始化filter
  6. 实例化并启动servlet

针对几个重点的部分作详细研究。

3.3.2 ContextConfig

对于该事件的处理主要在ContextConfig#webConfig

  1. 首先解析web.xml
WebXml webXml = createWebXml();

// Parse context level web.xml
InputSource contextWebXml = getContextWebXmlSource();
if (!webXmlParser.parseWebXml(contextWebXml, webXml, false)) {
    ok = false;
}

  1. 解析web-fragment.xml,tomcat提供的jar包会忽略此xml文件
Map<String,WebXml> fragments = processJarsForWebFragments(webXml, webXmlParser);

Set<WebXml> orderedFragments = null;
orderedFragments =WebXml.orderWebFragments(webXml, fragments, sContext);
  1. 处理 javax.servlet.ServletContainerInitializer实现类
processServletContainerInitializers();

protected void processServletContainerInitializers() {

    List<ServletContainerInitializer> detectedScis;
    try {
        WebappServiceLoader<ServletContainerInitializer> loader = new WebappServiceLoader<>(context);
        detectedScis = loader.load(ServletContainerInitializer.class);
    } catch (IOException e) {
        //...
    }

    for (ServletContainerInitializer sci : detectedScis) {
        initializerClassMap.put(sci, new HashSet<Class<?>>());

        HandlesTypes ht;
        try {
            ht = sci.getClass().getAnnotation(HandlesTypes.class);
        } catch (Exception e) {
            //...
            continue;
        }
        if (ht == null) {
            continue;
        }
        Class<?>[] types = ht.value();
        if (types == null) {
            continue;
        }

        for (Class<?> type : types) {
            if (type.isAnnotation()) {
                handlesTypesAnnotations = true;
            } else {
                handlesTypesNonAnnotations = true;
            }
            Set<ServletContainerInitializer> scis =
                typeInitializerMap.get(type);
            if (scis == null) {
                scis = new HashSet<>();
                typeInitializerMap.put(type, scis);
            }
            scis.add(sci);
        }
    }
}

获取ServletContainerInitializer的实现类,并将其保存在ContextConfig的map中,ServletContainerInitializer是servlet的spi机制,可以通过 HandlesTypes 筛选出相关的 servlet 类,并对 ServletContext 进行额外处理。自定义的ServletContainerInitializer的实现类,需要将类名的项目路径配置在META-INF/services/javax.servlet.ServletContainerInitializer文件中。以下是一个例子。

@HandlesTypes( Test.class )
public class CustomServletContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> c, ServletContext ctx) throws ServletException {
        for ( Class<?> type : c ) {
            System.out.println( type.getName() );
        }
    }
}

javax.servlet.ServletContainerInitializer

com.test.CustomServletContainerInitializer
  1. 如果没有配置web.xml,则会先扫描 WEB-INF/classes 目录下面的 class 文件,然后扫描 WEB-INF/lib 目录下面的 jar 包,解析字节码读取 servlet 相关的注解配置类,将解析完的信息设置到WebXml对象中。重点代码如下:

ContextConfig#processAnnotationsStream

protected void processAnnotationsStream(InputStream is, WebXml fragment,
                                        boolean handlesTypesOnly, Map<String,JavaClassCacheEntry> javaClassCache)
    throws ClassFormatException, IOException {
    //对字节码文件进行解析。
    ClassParser parser = new ClassParser(is);
    JavaClass clazz = parser.parse();
    //处理注解@HandlesTypes
    checkHandlesTypes(clazz, javaClassCache);

    if (handlesTypesOnly) {
        return;
    }
	//获取其注解,并把 WebServlet、WebFilter、WebListener 注解的类添加到 WebXml 实例中
    processClass(fragment, clazz);
}

//解析servlet3.0注解
protected void processClass(WebXml fragment, JavaClass clazz) {
    AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
    if (annotationsEntries != null) {
        String className = clazz.getClassName();
        for (AnnotationEntry ae : annotationsEntries) {
            String type = ae.getAnnotationType();
            if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) {
                processAnnotationWebServlet(className, ae, fragment);
            }else if ("Ljavax/servlet/annotation/WebFilter;".equals(type)) {
                processAnnotationWebFilter(className, ae, fragment);
            }else if ("Ljavax/servlet/annotation/WebListener;".equals(type)) {
                fragment.addListener(className);
            } else {
                // Unknown annotation - ignore
            }
        }
    }
}
  1. 将解析出的filter、servlet、session、cookie等属性加载到context中
private void configureContext(WebXml webxml) {
    //省略部分代码...
    // 设置 Filter
    for (FilterDef filter : webxml.getFilters().values()) {
        if (filter.getAsyncSupported() == null) {
            filter.setAsyncSupported("false");
        }
        context.addFilterDef(filter);
    }

    // 设置 FilterMapping,即 Filter 的 URL 映射
    for (FilterMap filterMap : webxml.getFilterMappings()) {
        context.addFilterMap(filterMap);
    }
    // 往 Context 中添加子容器 Wrapper,即 Servlet
    for (ServletDef servlet : webxml.getServlets().values()) {
        Wrapper wrapper = context.createWrapper();
        wrapper.setName(servlet.getServletName());
        Map<String,String> params = servlet.getParameterMap();
        for (Entry<String, String> entry : params.entrySet()) {
            wrapper.addInitParameter(entry.getKey(), entry.getValue());
        }
        wrapper.setServletClass(servlet.getServletClass());
        wrapper.setOverridable(servlet.isOverridable());
        context.addChild(wrapper);
    }
    //还有很多属性被加载,这里就不一一赘述了
}

context.addChild(wrapper)时,会调用StandardContext的addChild,然后会调用ContainerBase的addChild,最后进行wrapper的启动。Wrapper的初始化和Context没什么区别。

3.3.2.1 Wrapper的启动

StandardWrapper没有子容器,所以启动时主要完成了jmx事件通知。

StandardWrapper#startInternal

protected synchronized void startInternal() throws LifecycleException {

    // 发出 j2ee.state.starting 事件通知
    if (this.getObjectName() != null) {
        Notification notification = new Notification("j2ee.state.starting",
                                                     this.getObjectName(),
                                                     sequenceNumber++);
        broadcaster.sendNotification(notification);
    }

    // 调用ContainerBase的startInternal,见Engine启动的ContainerBase#startInternal
    super.startInternal();

    setAvailable(0L);

    //running 事件通知
    if (this.getObjectName() != null) {
        Notification notification =
            new Notification("j2ee.state.running", this.getObjectName(),
                             sequenceNumber++);
        broadcaster.sendNotification(notification);
    }

}
3.3.2.2 加载静态文件

ContextConfig#webConfig

加载所有jar包下 META-INF/resources/的静态资源文件。

processResourceJARs(resourceJars);

protected void processResourceJARs(Set<WebXml> fragments) {
    for (WebXml fragment : fragments) {
        URL url = fragment.getURL();
        try {
            if ("jar".equals(url.getProtocol()) || url.toString().endsWith(".jar")) {
                try (Jar jar = JarFactory.newInstance(url)) {
                    jar.nextEntry();
                    String entryName = jar.getEntryName();
                    while (entryName != null) {
                        if (entryName.startsWith("META-INF/resources/")) {
                            context.getResources().createWebResourceSet(
                                WebResourceRoot.ResourceSetType.RESOURCE_JAR,
                                "/", url, "/META-INF/resources");
                            break;
                        }
                        jar.nextEntry();
                        entryName = jar.getEntryName();
                    }
                }
            } else if ("file".equals(url.getProtocol())) {
                File file = new File(url.toURI());
                File resources = new File(file, "META-INF/resources/");
                if (resources.isDirectory()) {
                    context.getResources().createWebResourceSet(
                        WebResourceRoot.ResourceSetType.RESOURCE_JAR,
                        "/", resources.getAbsolutePath(), null, "/");
                }
            }
        } catch (IOException ioe) {
            //...
        } 
    }
}
3.3.2.3 设置ServletContainerInitializer

最后将所有ServletContainerInitializer实现类设置到context中。

if (ok) {
    for (Map.Entry<ServletContainerInitializer,
         Set<Class<?>>> entry :
         initializerClassMap.entrySet()) {
        if (entry.getValue().isEmpty()) {
            context.addServletContainerInitializer(
                entry.getKey(), null);
        } else {
            context.addServletContainerInitializer(
                entry.getKey(), entry.getValue());
        }
    }
}

3.3.3 调用ServletContainerInitializer

在ContextConfig处理完start事件后,会先调用ServletContainerInitializer#onStartup方法。

StandardContext#startInternal

//调用 ServletContainerInitializer#onStartup()
for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
     initializers.entrySet()) {
    try {
        entry.getKey().onStartup(entry.getValue(),
                                 getServletContext());
    } catch (ServletException e) {
        log.error(sm.getString("standardContext.sciFail"), e);
        ok = false;
        break;
    }
}

3.3.4 启动listener

public boolean listenerStart() {

    // 实例化所有listener
    String[] listeners = findApplicationListeners();
    Object[] results = new Object[listeners.length];
    boolean ok = true;
    for (int i = 0; i < results.length; i++) {
        try {
            String listener = listeners[i];
            results[i] = getInstanceManager().newInstance(listener);
        } catch (Throwable t) {
           //...
            ok = false;
        }
    }

    // 将listener按照类型分为2个list存储
    List<Object> eventListeners = new ArrayList<>();
    List<Object> lifecycleListeners = new ArrayList<>();
    for (int i = 0; i < results.length; i++) {
        if ((results[i] instanceof ServletContextAttributeListener)
            || (results[i] instanceof ServletRequestAttributeListener)
            || (results[i] instanceof ServletRequestListener)
            || (results[i] instanceof HttpSessionIdListener)
            || (results[i] instanceof HttpSessionAttributeListener)) {
            eventListeners.add(results[i]);
        }
        if ((results[i] instanceof ServletContextListener)
            || (results[i] instanceof HttpSessionListener)) {
            lifecycleListeners.add(results[i]);
        }
    }
	//...
    //调用ServletContextListener的contextInitialized方法
    for (int i = 0; i < instances.length; i++) {
        if (!(instances[i] instanceof ServletContextListener))
            continue;
        ServletContextListener listener =
            (ServletContextListener) instances[i];
        try {
            fireContainerEvent("beforeContextInitialized", listener);
            if (noPluggabilityListeners.contains(listener)) {
                listener.contextInitialized(tldEvent);
            } else {
                listener.contextInitialized(event);
            }
            fireContainerEvent("afterContextInitialized", listener);
        } catch (Throwable t) {
            //...
            ok = false;
        }
    }
    return ok;

}
  1. 实例化所有的listener。
  2. 根据类型将listener分为eventListenerslifecycleListeners
  3. 找出lifecycleListeners中的ServletContextListener,执行它的contextInitialized方法。

3.3.5 初始化filter

public boolean filterStart() {

    // Instantiate and record a FilterConfig for each defined filter
    boolean ok = true;
    synchronized (filterConfigs) {
        filterConfigs.clear();
        for (Entry<String,FilterDef> entry : filterDefs.entrySet()) {
            String name = entry.getKey();

            try {
                ApplicationFilterConfig filterConfig =
                    new ApplicationFilterConfig(this, entry.getValue());
                filterConfigs.put(name, filterConfig);
            } catch (Throwable t) {
                ok = false;
            }
        }
    }

    return ok;
}



ApplicationFilterConfig(Context context, FilterDef filterDef)
    throws ClassCastException, ReflectiveOperationException, ServletException,
NamingException, IllegalArgumentException, SecurityException {

    super();

    this.context = context;
    this.filterDef = filterDef;
    // Allocate a new filter instance if necessary
    if (filterDef.getFilter() == null) {
        getFilter();
    } else {
        this.filter = filterDef.getFilter();
        context.getInstanceManager().newInstance(filter);
        initFilter();
    }
}

将filterDefs转换成ApplicationFilterConfig,filterDefs是ContextConfig在解析servlet注解时用来保存filter信息的对象,并在ApplicationFilterConfig的构造方法中完成调用filter.init()。举个例子。这里会调用HelloFilter的init方法。

@WebFilter(
    urlPatterns = {"/*"}
)
public class HelloFilter implements Filter {
    public HelloFilter() {
    }

    public void init(FilterConfig filterConfig) throws ServletException {
        System.out.println("filter is init");
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("filter is running");
        chain.doFilter(request, response);
    }

    public void destroy() {
        System.out.println("filter is destroy");
    }
}

3.3.6 加载Wrapper

public boolean loadOnStartup(Container[] children) {

    TreeMap<Integer, ArrayList<Wrapper>> map = new TreeMap<>();
    for (int i = 0; i < children.length; i++) {
        Wrapper wrapper = (Wrapper) children[i];
        int loadOnStartup = wrapper.getLoadOnStartup();
        if (loadOnStartup < 0)
            continue;
        Integer key = Integer.valueOf(loadOnStartup);
        ArrayList<Wrapper> list = map.get(key);
        if (list == null) {
            list = new ArrayList<>();
            map.put(key, list);
        }
        list.add(wrapper);
    }

    // Load the collected "load on startup" servlets
    for (ArrayList<Wrapper> list : map.values()) {
        for (Wrapper wrapper : list) {
            try {
                wrapper.load();
            } catch (ServletException e) {
                //...
            }
        }
    }
    return true;

}
  1. 首先根据loadOnStartup大小将wrapper进行排序,loadOnStartup值相同的放在同一个list。
  2. loadOnStartup>=0的会在启动阶段被加载,而如果loadOnStartup为默认值-1的话,是在首次调用时加载,这里的load方法调用的是StandardWrapper#load()
public synchronized void load() throws ServletException {
    // 实例化 Servlet,并且调用 init 方法完成初始化
    instance = loadServlet();
}

public synchronized Servlet loadServlet() throws ServletException {
    InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();

    servlet = (Servlet) instanceManager.newInstance(servletClass);

    //调用servlet的init
    initServlet(servlet);

    fireContainerEvent("load", this);
}

到这里Engine的启动就结束了。

3.4MapperListener启动

public void startInternal() throws LifecycleException {

    setState(LifecycleState.STARTING);

    Engine engine = service.getContainer();
    if (engine == null) {
        return;
    }

    findDefaultHost();

    addListeners(engine);
    
    Container[] conHosts = engine.findChildren();
    for (Container conHost : conHosts) {
        Host host = (Host) conHost;
        if (!LifecycleState.NEW.equals(host.getState())) {
            // Registering the host will register the context and wrappers
            registerHost(host);
        }
    }
}
  1. 设置当前defaultName,此测试用例为localhost
  2. MapperListener作为ContainerListenerLifecycleListener递归设置到当前容器及其所有子容器
  3. 将Engine启动时解析出来的Wrapper放到MapperListener中,以备请求时进行匹配。
private void registerHost(Host host) {

    String[] aliases = host.findAliases();
    //设置host
    mapper.addHost(host.getName(), aliases, host);

    for (Container container : host.findChildren()) {
        if (container.getState().isAvailable()) {
            registerContext((Context) container);
        }
    }

    // Default host may have changed
    findDefaultHost();

}

private void registerContext(Context context) {

    String contextPath = context.getPath();
    if ("/".equals(contextPath)) {
        contextPath = "";
    }
    Host host = (Host)context.getParent();

    WebResourceRoot resources = context.getResources();
    String[] welcomeFiles = context.findWelcomeFiles();
    List<WrapperMappingInfo> wrappers = new ArrayList<>();

    for (Container container : context.findChildren()) {
        //将wrapper封装成WrapperMappingInfo
        prepareWrapperMappingInfo(context, (Wrapper) container, wrappers);
    }
    //将所有wrapper设置到Mapper中
    mapper.addContextVersion(host.getName(), host, contextPath,
                             context.getWebappVersion(), context, welcomeFiles, resources,
                             wrappers);


}

private final Map<Context, ContextVersion> contextObjectToContextVersionMap =
    new ConcurrentHashMap<>();

public void addContextVersion(String hostName, Host host, String path,
                              String version, Context context, String[] welcomeResources,
                              WebResourceRoot resources, Collection<WrapperMappingInfo> wrappers) {
    //省略了部分代码
    contextObjectToContextVersionMap.put(context, newContextVersion);
}

首先将Host设置到Mapper,然后解析子容器Context,将解析出的Wrapper封装成WrapperMappingInfo设置到MapperListener的Mapper中。这里放一张截图方便理解。其中/tomcat-test是测试用例servlet,其他的是Tomcat自带。

Tomcat源码解析:启动_第2张图片

3.5 Connector启动

StandardConnector#startInternal

protected void startInternal() throws LifecycleException {

    //校验端口
    if (getPortWithOffset() < 0) {
        throw new LifecycleException(sm.getString(
            "coyoteConnector.invalidPort", Integer.valueOf(getPortWithOffset())));
    }

    setState(LifecycleState.STARTING);

    try {
        //
        protocolHandler.start();
    } catch (Exception e) {
        //...
    }
}

根据Connector的init阶段,这里的protocolHandler的实现是Http11NioProtocol,而Http11NioProtocol的start方法会调用父类AbstractProtocol#start(),这个方法里调用了endpoint的start。

AbstractProtocol#start()

public void start() throws Exception {
  
    endpoint.start();
    
}

3.5.1 Endpoint启动

Connector启动的重点是在Endpoint的启动,这里会启动两个线程组,Pollers

NioEndpoint#start()

public void startInternal() throws Exception {

    if (!running) {
        running = true;
        paused = false;
		//SocketProcessor对象的缓存
        if (socketProperties.getProcessorCache() != 0) {
            processorCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                                                     socketProperties.getProcessorCache());
        }
        //缓存poller事件
        if (socketProperties.getEventCache() != 0) {
            eventCache = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                                                 socketProperties.getEventCache());
        }
        //字节缓冲区高速缓存
        if (socketProperties.getBufferPool() != 0) {
            nioChannels = new SynchronizedStack<>(SynchronizedStack.DEFAULT_SIZE,
                                                  socketProperties.getBufferPool());
        }

        // Create worker collection
        if (getExecutor() == null) {
            createExecutor();
        }
		//初始化连接数计数器,默认是1000 
        //private int maxConnections = 10000;
        initializeConnectionLatch();

        // Start poller threads
        pollers = new Poller[getPollerThreadCount()];
        for (int i = 0; i < pollers.length; i++) {
            pollers[i] = new Poller();
            Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-" + i);
            pollerThread.setPriority(threadPriority);
            pollerThread.setDaemon(true);
            pollerThread.start();
        }

        startAcceptorThreads();
    }
}

除了配置一些缓存之外,这里创建了三种不同类型的Thread。Poller线程、Acceptor线程以及一个线程池。

  1. create线程池,

AbstractEndpoint#createExecutor

private int minSpareThreads = 10;

private int maxThreads = 200;

public void createExecutor() {
    internalExecutor = true;
    TaskQueue taskqueue = new TaskQueue();
    TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());
    executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
    taskqueue.setParent( (ThreadPoolExecutor) executor);
}

这里默认的coresize=10,maxsize=200,keepalive=60s,这个线程池用来处理servlet请求。

  1. start Poller Thread,该线程用于监听Socket事件,当Socket可读或者可写时,调用上面的线程池处理Socket请求。
 private int pollerThreadCount = 1;

pollers = new Poller[getPollerThreadCount()];
for (int i = 0; i < pollers.length; i++) {
    pollers[i] = new Poller();
    Thread pollerThread = new Thread(pollers[i], getName() + "-ClientPoller-" + i);
    pollerThread.setPriority(threadPriority);
    pollerThread.setDaemon(true);
    pollerThread.start();
}
  1. start Acceptor Thread,用于接收新连接,并将新连接添加到Poller事件队列中。
 protected int acceptorThreadCount = 1;

protected void startAcceptorThreads() {
    int count = getAcceptorThreadCount();
    acceptors = new ArrayList<>(count);

    for (int i = 0; i < count; i++) {
        Acceptor<U> acceptor = new Acceptor<>(this);
        String threadName = getName() + "-Acceptor-" + i;
        acceptor.setThreadName(threadName);
        acceptors.add(acceptor);
        Thread t = new Thread(acceptor, threadName);
        t.setPriority(getAcceptorThreadPriority());
        t.setDaemon(getDaemon());
        t.start();
    }
}

放一张Endpoint启动完之后主要的线程状态图。

Tomcat源码解析:启动_第3张图片

从图中可以看出Acceptor ThreadClientPoller Thread都处于running状态,而线程池则是wait状态,这里当ClientPoller Thread接收到socket请求时,会启用线程池处理servlet请求。

到这里Tomcat就成功启动完成了。

参考:

https://blog.csdn.net/Dwade_mia/article/details/79244157
https://blog.csdn.net/Dwade_mia/article/details/79328151

你可能感兴趣的:(Tomcat)