【转】Tomcat7启动的总过程 (有时间自己写下tomcat8的)
首先,說(shuō)明tomcat8和tomcat7的啟動(dòng)過(guò)程不一樣,這篇是針對(duì)tomcat7的。
Tomcat啟動(dòng)的總過(guò)程
通過(guò)上面的介紹,我們總體上清楚了各個(gè)組件的生命周期的各個(gè)階段具體都是如何運(yùn)作的。接下來(lái)我們就來(lái)看看,Tomcat具體是如何一步步啟動(dòng)起來(lái)的。我們都知道任何Java程序都有一個(gè)main函數(shù)入口,Tomcat中的main入口是org.apache.catalina.startup.Bootstrap#main,下面我們就來(lái)分析一下它的代碼:
org.apache.catalina.startup.Bootstrap#main
public static void main(String args[]) {if (daemon == null) {// Don't set daemon until init() has completed// 1 Bootstrap bootstrap = new Bootstrap();try {// 2 bootstrap.init();} catch (Throwable t) {handleThrowable(t);t.printStackTrace();return;}// 3daemon = 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")) {// 4daemon.setAwait(true);daemon.load(args);daemon.start();} 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 reportingif (t instanceof InvocationTargetException &&t.getCause() != null) {t = t.getCause();}handleThrowable(t);t.printStackTrace();System.exit(1);}}下面我們逐一來(lái)分析一下上述代碼中標(biāo)注了數(shù)字的地方:
接下來(lái)我們分別分析一下BootStrap的init,load,start方法具體做了哪些工作。
BootStrap#init方法
首先來(lái)看org.apache.catalina.startup.Bootstrap#init方法,它的代碼如下:
org.apache.catalina.startup.Bootstrap#init
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() methodif (log.isDebugEnabled())log.debug("Loading startup class");// 1Class<?> startupClass =catalinaLoader.loadClass("org.apache.catalina.startup.Catalina");Object startupInstance = startupClass.newInstance();// Set the shared extensions class loaderif (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);// 2 method.invoke(startupInstance, paramValues);// 3catalinaDaemon = startupInstance;}下面我們重點(diǎn)逐一來(lái)分析一下上述代碼中標(biāo)注了數(shù)字的地方:
BootStrap#load
接下來(lái)我們?cè)賮?lái)看看org.apache.catalina.startup.Bootstrap#load方法,通過(guò)查看源代碼,我們知道此方法通過(guò)反射調(diào)用了org.apache.catalina.startup.Catalina#load方法,那我們就來(lái)看看Catalina的load方法,Catalina#load方法代碼如下:
org.apache.catalina.startup.Catalina#load
public void load() {// 1 Digester digester = createStartDigester();InputSource inputSource = null;InputStream inputStream = null;File file = null;try {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);}}try {inputSource.setByteStream(inputStream);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 }}getServer().setCatalina(this);// Stream redirection initStreams();// Start the new servertry {// 2 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);}}}上面的代碼,我只保留了主流程核心的代碼,下面我們重點(diǎn)逐一來(lái)分析一下上述代碼中標(biāo)注了數(shù)字的地方:
大家可以自行查看下源代碼,我們會(huì)發(fā)現(xiàn)如下的一個(gè)調(diào)用流程:
init call stack
org.apache.catalina.core.StandardServer#init ->org.apache.catalina.core.StandardService#init -->org.apache.catalina.connector.Connector#init -->org.apache.catalina.core.StandardEngine#init因?yàn)镾tandardService,Connector,StandardEngine實(shí)現(xiàn)了LifeCycle接口,因此符合我們上文所獲的生命周期的管理,最終都是通過(guò)他們自己實(shí)現(xiàn)的initInternal方法進(jìn)行初始化
讀到這里的時(shí)候,我想大家應(yīng)該和我一樣,以為StandardEngine#init方法會(huì)調(diào)用StandardHost#init方法,但是當(dāng)我們查看StandardEngine#init方法的時(shí)候,發(fā)現(xiàn)并沒(méi)有進(jìn)行StandardHost的初始化,它到底做了什么呢?讓我們來(lái)具體分析一下,我們首先拿StanderEngine的繼承關(guān)系圖來(lái)看下:通過(guò)上圖以及前面說(shuō)的LifeCyecle的模板方法模式,我們知道StandardEngine的初始化鉤子方法initInternal方法最終調(diào)用了ContainerBase的initInternal方法,那我們拿ContainerBase#initInternal方法的代碼看看:
org.apache.catalina.core.ContainerBase#initInternal
protected void initInternal() throws LifecycleException {BlockingQueue<Runnable> startStopQueue =new LinkedBlockingQueue<Runnable>();startStopExecutor = new ThreadPoolExecutor(getStartStopThreadsInternal(),getStartStopThreadsInternal(), 10, TimeUnit.SECONDS,startStopQueue,new StartStopThreadFactory(getName() + "-startStop-"));startStopExecutor.allowCoreThreadTimeOut(true);super.initInternal(); }我們可以看到StandardEngine的初始化僅僅是創(chuàng)建了一個(gè)ThreadPoolExecutor,當(dāng)看到這里的時(shí)候,筆者當(dāng)時(shí)也納悶了,StandardEngine#init竟然沒(méi)有調(diào)用StandardHost#init方法,那么StandardHost的init方法是什么時(shí)候被調(diào)用的呢?遇到這種不知道到底方法怎么調(diào)用的時(shí)候怎么辦呢?筆者介紹個(gè)方法給大家。我們現(xiàn)在需要知道StandardHost#init方法何時(shí)被調(diào)用的,而我們知道init最終會(huì)調(diào)用鉤子的initInternal方法,因此這個(gè)時(shí)候,我們可以在StandardHost中override initInternal方法,增加了實(shí)現(xiàn)方法以后,有兩種方法可以用,一種就是設(shè)置個(gè)斷點(diǎn)debug一下就可以看出線程調(diào)用棧了,另外一種就是在新增的方法中打印出調(diào)用棧。筆者這里采用第二種方法,我們?cè)黾尤缦碌膇nitInternal方法到StandardHost中:
org.apache.catalina.core.StandardHost#initInternal
protected void initInternal() throws LifecycleException {Throwable ex = new Throwable();StackTraceElement[] stackElements = ex.getStackTrace();if (stackElements != null) {for (int i = stackElements.length - 1; i >= 0; i--) {System.out.print(stackElements[i].getClassName() + "\t");System.out.print(stackElements[i].getMethodName() + "\t");System.out.print(stackElements[i].getFileName() + "\t");System.out.println(stackElements[i].getLineNumber());}}super.initInternal(); }上面的代碼將會(huì)打印出方法調(diào)用堆棧,對(duì)于調(diào)試非常有用,上面的方法運(yùn)行以后在控制臺(tái)打印出了如下的堆棧信息:
stack info
java.lang.Thread run Thread.java 680 java.util.concurrent.ThreadPoolExecutor$Worker run ThreadPoolExecutor.java 918 java.util.concurrent.ThreadPoolExecutor$Worker runTask ThreadPoolExecutor.java 895 java.util.concurrent.FutureTask run FutureTask.java 138 java.util.concurrent.FutureTask$Sync innerRun FutureTask.java 303 org.apache.catalina.core.ContainerBase$StartChild call ContainerBase.java 1549 org.apache.catalina.core.ContainerBase$StartChild call ContainerBase.java 1559 org.apache.catalina.util.LifecycleBase start LifecycleBase.java 139 org.apache.catalina.util.LifecycleBase init LifecycleBase.java 102 org.apache.catalina.core.StandardHost initInternal StandardHost.java 794通過(guò)控制臺(tái)的信息,我們看到是StartChild#call方法調(diào)用的,而我們查看StartChild#call方法其實(shí)是在StandardEngine的startInternal方法中通過(guò)異步線程池去初始化子容器。因此到這里我們就理清楚了,StarndardHost的init方法是在調(diào)用start方法的時(shí)候被初始化。那么接下來(lái)我們就來(lái)看看,start方法的整體調(diào)用流程。
BootStrap#start
采用分析load方法一樣的方法,經(jīng)過(guò)對(duì)BootStrap#start的分析,我們最終可以得到得到如下的調(diào)用鏈:
org.apache.catalina.startup.Bootstrap#start call stack
org.apache.catalina.startup.Bootstrap#start ->org.apache.catalina.startup.Catalina#start 通過(guò)反射調(diào)用 -->org.apache.catalina.core.StandardServer#start --->org.apache.catalina.core.StandardService#start ---->org.apache.catalina.core.StandardEngine#start ---->org.apache.catalina.Executor#start ---->org.apache.catalina.connector.Connector#start綜合上文的描述我們總體得到如下的調(diào)用鏈:
org.apache.catalina.startup.Bootstrap#main call stack
org.apache.catalina.startup.Bootstrap#main ->org.apache.catalina.startup.Bootstrap#init ->org.apache.catalina.startup.Bootstrap#load -->org.apache.catalina.startup.Catalina#load --->org.apache.catalina.core.StandardServer#init ---->org.apache.catalina.core.StandardService#init ----->org.apache.catalina.connector.Connector#init ----->org.apache.catalina.core.StandardEngine#init ->org.apache.catalina.startup.Bootstrap#start -->org.apache.catalina.startup.Catalina#start 通過(guò)反射調(diào)用 --->org.apache.catalina.core.StandardServer#start ---->org.apache.catalina.core.StandardService#start ----->org.apache.catalina.core.StandardEngine#start ----->org.apache.catalina.Executor#start ----->org.apache.catalina.connector.Connector#start通過(guò)上面的分析我們已經(jīng)搞清楚了Tomcat啟動(dòng)的總體的過(guò)程,但是有一些關(guān)鍵的步驟,我們還需要進(jìn)行進(jìn)一步的深入探究。let’s do it.
?
?
?
?
Reference
Tomcat啟動(dòng)過(guò)程(Tomcat源代碼閱讀系列之三)
轉(zhuǎn)載于:https://www.cnblogs.com/549294286/p/3717714.html
總結(jié)
以上是生活随笔為你收集整理的【转】Tomcat7启动的总过程 (有时间自己写下tomcat8的)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: PL/SQL Developer连接本地
- 下一篇: C#统计字数