揭開tomcat神秘的面紗之bootstrap初始化

在上文揭開tomcat神秘的面紗之啟動(dòng)腳本中膜钓,本菜鳥分析到拧额,最終執(zhí)行會(huì)執(zhí)行以下命令

/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

簡(jiǎn)單來(lái)說(shuō)就是

java org.apache.catalina.startup.Bootstrap start

這個(gè)bootstrap類從何而來(lái)?

不由得想到上文提到的啟動(dòng)參數(shù)

-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參數(shù)中指定了classPath參數(shù),其中提到了bootstrap.jartomcat-juli.jar
關(guān)于classPath該參數(shù),本菜鳥開發(fā)過(guò)程中也一直沒有用到過(guò)骡苞,不得不百度了一下,才了解到是在啟動(dòng)過(guò)程中引用到j(luò)ar的意思楷扬。
該jar位于bin目錄下解幽,如圖:

tomcat/bin路徑

本菜鳥從github上將代碼拉了下來(lái),來(lái)看看到底tomcat內(nèi)部是什么樣子的烘苹,在看代碼之前躲株,將《深入剖析tomcat》此書看過(guò)了,大學(xué)的時(shí)候镣衡,就已經(jīng)讀過(guò)該書霜定,不得不說(shuō),畢業(yè)后懶惰了廊鸥,畢業(yè)快三年了才想起來(lái)重新拿起來(lái)讀一遍望浩。

public final class Bootstrap {
    private static final Object daemonLock = new Object(); //初始化并發(fā)鎖
    private static volatile Bootstrap daemon = null; //單例
    private static final File catalinaBaseFile;   //jvm啟動(dòng)參數(shù)catalina.base
    private static final File catalinaHomeFile;//jvm啟動(dòng)參數(shù)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;//共享類加載器
}

在啟動(dòng)過(guò)程中曾雕,會(huì)首先調(diào)用靜態(tài)方法,將參數(shù)catalina.basecatalina.home存放在System的Properties中助被。
在bootstrap的main方法中,首先會(huì)將Bootstrap初始化(init)切诀,然后加載(load)揩环,最后啟動(dòng)(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) {
        /取第一個(gè)啟動(dòng)jvm參數(shù)為指令丰滑,java org.apache.catalina.startup.Bootstrap start 時(shí),取得為start
        command = args[args.length - 1];
    }

    if (command.equals("startd")) { //如果為startd倒庵,當(dāng)做start處理
        args[args.length - 1] = "start";
        daemon.load(args); //加載
        daemon.start(); //啟動(dòng)
    } else if (command.equals("stopd")) {//stopd當(dāng)做stop處理
        args[args.length - 1] = "stop";
        daemon.stop();
    } else if (command.equals("start")) {
        daemon.setAwait(true); 
        daemon.load(args); //加載
        daemon.start();//啟動(dòng)
        if (null == daemon.getServer()) {
            System.exit(1);
        }
    } else if (command.equals("stop")) {
        daemon.stopServer(args);
    }
}

而在初始化過(guò)程中褒墨,初始化了三個(gè)類加載器和catalina對(duì)象。

 public void init() throws Exception {
        initClassLoaders();//初始化三個(gè)類加載器
        Thread.currentThread().setContextClassLoader(catalinaLoader);//將當(dāng)前線程類加載器設(shè)置為catalina類加載器
        SecurityClassLoad.securityClassLoad(catalinaLoader);
        Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");//初始化Catalina對(duì)象擎宝,
        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); //利用反射郁妈,設(shè)置類加載器為sharedLoader
        catalinaDaemon = startupInstance;將Catalina對(duì)象關(guān)聯(lián)在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);//獲取所有依賴路徑,根據(jù)URL绍申,GLOB,JAR,DIR依賴路徑創(chuàng)建類加載器
    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);
}

當(dāng)?shù)谝淮蝹魅隿ommon,創(chuàng)建common.loader的時(shí)候會(huì)發(fā)生什么噩咪?
本菜鳥看到顾彰,會(huì)從CatalinaProperties中獲取common.loader的配置路徑,而初始化CatalinaProperties程中會(huì)去加載tomcat路徑/conf/catalina.properties配置,也就是說(shuō)CatalinaProperties讀取的路徑先讀catalina.properties服務(wù)器配置文件胃碾,如果沒有涨享,讀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。
萬(wàn)事俱備俄周,只欠東風(fēng)吁讨,所有jar包都找到了,最后一步創(chuàng)建類加載器栈源。


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<>();
        //將所有依賴轉(zhuǎn)換為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()]);
        //創(chuàng)建類加載器為url類加載器
        return AccessController.doPrivileged(
                new PrivilegedAction<URLClassLoader>() {
                    @Override
                    public URLClassLoader run() {
                        if (parent == null)
                            return new URLClassLoader(array);
                        else
                            return new URLClassLoader(array, parent);
                    }
                });
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末挡爵,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子甚垦,更是在濱河造成了極大的恐慌茶鹃,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件艰亮,死亡現(xiàn)場(chǎng)離奇詭異闭翩,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)迄埃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門疗韵,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人侄非,你說(shuō)我怎么就攤上這事蕉汪。” “怎么了逞怨?”我有些...
    開封第一講書人閱讀 158,369評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵者疤,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我叠赦,道長(zhǎng)驹马,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,799評(píng)論 1 285
  • 正文 為了忘掉前任除秀,我火速辦了婚禮糯累,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘册踩。我一直安慰自己泳姐,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,910評(píng)論 6 386
  • 文/花漫 我一把揭開白布暂吉。 她就那樣靜靜地躺著仗岸,像睡著了一般允耿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上扒怖,一...
    開封第一講書人閱讀 50,096評(píng)論 1 291
  • 那天较锡,我揣著相機(jī)與錄音,去河邊找鬼盗痒。 笑死蚂蕴,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的俯邓。 我是一名探鬼主播骡楼,決...
    沈念sama閱讀 39,159評(píng)論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼稽鞭!你這毒婦竟也來(lái)了鸟整?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,917評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤朦蕴,失蹤者是張志新(化名)和其女友劉穎篮条,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吩抓,經(jīng)...
    沈念sama閱讀 44,360評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡涉茧,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,673評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了疹娶。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片伴栓。...
    茶點(diǎn)故事閱讀 38,814評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖雨饺,靈堂內(nèi)的尸體忽然破棺而出钳垮,到底是詐尸還是另有隱情,我是刑警寧澤额港,帶...
    沈念sama閱讀 34,509評(píng)論 4 334
  • 正文 年R本政府宣布扔枫,位于F島的核電站,受9級(jí)特大地震影響锹安,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜倚舀,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,156評(píng)論 3 317
  • 文/蒙蒙 一叹哭、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧痕貌,春花似錦风罩、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)入宦。三九已至,卻和暖如春室琢,著一層夾襖步出監(jiān)牢的瞬間乾闰,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工盈滴, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留涯肩,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,641評(píng)論 2 362
  • 正文 我出身青樓巢钓,卻偏偏與公主長(zhǎng)得像病苗,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子症汹,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,728評(píng)論 2 351