• tomcat10.1.0源码分析


    组件成员

    server.xml

    
    
    
    <Server port="8005" shutdown="SHUTDOWN">
      <Listener className="org.apache.catalina.startup.VersionLoggerListener" />
      
      
      <Listener className="org.apache.catalina.core.AprLifecycleListener" SSLEngine="on" />
      
      <Listener className="org.apache.catalina.core.JreMemoryLeakPreventionListener" />
      <Listener className="org.apache.catalina.mbeans.GlobalResourcesLifecycleListener" />
      <Listener className="org.apache.catalina.core.ThreadLocalLeakPreventionListener" />
     
      
      <GlobalNamingResources>
        
        <Resource name="UserDatabase" auth="Container"
                  type="org.apache.catalina.UserDatabase"
                  description="User database that can be updated and saved"
                  factory="org.apache.catalina.users.MemoryUserDatabaseFactory"
                  pathname="conf/tomcat-users.xml" />
      GlobalNamingResources>
     
      
      <Service name="Catalina">
     
        
        
     
     
        
        <Connector port="8080" protocol="HTTP/1.1"
                   connectionTimeout="20000"
                   redirectPort="8443" />
        
        
        
        
     
        
        <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" />
     
     
        
     
        
        <Engine name="Catalina" defaultHost="localhost">
     
          
          
     
          
          <Realm className="org.apache.catalina.realm.LockOutRealm">
            
            <Realm className="org.apache.catalina.realm.UserDatabaseRealm"
                   resourceName="UserDatabase"/>
          Realm>
     
          <Host name="localhost"  appBase="webapps"
                unpackWARs="true" autoDeploy="true">
     
            
            
     
            
            <Valve className="org.apache.catalina.valves.AccessLogValve" directory="logs"
                   prefix="localhost_access_log" suffix=".txt"
                   pattern="%h %l %u %t "%r" %s %b" />
     
          Host>
        Engine>
      Service>
    Server>
    
    • 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
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142

    server

    代表一个tomcat实例,可包含多个service,管理所有的service生命周期

    service

    包含多个connector和一个container

    container

    容器:负责处理请求

    engine

    只有一个,可包含多个host

    host

    虚拟主机,虚拟主机允许Tomcat引擎在将配置在一台机器上的多个域名

    context

    每个虚拟主机又可以支持多个web应用部署在它下边,这就是我们所熟知的上下文对象

    wrapper

    在上下文中又可以部署多个servlet,并且每个servlet都会被一个包装组件(Wrapper)所包含(一个wrapper对应一个servlet)

    在这里插入图片描述
    在这里插入图片描述

    connector

    连接器:负责接收请求,处理与客户端的通信,传递给container
    Tomcat源码中与connector相关的类位于org.apache.coyote包中,Connector分为以下几类:

    Http Connector

    基于HTTP协议,负责建立HTTP连接。它又分为BIO Http Connector与NIO Http Connector两种,后者提供非阻塞IO与长连接Comet支持。默认情况下,Tomcat使用的就是这个Connector。

    AJP Connector

    基于AJP协议,AJP是专门设计用来为tomcat与http服务器之间通信专门定制的协议,能提供较高的通信速度和效率。如与Apache服务器集成时,采用这个协议。

    APR HTTP Connector

    用C实现,通过JNI调用的。主要提升对静态资源(如HTML、图片、CSS、JS等)的访问性能。现在这个库已独立出来可用在任何项目中。Tomcat在配置APR之后性能非常强劲

    Connector的三种运行模式: bio nio apr

    在这里插入图片描述

    Endpoint

    实现了建立连接、处理连接、和关闭连接等操作

    在这里插入图片描述

    Pipeline & Value

    在Catalina中,我们有4种容器,request请求在这4个容器中流转,容器间不是直接调用,而是通过pipeline组件,每个容器都有自己的Pipeline组件,每个Pipeline组件上至少会设定一个Valve(阀门),这个Valve我们称之为BaseValve(基础阀)。可以说基础阀是两个容器之间的桥梁。
    在这里插入图片描述

    图中可以看到在同一个Pipeline上可以有多个Valve,每个Valve都可以做一些操作,无论是Pipeline还是Valve操作的都是Request和Response。而在容器之间Pipeline和Valve则起到了桥梁的作用

    初始化

    catalina组件初始化阶段调用createStartDigester创建Digester对象,解析server.xml规则,实例化组件对象并部分赋值。(反射)

    bootstrap 创建catalina 使用反射

    Catalina及它依赖的类比如 XML parser与应用级别的类隔离(以使用不同类加载器的方法),

    Catalina是由catalinaLoader加载的,而Bootstrap是由AppClassLoader加载的,所以在Bootstrap中我们无法直接调用Catalina的方法

    bootstrap 加载一些公共类、配置环境、实例化Catalina类,具体的业务代码还是在catalina类里面的。这样反射的好处是解耦,可以不依赖应用层的classpath独立加载

    在这里插入图片描述

    tomcat启动过程

    在这里插入图片描述

    url请求流程

    在这里插入图片描述

    mapper 请求路由实现

    收到客户端请求后,容器根据请求 URL 的上下文名称匹配 Web 应用程序,然后根据去除上下文路径和路径参数的路径,按以下规则顺序匹配,并且只使用第一个匹配的 Servlet,后续不再尝试匹配:

    • 精确匹配,查找一个与请求路径完全匹配的 Servlet
    • 前缀路径匹配,递归的尝试匹配最长路径前缀的 Servlet,通过使用 “/” 作为路径分隔符,在路径树上一次一个目录的匹配,选择路径最长的
    • 扩展名匹配,如果 URL 最后一部分包含扩展名,如 .jsp,则尝试匹配处理此扩展名请求的 Servlet

    如果前三个规则没有匹配成功,那么容器要为请求提供一个默认 Servlet,容器在匹配时区分大小写。

    举例:
    在这里插入图片描述

    实现请求映射的一般方法是,首先构建一个路由表,然后按照规范进行匹配,最后返回匹配结果。Tomcat 就是如此,与请求映射相关的类有三个,分别是:

    • Mapper: 存储请求路由表并执行匹配
    • MapperListener: 查询所有的 Host、Context、Wrapper 构建路由表
    • MappingData: 请求映射结果

    在这里插入图片描述

    启动时解析过程

    在这里插入图片描述

    双亲委派模型

    双亲委派模型

    TOMCAT类加载器

    Tomcat的自定义类加载器,WebAppClassLoader打破了双亲委托机制:

     public Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    
            synchronized (JreCompat.isGraalAvailable() ? this : getClassLoadingLock(name)) {
                if (log.isDebugEnabled()) {
                    log.debug("loadClass(" + name + ", " + resolve + ")");
                }
                Class<?> clazz = null;
    
                // Log access to stopped class loader
                checkStateForClassLoading(name);
    
                // (0) Check our previously loaded local class cache 
                // 先在本地cache查找该类是否已经加载过
                clazz = findLoadedClass0(name);
                if (clazz != null) {
                    if (log.isDebugEnabled()) {
                        log.debug("  Returning class from cache");
                    }
                    if (resolve) {
                        resolveClass(clazz);
                    }
                    return clazz;
                }
    
                // (0.1) Check our previously loaded class cache
                clazz = JreCompat.isGraalAvailable() ? null : findLoadedClass(name);
                if (clazz != null) {
                    if (log.isDebugEnabled()) {
                        log.debug("  Returning class from cache");
                    }
                    if (resolve) {
                        resolveClass(clazz);
                    }
                    return clazz;
                }
    
                // (0.2) Try loading the class with the bootstrap class loader, to prevent
                //       the webapp from overriding Java SE classes. This implements
                //       SRV.10.7.2
                String resourceName = binaryNameToPath(name, false);
    
                ClassLoader javaseLoader = getJavaseClassLoader();
                boolean tryLoadingFromJavaseLoader;
                try {
                    // Use getResource as it won't trigger an expensive
                    // ClassNotFoundException if the resource is not available from
                    // the Java SE class loader. However (see
                    // https://bz.apache.org/bugzilla/show_bug.cgi?id=58125 for
                    // details) when running under a security manager in rare cases
                    // this call may trigger a ClassCircularityError.
                    // See https://bz.apache.org/bugzilla/show_bug.cgi?id=61424 for
                    // details of how this may trigger a StackOverflowError
                    // Given these reported errors, catch Throwable to ensure any
                    // other edge cases are also caught
                    URL url;
                    if (securityManager != null) {
                        PrivilegedAction<URL> dp = new PrivilegedJavaseGetResource(resourceName);
                        url = AccessController.doPrivileged(dp);
                    } else {
                        url = javaseLoader.getResource(resourceName);
                    }
                    tryLoadingFromJavaseLoader = (url != null);
                } catch (Throwable t) {
                    // Swallow all exceptions apart from those that must be re-thrown
                    ExceptionUtils.handleThrowable(t);
                    // The getResource() trick won't work for this class. We have to
                    // try loading it directly and accept that we might get a
                    // ClassNotFoundException.
                    tryLoadingFromJavaseLoader = true;
                }
    
                if (tryLoadingFromJavaseLoader) {
                    try {
                        clazz = javaseLoader.loadClass(name);
                        if (clazz != null) {
                            if (resolve) {
                                resolveClass(clazz);
                            }
                            return clazz;
                        }
                    } catch (ClassNotFoundException e) {
                        // Ignore
                    }
                }
    
                // (0.5) Permission to access this class when using a SecurityManager
                if (securityManager != null) {
                    int i = name.lastIndexOf('.');
                    if (i >= 0) {
                        try {
                            securityManager.checkPackageAccess(name.substring(0,i));
                        } catch (SecurityException se) {
                            String error = sm.getString("webappClassLoader.restrictedPackage", name);
                            log.info(error, se);
                            throw new ClassNotFoundException(error, se);
                        }
                    }
                }
    
                boolean delegateLoad = delegate || filter(name, true);
    
                // (1) Delegate to our parent if requested
                if (delegateLoad) {
                    if (log.isDebugEnabled()) {
                        log.debug("  Delegating to parent classloader1 " + parent);
                    }
                    try {
                        clazz = Class.forName(name, false, parent);
                        if (clazz != null) {
                            if (log.isDebugEnabled()) {
                                log.debug("  Loading class from parent");
                            }
                            if (resolve) {
                                resolveClass(clazz);
                            }
                            return clazz;
                        }
                    } catch (ClassNotFoundException e) {
                        // Ignore
                    }
                }
    
                // (2) Search local repositories
                if (log.isDebugEnabled()) {
                    log.debug("  Searching local repositories");
                }
                try {
                    clazz = findClass(name);
                    if (clazz != null) {
                        if (log.isDebugEnabled()) {
                            log.debug("  Loading class from local repository");
                        }
                        if (resolve) {
                            resolveClass(clazz);
                        }
                        return clazz;
                    }
                } catch (ClassNotFoundException e) {
                    // Ignore
                }
    
                // (3) Delegate to parent unconditionally
                if (!delegateLoad) {
                    if (log.isDebugEnabled()) {
                        log.debug("  Delegating to parent classloader at end: " + parent);
                    }
                    try {
                        clazz = Class.forName(name, false, parent);
                        if (clazz != null) {
                            if (log.isDebugEnabled()) {
                                log.debug("  Loading class from parent");
                            }
                            if (resolve) {
                                resolveClass(clazz);
                            }
                            return clazz;
                        }
                    } catch (ClassNotFoundException e) {
                        // Ignore
                    }
                }
            }
    
            throw new ClassNotFoundException(name);
        }
    
    • 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
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 0先在本地cache查找该类是否已经加载过
    • 0.1 查找该类是否已经加载过
    • 0.2 尝试使用bootstrap class loader加载

    为防止Web应用自己的类覆盖JRE的核心类
    因为Tomcat需打破双亲委托,假如Web应用里自定义了一个叫Object的类,若先加载该Object类,就会覆盖JRE的Object类,所以Tomcat类加载器优先尝试委托给BootstrapClassLoader去加载,BootstrapClassLoader发现自己已经加载了Object类,直接返回给Tomcat的类加载器,这样Tomcat的类加载器就不会去加载Web应用下的Object类了,避免覆盖JRE核心类

    • 0.5 安全管理器权限判断
    • 1 过滤排除一些指定的类名,这些类委托父类先加载
    • 2 尝试在本地资源下搜索class并加载
    • 3 尝试用系统类加载器(Class.forName的默认加载器就是系统类加载器,也就是AppClassLoader)来加载

    Tomcat 类加载器打破了双亲委托,没有一上来就直接委托给父加载器,为避免本地目录类覆盖JRE核心类先使用了BootstrapClassLoader加载,之后先在本地目录搜索并加载

  • 相关阅读:
    提分必练!中创教育PMP全真模拟题分享来喽
    CNN系列
    Java注解
    大聪明教你学Java | 比校验文件后缀名更靠谱的上传文件校验方式 —— 文件魔数校验
    JavaWeb基础
    如何实现异步通知的重试机制
    哈希思想的应用 - 位图,布隆过滤器
    公司内部网络架设悟空CRM客户管理系统 cpolar无需公网IP实现内网,映射端口外网访问
    【Zookeeper】——服务器动态上下线监听案例&分布式锁案例
    Linux网络套接字之TCP网络程序
  • 原文地址:https://blog.csdn.net/WangMapleWang/article/details/127627752