当我们启动tomcat一般是运行%TOMCAT_HOME%\bin\startup.bat文件,这个文件实际上调用了%TOMCAT_HOME%\bin\catalina.bat批处理文件:
set "EXECUTABLE=%CATALINA_HOME%\bin\catalina.bat"
......
set CMD_LINE_ARGS=
:setArgs
if ""%1""=="""" goto doneSetArgs
set CMD_LINE_ARGS=%CMD_LINE_ARGS% %1
shift
goto setArgs
:doneSetArgs
call "%EXECUTABLE%" start %CMD_LINE_ARGS%
startup.bat将start命令和控制台的所有参数都传给了catalina.bat文件,下面来看catalina.bat文件中是怎么启动Tomcat的?注意到下面这句
%_EXECJAVA% %JAVA_OPTS% %CATALINA_OPTS% %JPDA_OPTS% %DEBUG_OPTS% -Djava.endorsed.dirs="%JAVA_ENDORSED_DIRS%" -classpath "%CLASSPATH%" -Djava.security.manager -Djava.security.policy=="%SECURITY_POLICY_FILE%" -Dcatalina.base="%CATALINA_BASE%" -Dcatalina.home="%CATALINA_HOME%" -Djava.io.tmpdir="%CATALINA_TMPDIR%" %MAINCLASS% %CMD_LINE_ARGS% %ACTION%
_EXECJAVA=_RUNJAVA="%JRE_HOME%\bin\java",这是在%TOMCAT_HOME%\bin\setclasspath.bat文件中设置的,也就是说这句要运行一个JAVA程序,后面紧接着就应该是运行的类%MAINCLASS%,MAINCLASS=org.apache.catalina.startup.Bootstrap,不错org.apache.catalina.startup.Bootstrap类正是Tomcat的入口类,Tomcat正是从Bootstrap类的main方法开始运行的。
下面来看看Bootstrap的main方法做了哪些事情:
public static void main(String args[]) {
if (daemon == null) {
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.init();
} catch (Throwable t) {
handleThrowable(t);
t.printStackTrace();
return;
}
daemon = bootstrap;
}
try {
...
if (command.equals("start"))
daemon.start();
...
} catch (Throwable t) {
...
}
}
从以上代码可以看出main方法做了3件事情:
1、初始化;
2、装配tomcat容器
3、启动tomcat容器
public void init()
throws Exception
{
// Set Catalina path
setCatalinaHome();
setCatalinaBase();
initClassLoaders();
Thread.currentThread().setContextClassLoader(catalinaLoader);
SecurityClassLoad.securityClassLoad(catalinaLoader);
// Load our startup class and call its process() method
if (log.isDebugEnabled())
log.debug("Loading startup class");
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;
}
1、设置catalina.home系统属性
public void load() {
initDirs();
// Before digester - it may be needed
initNaming();
// Create and execute our Digester
Digester digester = createStartDigester();
digester.push(this);
digester.parse(inputSource);
getServer().init();
}
}
1、初始化目录和命名规则,为digester解析XML做准备,digester准备后面再研究一下
// Initialize our defined Services
for (int i = 0; i < services.length; i++) {
services[i].init();
}
从代码可以看出,一个tomcat容器是可以同时服务于多个应用的,tomcat配置多个services可以参考 http://www.ff-bb.cn/logs/109466274.html service的初始化是在StandardService类的initInternal方法中完成的
protected void initInternal() throws LifecycleException {
if (container != null) {
container.init();
}
// Initialize any Executors
for (Executor executor : findExecutors()) {
if (executor instanceof LifecycleMBeanBase) {
((LifecycleMBeanBase) executor).setDomain(getDomain());
}
executor.init();
}
// Initialize our defined Connectors
synchronized (connectors) {
for (Connector connector : connectors) {
connector.init();
}
}
}
public void start() {
...
long t1 = System.nanoTime();
// Start the new server
try {
getServer().start();
} catch (LifecycleException e) {
log.error("Catalina.start: ", e);
}
...
if (await) {
await();
stop();
}
}
1、tomcat中大量引入了模板方法模式,org.apache.catalina.util.LifecycleBase被所有容器继承,其中initInternal和startInternal方法是抽象方法,在初始化和启动时被每个容器会调用其init和start方法,这两个方法会去调用对应抽象方法***Internal,这些抽象方法都是在子类中实现的
2、tomcat中大量使用了类的反射,基本上都是通过配置文件获取类名,然后利用反射得到class对象,再通过newInstance获取对象。