当前位置:   article > 正文

史上最全的Tomcat源码分析(有源码下载地址)

tomcat源码

前言

tomcat源码 https://github.com/huyufan1995/tomcat.git

Tomcat 8 的源码解读,需要大家对tomcat有一定了解,剩下的就是理解就好,个人认为源码里面 mybatis 和 tomcat 就简单理解而言还是算简单的。 tomcat8 大量用了模板设计模式。tomcat8去掉了Bio的默认实现。
请大家认真观看下图:脑子有个映像,下面会详细讲解

在这里插入图片描述

  1. 每个Tomcat都只有一个Server,表示整个服务环境。一个Server中可以有多个Service。Server就掌管着多个Service的死活

  2. Service是对外提供服务的,一个Server中可以有多个Service,每一个Service中可以有多个Connector和一个Container(Engine)

  3. Connector主要用来接收请求,解析请求内容,封装request和response,然后将准备好的数据交给Container处理

  4. Container就是我们常说的容器,里面可以有多个Host,一个host表示一个虚拟主机,就是一个对应一个WebApps. 最后Container处理完请求之后会将响应内容返回给Connecter,再由Connecter返回给客户端

Tomcat中最重要的两部分

Connector 和 conntainer
connector架构:最底层使用的是Socket进行连接的,Request和Response是按照Http协议来封装的,所以Connector同时需要实现TCP/IP协议和Http协议在这里插入图片描述

Connector中具体用事件处理器来处理请求【ProtocoHandler】,不同的ProtocoHandler代表不同的连接类型【所以一个Service中可以有多个Connector】 例如:Http11Protocol使用普通的Socket来连接的,Http11NioProtocol使用NioSocket连接。 Endpoint用于处理底层Socket的网络连接,用来实现Tcp/Ip协议。 Acceptor:用于监听和接收请求。 Handler:请求的初步处理,并且调用Processor AsynTimeout:检查异步的Request请求是否超时 Processor用于将Endpoint接收Socket封装成Request,用来实现HTTP协议 Adapter 用于将Request交给Container 进行具体处理,即将请求适配到Servlet容器 Container 架构:Container就是一个Engine。Container用于封装和管理Servlet,以及具体处理Request请求

Container(Engine)说明

## 1.引入库
context:就是一个webapp 就是一个应用。
wrapper就是一个servlet包装器
Engine:Container(容器/引擎), 用来管理多个站点,一个Service最多只能有一个Engine
Host:虚拟主机,一个Container可以有多个虚拟主机
Context:一个应用程序,就是我们平时开发的一个项目,或者说是一套web引用程序
Wrapper:用来包装Servlet,每一个Wraper都包装着一个Servlet

在这里插入图片描述

Connector 详解

EndPoint:
提供字节流给Processor
监听通信端口,是对传输层的抽象,用来实现 TCP/IP 协议的。 对应的抽象类为AbstractEndPoint,有很多实现类,比如NioEndPoint,JIoEndPoint等。在其中有两个组件,一个 是Acceptor,另外一个是SocketProcessor。 Acceptor用于监听Socket连接请求,SocketProcessor用于处理接收到的Socket请求。
Processor:
提供Tomcat Request对象给Adapter
Processor是用于实现HTTP协议的,也就是说Processor是针对应用层协议的抽象。 Processor接受来自EndPoint的Socket,然后解析成Tomcat Request和Tomcat Response对象,最后通过Adapter 提交给容器。 对应的抽象类为AbstractProcessor,有很多实现类,比如AjpProcessor、Http11Processor等。
Adapter:
提供ServletRequest给容器
ProtocolHandler接口负责解析请求并生成 Tomcat Request 类。 需要把这个 Request 对象转换成 ServletRequest。 Tomcat 引入CoyoteAdapter,这是适配器模式的经典运用,连接器调用 CoyoteAdapter 的 sevice 方法,传入的是 Tomcat Request 对象,CoyoteAdapter 负责将 Tomcat Request 转成 ServletRequest,再调用容器的 service 方 法。
在这里插入图片描述
1.Endpoint接收Socket连接,生成一个SocketProcessor任务提交到线程池去处理
2.SocketProcessor的run方法会调用Processor组件去解析应用层协议,Processor通过解析生成Request对象后,会调 用Adapter的service方法。

源码跟踪

大家对下面这个结构图不陌生了吧,具体就不单独介绍了
在这里插入图片描述

tomcat启动流程时序图

在这里插入图片描述
由图中我们可以看到从Bootstrap类的main方法开始, tomcat会以链的方式逐级调用各个模块的init()方法进行初始化, 待各个模块都初始化后, 又会逐级调用各个模块的start()方法启动各个模块. 下面我们通过部分源码看一下这个过程.
关键源码均有注释

public static void main(String[] args) {
        synchronized(daemonLock) {
            if (daemon == null) {
                Bootstrap bootstrap = new Bootstrap();

                try {
                	// 初始化加载器,创建catalia对象(tomcat所有的动作均是由它带头,轰炸机)
                    bootstrap.init();
                } catch (Throwable var5) {
                    handleThrowable(var5);
                    var5.printStackTrace();
                    return;
                }

                daemon = bootstrap;
            } else {
                Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
            }
        }
		//主要先执行 load加载,然后 start启动, 这里用的就是模板设计模式启动的
        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();
                //执行完init()方法后会判断启动参数的值,
                // 由于我们采取默认的启动方式, 所以main方法的参数是start, 会进入下面的判断代码块
            } else if (command.equals("start")) {
            // 让当前主线程阻塞  类似 countdownLatch.await
                daemon.setAwait(true);
                // 1.加载 各个组件对象
                daemon.load(args);
                // 2.启动
                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 var7) {
            Throwable t = var7;
            if (var7 instanceof InvocationTargetException && var7.getCause() != null) {
                t = var7.getCause();
            }

            handleThrowable(t);
            t.printStackTrace();
            System.exit(1);
        }

    }

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70

可以看到在设置等待后, 调用了本类对象的load()方法. 我们跟进查看load()方法的源码.
load方法注解就是通过 反射执行 catalin 的 load方法

private void load(String[] arguments)
        throws Exception {

        // Call the load() method
        String methodName = "load";
        Object param[];
        Class<?> paramTypes[];
        if (arguments==null || arguments.length==0) {
            paramTypes = null;
            param = null;
        } else {
            paramTypes = new Class[1];
            paramTypes[0] = arguments.getClass();
            param = new Object[1];
            param[0] = arguments;
        }
        Method method =
            catalinaDaemon.getClass().getMethod(methodName, paramTypes);
        if (log.isDebugEnabled())
            log.debug("Calling startup class " + method);
        method.invoke(catalinaDaemon, param);
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

通过跟踪源码我们可以看到catalinaDaemon是Bootstrap类的一个私有成员变量
在这里插入图片描述
catalinaDaemor 是在 bootstrap中 init方法初始化完成的,我们可以看看init源码
可以看到init()方法创建了一个Catalina对象, 并把该对象赋给了catalinaDaemon.

public void init()
    throws Exception
{

    // Set Catalina path
    setCatalinaHome();
    setCatalinaBase();

    initClassLoaders();

    Thread.currentThread().setContextClassLoader(catalinaLoader);

    SecurityClassLoad.securityClassLoad(catalinaLoader);

    // Load our startup class and call its process() method
    if (log.isDebugEnabled())
        log.debug("Loading startup class");
    Class<?> startupClass =
        catalinaLoader.loadClass
        ("org.apache.catalina.startup.Catalina");
    Object startupInstance = startupClass.newInstance();

    // Set the shared extensions class loader
    if (log.isDebugEnabled())
        log.debug("Setting startup class properties");
    String methodName = "setParentClassLoader";
    Class<?> paramTypes[] = new Class[1];
    paramTypes[0] = Class.forName("java.lang.ClassLoader");
    Object paramValues[] = new Object[1];
    paramValues[0] = sharedLoader;
    Method method =
        startupInstance.getClass().getMethod(methodName, paramTypes);
    method.invoke(startupInstance, paramValues);

    catalinaDaemon = startupInstance;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

上面认真看了之后就知道 bootstrap就是先load 反射初始化catalina 这个对象,执行他的load方法

在这里插入图片描述
==然后 catalina 执行 它的 getserver方法 ==
然后我们看 Server对象 的init方法
Server是一个接口,唯一的实现类是StandardServer
在这里插入图片描述
由于继承关系先执行 父类的init方法
在这里插入图片描述
==在父类方法中调用 initInternal(); == 注意看下面的方法修饰为抽象方法,这就应用了模板设计模式,由子类具体实现逻辑。
在这里插入图片描述
执行StandardServer 中的 initInternal方法

protected void initInternal() throws LifecycleException {

        super.initInternal();

        // Register global String cache
        // Note although the cache is global, if there are multiple Servers
        // present in the JVM (may happen when embedding) then the same cache
        // will be registered under multiple names
        onameStringCache = register(new StringCache(), "type=StringCache");

        // Register the MBeanFactory
        MBeanFactory factory = new MBeanFactory();
        factory.setContainer(this);
        onameMBeanFactory = register(factory, "type=MBeanFactory");

        // Register the naming resources
        globalNamingResources.init();

        // Populate the extension validator with JARs from common and shared
        // class loaders
        if (getCatalina() != null) {
            ClassLoader cl = getCatalina().getParentClassLoader();
            // Walk the class loader hierarchy. Stop at the system class loader.
            // This will add the shared (if present) and common class loaders
            while (cl != null && cl != ClassLoader.getSystemClassLoader()) {
                if (cl instanceof URLClassLoader) {
                    URL[] urls = ((URLClassLoader) cl).getURLs();
                    for (URL url : urls) {
                        if (url.getProtocol().equals("file")) {
                            try {
                                File f = new File (url.toURI());
                                if (f.isFile() &&
                                        f.getName().endsWith(".jar")) {
                                    ExtensionValidator.addSystemResource(f);
                                }
                            } catch (URISyntaxException e) {
                                // Ignore
                            } catch (IOException e) {
                                // Ignore
                            }
                        }
                    }
                }
                cl = cl.getParent();
            }
        }
        // 初始化servers中的init方法
        for (int i = 0; i < services.length; i++) {
            services[i].init();
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

==其中 for (int i = 0; i < services.length; i++) {
services[i].init();
} == 是开启子容器的执行, 继续执行 父类 中的init,然后父类中的init调用子类的initInternal 完成初始化

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号