前言java
Tomcat是後端服務最多見的web容器,對於後端程序員來講,瞭解它對我的自身技術能力以及業務開發能力都是必要的。程序員
tomcat 版本:9.0.16web
1. main 方法apache
Tomcat是能夠獨立啓動的,java程序啓動是須要main方法的,所以讀tomcat源碼就從它的main方法開始。Tomcat的main方法在org.apache.catalina.startup.Bootstrap 裏bootstrap
public final class Bootstrap { …… /** * Daemon object used by main. */ private static final Object daemonLock = new Object(); …… /** * Main method and entry point when starting Tomcat via the provided * scripts. * * @param args Command line arguments to be processed */ public static void main(String args[]) { synchronized (daemonLock) { if (daemon == null) { // Don't set daemon until init() has completed Bootstrap bootstrap = new Bootstrap(); try { bootstrap.init(); } catch (Throwable t) { handleThrowable(t); t.printStackTrace(); return; } daemon = bootstrap; } else { // When running as a service the call to stop will be on a new // thread so make sure the correct class loader is used to // prevent a range of class not found exceptions. Thread.currentThread().setContextClassLoader(daemon.catalinaLoader); } } 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(); if (null == daemon.getServer()) { System.exit(1); } } else if (command.equals("stop")) { daemon.stopServer(args); } else if (command.equals("configtest")) { daemon.load(args); if (null == daemon.getServer()) { System.exit(1); } System.exit(0); } else { log.warn("Bootstrap: command \"" + command + "\" does not exist."); } } catch (Throwable t) { // Unwrap the Exception for clearer error reporting if (t instanceof InvocationTargetException && t.getCause() != null) { t = t.getCause(); } handleThrowable(t); t.printStackTrace(); System.exit(1); } } …… }
上面代碼邏輯很簡單,就是建立一個 Bootstrap 對象,調用它的 init 方法初始化,而後根據啓動參數,分別調用 Bootstrap 對象的不一樣方法。
1.1 init方法 後端
首先看 Bootstrap 的 init 方法tomcat
/** * Initialize daemon. * @throws Exception Fatal initialization error */ public void init() throws Exception { 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.getConstructor().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; }
init 方法也比較簡單,先調用了 initClassLoaders() 來初始化一些 ClassLoader,tomcat 須要加載應用程序裏,因此須要 ClassLoader,而後設置了一下當天線程的 contextClassLoader 爲
catalinaLoader,這個 catalinaLoader就是在 initClassLoaders() 初始化的,最後經過反射建立了一個 Catalina 類型的 startupInstance 對象,並調用了它的 setParentClassLoader 方法。ide
1.1.1 initClassLoaders 方法this
接着看一下 initClassLoaders() 方法url
ClassLoader commonLoader = null; ClassLoader catalinaLoader = null; ClassLoader sharedLoader = null; private void initClassLoaders() { try { 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 = createClassLoader("server", commonLoader); sharedLoader = createClassLoader("shared", commonLoader); } catch (Throwable t) { handleThrowable(t); log.error("Class loader creation threw exception", t); System.exit(1); } }
initClassLoaders() 就是爲了初始化 Bootstrap 類裏的三個 ClassLoader 成員變量,看 createClassLoader 方法的定義
private ClassLoader createClassLoader(String name, ClassLoader parent)
能夠看出,catalinaLoader 和 sharedLoader 的 parentClassLoader 是 commonLoader。
1.1.2 initClassLoaders 方法
下面看看 createClassLoader 方法
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); }
方法的邏輯也比較簡單
就是從 catalina.property文件裏找 common.loader, shared.loader, server.loader 對應的值,而後構形成Repository 列表,再將Repository 列表傳入ClassLoaderFactory.createClassLoader 方法,ClassLoaderFactory.createClassLoader 返回的是 URLClassLoader,而Repository 列表就是這個URLClassLoader 能夠加在的類的路徑。
在catalina.property文件裏
common.loader="${catalina.base}/lib","${catalina.base}/lib/*.jar","${catalina.home}/lib","${catalina.home}/lib/*.jar" server.loader= shared.loader=
其中 shared.loader, server.loader 是沒有值的,createClassLoader 方法裏若是沒有值的話,就返回傳入的 parent ClassLoader,也就是說,commonLoader,catalinaLoader,sharedLoader 實際上是一個對象。在Tomcat以前的版本里,這三個是不一樣的URLClassLoader對象。
Class<?> startupClass = catalinaLoader.loadClass("org.apache.catalina.startup.Catalina"); Object startupInstance = startupClass.getConstructor().newInstance();
初始化完三個ClassLoader對象後,init() 方法就使用 catalinaClassLoader 加載了org.apache.catalina.startup.Catalina 類,並建立了一個對象,而後經過反射調用這個對象的 setParentClassLoader 方法,傳入的參數是 sharedClassLoader。最後吧這個 Catania 對象複製給 catalinaDaemon 屬性。
1.2 start方法
main 方法初始化完 Bootstrap 對象後,就根據傳入的參數,分別調用 Bootstrap 不一樣的方法,以 "start" 參數爲例,調用了這三個方法
daemon.setAwait(true); daemon.load(args); daemon.start();
這三個方法的調用的共同點是經過反射去調用 init 方法裏初始化過的 Catalina 對象的同名的方法。這三個方法將在下一篇文章總結。
1.2 Bootstrap 的 static 塊
Bootstrap 有一個 static 的代碼塊,這個代碼塊的做用是用來初始化 Bootstrap 裏的兩個static屬性的
private static final File catalinaBaseFile; private static final File catalinaHomeFile;
2. 小結能夠看出 Bootstrap 的做用是初始化公共資源,建立一個 Catalina 類,並執行其相關方法,起了一個引導的做用。