Tomcat8源码解析(一)

简介: Tomcat8源码解析 Tomcat总体架构 Tomcat源码搭建 tomcat软件和源码文件下载链接:https://tomcat.apache.org/download-80.cgi 创建一个to

Tomcat8源码解析

Tomcat总体架构

  • 在这里插入图片描述

Connector:开启Socket并监听客户端请求,返回响应数据;
Container:负责具体的请求处理;

一个Service负责维护多个Connector和一个Container,这样来自Connector的请求只能有它所属的Service维护的Container处理;

Engine:代表整个servlet引擎
Host:表示一个虚拟主机
Context:表示一个应用
wrapper:表示一个servlet

Tomcat源码搭建

  1. tomcat软件和源码文件下载链接:https://tomcat.apache.org/download-80.cgi
  • 在这里插入图片描述
  1. 创建一个tomcat目录文件夹,存放下载的文件,然后创建一个pom.xml文件。然后使用idea打开该tomcat目录即可。
  • 在这里插入图片描述
  • pom.xml文件如下
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.apache.tomcat</groupId>
    <artifactId>tomcat</artifactId>
    <version>1.0-SNAPSHOT</version>

    <name>tomcat</name>
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.easymock</groupId>
            <artifactId>easymock</artifactId>
            <version>3.4</version>
        </dependency>
        <dependency>
            <groupId>org.apache.ant</groupId>
            <artifactId>ant</artifactId>
            <version>1.10.1</version>
        </dependency>
        <dependency>
            <groupId>wsdl4j</groupId>
            <artifactId>wsdl4j</artifactId>
            <version>1.6.2</version>
        </dependency>
        <dependency>
            <groupId>javax.xml</groupId>
            <artifactId>jaxrpc</artifactId>
            <version>1.1</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jdt</groupId>
            <artifactId>org.eclipse.jdt.core</artifactId>
            <version>3.13.0</version>
        </dependency>
        <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>4.5.1</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.6.1</version>
                <configuration>
                    <encoding>UTF-8</encoding>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  1. 在idea设置如下图所示:
  • 在这里插入图片描述
  • 在这里插入图片描述
  1. 最后启动即可。启动成功访问路径:http://localhost:8080/
  • 在这里插入图片描述

Tomcat源码分析

1.Tomcat初始化阶段
# 1.Bootstrap启动类的main方法
public static void main(String args[]) {
  if (daemon == null) {
        //实例化BootStrap
        Bootstrap bootstrap = new Bootstrap();
        try {
            //初始化BootStrap
            bootstrap.init();
        } catch (Throwable t) {
            handleThrowable(t);
            t.printStackTrace();
            return;
        }
        daemon = bootstrap;
    } else {
        // When running as a service the call to stop will be on a new
        // thread so make sure the correct class loader is used to prevent
        // a range of class not found exceptions.
        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")) {
            //当命令是start,执行下面操作
            daemon.setAwait(true); //为了让tomcat在关闭端口阻塞监听关闭命令
            daemon.load(args);     //实际上调用catalina.load()方法,初始化server,service,engine,executor,connector
            daemon.start();        //实际上调用catalina.start()方法,启动server,service,engine,executor,connector;Host,Context,Wrapper
            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 t) {
        // Unwrap the Exception for clearer error reporting
        if (t instanceof InvocationTargetException &&
                t.getCause() != null) {
            t = t.getCause();
        }
        handleThrowable(t);
        t.printStackTrace();
        System.exit(1);
    }
}

# 2.初始化BootStrap
public void init() throws Exception {
    //初始化类加载器
    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");
    //加载Catalina类
    Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
    //使用Catalina类的构造方法,创建Catalina实例对象
    Object startupInstance = startupClass.getConstructor().newInstance();

    //执行Catalina的setParentClassLoader方法,设置父类加载器
    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
    catalinaDaemon = startupInstance;
}

# 3.初始化类加载器
private void initClassLoaders() {
    try {
        //commonLoader的父类加载器就设置为null,即打破了双亲委派机制。
        commonLoader = createClassLoader("common", null);
        if( commonLoader == null ) {
            // no config file, default to this loader - we might be in a 'single' env.
            commonLoader=this.getClass().getClassLoader();
        }
        //catalinaLoader和sharedLoader,的父类加载器设置为commonLoader
        catalinaLoader = createClassLoader("server", commonLoader);
        sharedLoader = createClassLoader("shared", commonLoader);
    } catch (Throwable t) {
        handleThrowable(t);
        log.error("Class loader creation threw exception", t);
        System.exit(1);
    }
}

# 4.daemon.load(args);初始化操作
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;
    }
    //执行catalina的load()方法。
    Method method =
        catalinaDaemon.getClass().getMethod(methodName, paramTypes);
    if (log.isDebugEnabled())
        log.debug("Calling startup class " + method);
    method.invoke(catalinaDaemon, param);

}
从上面的代码可以看出,tomcat的启动类入口是Bootstrap类的main方法。
可以看出这里有几个重要的步骤:
1.初始化Bootstrap,主要是反射创建catalina对象;初始化tomcat类加载器
2.调用Bootstrap的load方法,初始化tomcat相关组件,实际上是调用catalina的load方法。
  • 执行的catalina.load()方法,接着源码跟踪分析
/**
 * 会去初始化一些资源,优先加载conf/server.xml,找不到再去加载server-embed.xml;
 * 此外,load方法还会初始化Server
 */
public void load() {
    if (loaded) {
        return;
    }
    loaded = true;
    long t1 = System.nanoTime();

    //初始化目录
    initDirs();

    //初始化命名空间
    initNaming();

    //解析器,解析Server.xml文件
    Digester digester = createStartDigester();

    //读取Server.xml文件
    InputSource inputSource = null;
    InputStream inputStream = null;
    File file = null;
    try {
        try {
            file = configFile();
            inputStream = new FileInputStream(file);
            inputSource = new InputSource(file.toURI().toURL().toString());
        } catch (Exception e) {
            if (log.isDebugEnabled()) {
                log.debug(sm.getString("catalina.configFail", file), e);
            }
        }
       
        try {
            inputSource.setByteStream(inputStream);
            digester.push(this);

            //开始解析Server.xml文件(重点)
            digester.parse(inputSource);
        } 
    }

    //设置server的Catalina等信息
    getServer().setCatalina(this);
    getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
    getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

    // Stream redirection
    initStreams();

    // Start the new server
    try {

        //初始化Server,这个init方法是父类LifecycleBase的方法(重点)
        getServer().init();
    } catch (LifecycleException e) {
        if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
            throw new java.lang.Error(e);
        } else {
            log.error("Catalina.start", e);
        }
    }
    long t2 = System.nanoTime();
    if(log.isInfoEnabled()) {
        log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
    }
}
catalina.load方法,主要是做了2个重要的操作:
1.解析server.xml文件。
2.初始化Server。
  • 在这里插入图片描述
  • 执行的getServer().init()方法初始化Server,源码分析
# 1.LifecycleBase的init()方法
@Override
public final synchronized void init() throws LifecycleException {
    if (!state.equals(LifecycleState.NEW)) {
        invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
    }

    try {
        setStateInternal(LifecycleState.INITIALIZING, null, false);
        //调用子类的initInternal方法(重要)
        initInternal();
        setStateInternal(LifecycleState.INITIALIZED, null, false);
    } catch (Throwable t) {
        ExceptionUtils.handleThrowable(t);
        setStateInternal(LifecycleState.FAILED, null, false);
        throw new LifecycleException(
                sm.getString("lifecycleBase.initFail",toString()), t);
    }
}

# 2.StandardServer的initInternal()方法
@Override
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");

     //注册JMX
     // 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();
         }
     }
     // Initialize our defined Services
     for (int i = 0; i < services.length; i++) {
         //初始化services(重点)
         services[i].init();
     }
 }
Server的初始化init()方法,会先调用父类LifecycleBase的init()方法,然后再调用子类Server的initInternal()方法
这个调用的模式:父类init--->子类initInternal,在后面的services,connector,engine初始化是一样的模式。
  • services[i].init(),初始化services,源码分析
# 1.standardService的initInternal()方法
@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

    //初始化engine(重点)
    if (engine != null) {
        engine.init();
    }

    //初始化线程池
    // Initialize any Executors
    for (Executor executor : findExecutors()) {
        if (executor instanceof JmxEnabled) {
            ((JmxEnabled) executor).setDomain(getDomain());
        }
        executor.init();
    }

    //初始化mapper映射监听器
    // Initialize mapper listener
    mapperListener.init();

    // Initialize our defined Connectors
    synchronized (connectorsLock) {
        for (Connector connector : connectors) {
            try {
                //初始化connector(重点)
                connector.init();
            } catch (Exception e) {
                String message = sm.getString(
                        "standardService.connector.initFailed", connector);
                log.error(message, e);

                if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))
                    throw new LifecycleException(message);
            }
        }
    }
}
从services的初始化分析,可以得到services的初始化,会初始化engine和connector。(executor、mapperListener)
  • engine.init(),初始化engine,源码分析
# 1.standardEngine的initInternal()方法
@Override
protected void initInternal() throws LifecycleException {
    // Ensure that a Realm is present before any attempt is made to start
    // one. This will create the default NullRealm if necessary.
    getRealm();
    super.initInternal();
}

# 2.ContainerBase的initInternal()方法
@Override
protected void initInternal() throws LifecycleException {
    BlockingQueue<Runnable> startStopQueue = new LinkedBlockingQueue<>();
    startStopExecutor = new ThreadPoolExecutor(
            getStartStopThreadsInternal(),
            getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,
            startStopQueue,
            new StartStopThreadFactory(getName() + "-startStop-"));
    startStopExecutor.allowCoreThreadTimeOut(true);
    super.initInternal();
}

# 3.LifecycleMBeanBase的initInternal()方法
@Override
protected void initInternal() throws LifecycleException {
    // If oname is not null then registration has already happened via
    // preRegister().
    if (oname == null) {
        mserver = Registry.getRegistry(null, null).getMBeanServer();
        oname = register(this, getObjectNameKeyProperties());
    }
}
  • connector.init(),初始化connector,源码分析
# 1.connector的initInternal()方法
@Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

    //初始化适配器
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);

    //给parseBodyMethodsSet设置一个默认值
    if (null == parseBodyMethodsSet) {
        setParseBodyMethods(getParseBodyMethods());
    }

    if (protocolHandler.isAprRequired() && !AprLifecycleListener.isAprAvailable()) {
        throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoApr",
                getProtocolHandlerClassName()));
    }
    if (AprLifecycleListener.isAprAvailable() && AprLifecycleListener.getUseOpenSSL() &&
            protocolHandler instanceof AbstractHttp11JsseProtocol) {
        AbstractHttp11JsseProtocol<?> jsseProtocolHandler =
                (AbstractHttp11JsseProtocol<?>) protocolHandler;
        if (jsseProtocolHandler.isSSLEnabled() &&
                jsseProtocolHandler.getSslImplementationName() == null) {
            // OpenSSL is compatible with the JSSE configuration, so use it if APR is available
            jsseProtocolHandler.setSslImplementationName(OpenSSLImplementation.class.getName());
        }
    }

    try {
        //初始化protocolHandler(重点)
        protocolHandler.init();
    } catch (Exception e) {
        throw new LifecycleException(
                sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);
    }
}
从connector的初始化分析,可以得到connector的初始化,会初始化protocolHandler
  • protocolHandler.init(),初始化protocolHandler,源码分析
# 1.AbstractProtocol的init()方法
@Override
public void init() throws Exception {
    if (getLog().isInfoEnabled()) {
        getLog().info(sm.getString("abstractProtocolHandler.init", getName()));
    }

    if (oname == null) {
        // Component not pre-registered so register it
        oname = createObjectName();
        if (oname != null) {
            Registry.getRegistry(null, null).registerComponent(this, oname, null);
        }
    }

    if (this.domain != null) {
        rgOname = new ObjectName(domain + ":type=GlobalRequestProcessor,name=" + getName());
        Registry.getRegistry(null, null).registerComponent(
                getHandler().getGlobal(), rgOname, null);
    }

    String endpointName = getName();
    endpoint.setName(endpointName.substring(1, endpointName.length()-1));
    endpoint.setDomain(domain);

    //初始化endpoint(重点)
    endpoint.init();
}
从protocolHandler的初始化分析,可以得到protocolHandler的初始化,会初始化endpoint
  • endpoint.init(),初始化endpoint,源码分析
# 1.AbstractEndpoint的init()方法
public void init() throws Exception {
    if (bindOnInit) {
        bind();
        bindState = BindState.BOUND_ON_INIT;
    }
    if (this.domain != null) {
        // Register endpoint (as ThreadPool - historical name)
        oname = new ObjectName(domain + ":type=ThreadPool,name=\"" + getName() + "\"");
        Registry.getRegistry(null, null).registerComponent(this, oname, null);

        ObjectName socketPropertiesOname = new ObjectName(domain +
                ":type=ThreadPool,name=\"" + getName() + "\",subType=SocketProperties");
        socketProperties.setObjectName(socketPropertiesOname);
        Registry.getRegistry(null, null).registerComponent(socketProperties, socketPropertiesOname, null);

        for (SSLHostConfig sslHostConfig : findSslHostConfigs()) {
            registerJmx(sslHostConfig);
        }
    }
}
到此tomcat的初始化阶段就已经完成了。使用的是责任链模式,一步一步的初始化。
组件初始化的顺序:
Server-->Service-->Engine-->Connector-->ProtocolHandler-->Endpoint
可以看到,Host,Context,Wrapper还没有开始初始化。这些将在tomcat的start启动阶段初始化。
目录
相关文章
|
7月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
651 29
|
7月前
|
监控 Java 应用服务中间件
Tomcat log日志解析
理解和解析Tomcat日志文件对于诊断和解决Web应用中的问题至关重要。通过分析 `catalina.out`、`localhost.log`、`localhost_access_log.*.txt`、`manager.log`和 `host-manager.log`等日志文件,可以快速定位和解决问题,确保Tomcat服务器的稳定运行。掌握这些日志解析技巧,可以显著提高运维和开发效率。
544 13
|
7月前
|
前端开发 数据安全/隐私保护 CDN
二次元聚合短视频解析去水印系统源码
二次元聚合短视频解析去水印系统源码
187 4
|
7月前
|
JavaScript 算法 前端开发
JS数组操作方法全景图,全网最全构建完整知识网络!js数组操作方法全集(实现筛选转换、随机排序洗牌算法、复杂数据处理统计等情景详解,附大量源码和易错点解析)
这些方法提供了对数组的全面操作,包括搜索、遍历、转换和聚合等。通过分为原地操作方法、非原地操作方法和其他方法便于您理解和记忆,并熟悉他们各自的使用方法与使用范围。详细的案例与进阶使用,方便您理解数组操作的底层原理。链式调用的几个案例,让您玩转数组操作。 只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
7月前
|
移动开发 前端开发 JavaScript
从入门到精通:H5游戏源码开发技术全解析与未来趋势洞察
H5游戏凭借其跨平台、易传播和开发成本低的优势,近年来发展迅猛。接下来,让我们深入了解 H5 游戏源码开发的技术教程以及未来的发展趋势。
|
7月前
|
存储 前端开发 JavaScript
在线教育网课系统源码开发指南:功能设计与技术实现深度解析
在线教育网课系统是近年来发展迅猛的教育形式的核心载体,具备用户管理、课程管理、教学互动、学习评估等功能。本文从功能和技术两方面解析其源码开发,涵盖前端(HTML5、CSS3、JavaScript等)、后端(Java、Python等)、流媒体及云计算技术,并强调安全性、稳定性和用户体验的重要性。
|
7月前
|
负载均衡 JavaScript 前端开发
分片上传技术全解析:原理、优势与应用(含简单实现源码)
分片上传通过将大文件分割成多个小的片段或块,然后并行或顺序地上传这些片段,从而提高上传效率和可靠性,特别适用于大文件的上传场景,尤其是在网络环境不佳时,分片上传能有效提高上传体验。 博客不应该只有代码和解决方案,重点应该在于给出解决方案的同时分享思维模式,只有思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
|
14天前
|
缓存 Java 应用服务中间件
Spring Boot配置优化:Tomcat+数据库+缓存+日志,全场景教程
本文详解Spring Boot十大核心配置优化技巧,涵盖Tomcat连接池、数据库连接池、Jackson时区、日志管理、缓存策略、异步线程池等关键配置,结合代码示例与通俗解释,助你轻松掌握高并发场景下的性能调优方法,适用于实际项目落地。
203 4
|
4月前
|
Java 应用服务中间件 Linux
在Java 12环境中配置和部署Apache Tomcat的步骤。
这段部署Tomcat的冒险旅程充满技术挑战,但同时也像游戏一样充满乐趣。它需要你提前准备,仔细执行,并随时准备解决意外情况。成功后,你就可以在这匹强壮的网络野马上,带着你的Java应用,冲向Web开发的璀璨星空。
130 56

推荐镜像

更多
  • DNS