日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【转】Tomcat7启动的总过程 (有时间自己写下tomcat8的)

發布時間:2025/3/21 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【转】Tomcat7启动的总过程 (有时间自己写下tomcat8的) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先,說明tomcat8和tomcat7的啟動過程不一樣,這篇是針對tomcat7的。

Tomcat啟動的總過程

通過上面的介紹,我們總體上清楚了各個組件的生命周期的各個階段具體都是如何運作的。接下來我們就來看看,Tomcat具體是如何一步步啟動起來的。我們都知道任何Java程序都有一個main函數入口,Tomcat中的main入口是org.apache.catalina.startup.Bootstrap#main,下面我們就來分析一下它的代碼:

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);}}

下面我們逐一來分析一下上述代碼中標注了數字的地方:

  • 標注1的代碼初始化了自舉類的實例,標注2的代碼對BootStrap實例進行了初始化,標注3的代碼將實例賦值給了daemon。
  • 標注4的代碼首先調用了BootStrap的load方法,然后調用了start方法。
  • 接下來我們分別分析一下BootStrap的init,load,start方法具體做了哪些工作。

    BootStrap#init方法

    首先來看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;}

    下面我們重點逐一來分析一下上述代碼中標注了數字的地方:

  • 標注1的代碼通過反射實例化了org.apache.catalina.startup.Catalina類的實例;
  • 標注2的代碼調用了Catalina實例的setParentClassLoader方法設置了父親ClassLoader,對于ClassLoader方面的內容,我們在本系列的后續文章再來看看。標注3的代碼將Catalina實例賦值給了Bootstrap實例的catalinaDaemon.
  • BootStrap#load

    接下來我們再來看看org.apache.catalina.startup.Bootstrap#load方法,通過查看源代碼,我們知道此方法通過反射調用了org.apache.catalina.startup.Catalina#load方法,那我們就來看看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);}}}

    上面的代碼,我只保留了主流程核心的代碼,下面我們重點逐一來分析一下上述代碼中標注了數字的地方:

  • 標注1的代碼創建Digester實例解析”conf/server.xml”文件
  • 標注2的代碼最終調用了StandardServer的init方法。
  • 大家可以自行查看下源代碼,我們會發現如下的一個調用流程:

    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

    因為StandardService,Connector,StandardEngine實現了LifeCycle接口,因此符合我們上文所獲的生命周期的管理,最終都是通過他們自己實現的initInternal方法進行初始化

    讀到這里的時候,我想大家應該和我一樣,以為StandardEngine#init方法會調用StandardHost#init方法,但是當我們查看StandardEngine#init方法的時候,發現并沒有進行StandardHost的初始化,它到底做了什么呢?讓我們來具體分析一下,我們首先拿StanderEngine的繼承關系圖來看下:通過上圖以及前面說的LifeCyecle的模板方法模式,我們知道StandardEngine的初始化鉤子方法initInternal方法最終調用了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的初始化僅僅是創建了一個ThreadPoolExecutor,當看到這里的時候,筆者當時也納悶了,StandardEngine#init竟然沒有調用StandardHost#init方法,那么StandardHost的init方法是什么時候被調用的呢?遇到這種不知道到底方法怎么調用的時候怎么辦呢?筆者介紹個方法給大家。我們現在需要知道StandardHost#init方法何時被調用的,而我們知道init最終會調用鉤子的initInternal方法,因此這個時候,我們可以在StandardHost中override initInternal方法,增加了實現方法以后,有兩種方法可以用,一種就是設置個斷點debug一下就可以看出線程調用棧了,另外一種就是在新增的方法中打印出調用棧。筆者這里采用第二種方法,我們增加如下的initInternal方法到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(); }

    上面的代碼將會打印出方法調用堆棧,對于調試非常有用,上面的方法運行以后在控制臺打印出了如下的堆棧信息:

    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

    通過控制臺的信息,我們看到是StartChild#call方法調用的,而我們查看StartChild#call方法其實是在StandardEngine的startInternal方法中通過異步線程池去初始化子容器。因此到這里我們就理清楚了,StarndardHost的init方法是在調用start方法的時候被初始化。那么接下來我們就來看看,start方法的整體調用流程。

    BootStrap#start

    采用分析load方法一樣的方法,經過對BootStrap#start的分析,我們最終可以得到得到如下的調用鏈:

    org.apache.catalina.startup.Bootstrap#start call stack

    org.apache.catalina.startup.Bootstrap#start ->org.apache.catalina.startup.Catalina#start 通過反射調用 -->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

    綜合上文的描述我們總體得到如下的調用鏈:

    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 通過反射調用 --->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

    通過上面的分析我們已經搞清楚了Tomcat啟動的總體的過程,但是有一些關鍵的步驟,我們還需要進行進一步的深入探究。let’s do it.

    ?

    ?

    ?

    ?

    Reference

    Tomcat啟動過程(Tomcat源代碼閱讀系列之三)

    轉載于:https://www.cnblogs.com/549294286/p/3717714.html

    總結

    以上是生活随笔為你收集整理的【转】Tomcat7启动的总过程 (有时间自己写下tomcat8的)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。