面试官:来说说Tomcat的启动过程是什么样子的(上)

简介: 阿粉最近在疯狂的研究各种用的工具里面的源码实现,之前给大家都专门的去扣了一下 JDK 里面自带的exe程序,这次阿粉开始更加无聊,直接开始搞Tomcat。

1.Tomcat分析

阿粉知道作为一个 Java 资深开发人员,对 Tomcat 那是再熟悉不过了,bin目录、conf目录、webapps目录,对这些目录熟悉的简直不能再熟悉了。一言不合就是一个shutdown.sh,或者来个shutdown.bat,但是你知道你的启动startup.bat,和startup.sh他们的启动过程是什么过程么?接下来我们就开始进入分析吧。

2.Tomcat的整体结构图

这个整体结构图可不是大家想的目录结构图,目录结构图阿粉就不给大家展示了,自己去打开你的 Tomcat,里面就有你想看到目录结构图,那么整体结构图是什么样子的呢?

114.jpg

给大家解释一下这个图的意思,

  • Server:整个服务器。
  • Service:具体的服务。
  • Connector:提供Socket和request,response的连接。
  • Container:用于封装和管理Servlet,以及具体的处理请求。

这个图就把里面的包含关系说的是明明白白了,为什么这么说呢?因为一个Server中可以存在多个服务,也就是多个Service,而一个Service中可以存在多个Connector,但是只能存在一个Container,是不是就非常的清晰了呢?

3.Tomcat的启动过程

接下来我们就来看看源码里面的启动过程吧,Bootstrap类中的启动过程。

这个类的位置是在tomcat的catalina的包里面,大家看一下主方法也就是所谓的main方法,

public static void main(String[] args) {
        //对象初始化
        if (daemon == null) {
            Bootstrap bootstrap = new Bootstrap();
            try {
                bootstrap.init();
            } catch (Throwable var3) {
                handleThrowable(var3);
                var3.printStackTrace();
                return;
            }
            daemon = bootstrap;
        } else {
            Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
        }
        try {
            String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }
            if (command.equals("startd")) {
                args[args.length - 1] = "start";
                //加载
                daemon.load(args);
                //启动
                daemon.start();
            } else if (command.equals("stopd")) {
                args[args.length - 1] = "stop";
                //停止
                daemon.stop();
            } else if (command.equals("start")) {
                daemon.setAwait(true);
                //加载并且启动
                daemon.load(args);
                daemon.start();
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
            } else if (command.equals("stop")) {
                daemon.stopServer(args);
            } else if (command.equals("configtest")) {
                daemon.load(args);
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
                System.exit(0);
            } else {
                log.warn("Bootstrap: command \"" + command + "\" does not exist.");
            }
        } catch (Throwable var4) {
            Throwable t = var4;
            if (var4 instanceof InvocationTargetException && var4.getCause() != null) {
                t = var4.getCause();
            }
            handleThrowable(t);
            t.printStackTrace();
            System.exit(1);
        }
    }

main方法里面的存在也很简单,先进行init的操作,然后再执行start,也就是说,启动过程中,首先要进行初始化,然后接下来再进行启动,最后阶段在来个stop,这样才算完整嘛。

  • load方法:其实说白了load方法就是根据server.xml文件创建Server并且调用Server的init方法进行初始化。
  • start方法:start方法很直白,启动服务器。
  • stop方法:stop方法同样,停止服务器。

在这里的start方法和stop方法调用的分别就是调用了Server内部的start和stop方法,而这三个方法都是按照图中的层级结构来的,先从Server的load,start,stop,然后Server的start再调用Service的start,而Service的start调用的就是Connector和Container的start方法了,这从外到内的启动,就可以把Tomcat完整的启动起来了。

我们在接下来就继续从外到内的启动开始分析一波。

3.1 Catalina启动过程

上面的启动入口我们已经成功找到了,那么我们就继续来,对象初始化完成后,执行了init的方法

Bootstrap bootstrap = new Bootstrap();
            try {
                bootstrap.init();
            } catch (Throwable var3) {

就是上面的这个,如果参数为空了,那么就开始调用start了,那么start方法是什么呢?

public void start() throws Exception {
        if (this.catalinaDaemon == null) {
            this.init();
        }
        Method method = this.catalinaDaemon.getClass().getMethod("start", (Class[])null);
        method.invoke(this.catalinaDaemon, (Object[])null);
    }

上面的start方法就是直接使用invoke的方法映射到了catalinaDaemon,也就是到了Catalina的start的方法上,

而这个Catalina的启动无非也就是调用了同样的方法,setAwait方法,load方法,start方法,

  • setAwait方法:用于设置Server启动完成时,是否进入等待,如果是true,那就等待,如果不是false,那就不进入等待。
  • load方法:创建并且初始化Server,
  • start方法:同样是启动服务器

同样的setAwait方法比较少,阿粉就不给大家看了,无非就是个判断,而load方法一定得看,

if (!this.loaded) {
            this.loaded = true;
            long t1 = System.nanoTime();
                    try {
                        inputSource.setByteStream((InputStream)inputStream);
                        digester.push(this);
                        digester.parse(inputSource);
                        break label242;
                    } catch (SAXParseException var28) {
                        log.warn("Catalina.start using " + this.getConfigFile() + ": " + var28.getMessage());
                        return;
                    } catch (Exception var29) {
                        log.warn("Catalina.start using " + this.getConfigFile() + ": ", var29);
                    }
                } finally {
                    if (inputStream != null) {
                        try {
                            ((InputStream)inputStream).close();
                        } catch (IOException var23) {
                            ;
                        }
                    }
                }
                return;
            }
 try {
    //此处同样调用的Server的init方法,
                this.getServer().init();
            } catch (LifecycleException var24) {
                if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
                    throw new Error(var24);
                }
                log.error("Catalina.start", var24);
            }
            long t2 = System.nanoTime();
            if (log.isInfoEnabled()) {
                log.info("Initialization processed in " + (t2 - t1) / 1000000L + " ms");
            }
        }

而从这里就开始进入下一步了,Server的启动过程,因为从Catalina里面已经找到了getServer的初始化方法,接下来就是要进行Server的初始化,然后加载,然后启动的过程了。

3.2 Server的启动过程

Server是Tomcat里面的接口,而不是类,那么我们就只能去找实现它的子类来于是就找到了StandardServer extends LifecycleMBeanBase implements Server。

阿粉一看有继承,还有实现,那就先看看LifecycleMBeanBase这个被继承的类,于是再次去看了它,

public abstract class LifecycleMBeanBase extends LifecycleBase implements JmxEnabled {

嗯?还有继承?继续往下扒拉,

public abstract class LifecycleBase implements Lifecycle {

终于算是找到了,

115.jpg116.jpg

阿粉一看这init方法和start方法又调用了initInternal()和startInternal(),找来找去又回去了,而阿粉也从这里知道了,模板方法,是有自己的子类具体实现

相关文章
|
2月前
|
人工智能 前端开发 Java
【Tomcat源码分析】启动过程深度解析 (二)
本文深入探讨了Tomcat启动Web应用的过程,重点解析了其加载ServletContextListener及Servlet的机制。文章从Bootstrap反射调用Catalina的start方法开始,逐步介绍了StandardServer、StandardService、StandardEngine、StandardHost、StandardContext和StandardWrapper的启动流程。每个组件通过Lifecycle接口协调启动,子容器逐层启动,直至整个服务器完全启动。此外,还详细分析了Pipeline及其Valve组件的作用,展示了Tomcat内部组件间的协作机制。
【Tomcat源码分析】启动过程深度解析 (二)
|
6月前
|
设计模式 算法 前端开发
Tomcat的源码剖析, 启动过程你会吗?
基于JMX Tomcat会为每个组件进行注册过程,通过Registry管理起来,而Registry是基于JMX来实现的,因此在看组件的init和start过程实际上就是初始化MBean和触发MBean的start方法,会大量看到形如: Registry.getRegistry(null, null).invoke(mbeans, "init", false); Registry.getRegistry(null, null).invoke(mbeans, "start", false); 这样的代码,这实际上就是通过JMX管理各种组件的行为和生命期。
36 0
|
XML 应用服务中间件 数据格式
Tomcat - Tomcat 8.5.55 启动过程源码分析阶段三_start阶段
Tomcat - Tomcat 8.5.55 启动过程源码分析阶段三_start阶段
105 0
|
前端开发 Java 应用服务中间件
Tomcat - Tomcat 8.5.55 启动过程源码分析阶段二_load加载初始化
Tomcat - Tomcat 8.5.55 启动过程源码分析阶段二_load加载初始化
97 0
|
前端开发 Java 应用服务中间件
Tomcat - Tomcat 8.5.55 启动过程源码分析阶段一_init实例化Bootstrap
Tomcat - Tomcat 8.5.55 启动过程源码分析阶段一_init实例化Bootstrap
103 0
|
网络协议 安全 Java
面试官:Tomcat 有哪些组成部分?讲讲工作原理?面试必问。。(3)
面试官:Tomcat 有哪些组成部分?讲讲工作原理?面试必问。。(3)
138 0
面试官:Tomcat 有哪些组成部分?讲讲工作原理?面试必问。。(3)
|
安全 Java 应用服务中间件
面试官:Tomcat 有哪些组成部分?讲讲工作原理?面试必问。。(2)
面试官:Tomcat 有哪些组成部分?讲讲工作原理?面试必问。。(2)
131 0
面试官:Tomcat 有哪些组成部分?讲讲工作原理?面试必问。。(2)
|
XML 监控 前端开发
面试官:Tomcat 有哪些组成部分?讲讲工作原理?面试必问。。(1)
面试官:Tomcat 有哪些组成部分?讲讲工作原理?面试必问。。(1)
142 0
面试官:Tomcat 有哪些组成部分?讲讲工作原理?面试必问。。(1)
|
存储 XML Java
Tomcat启动过程原理详解
Tomcat启动过程原理详解
107 0
|
Java 应用服务中间件
面试官:既然启动流程不太了解,那你知道Tomcat的生命周期是什么样子的么?
上一次的文章中,阿粉在面试官面前说了对启动流程不太理解,然后和他聊了一会,然后他又提出了你既然不是特别了解启动流程的话,那你对Tomcat的生命周期熟悉么?
面试官:既然启动流程不太了解,那你知道Tomcat的生命周期是什么样子的么?