美文网首页
揭开tomcat神秘的面纱之bootstrap初始化

揭开tomcat神秘的面纱之bootstrap初始化

作者: 一滴水的坚持 | 来源:发表于2018-11-04 18:40 被阅读0次

    在上文揭开tomcat神秘的面纱之启动脚本中,本菜鸟分析到,最终执行会执行以下命令

    /Library/Java/JavaVirtualMachines/jdk1.8.0_131.jdk/Contents/Home/bin/java 
    -Djava.util.logging.config.file=/Users/jetty/Documents/software/apache-tomcat-7.0.76/conf/logging.properties 
    -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager 
    -Djdk.tls.ephemeralDHKeySize=2048 
    -Djava.endorsed.dirs=/Users/jetty/Documents/software/apache-tomcat-7.0.76/endorsed 
    -classpath /Users/jetty/Documents/software/apache-tomcat7.0.76/bin/bootstrap.jar:/Users/jetty/Documents/software/apache-tomcat-7.0.76/bin/tomcat-juli.jar 
    -Dcatalina.base=/Users/jetty/Documents/software/apache-tomcat-7.0.76 
    -Dcatalina.home=/Users/jetty/Documents/software/apache-tomcat-7.0.76 
    -Djava.io.tmpdir=/Users/jetty/Documents/software/apache-tomcat-7.0.76/temp
    org.apache.catalina.startup.Bootstrap start
    

    简单来说就是

    java org.apache.catalina.startup.Bootstrap start
    

    这个bootstrap类从何而来?

    不由得想到上文提到的启动参数

    -classpath /Users/jetty/Documents/software/apache-tomcat7.0.76/bin/bootstrap.jar:/Users/jetty/Documents/software/apache-tomcat-7.0.76/bin/tomcat-juli.jar 
    

    可以看到,在jvm参数中指定了classPath参数,其中提到了bootstrap.jartomcat-juli.jar
    关于classPath该参数,本菜鸟开发过程中也一直没有用到过,不得不百度了一下,才了解到是在启动过程中引用到jar的意思。
    该jar位于bin目录下,如图:

    tomcat/bin路径
    本菜鸟从github上将代码拉了下来,来看看到底tomcat内部是什么样子的,在看代码之前,将《深入剖析tomcat》此书看过了,大学的时候,就已经读过该书,不得不说,毕业后懒惰了,毕业快三年了才想起来重新拿起来读一遍。
    public final class Bootstrap {
        private static final Object daemonLock = new Object(); //初始化并发锁
        private static volatile Bootstrap daemon = null; //单例
        private static final File catalinaBaseFile;   //jvm启动参数catalina.base
        private static final File catalinaHomeFile;//jvm启动参数catalina.home
        private static final Pattern PATH_PATTERN = Pattern.compile("(\".*?\")|(([^,])*)");
        private Object catalinaDaemon = null;
        ClassLoader commonLoader = null;//公共类加载器,catalinaLoader,sharedLoader两者共同引用的
        ClassLoader catalinaLoader = null;//tomcat的jvm类加载器
        ClassLoader sharedLoader = null;//共享类加载器
    }
    

    在启动过程中,会首先调用静态方法,将参数catalina.basecatalina.home存放在System的Properties中。
    在bootstrap的main方法中,首先会将Bootstrap初始化(init),然后加载(load),最后启动(start)。

    public static void main(String args[]) {
        synchronized (daemonLock) {//加锁,初始化一次daemon
            if (daemon == null) { 
                Bootstrap bootstrap = new Bootstrap();
                try {
                    bootstrap.init();//初始化
                } catch (Throwable t) {
                    handleThrowable(t);
                    t.printStackTrace();
                    return;
                }
                daemon = bootstrap;
            } else {
                Thread.currentThread().setContextClassLoader(daemon.catalinaLoader);
            }
        }
        String command = "start"; 
        if (args.length > 0) {
            /取第一个启动jvm参数为指令,java org.apache.catalina.startup.Bootstrap start 时,取得为start
            command = args[args.length - 1];
        }
    
        if (command.equals("startd")) { //如果为startd,当做start处理
            args[args.length - 1] = "start";
            daemon.load(args); //加载
            daemon.start(); //启动
        } else if (command.equals("stopd")) {//stopd当做stop处理
            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);
        }
    }
    

    而在初始化过程中,初始化了三个类加载器和catalina对象。

     public void init() throws Exception {
            initClassLoaders();//初始化三个类加载器
            Thread.currentThread().setContextClassLoader(catalinaLoader);//将当前线程类加载器设置为catalina类加载器
            SecurityClassLoad.securityClassLoad(catalinaLoader);
            Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");//初始化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); //利用反射,设置类加载器为sharedLoader
            catalinaDaemon = startupInstance;将Catalina对象关联在bootstrap
        }
    
        private void initClassLoaders() {
           commonLoader = createClassLoader("common", null);
           catalinaLoader = createClassLoader("server", commonLoader);
           sharedLoader = createClassLoader("shared", commonLoader);
        }
    
     private ClassLoader createClassLoader(String name, ClassLoader parent)
        throws Exception {
        String value = CatalinaProperties.getProperty(name + ".loader");
        //获取指定类加载器的加载jar包路径  common.loader路径为${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
        if ((value == null) || (value.equals("")))
            return parent;
        value = replace(value);
    
        List<Repository> repositories = new ArrayList<>();
        String[] repositoryPaths = getPaths(value);//获取所有依赖路径,根据URL,GLOB,JAR,DIR依赖路径创建类加载器
        for (String repository : repositoryPaths) {
            try {
                URL url = new URL(repository);
                repositories.add(new Repository(repository, RepositoryType.URL)); 
                continue;
            } catch (MalformedURLException e) {
            }
            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));
            }
        }
        return ClassLoaderFactory.createClassLoader(repositories, parent);
    }
    

    当第一次传入common,创建common.loader的时候会发生什么?
    本菜鸟看到,会从CatalinaProperties中获取common.loader的配置路径,而初始化CatalinaProperties程中会去加载tomcat路径/conf/catalina.properties配置,也就是说CatalinaProperties读取的路径先读catalina.properties服务器配置文件,如果没有,读bootstrap.jar包中的catalina.properties文件

    public class CatalinaProperties {
    
        private static final Log log = LogFactory.getLog(CatalinaProperties.class);
    
        private static Properties properties = null;
        static {
            loadProperties();
        }
        public static String getProperty(String name) {
            return properties.getProperty(name);
        }
        private static void loadProperties() {
            InputStream is = null;
            String fileName = "catalina.properties";
            String configUrl = System.getProperty("catalina.config");//这里返回为null
            if (configUrl != null) {
                if (configUrl.indexOf('/') == -1) {
                    fileName = configUrl;
                } else {
                    is = (new URL(configUrl)).openStream();
                }
            }
            if (is == null) { 
                File home = new File(Bootstrap.getCatalinaBase());
                File conf = new File(home, "conf");
                File propsFile = new File(conf, fileName);//\/conf/catalina.properties文件读取
                is = new FileInputStream(propsFile);
            }
            if (is == null) {/还没有就从bootstrap.jar包中读取
                is = CatalinaProperties.class.getResourceAsStream
                    ("/org/apache/catalina/startup/catalina.properties");/
      
            if (is != null) {
                properties = new Properties();
                properties.load(is);//加载properties
              
            }
            if ((is == null)) {
                properties = new Properties();
            }
            Enumeration<?> enumeration = properties.propertyNames();
            while (enumeration.hasMoreElements()) {
                String name = (String) enumeration.nextElement();
                String value = properties.getProperty(name);
                if (value != null) {
                    System.setProperty(name, value);//存放在System.Properties中
                }
            }
        }
    }
    

    catalina.properties文件中都有哪些配置?具体如下:

    
    package.access=sun.,org.apache.catalina.,org.apache.coyote.,org.apache.jasper.,\
    org.apache.naming.resources.,org.apache.tomcat.
    
    package.definition=sun.,java.,org.apache.catalina.,org.apache.coyote.,\
    org.apache.jasper.,org.apache.naming.,org.apache.tomcat.
    
    common.loader=${catalina.base}/lib,${catalina.base}/lib/*.jar,${catalina.home}/lib,${catalina.home}/lib/*.jar
    server.loader=
    shared.loader=
    tomcat.util.scan.DefaultJarScanner.jarsToSkip=\
    bootstrap.jar,commons-daemon.jar,tomcat-juli.jar,\
    annotations-api.jar,el-api.jar,jsp-api.jar,servlet-api.jar,websocket-api.jar,\
    catalina.jar,catalina-ant.jar,catalina-ha.jar,catalina-tribes.jar,\
    jasper.jar,jasper-el.jar,ecj-*.jar,\
    tomcat-api.jar,tomcat-util.jar,tomcat-coyote.jar,tomcat-dbcp.jar,\
    tomcat-jni.jar,tomcat-spdy.jar,\
    tomcat-i18n-en.jar,tomcat-i18n-es.jar,tomcat-i18n-fr.jar,tomcat-i18n-ja.jar,\
    tomcat-juli-adapters.jar,catalina-jmx-remote.jar,catalina-ws.jar,\
    tomcat-jdbc.jar,\
    tools.jar,\
    commons-beanutils*.jar,commons-codec*.jar,commons-collections*.jar,\
    commons-dbcp*.jar,commons-digester*.jar,commons-fileupload*.jar,\
    commons-httpclient*.jar,commons-io*.jar,commons-lang*.jar,commons-logging*.jar,\
    commons-math*.jar,commons-pool*.jar,\
    jstl.jar,taglibs-standard-spec-*.jar,\
    geronimo-spec-jaxrpc*.jar,wsdl4j*.jar,\
    ant.jar,ant-junit*.jar,aspectj*.jar,jmx.jar,h2*.jar,hibernate*.jar,httpclient*.jar,\
    jmx-tools.jar,jta*.jar,log4j.jar,log4j-1*.jar,mail*.jar,slf4j*.jar,\
    xercesImpl.jar,xmlParserAPIs.jar,xml-apis.jar,\
    junit.jar,junit-*.jar,hamcrest*.jar,org.hamcrest*.jar,ant-launcher.jar,\
    cobertura-*.jar,asm-*.jar,dom4j-*.jar,icu4j-*.jar,jaxen-*.jar,jdom-*.jar,\
    jetty-*.jar,oro-*.jar,servlet-api-*.jar,tagsoup-*.jar,xmlParserAPIs-*.jar,\
    xom-*.jar
    
    org.apache.catalina.startup.ContextConfig.jarsToSkip=
    org.apache.catalina.startup.TldConfig.jarsToSkip=tomcat7-websocket.jar
    tomcat.util.buf.StringCache.byte.enabled=true
    

    可以看到,三种类加载器其他配置都可以指定一些其他不同的jar。
    万事俱备,只欠东风,所有jar包都找到了,最后一步创建类加载器。

    
    public final class ClassLoaderFactory {
    
    
        private static final Log log = LogFactory.getLog(ClassLoaderFactory.class);
    
        public static ClassLoader createClassLoader(List<Repository> repositories,
                                                    final ClassLoader parent)
            throws Exception {
            Set<URL> set = new LinkedHashSet<>();
            //将所有依赖转换为url
            if (repositories != null) {
                for (Repository repository : repositories)  {
                    if (repository.getType() == RepositoryType.URL) {
                        URL url = buildClassLoaderUrl(repository.getLocation());
                        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);
                        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);
                        set.add(url);
                    } else if (repository.getType() == RepositoryType.GLOB) {
                        File directory=new File(repository.getLocation());
                        directory = directory.getCanonicalFile();
                        if (!validateFile(directory, RepositoryType.GLOB)) {
                            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]);
                            file = file.getCanonicalFile();
                            if (!validateFile(file, RepositoryType.JAR)) {
                                continue;
                            }
                            URL url = buildClassLoaderUrl(file);
                            set.add(url);
                        }
                    }
                }
            }
            final URL[] array = set.toArray(new URL[set.size()]);
            //创建类加载器为url类加载器
            return AccessController.doPrivileged(
                    new PrivilegedAction<URLClassLoader>() {
                        @Override
                        public URLClassLoader run() {
                            if (parent == null)
                                return new URLClassLoader(array);
                            else
                                return new URLClassLoader(array, parent);
                        }
                    });
        }
    }
    

    相关文章

      网友评论

          本文标题:揭开tomcat神秘的面纱之bootstrap初始化

          本文链接:https://www.haomeiwen.com/subject/jjfdxqtx.html