博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
tomcat之启动类BootStrap
阅读量:4189 次
发布时间:2019-05-26

本文共 27174 字,大约阅读时间需要 90 分钟。

tomcat之BootStrap

BootStrap启动类

BootStrap是怎样启动tomcat的呢?

BootStrap做了什么工作呢?

BootStrap启动tomcat

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, or null     * 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,     * or null 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        Set
set = 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(List
repositories, 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启动目前只用了工厂类里面的一个方法而以。

当类加载器通过工厂类产生之后,BootStrap会利用类的反射将shareLoader加载器作为参数,调用catalina的setParentClassLoader方法。也就是设置了catalina的加载器,代码块如下所示:

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;    }

当BootStrap初始化玩开始setAwait,目前BootStrap会利用类的反射调用Catalina类里面的setAwait方法,然而setAwait方法进行简单的赋值,初始化await是false如代码所示

protected boolean await = false;public void setAwait(boolean b) {    await = b;}

设置完await之后,BootStrap开始调用,load(String[] arguments),该方法会利用类的反射调用调用catalina里面load()方法:代码如下所示:

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); }

接下来,我们一探究竟,catalina的load()这个家伙到底做了些什么事情。

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");        }    }

load()方法虽然他里面的代码很长,我们需要抓住里面一些重要的逻辑,逻辑一:loaded 初始化是否被加载,初始值为false.逻辑二:初始化目录,初始化命名资源。接下来初始化目录干了什么:代码块如下:

protected void initDirs() {

String temp = System.getProperty(“java.io.tmpdir”);
if (temp == null || (!(new File(temp)).isDirectory())) {
log.error(sm.getString(“embedded.notmp”, temp));
}
}

Java System.getProperty(“java.io.tmpdir”) 获取系统临时目录

System.getProperty(“java.io.tmpdir”) 是获取操作系统的缓存临时目录

这里的临时目录是 /catalina-home/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 );            }        }    }

Digester digester = createStartDigester();该方法利用Digester技术来解析Server.xml文档。然后创建相应的对象。Server.xml文档需要被输入流对象读取转换成输入流,然后digester对象对输入流按照指定好的规则进行解析处理。

digester创建Server对象,并在server对象里面设置service对象。

@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/

你可能感兴趣的文章
头痛与早餐
查看>>
[转]在ASP.NET 2.0中操作数据::创建一个数据访问层
查看>>
Linux命令之chmod详解
查看>>
【java小程序实战】小程序注销功能实现
查看>>
webkit入门准备
查看>>
在Ubuntu 12.04 64bit上配置,安装和运行go程序
查看>>
ATS程序功能和使用方法详解
查看>>
常用Linux命令总结
查看>>
Tafficserver旁路接入方案综述
查看>>
在CentOS 6.3 64bit上如何从源码生成rpm包?
查看>>
利用lua中的string.gsub来巧妙实现json中字段的正则替换
查看>>
ATS名词术语(待续)
查看>>
ATS缓存相关话题
查看>>
ATS中的RAM缓存简介
查看>>
CDN和Web Cache领域相关的经典书籍推荐
查看>>
在Oracle VM VirtualBox中如何安装64位虚拟机系统
查看>>
安装和使用Oracle VM VirtualBox中的要点,注意事项和遇到的问题
查看>>
ATS上的hosting.config和volume.config文件解读
查看>>
将日志中的指定字段对齐显示输出
查看>>
Linux上chown命令的高级用法
查看>>