在上文揭開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.jar
和tomcat-juli.jar
關(guān)于classPath該參數(shù),本菜鳥開發(fā)過(guò)程中也一直沒有用到過(guò)骡苞,不得不百度了一下,才了解到是在啟動(dòng)過(guò)程中引用到j(luò)ar的意思楷扬。
該jar位于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.base
和catalina.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);
}
});
}
}