本文共 27174 字,大约阅读时间需要 90 分钟。
BootStrap作为tomcat启动入口,因此BootStrap就有main函数入口,即一定有public static void main(String[] args){}; main函数由jvm底层函数调用,开启了一个jvm进程。下面附上BootStrap的main函数源码
public static void main(String args[]) { synchronized (daemonLock) { 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); } }
接下来对上面的main函数做一些陈述,首先我们看到main函数第一行代码就有synchronized(daemonLock),知识有限,不知道为什么要加上锁,先往后分析,daemon是BootStrap的引用,一开始daemon为空,然后new了一个BootStrap对象,但没有立马把这个对象赋值给daemon引用,而是在等初始化操作完成后,再去赋值。其实在初始化之前,我们调用main方法的时候,需要加载类BootStrap,在加载BootStrap类,类中的静态代码内容会被加载和初始化。因此,我们可以先看一下静态代码块里面的内容。下面我直接在代码中加解释.
static { // Will always be non-null,user.dir:用户目录,这个就是当前代码所在的根目录,可以看 // 可以看下面的图一 String userDir = System.getProperty("user.dir"); // Home first,这里的Globals类,里面定义了常用的包文件路径,常用的类全路径。 //针对,Globals.CATALINA_HOME_PROP 在类中的定义为 public static final String //CATALINA_HOME_PROP = "catalina.home"; String home = System.getProperty(Globals.CATALINA_HOME_PROP); File homeFile = null;// 如果设置了catalina.home的文件路径。 if (home != null) { //新建一个管理文件的对象 File f = new File(home); try { //homeFile对象获取了home文件夹下面的所有的文件对象,如下面图二。 homeFile = f.getCanonicalFile(); } catch (IOException ioe) { homeFile = f.getAbsoluteFile(); } } //这里寻找是用户目录下是否存在BootStrap如果存在,就用这个目录作为catalina_home: 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(); } } } //还是使用用户当前目录作为catalina_home if (homeFile == null) { // Second fall-back. Use current directory File f = new File(userDir); try { homeFile = f.getCanonicalFile(); } catch (IOException ioe) { homeFile = f.getAbsoluteFile(); } } //最后把homeFile赋值给catalinaHome catalinaHomeFile = homeFile; System.setProperty( Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath()); // Then base,主要是设置catalina_base的文件路径和文件管理对象 String base = System.getProperty(Globals.CATALINA_BASE_PROP); 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()); }图一 图二
经过对static文件的分析,我们知道,static代码块的主要作用初始化catalinahome和catalinebasehome.
接下来我们分析init()方法到底干了什么事情。
/**
* Initialize daemon. * @throws Exception Fatal initialization error */ 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;}
上面的代码中,我们可以看到初始化第一步的工作就是初始化类加载器,第二给安全类加载器设置类加载器,第三步使用类加载器,加载catalina类,哈哈。然后利用类的反射,new了个catalina对象出来,真是神奇啊。竟然还能用类的反射给catalina类设置类加载器哈哈,最后把new的实列赋值给catalinaDaemon引用了。
接下来分析initClass类里面都干了些什么东东
private void initClassLoaders() { try { 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 = createClassLoader("server", commonLoader); sharedLoader = createClassLoader("shared", commonLoader); } catch (Throwable t) { handleThrowable(t); log.error("Class loader creation threw exception", t); System.exit(1); } }
看了上代码,在产生三个类加载器,commonLoader类加载,cataLinaLoader,sharedLoader;后面两者父加载器是commonLoader类加载器,而commonLoader的父在器是SystemLoader加载器。
接下来看下,具体类的加载器具体是怎样的产生的吧,这就不得不说tomcat的类加载器工厂啦 ,真是太厉害了,tomcat.其实所谓的类加载其实就是一个功能,帮你读取某个地方的class文件。我们主要还是看下,工厂类把。源码如下。
public final class ClassLoaderFactory { private static final Log log = LogFactory.getLog(ClassLoaderFactory.class); // --------------------------------------------------------- Public Methods /** * Create and return a new class loader, based on the configuration * defaults and the specified directory paths: * * @param unpacked Array of pathnames to unpacked directories that should * be added to the repositories of the class loader, ornull
* for no unpacked directories to be considered * @param packed Array of pathnames to directories containing JAR files * that should be added to the repositories of the class loader, * ornull
for no directories of JAR files to be considered * @param parent Parent class loader for the new class loader, or *null
for the system class loader. * @return the new class loader * * @exception Exception if an error occurs constructing the class loader */ public static ClassLoader createClassLoader(File unpacked[], File packed[], final ClassLoader parent) throws Exception { if (log.isDebugEnabled()) log.debug("Creating new class loader"); // Construct the "class path" for this class loader Setset = new LinkedHashSet<>(); // Add unpacked directories if (unpacked != null) { for (int i = 0; i < unpacked.length; i++) { File file = unpacked[i]; if (!file.canRead()) continue; file = new File(file.getCanonicalPath() + File.separator); URL url = file.toURI().toURL(); if (log.isDebugEnabled()) log.debug(" Including directory " + url); set.add(url); } } // Add packed directory JAR files if (packed != null) { for (int i = 0; i < packed.length; i++) { File directory = packed[i]; if (!directory.isDirectory() || !directory.canRead()) continue; String filenames[] = directory.list(); if (filenames == null) { continue; } 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]); if (log.isDebugEnabled()) log.debug(" Including jar file " + file.getAbsolutePath()); URL url = file.toURI().toURL(); set.add(url); } } } // Construct the class loader itself final URL[] array = set.toArray(new URL[set.size()]); return AccessController.doPrivileged( new PrivilegedAction () { @Override public URLClassLoader run() { if (parent == null) return new URLClassLoader(array); else return new URLClassLoader(array, parent); } }); } /** * Create and return a new class loader, based on the configuration * defaults and the specified directory paths: * * @param repositories List of class directories, jar files, jar directories * or URLS that should be added to the repositories of * the class loader. * @param parent Parent class loader for the new class loader, or * null
for the system class loader. * @return the new class loader * * @exception Exception if an error occurs constructing the class loader */ public static ClassLoader createClassLoader(Listrepositories, final ClassLoader parent) throws Exception { if (log.isDebugEnabled()) log.debug("Creating new class loader"); // Construct the "class path" for this class loader Set set = new LinkedHashSet<>(); if (repositories != null) { for (Repository repository : repositories) { if (repository.getType() == RepositoryType.URL) { URL url = buildClassLoaderUrl(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 = buildClassLoaderUrl(directory); 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 = buildClassLoaderUrl(file); 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(); if (filenames == null) { continue; } 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 = buildClassLoaderUrl(file); set.add(url); } } } } // Construct the class loader itself final URL[] array = set.toArray(new URL[set.size()]); if (log.isDebugEnabled()) for (int i = 0; i < array.length; i++) { log.debug(" location " + i + " is " + array[i]); } return AccessController.doPrivileged( new PrivilegedAction () { @Override public URLClassLoader run() { if (parent == null) return new URLClassLoader(array); else return new URLClassLoader(array, parent); } }); } private static boolean validateFile(File file, RepositoryType type) throws IOException { if (RepositoryType.DIR == type || RepositoryType.GLOB == type) { if (!file.isDirectory() || !file.canRead()) { String msg = "Problem with directory [" + file + "], exists: [" + file.exists() + "], isDirectory: [" + file.isDirectory() + "], canRead: [" + file.canRead() + "]"; File home = new File (Bootstrap.getCatalinaHome()); home = home.getCanonicalFile(); File base = new File (Bootstrap.getCatalinaBase()); base = base.getCanonicalFile(); File defaultValue = new File(base, "lib"); // Existence of ${catalina.base}/lib directory is optional. // Hide the warning if Tomcat runs with separate catalina.home // and catalina.base and that directory is absent. if (!home.getPath().equals(base.getPath()) && file.getPath().equals(defaultValue.getPath()) && !file.exists()) { log.debug(msg); } else { log.warn(msg); } return false; } } else if (RepositoryType.JAR == type) { if (!file.canRead()) { log.warn("Problem with JAR file [" + file + "], exists: [" + file.exists() + "], canRead: [" + file.canRead() + "]"); return false; } } return true; } /* * These two methods would ideally be in the utility class * org.apache.tomcat.util.buf.UriUtil but that class is not visible until * after the class loaders have been constructed. */ private static URL buildClassLoaderUrl(String urlString) throws MalformedURLException { // URLs passed to class loaders may point to directories that contain // JARs. If these URLs are used to construct URLs for resources in a JAR // the URL will be used as is. It is therefore necessary to ensure that // the sequence "!/" is not present in a class loader URL. String result = urlString.replaceAll("!/", "%21/"); return new URL(result); } private static URL buildClassLoaderUrl(File file) throws MalformedURLException { // Could be a directory or a file String fileUrlString = file.toURI().toString(); fileUrlString = fileUrlString.replaceAll("!/", "%21/"); return new URL(fileUrlString); } public enum RepositoryType { DIR, GLOB, JAR, URL } public static class Repository { private final String location; private final RepositoryType type; public Repository(String location, RepositoryType type) { this.location = location; this.type = type; } public String getLocation() { return location; } public RepositoryType getType() { return type; } }}
上述代码需要细细品,tomcat启动目前只用了工厂类里面的一个方法而以。
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;
## catalina中的方法 /** * Set the shared extensions class loader. * * @param parentClassLoader The shared extensions class loader. */ public void setParentClassLoader(ClassLoader parentClassLoader) { this.parentClassLoader = parentClassLoader; }
protected boolean await = false;public void setAwait(boolean b) { await = b;}
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; } Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes); if (log.isDebugEnabled()) { log.debug("Calling startup class " + method); } method.invoke(catalinaDaemon, param); }
public void load() { if (loaded) { return; } loaded = true; long t1 = System.nanoTime(); initDirs(); // Before digester - it may be needed initNaming(); // Create and execute our Digester Digester digester = createStartDigester(); 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); } } if (inputStream == null) { try { inputStream = getClass().getClassLoader() .getResourceAsStream(getConfigFile()); inputSource = new InputSource (getClass().getClassLoader() .getResource(getConfigFile()).toString()); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug(sm.getString("catalina.configFail", getConfigFile()), e); } } } // This should be included in catalina.jar // Alternative: don't bother with xml, just create it manually. if (inputStream == null) { try { inputStream = getClass().getClassLoader() .getResourceAsStream("server-embed.xml"); inputSource = new InputSource (getClass().getClassLoader() .getResource("server-embed.xml").toString()); } catch (Exception e) { if (log.isDebugEnabled()) { log.debug(sm.getString("catalina.configFail", "server-embed.xml"), e); } } } if (inputStream == null || inputSource == null) { if (file == null) { log.warn(sm.getString("catalina.configFail", getConfigFile() + "] or [server-embed.xml]")); } else { log.warn(sm.getString("catalina.configFail", file.getAbsolutePath())); if (file.exists() && !file.canRead()) { log.warn("Permissions incorrect, read permission is not allowed on the file."); } } return; } try { inputSource.setByteStream(inputStream); digester.push(this); digester.parse(inputSource); } catch (SAXParseException spe) { log.warn("Catalina.start using " + getConfigFile() + ": " + spe.getMessage()); return; } catch (Exception e) { log.warn("Catalina.start using " + getConfigFile() + ": " , e); return; } } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { // Ignore } } } getServer().setCatalina(this); getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); // Stream redirection initStreams(); // Start the new server try { 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"); } }
protected void initDirs() {
String temp = System.getProperty(“java.io.tmpdir”); if (temp == null || (!(new File(temp)).isDirectory())) { log.error(sm.getString(“embedded.notmp”, temp)); } }protected void initNaming() { // Setting additional variables if (!useNaming) { log.info( "Catalina naming disabled"); System.setProperty("catalina.useNaming", "false"); } else { System.setProperty("catalina.useNaming", "true"); String value = "org.apache.naming"; String oldValue = System.getProperty(javax.naming.Context.URL_PKG_PREFIXES); if (oldValue != null) { value = value + ":" + oldValue; } ***System.setProperty(javax.naming.Context.URL_PKG_PREFIXES, value);*** if( log.isDebugEnabled() ) { log.debug("Setting naming prefix=" + value); } value = System.getProperty (javax.naming.Context.INITIAL_CONTEXT_FACTORY); if (value == null) { ***System.setProperty (javax.naming.Context.INITIAL_CONTEXT_FACTORY, "org.apache.naming.java.javaURLContextFactory");*** } else { log.debug( "INITIAL_CONTEXT_FACTORY already set " + value ); } } }
@Override public void addService(Service service) { service.setServer(this); synchronized (servicesLock) { Service results[] = new Service[services.length + 1]; System.arraycopy(services, 0, results, 0, services.length); results[services.length] = service; services = results; if (getState().isAvailable()) { try { service.start(); } catch (LifecycleException e) { // Ignore } } // Report this property change to interested listeners support.firePropertyChange("service", null, service); } }
转载地址:http://vusoi.baihongyu.com/