首页 > 其他 > 详细

Tomcat 8(七)解读Bootstrap

时间:2014-04-12 12:34:50      阅读:577      评论:0      收藏:0      [点我收藏+]

上一篇文章分析了通过startup.bat启动Tomcat相当于执行如下代码,即运行Bootstrap start

start "Tomcat" "C:\Program Files\Java\jdk1.7.0_51\bin\java"
-Djava.util.logging.config.file="D:\Program Files\apache-tomcat-8.0.3\conf\logging.properties"
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.endorsed.dirs="D:\Program Files\apache-tomcat-8.0.3\endorsed"
-classpath "D:\Program Files\apache-tomcat-8.0.3\bin\bootstrap.jar;D:\Program Files\apache-tomcat-8.0.3\bin\tomcat-juli.jar"
-Dcatalina.base="D:\Program Files\apache-tomcat-8.0.3"
-Dcatalina.home="D:\Program Files\apache-tomcat-8.0.3"
-Djava.io.tmpdir="D:\Program Files\apache-tomcat-8.0.3\temp"
org.apache.catalina.startup.Bootstrap start

今天来看看Bootstrap的执行流程

1. 执行static

为什么没有执行main方法,而先执行static块呢?

原来一个类的运行,JVM做会以下几件事情:类装载、链接、初始化、实例化,而初始化阶段做的事情是初始化静态变量和执行静态方法等

static块的作用是设置catalinaBaseFile、catalinaHomeFile

static块:

static {
        // Will always be non-null
		//System.getProperty("user.dir"),获取当前目录
		//由于是在$CATALINA_HOME\bin下运行的Bootstrap,所以userDir为$CATALINA_HOME\bin
        String userDir = System.getProperty("user.dir");

        // Home first
		//Globals是存放全局常量的类
		//Globals.CATALINA_HOME_PROP = "catalina.home"
		//catalina.home在运行Bootstrap时已设置(Tomcat的根目录)
        String home = System.getProperty(Globals.CATALINA_HOME_PROP);
        File homeFile = null;
		
		//获取Tomcat的绝对路径
        if (home != null) {
            File f = new File(home);
            try {
                homeFile = f.getCanonicalFile();
            } catch (IOException ioe) {
                homeFile = f.getAbsoluteFile();
            }
        }

        if (homeFile == null) {
            // First fall-back. See if current directory is a bin directory
            // in a normal Tomcat install
            File bootstrapJar = new File(userDir, "bootstrap.jar");

            if (bootstrapJar.exists()) {
                File f = new File(userDir, "..");
                try {
                    homeFile = f.getCanonicalFile();
                } catch (IOException ioe) {
                    homeFile = f.getAbsoluteFile();
                }
            }
        }

        if (homeFile == null) {
            // Second fall-back. Use current directory
            File f = new File(userDir);
            try {
                homeFile = f.getCanonicalFile();
            } catch (IOException ioe) {
                homeFile = f.getAbsoluteFile();
            }
        }
		
		//设置catalinaHomeFile
        catalinaHomeFile = homeFile;
        System.setProperty(
                Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath());

        // Then base
        String base = System.getProperty(Globals.CATALINA_BASE_PROP);
		//设置catalinaBaseFile
        if (base == null) {
            catalinaBaseFile = catalinaHomeFile;
        } else {
            File baseFile = new File(base);
            try {
                baseFile = baseFile.getCanonicalFile();
            } catch (IOException ioe) {
                baseFile = baseFile.getAbsoluteFile();
            }
            catalinaBaseFile = baseFile;
        }
        System.setProperty(
                Globals.CATALINA_BASE_PROP, catalinaBaseFile.getPath());
    }

对比getAbsoluteFile()、getCanonicalFile()

getAbsoluteFile获取绝对文件(如果文件路径中包含...,不解析)

getCanonicalFile获取经典文件(如果文件路径中包含...,解析)

创建Test.java

import java.io.File;
import java.io.IOException;


public class Test {
	public static void main(String[] args) throws IOException {
		File f = new File("D://Program Files//apache-tomcat-8.0.3");
		File aFile = f.getAbsoluteFile();
		File bFile = f.getCanonicalFile();
		System.out.println("文件路径不包含.或..");
		System.out.println("getAbsoluteFile()--->" + aFile.toString());
		System.out.println("getCanonicalFile()--->" + bFile.toString());
		
		File f1 = new File("D://Program Files//apache-tomcat-8.0.3//..");
		File aFile1 = f1.getAbsoluteFile();
		File bFile1 = f1.getCanonicalFile();
		System.out.println("文件路径包含.或..");
		System.out.println("getAbsoluteFile()--->" + aFile1.toString());
		System.out.println("getCanonicalFile()--->" + bFile1.toString());
	}
}

输出:

文件路径不包含.或..
getAbsoluteFile()--->D:\Program Files\apache-tomcat-8.0.3
getCanonicalFile()--->D:\Program Files\apache-tomcat-8.0.3
文件路径包含.或..
getAbsoluteFile()--->D:\Program Files\apache-tomcat-8.0.3\..
getCanonicalFile()--->D:\Program Files

接写来将执行main函数(以下以首次运行Bootstrap start进行解读)

2. 执行main函数

main函数:

public static void main(String args[]) {

        if (daemon == null) {
            // Don‘t set daemon until init() has completed
	//***2.1***
            Bootstrap bootstrap = new Bootstrap();
            try {
	//***2.2***
                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);
        }
			
	//***2.3***
        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")) {
	//***2.4***
                daemon.setAwait(true);
	//***2.5***
                daemon.load(args);
	//***2.6***
                daemon.start();
            } 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.1 创建Bootstrap

2.2 调用Bootstrap.init()

init方法:

public void init() throws Exception {
		
	//创建commonLoader、catalinaLoader、sharedLoader
        initClassLoaders();
		
	//为当前线程设置ClassLoader
        Thread.currentThread().setContextClassLoader(catalinaLoader);
		
	//设置SecurityClassLoad。具体作用还不清楚。。。
        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");
        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;
        //通过反射设置Catalina的parentClassLoader
		Method method =
            startupInstance.getClass().getMethod(methodName, paramTypes);
        method.invoke(startupInstance, paramValues);
		
	//将实例化的Catalina赋值给catalinaDaemon
        catalinaDaemon = startupInstance;

   }

initClassLoaders方法:

private void initClassLoaders() {
        try {
			//创建commonLoader
            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
            catalinaLoader = createClassLoader("server", commonLoader);
            sharedLoader = createClassLoader("shared", commonLoader);
        } catch (Throwable t) {
            handleThrowable(t);
            log.error("Class loader creation threw exception", t);
            System.exit(1);
        }
   }

createClassLoader方法:

private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {
	//CatalinaProperties解析$CATALINA_HOME\conf\catalina.properties,
	//并将catalina.properties内的属性存为系统属性
	//catalina.properties内common.loader="${catalina.base}/lib",
	//"${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
        //读取common.loader
		String value = CatalinaProperties.getProperty(name + ".loader");
        if ((value == null) || (value.equals("")))
            return parent;
	//将${catalina.base},${catalina.home}替换为Tomcat的绝对路径
        value = replace(value);

        List<Repository> repositories = new ArrayList<>();

        String[] repositoryPaths = getPaths(value);

        for (String repository : repositoryPaths) {
            // Check for a JAR URL repository
            try {
                @SuppressWarnings("unused")
                URL url = new URL(repository);
                repositories.add(
                        new Repository(repository, RepositoryType.URL));
                continue;
            } catch (MalformedURLException e) {
                // Ignore
            }

            // Local repository
            if (repository.endsWith("*.jar")) {
                repository = repository.substring
                    (0, repository.length() - "*.jar".length());
                repositories.add(
                        new Repository(repository, RepositoryType.GLOB));
            } else if (repository.endsWith(".jar")) {
                repositories.add(
                        new Repository(repository, RepositoryType.JAR));
            } else {
                repositories.add(
                        new Repository(repository, RepositoryType.DIR));
            }
        }
		
	//ClassLoaderFactory依据repositories的内容创建ClassLoader
        return ClassLoaderFactory.createClassLoader(repositories, parent);
    }

ClassLoaderFactory依据repositories的内容创建ClassLoader时,repositories包含的四个值均是$CATALINA_HOME\lib这个路径。那么创建的ClassLoader会不会有重复的jar呢?

查看ClassLoaderFactory.createClassLoader方法,即可找到答案

public static ClassLoader createClassLoader(List<Repository> repositories,
                                                final ClassLoader parent)
        throws Exception {

        if (log.isDebugEnabled())
            log.debug("Creating new class loader");

        // Construct the "class path" for this class loader
        Set<URL> set = new LinkedHashSet<>();

        if (repositories != null) {
            for (Repository repository : repositories)  {
                if (repository.getType() == RepositoryType.URL) {
                    URL url = new URL(repository.getLocation());
                    if (log.isDebugEnabled())
                        log.debug("  Including URL " + url);
                    set.add(url);
                } else if (repository.getType() == RepositoryType.DIR) {
                    File directory = new File(repository.getLocation());
                    directory = directory.getCanonicalFile();
                    if (!validateFile(directory, RepositoryType.DIR)) {
                        continue;
                    }
                    URL url = directory.toURI().toURL();
                    if (log.isDebugEnabled())
                        log.debug("  Including directory " + url);
                    set.add(url);
                } else if (repository.getType() == RepositoryType.JAR) {
                    File file=new File(repository.getLocation());
                    file = file.getCanonicalFile();
                    if (!validateFile(file, RepositoryType.JAR)) {
                        continue;
                    }
                    URL url = file.toURI().toURL();
                    if (log.isDebugEnabled())
                        log.debug("  Including jar file " + url);
                    set.add(url);
                } else if (repository.getType() == RepositoryType.GLOB) {
                    File directory=new File(repository.getLocation());
                    directory = directory.getCanonicalFile();
                    if (!validateFile(directory, RepositoryType.GLOB)) {
                        continue;
                    }
                    if (log.isDebugEnabled())
                        log.debug("  Including directory glob "
                            + directory.getAbsolutePath());
                    String filenames[] = directory.list();
                    for (int j = 0; j < filenames.length; j++) {
                        String filename = filenames[j].toLowerCase(Locale.ENGLISH);
                        if (!filename.endsWith(".jar"))
                            continue;
                        File file = new File(directory, filenames[j]);
                        file = file.getCanonicalFile();
                        if (!validateFile(file, RepositoryType.JAR)) {
                            continue;
                        }
                        if (log.isDebugEnabled())
                            log.debug("    Including glob jar file "
                                + file.getAbsolutePath());
                        URL url = file.toURI().toURL();
                        set.add(url);
                    }
                }
            }
        }

ClassLoaderFactory在遍历repositories时,将jar文件的URL放在LinkedHashSet里,而LinkedHashSet里不会添加重复的数据。因此,创建的ClassLoader不会有重复的jar

2.3 解析参数

2.4 调用Bootstrap.setAwait(true)

Bootstrap.setAwait(true)内部通过反射,设置Catalinaawait属性(默认为false)true

启动Catalina过程中,当CatalinaTomcat的所有组件启动之后,会检查await属性,如果为true,会调用Catalina.await(),而Catalina.await()又会调用其内部的Serverawait()

if (await) {
            await();
            stop();
        }
public void await() {

        getServer().await();

    }

Server.await()包含一个while循环,此循环用于监听指定socket端口(默认为8005)的连接,当某个连接传入的参数为”SHUTDOWN”(默认为”SHUTDOWN”)时,终止此while循环(端口号和终止while循环的参数,在server.xmlServer标签设置)

Server.await()用来维持Bootstrapmain方法(main thread)处于运行状态,而线程池中监听http请求的线程是守护线程(daemon thread)

Tomcat的指定端口接收到关闭命令时,Server.await()内的while循环终止,然后Catalina会调用stop()方法,关闭Tomcat的所有组件,最终Bootstrapmain thread终止,Tomcat关闭

2.5 调用Bootstrap.load(args)

Bootstrap.load(args)内部通过反射调用Catalina.load(args),Catalina将利用Digest(Digest详解将在后面整理)解析server.xml,创建相应组件的实例,之后调用Server.init()Server初始化时,又会调用其内部的Serviceinit方法,即调用某一组件的init方法时,将触发其子组件的init方法

执行Bootstrap.load(args)将触发的动作

bubuko.com,布布扣

2.6 调用Bootstrap.start()

执行Bootstrap.start()将触发的动作

bubuko.com,布布扣



带注释的 Bootstrap.java文件下载地址:

http://download.csdn.net/detail/flyliuweisky547/7179505


 

Tomcat 8(七)解读Bootstrap,布布扣,bubuko.com

Tomcat 8(七)解读Bootstrap

原文:http://blog.csdn.net/flyliuweisky547/article/details/23464245

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!