当我们启动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获取对象。