本文代码基于的tomcat的源码版本为7.0.65。
一、tomcat组件介绍
tomcat由多个组件构成,组件的名称与之间的关系,我建议各位亲子查看tomcat的conf文件夹下的server.xml这个文件,就能有很直观的感受。这个文件中即定义了核心组件的名称,也通过xml的方式定义了包含关系。当然,这个默认文件可能包涵的组件不是很全面,但核心都包括了,如:Server,Service,Connector,Engine,Host等。
其中Server只能有1个,1个Server可以对应多个Service,每个Service可以对应多个Connector。
二、如何学习tomcat源码
跟代码是学习源码的最快手段。建议下载源码后用ant编译,然后倒入eclipse,在boostrap类中开始tomcat源码之路。
操作方法参见:http://my.oschina.net/psuyun/blog/228643?fromerr=kXXI4vdy
三、从bootstrap开始tomcat源码之路
1、最开始bootstrap.init();。
public void init()
throws Exception
{
//设置catalina_home和catalina_base的路径。在单tomcat实例下,这2个路径是一样的。
//如果要采用多个tomcat实例,则catalina_home路径为共享文件目录,catalina_base路径为实例独享文件目录。
//可参见:http://blog.csdn.net/xiaohai0504/article/details/7631014
setCatalinaHome();
setCatalinaBase();
//初始化类加载器,加载jar包
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
if (log.isDebugEnabled())
log.debug("Loading startup class");
//通过放射创建Catalina类的实力,并且调用了该类的setParentClassLoade(),设置该类的类加载器为sharedLoader。
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;
Method method =
startupInstance.getClass().getMethod(methodName, paramTypes);
method.invoke(startupInstance, paramValues);
catalinaDaemon = startupInstance;
}
private void initClassLoaders() {
try {
//commonLoader类加载器是tomcat所有类加载器的父加载器。
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的父加载器,conf的catalina.properties并没有配置server.loder需要加载的jar包
catalinaLoader = createClassLoader("server", commonLoader);
//同上
sharedLoader = createClassLoader("shared", commonLoader);
} catch (Throwable t) {
handleThrowable(t);
log.error("Class loader creation threw exception", t);
System.exit(1);
}
}
private ClassLoader createClassLoader(String name, ClassLoader parent)
throws Exception {
//配置文件读取的是conf文件夹下的catalina.properties
String value = CatalinaProperties.getProperty(name + ".loader");
if ((value == null) || (value.equals("")))
return parent;
value = replace(value);
List<repository> repositories = new ArrayList<repository>();
StringTokenizer tokenizer = new StringTokenizer(value, ",");
while (tokenizer.hasMoreElements()) {
String repository = tokenizer.nextToken().trim();
if (repository.length() == 0) {
continue;
}
// 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));
}
}
//采用URLClassLoader类来加载jar包
return ClassLoaderFactory.createClassLoader(repositories, parent);
}
</repository></repository>
bootstrap.init();已经完成。主要是生成了类加载器commonLoader,加载了catalina.properties中的common.loader配置的jar包。 通过反射生成了org.apache.catalina.startup.Catalina类的实例,并且设置了它的父加载器。
2、容器的初始化daemon.load(args);
private void load(String[] arguments)
throws Exception {
// Call the load() method
String methodName = "load";
Object param[];
Class<?> paramTypes[];
if (arguments==null || arguments.length==0) {
paramTypes = null;
param = null;
} else {
paramTypes = new Class[1];
paramTypes[0] = arguments.getClass();
param = new Object[1];
param[0] = arguments;
}
//通过反射调用Catalina实例的load()方法。
Method method =
catalinaDaemon.getClass().getMethod(methodName, paramTypes);
if (log.isDebugEnabled())
log.debug("Calling startup class " + method);
method.invoke(catalinaDaemon, param);
}
public void load() {
long t1 = System.nanoTime();
//如果没有配置catalina_home,会重新设置catalina_home的值
initDirs();
//设置了2个property属性javax.naming.Context.URL_PKG_PREFIXES和javax.naming.Context.INITIAL_CONTEXT_FACTORY
initNaming();
//使用Digester对conf/server.xml文件进行解析。Digester目前是Apache commons的一个项目。
//这里只是创建解析的切入点,后面调用parse()方法才开始解析。
Digester digester = createStartDigester();
InputSource inputSource = null;
InputStream inputStream = null;
File file = null;
try {
//获取conf/server.xml
file = configFile();
inputStream = new FileInputStream(file);
inputSource = new InputSource(file.toURI().toURL().toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail", file), e);
}
}
if (inputStream == null) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream(getConfigFile());
inputSource = new InputSource
(getClass().getClassLoader()
.getResource(getConfigFile()).toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail",
getConfigFile()), e);
}
}
}
// This should be included in catalina.jar
// Alternative: don't bother with xml, just create it manually.
if( inputStream==null ) {
try {
inputStream = getClass().getClassLoader()
.getResourceAsStream("server-embed.xml");
inputSource = new InputSource
(getClass().getClassLoader()
.getResource("server-embed.xml").toString());
} catch (Exception e) {
if (log.isDebugEnabled()) {
log.debug(sm.getString("catalina.configFail",
"server-embed.xml"), e);
}
}
}
if (inputStream == null || inputSource == null) {
if (file == null) {
log.warn(sm.getString("catalina.configFail",
getConfigFile() + "] or [server-embed.xml]"));
} else {
log.warn(sm.getString("catalina.configFail",
file.getAbsolutePath()));
if (file.exists() && !file.canRead()) {
log.warn("Permissions incorrect, read permission is not allowed on the file.");
}
}
return;
}
try {
inputSource.setByteStream(inputStream);
//开始解析xml
digester.push(this);
digester.parse(inputSource);
} catch (SAXParseException spe) {
log.warn("Catalina.start using " + getConfigFile() + ": " +
spe.getMessage());
return;
} catch (Exception e) {
log.warn("Catalina.start using " + getConfigFile() + ": " , e);
return;
} finally {
try {
inputStream.close();
} catch (IOException e) {
// Ignore
}
}
//Digester中设置了当前server为StandardServer
getServer().setCatalina(this);
// Stream redirection
initStreams();
// Start the new server
try {
//初始化Server,init()方法为LifeCycleBase的方法,Service,Connector等组件都集成了这个类,初始化的时候都用这个方法。
//采用的是模版方法
getServer().init();
} catch (LifecycleException e) {
if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE")) {
throw new java.lang.Error(e);
} else {
log.error("Catalina.start", e);
}
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");
}
}
public final synchronized void init() throws LifecycleException {
if (!state.equals(LifecycleState.NEW)) {
invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
}
//设置组件的状态为正在初始化。这里还用到了观察者模式,Server.xml中配置了很多Listener,Listener会注册到各个组件中,
//当组件的状态改变的时候,就会遍历组件的listeners。
setStateInternal(LifecycleState.INITIALIZING, null, false);
try {
//每个组件自己的初始化
initInternal();
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);
setStateInternal(LifecycleState.FAILED, null, false);
throw new LifecycleException(
sm.getString("lifecycleBase.initFail",toString()), t);
}
//设置组件的状态为初始化完成
setStateInternal(LifecycleState.INITIALIZED, null, false);
}
init()主要是对所有组件进行初始化,先从Server开始,1个Server又可能包涵多个Service,1个Service可能包涵多个Connctor,直到所有的组件都初始化完成。每个组件初始化前后,会更改当前组件的状态,对该组件进行监听的对象将根据组件状态确认是否要有所作用,这就是观察者模式。3、容器的启动。daemon.start();
public void start() {
//没有Server就重新初始化。
if (getServer() == null) {
load();
}
if (getServer() == null) {
log.fatal("Cannot start server. Server instance is not configured.");
return;
}
long t1 = System.nanoTime();
//
try {
getServer().start();
} catch (LifecycleException e) {
log.fatal(sm.getString("catalina.serverStartFail"), e);
try {
getServer().destroy();
} catch (LifecycleException e1) {
log.debug("destroy() failed for failed Server ", e1);
}
return;
}
long t2 = System.nanoTime();
if(log.isInfoEnabled()) {
log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms");
}
// Register shutdown hook
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);
// If JULI is being used, disable JULI's shutdown hook since
// shutdown hooks run in parallel and log messages may be lost
// if JULI's hook completes before the CatalinaShutdownHook()
LogManager logManager = LogManager.getLogManager();
if (logManager instanceof ClassLoaderLogManager) {
((ClassLoaderLogManager) logManager).setUseShutdownHook(
false);
}
}
if (await) {
await();
stop();
}
}
这里和容器的初始化有异曲同工之妙,都是采用模版方式,通过调用公共接口的实现方法start(),然后在start()中调用各自的startInternal();其中还包含着状态改变而通知监听者的观察者模式。 到这里,bootstrap的main()方法的主要流程已经结束了,分为3步:初始化类加载器,初始化组件,启动组件。其中用到了模版模式和观察者模式。后续我将继续介绍tomcat中各个组件是如何工作的。