先码后看 Tomcat怎么启动容器的——Boostrap篇 侵立删

来源:互联网 发布:破解收费辅助软件 编辑:程序博客网 时间:2024/05/16 02:28

转自:http://blog.csdn.net/dao_wolf/article/details/55050434

[plain] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. start "Tomcat" "C:\Program Files\Java\jdk1.7.0_51\bin\java"  
  2. -Djava.util.logging.config.file="D:\Program Files\apache-tomcat-8.0.3\conf\logging.properties"  
  3. -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager  
  4. -Djava.endorsed.dirs="D:\Program Files\apache-tomcat-8.0.3\endorsed"  
  5. -classpath "D:\Program Files\apache-tomcat-8.0.3\bin\bootstrap.jar;D:\Program Files\apache-tomcat-8.0.3\bin\tomcat-juli.jar"  
  6. -Dcatalina.base="D:\Program Files\apache-tomcat-8.0.3"  
  7. -Dcatalina.home="D:\Program Files\apache-tomcat-8.0.3"  
  8. -Djava.io.tmpdir="D:\Program Files\apache-tomcat-8.0.3\temp"  
  9. org.apache.catalina.startup.Bootstrap start  

今天来看看Bootstrap的执行流程

1. 执行static

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

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

static块的作用是设置catalinaBaseFile、catalinaHomeFile

static块:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. static {  
  2.         // Will always be non-null  
  3.         //System.getProperty("user.dir"),获取当前目录  
  4.         //由于是在$CATALINA_HOME\bin下运行的Bootstrap,所以userDir为$CATALINA_HOME\bin  
  5.         String userDir = System.getProperty("user.dir");  
  6.   
  7.         // Home first  
  8.         //Globals是存放全局常量的类  
  9.         //Globals.CATALINA_HOME_PROP = "catalina.home"  
  10.         //catalina.home在运行Bootstrap时已设置(Tomcat的根目录)  
  11.         String home = System.getProperty(Globals.CATALINA_HOME_PROP);  
  12.         File homeFile = null;  
  13.           
  14.         //获取Tomcat的绝对路径  
  15.         if (home != null) {  
  16.             File f = new File(home);  
  17.             try {  
  18.                 homeFile = f.getCanonicalFile();  
  19.             } catch (IOException ioe) {  
  20.                 homeFile = f.getAbsoluteFile();  
  21.             }  
  22.         }  
  23.   
  24.         if (homeFile == null) {  
  25.             // First fall-back. See if current directory is a bin directory  
  26.             // in a normal Tomcat install  
  27.             File bootstrapJar = new File(userDir, "bootstrap.jar");  
  28.   
  29.             if (bootstrapJar.exists()) {  
  30.                 File f = new File(userDir, "..");  
  31.                 try {  
  32.                     homeFile = f.getCanonicalFile();  
  33.                 } catch (IOException ioe) {  
  34.                     homeFile = f.getAbsoluteFile();  
  35.                 }  
  36.             }  
  37.         }  
  38.   
  39.         if (homeFile == null) {  
  40.             // Second fall-back. Use current directory  
  41.             File f = new File(userDir);  
  42.             try {  
  43.                 homeFile = f.getCanonicalFile();  
  44.             } catch (IOException ioe) {  
  45.                 homeFile = f.getAbsoluteFile();  
  46.             }  
  47.         }  
  48.           
  49.         //设置catalinaHomeFile  
  50.         catalinaHomeFile = homeFile;  
  51.         System.setProperty(  
  52.                 Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath());  
  53.   
  54.         // Then base  
  55.         String base = System.getProperty(Globals.CATALINA_BASE_PROP);  
  56.         //设置catalinaBaseFile  
  57.         if (base == null) {  
  58.             catalinaBaseFile = catalinaHomeFile;  
  59.         } else {  
  60.             File baseFile = new File(base);  
  61.             try {  
  62.                 baseFile = baseFile.getCanonicalFile();  
  63.             } catch (IOException ioe) {  
  64.                 baseFile = baseFile.getAbsoluteFile();  
  65.             }  
  66.             catalinaBaseFile = baseFile;  
  67.         }  
  68.         System.setProperty(  
  69.                 Globals.CATALINA_BASE_PROP, catalinaBaseFile.getPath());  
  70.     }  

对比getAbsoluteFile()、getCanonicalFile()

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

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

创建Test.Java

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. import java.io.File;  
  2. import java.io.IOException;  
  3.   
  4.   
  5. public class Test {  
  6.     public static void main(String[] args) throws IOException {  
  7.         File f = new File("D://Program Files//apache-tomcat-8.0.3");  
  8.         File aFile = f.getAbsoluteFile();  
  9.         File bFile = f.getCanonicalFile();  
  10.         System.out.println("文件路径不包含.或..");  
  11.         System.out.println("getAbsoluteFile()--->" + aFile.toString());  
  12.         System.out.println("getCanonicalFile()--->" + bFile.toString());  
  13.           
  14.         File f1 = new File("D://Program Files//apache-tomcat-8.0.3//..");  
  15.         File aFile1 = f1.getAbsoluteFile();  
  16.         File bFile1 = f1.getCanonicalFile();  
  17.         System.out.println("文件路径包含.或..");  
  18.         System.out.println("getAbsoluteFile()--->" + aFile1.toString());  
  19.         System.out.println("getCanonicalFile()--->" + bFile1.toString());  
  20.     }  
  21. }  

输出:

[plain] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. 文件路径不包含.或..  
  2. getAbsoluteFile()--->D:\Program Files\apache-tomcat-8.0.3  
  3. getCanonicalFile()--->D:\Program Files\apache-tomcat-8.0.3  
  4. 文件路径包含.或..  
  5. getAbsoluteFile()--->D:\Program Files\apache-tomcat-8.0.3\..  
  6. getCanonicalFile()--->D:\Program Files  

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

2. 执行main函数

main函数:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public static void main(String args[]) {  
  2.   
  3.         if (daemon == null) {  
  4.             // Don't set daemon until init() has completed  
  5.     //***2.1***  
  6.             Bootstrap bootstrap = new Bootstrap();  
  7.             try {  
  8.     //***2.2***  
  9.                 bootstrap.init();  
  10.             } catch (Throwable t) {  
  11.                 handleThrowable(t);  
  12.                 t.printStackTrace();  
  13.                 return;  
  14.             }  
  15. <span style="white-space:pre">  </span>  
  16.             daemon = bootstrap;  
  17.         } else {  
  18.             // When running as a service the call to stop will be on a new  
  19.             // thread so make sure the correct class loader is used to prevent  
  20.             // a range of class not found exceptions.  
  21.             Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);  
  22.         }  
  23.               
  24.     //***2.3***  
  25.         try {  
  26.             String command = "start";  
  27.             if (args.length > 0) {  
  28.                 command = args[args.length - 1];  
  29.             }  
  30.   
  31.             if (command.equals("startd")) {  
  32.                 args[args.length - 1] = "start";  
  33.                 daemon.load(args);  
  34.                 daemon.start();  
  35.             } else if (command.equals("stopd")) {  
  36.                 args[args.length - 1] = "stop";  
  37.                 daemon.stop();  
  38.             } else if (command.equals("start")) {  
  39.     //***2.4***  
  40.                 daemon.setAwait(true);  
  41.     //***2.5***  
  42.                 daemon.load(args);  
  43.     //***2.6***  
  44.                 daemon.start();  
  45.             } else if (command.equals("stop")) {  
  46.                 daemon.stopServer(args);  
  47.             } else if (command.equals("configtest")) {  
  48.                 daemon.load(args);  
  49.                 if (null==daemon.getServer()) {  
  50.                     System.exit(1);  
  51.                 }  
  52.                 System.exit(0);  
  53.             } else {  
  54.                 log.warn("Bootstrap: command \"" + command + "\" does not exist.");  
  55.             }  
  56.         } catch (Throwable t) {  
  57.             // Unwrap the Exception for clearer error reporting  
  58.             if (t instanceof InvocationTargetException &&  
  59.                     t.getCause() != null) {  
  60.                 t = t.getCause();  
  61.             }  
  62.             handleThrowable(t);  
  63.             t.printStackTrace();  
  64.             System.exit(1);  
  65.         }  
  66.   
  67.  }  

2.1 创建Bootstrap

2.2 调用Bootstrap.init()

init方法:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public void init() throws Exception {  
  2.           
  3.     //创建commonLoader、catalinaLoader、sharedLoader  
  4.         initClassLoaders();  
  5.           
  6.     //为当前线程设置ClassLoader  
  7.         Thread.currentThread().setContextClassLoader(catalinaLoader);  
  8.           
  9.     //设置SecurityClassLoad。具体作用还不清楚。。。  
  10.         SecurityClassLoad.securityClassLoad(catalinaLoader);  
  11.   
  12.         // Load our startup class and call its process() method  
  13.         if (log.isDebugEnabled())  
  14.             log.debug("Loading startup class");  
  15.     //通过反射实例化Catalina  
  16.         Class<?> startupClass =  
  17.             catalinaLoader.loadClass  
  18.             ("org.apache.catalina.startup.Catalina");  
  19.         Object startupInstance = startupClass.newInstance();  
  20.   
  21.         // Set the shared extensions class loader  
  22.         if (log.isDebugEnabled())  
  23.             log.debug("Setting startup class properties");  
  24.         String methodName = "setParentClassLoader";  
  25.         Class<?> paramTypes[] = new Class[1];  
  26.         paramTypes[0] = Class.forName("java.lang.ClassLoader");  
  27.         Object paramValues[] = new Object[1];  
  28.         paramValues[0] = sharedLoader;  
  29.         //通过反射设置Catalina的parentClassLoader  
  30.         Method method =  
  31.             startupInstance.getClass().getMethod(methodName, paramTypes);  
  32.         method.invoke(startupInstance, paramValues);  
  33.           
  34.     //将实例化的Catalina赋值给catalinaDaemon  
  35.         catalinaDaemon = startupInstance;  
  36.   
  37.    }  

initClassLoaders方法:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. private void initClassLoaders() {  
  2.         try {  
  3.             //创建commonLoader  
  4.             commonLoader = createClassLoader("common"null);  
  5.             if( commonLoader == null ) {  
  6.                 // no config file, default to this loader - we might be in a 'single' env.  
  7.                 commonLoader=this.getClass().getClassLoader();  
  8.             }  
  9.             //创建catalinaLoader、sharedLoader  
  10.             catalinaLoader = createClassLoader("server", commonLoader);  
  11.             sharedLoader = createClassLoader("shared", commonLoader);  
  12.         } catch (Throwable t) {  
  13.             handleThrowable(t);  
  14.             log.error("Class loader creation threw exception", t);  
  15.             System.exit(1);  
  16.         }  
  17.    }  

createClassLoader方法:

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. private ClassLoader createClassLoader(String name, ClassLoader parent)  
  2.         throws Exception {  
  3.     //CatalinaProperties解析$CATALINA_HOME\conf\catalina.properties,  
  4.     //并将catalina.properties内的属性存为系统属性  
  5.     //catalina.properties内common.loader="${catalina.base}/lib",  
  6.     //"${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"  
  7.         //读取common.loader  
  8.         String value = CatalinaProperties.getProperty(name + ".loader");  
  9.         if ((value == null) || (value.equals("")))  
  10.             return parent;  
  11.     //将${catalina.base},${catalina.home}替换为Tomcat的绝对路径  
  12.         value = replace(value);  
  13.   
  14.         List<Repository> repositories = new ArrayList<>();  
  15.   
  16.         String[] repositoryPaths = getPaths(value);  
  17.   
  18.         for (String repository : repositoryPaths) {  
  19.             // Check for a JAR URL repository  
  20.             try {  
  21.                 @SuppressWarnings("unused")  
  22.                 URL url = new URL(repository);  
  23.                 repositories.add(  
  24.                         new Repository(repository, RepositoryType.URL));  
  25.                 continue;  
  26.             } catch (MalformedURLException e) {  
  27.                 // Ignore  
  28.             }  
  29.   
  30.             // Local repository  
  31.             if (repository.endsWith("*.jar")) {  
  32.                 repository = repository.substring  
  33.                     (0, repository.length() - "*.jar".length());  
  34.                 repositories.add(  
  35.                         new Repository(repository, RepositoryType.GLOB));  
  36.             } else if (repository.endsWith(".jar")) {  
  37.                 repositories.add(  
  38.                         new Repository(repository, RepositoryType.JAR));  
  39.             } else {  
  40.                 repositories.add(  
  41.                         new Repository(repository, RepositoryType.DIR));  
  42.             }  
  43.         }  
  44.           
  45.     //ClassLoaderFactory依据repositories的内容创建ClassLoader  
  46.         return ClassLoaderFactory.createClassLoader(repositories, parent);  
  47.     }  

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

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

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public static ClassLoader createClassLoader(List<Repository> repositories,  
  2.                                                 final ClassLoader parent)  
  3.         throws Exception {  
  4.   
  5.         if (log.isDebugEnabled())  
  6.             log.debug("Creating new class loader");  
  7.   
  8.         // Construct the "class path" for this class loader  
  9.         Set<URL> set = new LinkedHashSet<>();  
  10.   
  11.         if (repositories != null) {  
  12.             for (Repository repository : repositories)  {  
  13.                 if (repository.getType() == RepositoryType.URL) {  
  14.                     URL url = new URL(repository.getLocation());  
  15.                     if (log.isDebugEnabled())  
  16.                         log.debug("  Including URL " + url);  
  17.                     set.add(url);  
  18.                 } else if (repository.getType() == RepositoryType.DIR) {  
  19.                     File directory = new File(repository.getLocation());  
  20.                     directory = directory.getCanonicalFile();  
  21.                     if (!validateFile(directory, RepositoryType.DIR)) {  
  22.                         continue;  
  23.                     }  
  24.                     URL url = directory.toURI().toURL();  
  25.                     if (log.isDebugEnabled())  
  26.                         log.debug("  Including directory " + url);  
  27.                     set.add(url);  
  28.                 } else if (repository.getType() == RepositoryType.JAR) {  
  29.                     File file=new File(repository.getLocation());  
  30.                     file = file.getCanonicalFile();  
  31.                     if (!validateFile(file, RepositoryType.JAR)) {  
  32.                         continue;  
  33.                     }  
  34.                     URL url = file.toURI().toURL();  
  35.                     if (log.isDebugEnabled())  
  36.                         log.debug("  Including jar file " + url);  
  37.                     set.add(url);  
  38.                 } else if (repository.getType() == RepositoryType.GLOB) {  
  39.                     File directory=new File(repository.getLocation());  
  40.                     directory = directory.getCanonicalFile();  
  41.                     if (!validateFile(directory, RepositoryType.GLOB)) {  
  42.                         continue;  
  43.                     }  
  44.                     if (log.isDebugEnabled())  
  45.                         log.debug("  Including directory glob "  
  46.                             + directory.getAbsolutePath());  
  47.                     String filenames[] = directory.list();  
  48.                     for (int j = 0; j < filenames.length; j++) {  
  49.                         String filename = filenames[j].toLowerCase(Locale.ENGLISH);  
  50.                         if (!filename.endsWith(".jar"))  
  51.                             continue;  
  52.                         File file = new File(directory, filenames[j]);  
  53.                         file = file.getCanonicalFile();  
  54.                         if (!validateFile(file, RepositoryType.JAR)) {  
  55.                             continue;  
  56.                         }  
  57.                         if (log.isDebugEnabled())  
  58.                             log.debug("    Including glob jar file "  
  59.                                 + file.getAbsolutePath());  
  60.                         URL url = file.toURI().toURL();  
  61.                         set.add(url);  
  62.                     }  
  63.                 }  
  64.             }  
  65.         }  

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()

[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. if (await) {  
  2.             await();  
  3.             stop();  
  4.         }  
[java] view plain copy
 
 在CODE上查看代码片派生到我的代码片
  1. public void await() {  
  2.   
  3.         getServer().await();  
  4.   
  5.     }  

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)将触发的动作


2.6 调用Bootstrap.start()

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




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

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

阅读全文
0 0
原创粉丝点击