Web全栈工程师修炼之路 -- 构建Tomcat源码

       作为一个优秀的程序开发者,我们不仅需要对自己编写的程序有深刻的理解和认识,同样的我们还需要对我们编写的程序的运行环境有一个彻底的认识,这样我们在能在这条路上走的更远。相信大家对tomcat都很熟悉,目前也是大陆使用的较多的一种开源的Web容器。对于Tomcat的基本使用这里不做过多的介绍。具体的下载和使用可以查看我在2018年写的https://blog.csdn.net/qq_38701478/article/details/85003063这篇文章。

    好了我们进入今天的主题,首先准备好源码,这里使用的版本是tomcat8.5-42

Web全栈工程师修炼之路 -- 构建Tomcat源码_第1张图片

首先我们来打开Idea 创建一个空的工程

Web全栈工程师修炼之路 -- 构建Tomcat源码_第2张图片

Web全栈工程师修炼之路 -- 构建Tomcat源码_第3张图片

点击完成,然后我们将下载好的源码包解压,放到上面刚刚创建的空工程对应的文件夹下

Web全栈工程师修炼之路 -- 构建Tomcat源码_第4张图片

 

然后我们需要进行两个操作,首先创建一个home目录,接着将conf文件夹和webapps文件夹拷贝到home目录中(其他的不需要)

Web全栈工程师修炼之路 -- 构建Tomcat源码_第5张图片

好了,接着我们在源码的目录下创建一个pom.xml的文件,因为我们要以Maven工程的模式来构建源码,pom文件中主要的内容就是tomcat 依赖的jar包,对应于lib目录下的那些。具体的内容如下:



 
    4.0.0
    org.apache.tomcat
    apache-tomcat-8.5.42-src
    Tomcat8.5
    8.5
 
    
        Tomcat8.5
        java
       
        
            
                java
            
        
       
        
            
                org.apache.maven.plugins
                maven-compiler-plugin
                2.3
                
                    UTF-8
                    1.8
                    1.8
                
            
        
    
 
    
        
            junit
            junit
            4.12
            test
        
        
            org.easymock
            easymock
            3.4
        
        
            ant
            ant
            1.7.0
        
        
            wsdl4j
            wsdl4j
            1.6.2
        
        
            javax.xml
            jaxrpc
            1.1
        
        
            org.eclipse.jdt.core.compiler
            ecj
            4.5.1
        
       
    

 接着我们回到Idea中,我们在项目中new一个module ,选择这从已经存在的项目中创建。

Web全栈工程师修炼之路 -- 构建Tomcat源码_第6张图片

Web全栈工程师修炼之路 -- 构建Tomcat源码_第7张图片

 

 

选中pom.xml文件即可。等待项目加载完成,(在此之前请见检查自己的maven环境是否正常)

导入成功之后项目结构如下:

Web全栈工程师修炼之路 -- 构建Tomcat源码_第8张图片

 

       好了,我们先来看一下,tomcat 的项目架构,我们都知道,一个Java项目的入口都是一个main方法,我们想要自己运行这个工程就要找到他的程序入口Main方法,首先我们都很熟悉,我们在启动tomcat的时候都是使用的startup.sh或者是startup.bat这两个脚本,这两个脚本一个是Linux下的一个是Windows平台下的,首先我们先来看一下startup.bat这个。

       我们发现 这个批处理文件中实际上调用的是catalina.bat,好吧,我们打开catalina.bat这个文件看看,虽然我们可能看不懂windows下面的批处理脚本,但是我们可以挑一些我们熟悉的东西看,大家仔细看的话,会发现一个叫做MAINCLASS的变量,如下图所示:

 

Web全栈工程师修炼之路 -- 构建Tomcat源码_第9张图片

 

      从字面上来理解,这个应该就是我们要找的著启动类了,也就是说Main 方法就是在该类中,好了,我们接着打开BootStrap这个类, 

 

 

我们看一下,发现里面确实有一个main方法,

 

好了,主启动类找到了,接下来我们需要配置一下运行时的参数,我们选择添加一个Application

Web全栈工程师修炼之路 -- 构建Tomcat源码_第10张图片

 

-Dcatalina.home=D:/projec/java/IdeaProjects/tomcat-src/apache-tomcat-8.5.42-src/home
-Dcatalina.base=D:/projec/java/IdeaProjects/tomcat-src/apache-tomcat-8.5.42-src/home
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.util.logging.config.file=D:/projec/java/IdeaProjects/tomcat-src/apache-tomcat-8.5.42-src/home/conf/logging.properties

 

好了,我们启动刚刚配置的BootStrap这个Application即可

 

 

点击debug ,就可以启动项目了,首次启动的过程可能有点漫长

 

 

我们发现8080端口成功的启动了,打开浏览器,我们发现可以正常的访问主页

Web全栈工程师修炼之路 -- 构建Tomcat源码_第11张图片

      如果发现访问的时候报错了,可以在ContextConfig中的configureStart函数中手动将JSP解析器初始 在770行webConfig();方法后加上context.addServletContainerInitializer(new JasperInitializer(), null); 即可。

       好了,我们到这里我们已经成功的构建了tomcat8.5版本的源码了,并且已经可以在本地运行了。下面我们就来看看Tomcat的项目架构,首先我们都知道,tomcat是一种web容器,主要用来发布我们的Web项目,用户可以通过Http请求来访问我们发布的项目。关于HTTP协议这里不做过多的介绍,相信大家也很熟悉,我们重点介绍Servlet容器。

首先我们来看这张图

Web全栈工程师修炼之路 -- 构建Tomcat源码_第12张图片

 

       在Tomcat内部逻辑上主要是有两个部分组成,一部分是接受客户端的HTTP请求的HTTP服务器,另一部分是处理Servlet的Servlet容器,HTTP服务器不直接处理业务请求,而是把请求转发给Servlet容器来进行处理,Servlet容器通过接口来调用实际的业务代码来完成用户的请求。 这样设计的好处就是达到了HTTP服务器和具体以的业务代码解耦的目的。

        我们都知道Servlet容器和Servlet接口共同组成了一套规范,叫做Servlet规范,在tomcat内部对这个规范做了具体的实现,同时还让他们具备了HTTP服务器的功能。

      我们可以回忆一下,在学习JavaWeb的时候,我们要实现某种业务功能,只需要实现一个Servlet,并把它注册Tomcat(Servlet容器)中,最后tomcat就可以帮我们完成剩下的工作了,现在是不是知道了原因了

      接下来我们再来看看tomcat的源码,上面我们已经到了Tomcat 的主启动类,我们来看一下main方法里面的内容

 public static void main(String args[]) {

        if (daemon == null) {
            // Don't set daemon until init() has completed
            Bootstrap bootstrap = new Bootstrap();
            try {
                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")) {
                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 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);
        }

    }

 

我们发现上述代码中有一行是bootstrap.init(); 这里我们可以跟踪进去看一下

 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");
        Class startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");
        Object startupInstance = startupClass.getConstructor().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;

    }

 

     我们发现这个方法其实是加载了 org.apache.catalina.startup.Catalina这个类,这里我们可以理解成bootstrap.init();方法其实就是在初始化org.apache.catalina.startup.Catalina这个类。也就是我们在启动tomcat的时候经常看到的日志信息中的那个catalina。

这里先给大家介绍一下Catalina这个对象,也被称为Catalina容器。其实大家应该也隐隐的感觉到了,之前给大家介绍的Servlet容器的名字就是叫Catalina。下面我们来看一下这张架构图:

Web全栈工程师修炼之路 -- 构建Tomcat源码_第13张图片

 

       上图就是Tomcat 的完整的架构,这里先简单说一下上图中每个组件的作用首先Catalina容器主要是调用业务代码,Coyote是一个连接器,主要负责通讯功能,Jsaper是JSP的解析工具,JavaEL用来解析服务端的表达式语言、Naming 提供JNDI 服务,Juli 提供日志服务。我们在使用tomcat的功能的时候其实都是被由上述组件共同完成的。、

 这里我们先来说说 Coyote连接器的作用(未完待续。。。。。。。。)

 

 

 

 

 

 

 

你可能感兴趣的:(环境搭建,tomcat)