一,前言
在本文中,我将主要介绍tomcat的启动阶段,包括main方法的运行,配置文件的加载,对象的实例化关联等。
二,Bootstrap的mian
当我们调试tomcat的时候,是从Bootstrap的main方法开始启动的。那这个main方法主要做了哪些工作,我们看看内部实现。
Bootstrap
public static void main(String args[]) {
Bootstrap bootstrap = new Bootstrap();
//初始化
bootstrap.init();
daemon = bootstrap;
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();
} else if (command.equals("stop")) {
daemon.stopServer(args);
}else other ...
} catch (Throwable t) {
...//异常
System.exit(1);
}
}
代码很简单,创建一个Bootstrap,然后初始化init,最后调用start方法启动。
三,init方法
那么初始化方法做了什么呢?我们接下来看看:
Bootstrap
public void init() throws Exception {
//初始化类加载器
initClassLoaders();
//设置当前上下文类加载器为catalinaLoader
Thread.currentThread().setContextClassLoader(catalinaLoader);
...//略
Class<?> startupClass =
catalinaLoader.loadClass
("org.apache.catalina.startup.Catalina");
//加载类Catalina,并实例化
Object startupInstance = startupClass.newInstance();
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);
//调用Catalina的setParentClassLoader方法设置父类加载器为catalinaLoader
method.invoke(startupInstance, paramValues);
catalinaDaemon = startupInstance;
}
初始化做了2件事,第一件事是初始化类加载器,并设置了线程上下文类加载器。第二件事是初始化Catalina,并设置它的父类加载器为当前线程上下文类加载器。
3.1,初始化类加载器
接下来我们就看看类加载器是如何初始化的。
Bootstrap
private void initClassLoaders() {
try {
commonLoader = createClassLoader("common", null);
if( commonLoader == null ) {
commonLoader=this.getClass().getClassLoader();
}
catalinaLoader = createClassLoader("server", commonLoader);
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
//....异常
System.exit(1);
}
}
上面代码创建了3个类加载器,至于这3个的用处,以后再说,我们看是如何实现的。
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
String value = CatalinaProperties.getProperty(name + ".loader");//第一处
if ((value == null) || (value.equals("")))
return parent;
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));
}
}
return ClassLoaderFactory.createClassLoader(repositories, parent);
}
我们看上面代码第一处,从属性文件中获取值。
我们打开conf/catalina.properties文件,发现
catalina.properties
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar"
server.loader=
shared.loader=
只有common.loader才有值,表示加载的是tomcatlib目录下的jar包。由于server.loader和shared.loader为空,catalinaLoader和sharedLoader都会引用的commonLoader。
最后我们看具体的创建过程,即ClassLoaderFactory.createClassLoader(repositories, parent)方法。
public static ClassLoader createClassLoader(List<Repository> repositories,
final ClassLoader parent)
throws Exception {
Set<URL> set = new LinkedHashSet<>();
if (repositories != null) {
for (Repository repository : repositories) {
...//略去其他类型
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);
}
}
// Construct the class loader itself
final URL[] array = set.toArray(new URL[set.size()]);
return AccessController.doPrivileged(
new PrivilegedAction<URLClassLoader>() {
@Override
public URLClassLoader run() {
if (parent == null)
return new URLClassLoader(array);
else
return new URLClassLoader(array, parent);
}
});
}
最后我们从代码中可以看到,生成了一个URLClassLoader加载器。记得tomcat6的时候这里生成的StandardClassLoader,看来StandardClassLoader没啥作用,在后面的tomcat中移除了。
3.2,初始化Catalina
初始化Catalina用反射即可,不多说了。
四,start启动
初始化完成之后,就可以启动了。我们看看启动start方法。
Bootstrap
//设置等待标志,为关闭做准备,暂时不关注
daemon.setAwait(true);
//加载参数,无参数,暂时不关注
daemon.load(args);
//启动
daemon.start();
public void start()
throws Exception {
if( catalinaDaemon==null ) init();
Method method = catalinaDaemon.getClass().getMethod("start", (Class [] )null);
method.invoke(catalinaDaemon, (Object [])null);
}
start方法很简单,利用反射调用了catalina的start方法。
Catalina的start方法做了哪些事情呢?我们下文继续。