Tomcat8源码解析(一)

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软件和源码文件下载链接:tomcat.apache.org/download-80…
  • 在这里插入图片描述
  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启动阶段初始化。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/4515.html

(0)
编程小号编程小号
上一篇 2022-12-27 21:28
下一篇 2023-04-10

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注