Tomcat启动过程源码分析四
前言
上一篇文章中我們討論了Bootstrap類中main方法中涉及到的init方法,今天這篇文章我們來查看下load方法。
daemon.setAwait(true); daemon.load(args);setAwait方法
在load方法執行前,執行了setAwait方法,跟進去查看
public void setAwait(boolean await)throws Exception {Class<?> paramTypes[] = new Class[1];paramTypes[0] = Boolean.TYPE;Object paramValues[] = new Object[1];paramValues[0] = Boolean.valueOf(await);Method method =catalinaDaemon.getClass().getMethod("setAwait", paramTypes);method.invoke(catalinaDaemon, paramValues);}代碼很簡單,和上一篇文章中的類似,使用了反射調用了catalinaDaemon對象的setAwait方法,傳遞了參數true,而我們在上一篇文章中,init方法的最后講解中可以看到catalinaDaemon對象是Catalina類的一個實例,所以查看Catalina對象的setAwait方法:
public void setAwait(boolean b) {await = b; }看來daemon.setAwait(true)這句代碼很簡單,就是使用了反射,設置了catalina實例的await屬性為true
load方法
/*** Load daemon.*/ private void load(String[] arguments)throws Exception {// Call the load() methodString 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);method.invoke(catalinaDaemon, param); }這段代碼有了前面的經驗讀起來很容易,Bootstrap類load方法是使用了反射調用了Catalina類的load方法,我們繼續查看Catalina類的load方法
/*** Start a new server instance.*/ public void load() {long t1 = System.nanoTime();//1 初始化相關屬性initDirs();//2 初始化相關屬性initNaming();//3 創建專門用來解析server.xml的Digester類,同時也隸屬于Jakarta Commons項目,專門用來解析xml工具包Digester digester = createStartDigester();............//到這里為止都是在解析server.xmlgetServer().setCatalina(this);// Stream redirectioninitStreams();// Start the new servertry {//4 初始化一個Server實例 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);}}long t2 = System.nanoTime();if(log.isInfoEnabled()) {log.info("Initialization processed in " + ((t2 - t1) / 1000000) + " ms");}}上面代碼省略了部分,用中文注釋注解出了相關含義,我們這里重點看下getServer().init(),getServer返回的是一個Server的實例,很明顯這里返回的應該是一個StandardServer實例,我們繼續查看StandardServer的init方法。
然而我們跟到StandardServer類中沒有直接的init方法,查看其實現的接口發現他實現了Server接口,Server繼承了LifeCycle接口,StandardServer繼承了LifecycleMBeanBase,LifecycleMBeanBase繼承了LifecycleBase,而LifecycleBase也實現了Lifecycle,所以調用的init方法應該是LifecycleBase的init方法。
@Override public final synchronized void init() throws LifecycleException {//1 一些通用的代碼if (!state.equals(LifecycleState.NEW)) {invalidTransition(Lifecycle.BEFORE_INIT_EVENT);}//一些通用的代碼setStateInternal(LifecycleState.INITIALIZING, null, false);try {initInternal();} catch (Throwable t) {ExceptionUtils.handleThrowable(t);setStateInternal(LifecycleState.FAILED, null, false);throw new LifecycleException(sm.getString("lifecycleBase.initFail",toString()), t);}//3 通用代碼setStateInternal(LifecycleState.INITIALIZED, null, false); }我們可以看到,在LifeCycleBase中除了通用的代碼還調用了initInternal方法。
我們看下在LifecycleBase類中initInternal方法做了什么。
在抽象類LifeCycleBase中只定義了initInternal方法,并沒有去實現而在StandardServer中實現了initInternal,所以調用了StandardServer對象的init方法實際上就是在調用initInternal方法,我們來看看StandardServer的initInternal方法做了什么。
/*** Invoke a pre-startup initialization. This is used to allow connectors* to bind to restricted ports under Unix operating environments.*/ @Override protected void initInternal() throws LifecycleException {super.initInternal();//不關心的代碼 開始............//不關心的代碼 結束//關注的代碼開始//初始化Service// Initialize our defined Servicesfor (int i = 0; i < services.length; i++) {services[i].init();} }前面的一篇文章我們說過Tomcat的架構是什么樣子的,Server中包含多個Service
在initInternal方法大末尾,我們看到了StandardServer獲取到了他內部所有Service然后調用每個Service的init方法。那么這個service數組里面都包含了哪些service呢。其實在調用init之前這個service數組就已經初始化好了,那么是在哪里初始化的呢?大家應該還記得上面的load方法中有個類叫做Digester,相關的代碼Digester digester = createStartDigester();,這個類在createStartDigester方法中通過解析server.xml文件,不僅來生成指定對象,更生成了不同對象之間的依賴關系,在這個方法內部,就將server內部的service全部都初始化了,其實StandardServer根據server.xml的格式默認只有一個service,他的指定實現類就是StandardService,關于digester這個類,有機會可以單獨寫一篇文章講解下使用方法。
我們現在知道了,在StandardServer的init方法中他調用了StandardService的init方法。我們繼續查看StandardService的init方法。
和StandardServer類似,調用init方法實際上是調用了initInternal方法。
/*** Invoke a pre-startup initialization. This is used to allow connectors* to bind to restricted ports under Unix operating environments.*/ @Override protected void initInternal() throws LifecycleException {super.initInternal();//如果service內部的container為空那么就初始化if (container != null) {container.init();}// Initialize any Executors//初始化executor,事實上在Service中代碼走到這里的時候,findExecutors會返回空數組,這里的代碼是不會執行的。for (Executor executor : findExecutors()) {if (executor instanceof LifecycleMBeanBase) {((LifecycleMBeanBase) executor).setDomain(getDomain());}executor.init();}// Initialize our defined Connectors//初始化 connectorssynchronized (connectorsLock) {for (Connector connector : connectors) {try {connector.init();} catch (Exception e) {String message = sm.getString("standardService.connector.initFailed", connector);log.error(message, e);if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))throw new LifecycleException(message);}}} }在StandardService的init方法中可以看出init方法主要還是將Service內部所有的connectors全部輪流調用init方法,是不是感覺很熟悉。是的!StandardService內部所有的connectors正是在server.xml中定義的。那么默認的就是有兩個了,分別對應處理http和ajp請求,我們加點代碼打印下看看是不是這樣。
測試代碼// Initialize our defined Connectorssynchronized (connectorsLock) {for (Connector connector : connectors) {try {System.out.println("connector名稱:"+connector.toString());connector.init();} catch (Exception e) {String message = sm.getString("standardService.connector.initFailed", connector);log.error(message, e);if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))throw new LifecycleException(message);}}}最終輸出如下
connector名稱:Connector[HTTP/1.1-8080] connector名稱:Connector[AJP/1.3-8009]好了,我們繼續看connector的init方法,需要注意的是,類似server,service都是有指定標準實現類的,而connector是沒有standardconnector這種實現類的,這主要是因為connector根據不同的協議是有多個對應實現的,來一起看connector的init方法。
@Override protected void initInternal() throws LifecycleException {super.initInternal();// Initialize adapteradapter = new CoyoteAdapter(this);protocolHandler.setAdapter(adapter);// Make sure parseBodyMethodsSet has a defaultif( null == parseBodyMethodsSet ) {setParseBodyMethods(getParseBodyMethods());}if (protocolHandler.isAprRequired() &&!AprLifecycleListener.isAprAvailable()) {throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerNoApr",getProtocolHandlerClassName()));}try {protocolHandler.init();} catch (Exception e) {throw new LifecycleException(sm.getString("coyoteConnector.protocolHandlerInitializationFailed"), e);}// Initialize mapper listenermapperListener.init(); }你會發現,和StandardService類似,Connector也實現LifeCycle接口,也實現了initInternal方法。
現在大家肯定都有疑問,為什么都要實現Lifecycle這個接口,都要實現initInternal方法,這個這里先說一下。Lifecycle是管理所有tomcat組件的接口,只要這個組件實現了Lifecycle這個接口,那么這個組件就可以隨著tomcat啟動而啟動,隨著tomcat停止而停止,一句話就是通過實現這個接口,tomcat可以統一管理所有組件的啟動和停止,而組件啟動的時候肯定會做一些通用的方法,所以有了init方法,而每個組件又會做一些自己的特有事情,所以init方法中又調用了initInternal方法來讓每個組件自己去實現自己特有的初始化事情,這種其實是一種設計模式,叫做模版設計模式,關于LifeCycle和tomcat中常用的設計模式我們會單獨寫文章來說明,這里只是提一下,方便大家理解。
在connector的initInternal方法中,可以看到除了一些常規的方法以外,有兩個方法需要關注一下,一個是protocolHandler.init(),另外個就是mapperListener.init(),而我們主要看一些關鍵組件,所以在這里就不講解mapperListener.init(),有興趣的可以自行查看,提示:這個是一個監聽器,監聽的是容器內部的映射關系變化,我們主要看protocolHandler.init()。
先看protocolHandler這個東西是哪個類的實例,找到初始化的地方,查看Connector的構造函數,可以看到:
public Connector(String protocol) {setProtocol(protocol);// Instantiate protocol handlertry {Class<?> clazz = Class.forName(protocolHandlerClassName);this.protocolHandler = (ProtocolHandler) clazz.newInstance();} catch (Exception e) {log.error(sm.getString("coyoteConnector.protocolHandlerInstantiationFailed"), e);} }實際上Connector是在解析server.xml的時候實例化的,在<connector>標簽上可以指定很多屬性,其中就包含該connector是哪個類的實例。
查看protocolHandlerClassName可以看到
/*** Coyote Protocol handler class name.* Defaults to the Coyote HTTP/1.1 protocolHandler.*/ protected String protocolHandlerClassName ="org.apache.coyote.http11.Http11Protocol";可以看出如果是處理http請求的Connector在init的時候,調用的是 org.apache.coyote.http11.Http11Protocol這個類的init方法,那我們繼續查看類Http11Protocol這個類的init方法。
可以查看Http11Protocol的繼承類圖:
Http11Protocol本身沒有init方法,我們查看其父類,可以在類AbstractProtocol中找到init方法,如下:
@Override public void init() throws Exception {//其他代碼...try {endpoint.init();} catch (Exception ex) {getLog().error(sm.getString("abstractProtocolHandler.initError",getName()), ex);throw ex;} }可以看到最后調用了endpoint.init(),這個endPoint指向的是哪個類呢?其實我們之前看到過,在Http11Protocol的構造函數中有如下代碼:
public Http11Protocol() {endpoint = new JIoEndpoint();cHandler = new Http11ConnectionHandler(this);((JIoEndpoint) endpoint).setHandler(cHandler);setSoLinger(Constants.DEFAULT_CONNECTION_LINGER);setSoTimeout(Constants.DEFAULT_CONNECTION_TIMEOUT);setTcpNoDelay(Constants.DEFAULT_TCP_NO_DELAY); }所以最后又調用了JIoEndpoint的init方法,我們先查看下JIoEndpoint,可以發現其中并沒有init方法,查看其父類AbstractEndpoint,發現有init方法,方法如下:
public final void init() throws Exception {testServerCipherSuitesOrderSupport();if (bindOnInit) {bind();bindState = BindState.BOUND_ON_INIT;} }其中bind方法是個抽象方法,查看其子實現在類JIoEndpoint中,
@Override public void bind() throws Exception {// Initialize thread count defaults for acceptor//為acceptor 初始化線程數量if (acceptorThreadCount == 0) {acceptorThreadCount = 1;}// Initialize maxConnections//初始化最大連接數if (getMaxConnections() == 0) {// User hasn't set a value - use the defaultsetMaxConnections(getMaxThreadsExecutor(true));}//如果線程工廠類為空,初始化if (serverSocketFactory == null) {if (isSSLEnabled()) {serverSocketFactory =handler.getSslImplementation().getServerSocketFactory(this);} else {serverSocketFactory = new DefaultServerSocketFactory(this);}}//初始化接收請求的線程。if (serverSocket == null) {try {if (getAddress() == null) {serverSocket = serverSocketFactory.createSocket(getPort(),getBacklog());} else {serverSocket = serverSocketFactory.createSocket(getPort(),getBacklog(), getAddress());}} catch (BindException orig) {String msg;if (getAddress() == null)msg = orig.getMessage() + " <null>:" + getPort();elsemsg = orig.getMessage() + " " +getAddress().toString() + ":" + getPort();BindException be = new BindException(msg);be.initCause(orig);throw be;}}}可以看出這個這個init方法就是初始化了幾個比較重要的屬性,包括線程數量,線程最大鏈接數,線程工廠類以及接收請求的線程。
到這里我們終于把connector的init方法查看完畢。
那么我們繼續回到StandServer內部的init方法
synchronized (connectorsLock) {for (Connector connector : connectors) {try {connector.init();} catch (Exception e) {String message = sm.getString("standardService.connector.initFailed", connector);log.error(message, e);if (Boolean.getBoolean("org.apache.catalina.startup.EXIT_ON_INIT_FAILURE"))throw new LifecycleException(message);}}}你會發現,connector初始化完畢以后,StandardServer的init方法也調用結束了,也就是說load方法到這里就結束了。
轉載于:https://www.cnblogs.com/coldridgeValley/p/5631610.html
總結
以上是生活随笔為你收集整理的Tomcat启动过程源码分析四的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: html与js加载的顺序问题defer
- 下一篇: Ubuntu android 开发配置