转自:http://blog.csdn.net/dao_wolf/article/details/55050434
- 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;
- }
- <span style="white-space:pre"> </span>
- 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)内部通过反射,设置Catalina的await属性(默认为false)为true
启动Catalina过程中,当Catalina将Tomcat的所有组件启动之后,会检查await属性,如果为true,会调用Catalina.await(),而Catalina.await()又会调用其内部的Server的await()
- if (await) {
- await();
- stop();
- }
- public void await() {
- getServer().await();
- }
Server.await()包含一个while循环,此循环用于监听指定socket端口(默认为8005)的连接,当某个连接传入的参数为”SHUTDOWN”(默认为”SHUTDOWN”)时,终止此while循环(端口号和终止while循环的参数,在server.xml的Server标签设置)
Server.await()用来维持Bootstrap的main方法(main thread)处于运行状态,而线程池中监听http请求的线程是守护线程(daemon thread)
当Tomcat的指定端口接收到关闭命令时,Server.await()内的while循环终止,然后Catalina会调用stop()方法,关闭Tomcat的所有组件,最终Bootstrap的main thread终止,Tomcat关闭
2.5 调用Bootstrap.load(args)
Bootstrap.load(args)内部通过反射调用Catalina.load(args),Catalina将利用Digest(Digest详解)解析server.xml,创建相应组件的实例,之后调用Server.init(),Server初始化时,又会调用其内部的Service的init方法,即调用某一组件的init方法时,将触发其子组件的init方法
执行Bootstrap.load(args)将触发的动作
2.6 调用Bootstrap.start()
执行Bootstrap.start()将触发的动作
带注释的 Bootstrap.java文件下载地址: