tomcat的启动从Bootstrap类的main方法开始。
public static void main(String args[]) { //单例 if (daemon == null) { daemon = new Bootstrap();//实例化Bootstrap对象 try { //实例化Catalina 并装载3个classloader,设置Catalina的父加载器 daemon.init(); } catch (Throwable t) { t.printStackTrace(); return; } } try { String command = "start"; if (args.length > 0) { command = args[args.length - 1]; } if (command.equals("startd")) { args[0] = "start"; daemon.load(args); daemon.start(); } else if (command.equals("stopd")) { args[0] = "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 { log.warn("Bootstrap: command "" + command + "" does not exist."); } } catch (Throwable t) { t.printStackTrace(); } }
/** * 一些初始化工作 初始化一个守护线程 * Initialize daemon. */ public void init() throws Exception { // Set Catalina path 初始化Tomcat的安装路径 homease setCatalinaHome(); setCatalinaBase(); initClassLoaders();//初始化变量classloader:catalinaloader、commonloader、sharedloader 三个加载器 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();//得到了Catalina的实例 // 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);//利用反射调用Catalina的setParentClassLoader方法 设置共享扩展类装入器 catalinaDaemon = startupInstance; //赋值给Catalina的成员变量。
}
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; } Method method = catalinaDaemon.getClass().getMethod(methodName, paramTypes); if (log.isDebugEnabled()) log.debug("Calling startup class " + method);//利用反射调用Catalina的load()方法 method.invoke(catalinaDaemon, param); }
Catalina的load方法。主要是解析conf下的service.xml文件
public void load() { long t1 = System.nanoTime(); initDirs();//System.setPropertity("catalina.home")和catalina.home 设置系统目录 // Before digester - it may be needed initNaming();//设置propertity naming // Create and execute our Digester Digester digester = createStartDigester(); InputSource inputSource = null; InputStream inputStream = null; File file = null; try { file = configFile();//读取con/server.xml的配置文件 inputStream = new FileInputStream(file); inputSource = new InputSource("file://" + file.getAbsolutePath()); } catch (Exception e) { ; } if (inputStream == null) { try { inputStream = getClass().getClassLoader() .getResourceAsStream(getConfigFile()); inputSource = new InputSource (getClass().getClassLoader() .getResource(getConfigFile()).toString()); } catch (Exception 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 ((inputStream == null) && (file != null)) { log.warn("Can't load server.xml from " + file.getAbsolutePath()); return; } try { inputSource.setByteStream(inputStream); digester.push(this); digester.parse(inputSource);//解析service.xml配置文件 inputStream.close(); } catch (Exception e) { log.warn("Catalina.start using " + getConfigFile() + ": " , e); return; } // Stream redirection initStreams(); // Start the new server if (server instanceof Lifecycle) { try { server.initialize(); } catch (LifecycleException e) { log.error("Catalina.start", e); } } long t2 = System.nanoTime(); if(log.isInfoEnabled()) log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms"); }
tomcat从Catalina的start方法开启了。
public void start() { if (server == null) { load(); } long t1 = System.nanoTime(); // Start the new server if (server instanceof Lifecycle) { try { ((Lifecycle) server).start();//这里的server是StandardService,在这里启动服务。 } catch (LifecycleException e) { log.error("Catalina.start: ", e); } } long t2 = System.nanoTime(); if(log.isInfoEnabled()) log.info("Server startup in " + ((t2 - t1) / 1000000) + " ms"); try { // Register shutdown hook if (useShutdownHook) { if (shutdownHook == null) { shutdownHook = new CatalinaShutdownHook(); } Runtime.getRuntime().addShutdownHook(shutdownHook); } } catch (Throwable t) { // This will fail on JDK 1.2. Ignoring, as Tomcat can run // fine without the shutdown hook. } if (await) { await(); stop(); } }
启动完成后,等待客户端的链接。
public void await() { server.await(); }
建立Socket,等待客户端的链接
public void await() { // Negative values - don't wait on port - tomcat is embedded or we just don't like ports if( port == -2 ) { // undocumented yet - for embedding apps that are around, alive. return; } if( port==-1 ) { while( true ) { try { Thread.sleep( 10000 ); } catch( InterruptedException ex ) { } if( stopAwait ) return; } } // Set up a server socket to wait on ServerSocket serverSocket = null; try { serverSocket = new ServerSocket(port, 1, InetAddress.getByName("localhost"));//创建了服务端Socket,绑定了localhost/127.0.0.1地址,端口号是8005 } catch (IOException e) { log.error("StandardServer.await: create[" + port + "]: ", e); System.exit(1); } // Loop waiting for a connection and a valid command while (true) { //循环等待客户端连接 // Wait for the next connection Socket socket = null; InputStream stream = null; try { socket = serverSocket.accept();//启动完成了,在等待客户端的链接 socket.setSoTimeout(10 * 1000); // Ten seconds stream = socket.getInputStream();//得到socket的输入流 } catch (AccessControlException ace) { log.warn("StandardServer.accept security exception: " + ace.getMessage(), ace); continue; } catch (IOException e) { log.error("StandardServer.await: accept: ", e); System.exit(1); } // Read a set of characters from the socket StringBuffer command = new StringBuffer(); int expected = 1024; // Cut off to avoid DoS attack while (expected < shutdown.length()) { if (random == null) random = new Random(); expected += (random.nextInt() % 1024); } while (expected > 0) { int ch = -1; try { ch = stream.read();//读取流中的数据 } catch (IOException e) { log.warn("StandardServer.await: read: ", e); ch = -1; } if (ch < 32) // Control character or EOF terminates loop break; command.append((char) ch);//将数据添加到stringbuffer中 expected--; } // Close the socket now that we are done with it try { socket.close(); } catch (IOException e) { ; } // Match against our command string boolean match = command.toString().equals(shutdown);//判断是否关闭应用 if (match) { break; } else log.warn("StandardServer.await: Invalid command '" + command.toString() + "' received"); } // Close the server socket and return try { serverSocket.close();//退出循环,关闭服务端socket } catch (IOException e) { ; } }