上一篇文章分析了通过startup.bat启动Tomcat相当于执行如下代码,即运行Bootstrap start
start "Tomcat" "C:\Program Files\Java\jdk1.7.0_51\bin\java"
-Djava.util.logging.config.file="D:\Program Files\apache-tomcat-8.0.3\conf\logging.properties"
-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
-Djava.endorsed.dirs="D:\Program Files\apache-tomcat-8.0.3\endorsed"
-classpath "D:\Program Files\apache-tomcat-8.0.3\bin\bootstrap.jar;D:\Program Files\apache-tomcat-8.0.3\bin\tomcat-juli.jar"
-Dcatalina.base="D:\Program Files\apache-tomcat-8.0.3"
-Dcatalina.home="D:\Program Files\apache-tomcat-8.0.3"
-Djava.io.tmpdir="D:\Program Files\apache-tomcat-8.0.3\temp"
org.apache.catalina.startup.Bootstrap start
今天来看看Bootstrap的执行流程
1. 执行static块
为什么没有执行main方法,而先执行static块呢?
原来一个类的运行,JVM做会以下几件事情:类装载、链接、初始化、实例化,而初始化阶段做的事情是初始化静态变量和执行静态方法等
static块的作用是设置catalinaBaseFile、catalinaHomeFile
static块:
static {
// Will always be non-null
//System.getProperty("user.dir"),获取当前目录
//由于是在$CATALINA_HOME\bin下运行的Bootstrap,所以userDir为$CATALINA_HOME\bin
String userDir = System.getProperty("user.dir");
// Home first
//Globals是存放全局常量的类
//Globals.CATALINA_HOME_PROP = "catalina.home"
//catalina.home在运行Bootstrap时已设置(Tomcat的根目录)
String home = System.getProperty(Globals.CATALINA_HOME_PROP);
File homeFile = null;
//获取Tomcat的绝对路径
if (home != null) {
File f = new File(home);
try {
homeFile = f.getCanonicalFile();
} catch (IOException ioe) {
homeFile = f.getAbsoluteFile();
}
}
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();
}
}
}
if (homeFile == null) {
// Second fall-back. Use current directory
File f = new File(userDir);
try {
homeFile = f.getCanonicalFile();
} catch (IOException ioe) {
homeFile = f.getAbsoluteFile();
}
}
//设置catalinaHomeFile
catalinaHomeFile = homeFile;
System.setProperty(
Globals.CATALINA_HOME_PROP, catalinaHomeFile.getPath());
// Then base
String base = System.getProperty(Globals.CATALINA_BASE_PROP);
//设置catalinaBaseFile
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());
}
对比getAbsoluteFile()、getCanonicalFile()
getAbsoluteFile获取绝对文件(如果文件路径中包含.和..,不解析)
getCanonicalFile获取经典文件(如果文件路径中包含.和..,解析)
创建Test.java
import java.io.File;
import java.io.IOException;
public class Test {
public static void main(String[] args) throws IOException {
File f = new File("D://Program Files//apache-tomcat-8.0.3");
File aFile = f.getAbsoluteFile();
File bFile = f.getCanonicalFile();
System.out.println("文件路径不包含.或..");
System.out.println("getAbsoluteFile()--->" + aFile.toString());
System.out.println("getCanonicalFile()--->" + bFile.toString());
File f1 = new File("D://Program Files//apache-tomcat-8.0.3//..");
File aFile1 = f1.getAbsoluteFile();
File bFile1 = f1.getCanonicalFile();
System.out.println("文件路径包含.或..");
System.out.println("getAbsoluteFile()--->" + aFile1.toString());
System.out.println("getCanonicalFile()--->" + bFile1.toString());
}
}
输出:
文件路径不包含.或..
getAbsoluteFile()--->D:\Program Files\apache-tomcat-8.0.3
getCanonicalFile()--->D:\Program Files\apache-tomcat-8.0.3
文件路径包含.或..
getAbsoluteFile()--->D:\Program Files\apache-tomcat-8.0.3\..
getCanonicalFile()--->D:\Program Files
接写来将执行main函数(以下以首次运行Bootstrap start进行解读)
2. 执行main函数
main函数:
public static void main(String args[]) {
if (daemon == null) {
// Don't set daemon until init() has completed
//***2.1***
Bootstrap bootstrap = new Bootstrap();
try {
//***2.2***
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);
}
//***2.3***
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")) {
//***2.4***
daemon.setAwait(true);
//***2.5***
daemon.load(args);
//***2.6***
daemon.start();
} 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);
}
}
2.1 创建Bootstrap
2.2 调用Bootstrap.init()
init方法:
public void init() throws Exception {
//创建commonLoader、catalinaLoader、sharedLoader
initClassLoaders();
//为当前线程设置ClassLoader
Thread.currentThread().setContextClassLoader(catalinaLoader);
//设置SecurityClassLoad。具体作用还不清楚。。。
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
//通过反射实例化Catalina
Class<?> startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
Object startupInstance = startupClass.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;
//通过反射设置Catalina的parentClassLoader
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
//将实例化的Catalina赋值给catalinaDaemon
catalinaDaemon = startupInstance;
}
initClassLoaders方法:
private void initClassLoaders() {
try {
//创建commonLoader
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、sharedLoader
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
handleThrowable(t);
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
createClassLoader方法:
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
//CatalinaProperties解析$CATALINA_HOME\conf\catalina.properties,
//并将catalina.properties内的属性存为系统属性
//catalina.properties内common.loader="${catalina.base}/lib",
//"${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
//读取common.loader
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals("")))
return parent;
//将${catalina.base},${catalina.home}替换为Tomcat的绝对路径
value = replace(value);
List<Repository> repositories = new ArrayList<>();
String[] repositoryPaths = getPaths(value);
for (String repository : repositoryPaths) {
// Check for a JAR URL repository
try {
@SuppressWarnings("unused")
URL url = new URL(repository);
repositories.add(
new Repository(repository, RepositoryType.URL));
continue;
} catch (MalformedURLException e) {
// Ignore
}
// Local repository
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));
}
}
//ClassLoaderFactory依据repositories的内容创建ClassLoader
return ClassLoaderFactory.createClassLoader(repositories, parent);
}
ClassLoaderFactory依据repositories的内容创建ClassLoader时,repositories包含的四个值均是$CATALINA_HOME\lib这个路径。那么创建的ClassLoader会不会有重复的jar呢?
查看ClassLoaderFactory.createClassLoader方法,即可找到答案
public static ClassLoader createClassLoader(List<Repository> repositories,
final ClassLoader parent)
throws Exception {
if (log.isDebugEnabled())
log.debug("Creating new class loader");
// Construct the "class path" for this class loader
Set<URL> set = new LinkedHashSet<>();
if (repositories != null) {
for (Repository repository : repositories) {
if (repository.getType() == RepositoryType.URL) {
URL url = new URL(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 = directory.toURI().toURL();
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 = file.toURI().toURL();
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();
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 = file.toURI().toURL();
set.add(url);
}
}
}
}
ClassLoaderFactory在遍历repositories时,将jar文件的URL放在LinkedHashSet里,而LinkedHashSet里不会添加重复的数据。因此,创建的ClassLoader不会有重复的jar
2.3 解析参数
2.4 调用Bootstrap.setAwait(true)
Bootstrap.setAwait(true)内部通过反射,设置Catalina的await属性(默认为false)为true
启动Catalina过程中,当Catalina将Tomcat的所有组件启动之后,会检查await属性,如果为true,会调用Catalina.await(),而Catalina.await()又会调用其内部的Server的await()
if (await) {
await();
stop();
}
public void await() {
getServer().await();
}
Server.await()包含一个while循环,此循环用于监听指定socket端口(默认为8005)的连接,当某个连接传入的参数为”SHUTDOWN”(默认为”SHUTDOWN”)时,终止此while循环(端口号和终止while循环的参数,在server.xml的Server标签设置)
Server.await()用来维持Bootstrap的main方法(main thread)处于运行状态,而线程池中监听http请求的线程是守护线程(daemon thread)
当Tomcat的指定端口接收到关闭命令时,Server.await()内的while循环终止,然后Catalina会调用stop()方法,关闭Tomcat的所有组件,最终Bootstrap的main thread终止,Tomcat关闭
2.5 调用Bootstrap.load(args)
Bootstrap.load(args)内部通过反射调用Catalina.load(args),Catalina将利用Digest(Digest详解)解析server.xml,创建相应组件的实例,之后调用Server.init(),Server初始化时,又会调用其内部的Service的init方法,即调用某一组件的init方法时,将触发其子组件的init方法
执行Bootstrap.load(args)将触发的动作
2.6 调用Bootstrap.start()
执行Bootstrap.start()将触发的动作
Bootstrap
private Object catalinaDaemon = Catalina;
ClassLoader commonLoader = URLClassloader;
ClassLoader catalinaLoader = URLClassloader;
ClassLoader sharedLoader = URLClassloader;
init()
initClassLoaders();
-Djava.security.manager
daemon = bootstrap
public void org.apache.catalina.startup.Catalina.setParentClassLoader(java.lang.ClassLoader)
org.apache.tomcat.util.digester.Digester
getServer().init();
//before_init resister event to listener
setStateInternal(LifecycleState.INITIALIZING, null, false);
//jmxbeanServer
initInternal();
///after_init resister event to listener
setStateInternal(LifecycleState.INITIALIZED, null, false);
带注释的 Bootstrap.java文件下载地址:
http://download.csdn.net/detail/flyliuweisky547/7179505
---------------------
作者:flyliuweisky547
来源:CSDN
原文:https://blog.csdn.net/flyliuweisky547/article/details/23464245
版权声明:本文为博主原创文章,转载请附上博文链接!