Tomcat启动过程源码分析四
前言
上一篇文章中我們討論了Bootstrap類(lèi)中main方法中涉及到的init方法,今天這篇文章我們來(lái)查看下load方法。
daemon.setAwait(true); daemon.load(args);setAwait方法
在load方法執(zhí)行前,執(zhí)行了setAwait方法,跟進(jìn)去查看
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);}代碼很簡(jiǎn)單,和上一篇文章中的類(lèi)似,使用了反射調(diào)用了catalinaDaemon對(duì)象的setAwait方法,傳遞了參數(shù)true,而我們?cè)谏弦黄恼轮?#xff0c;init方法的最后講解中可以看到catalinaDaemon對(duì)象是Catalina類(lèi)的一個(gè)實(shí)例,所以查看Catalina對(duì)象的setAwait方法:
public void setAwait(boolean b) {await = b; }看來(lái)daemon.setAwait(true)這句代碼很簡(jiǎn)單,就是使用了反射,設(shè)置了catalina實(shí)例的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); }這段代碼有了前面的經(jīng)驗(yàn)讀起來(lái)很容易,Bootstrap類(lèi)load方法是使用了反射調(diào)用了Catalina類(lèi)的load方法,我們繼續(xù)查看Catalina類(lèi)的load方法
/*** Start a new server instance.*/ public void load() {long t1 = System.nanoTime();//1 初始化相關(guān)屬性initDirs();//2 初始化相關(guān)屬性initNaming();//3 創(chuàng)建專門(mén)用來(lái)解析server.xml的Digester類(lèi),同時(shí)也隸屬于Jakarta Commons項(xiàng)目,專門(mén)用來(lái)解析xml工具包Digester digester = createStartDigester();............//到這里為止都是在解析server.xmlgetServer().setCatalina(this);// Stream redirectioninitStreams();// Start the new servertry {//4 初始化一個(gè)Server實(shí)例 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");}}上面代碼省略了部分,用中文注釋注解出了相關(guān)含義,我們這里重點(diǎn)看下getServer().init(),getServer返回的是一個(gè)Server的實(shí)例,很明顯這里返回的應(yīng)該是一個(gè)StandardServer實(shí)例,我們繼續(xù)查看StandardServer的init方法。
然而我們跟到StandardServer類(lèi)中沒(méi)有直接的init方法,查看其實(shí)現(xiàn)的接口發(fā)現(xiàn)他實(shí)現(xiàn)了Server接口,Server繼承了LifeCycle接口,StandardServer繼承了LifecycleMBeanBase,LifecycleMBeanBase繼承了LifecycleBase,而LifecycleBase也實(shí)現(xiàn)了Lifecycle,所以調(diào)用的init方法應(yīng)該是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中除了通用的代碼還調(diào)用了initInternal方法。
我們看下在LifecycleBase類(lèi)中initInternal方法做了什么。
在抽象類(lèi)LifeCycleBase中只定義了initInternal方法,并沒(méi)有去實(shí)現(xiàn)而在StandardServer中實(shí)現(xiàn)了initInternal,所以調(diào)用了StandardServer對(duì)象的init方法實(shí)際上就是在調(diào)用initInternal方法,我們來(lái)看看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();//不關(guān)心的代碼 開(kāi)始............//不關(guān)心的代碼 結(jié)束//關(guān)注的代碼開(kāi)始//初始化Service// Initialize our defined Servicesfor (int i = 0; i < services.length; i++) {services[i].init();} }前面的一篇文章我們說(shuō)過(guò)Tomcat的架構(gòu)是什么樣子的,Server中包含多個(gè)Service
在initInternal方法大末尾,我們看到了StandardServer獲取到了他內(nèi)部所有Service然后調(diào)用每個(gè)Service的init方法。那么這個(gè)service數(shù)組里面都包含了哪些service呢。其實(shí)在調(diào)用init之前這個(gè)service數(shù)組就已經(jīng)初始化好了,那么是在哪里初始化的呢?大家應(yīng)該還記得上面的load方法中有個(gè)類(lèi)叫做Digester,相關(guān)的代碼Digester digester = createStartDigester();,這個(gè)類(lèi)在createStartDigester方法中通過(guò)解析server.xml文件,不僅來(lái)生成指定對(duì)象,更生成了不同對(duì)象之間的依賴關(guān)系,在這個(gè)方法內(nèi)部,就將server內(nèi)部的service全部都初始化了,其實(shí)StandardServer根據(jù)server.xml的格式默認(rèn)只有一個(gè)service,他的指定實(shí)現(xiàn)類(lèi)就是StandardService,關(guān)于digester這個(gè)類(lèi),有機(jī)會(huì)可以單獨(dú)寫(xiě)一篇文章講解下使用方法。
我們現(xiàn)在知道了,在StandardServer的init方法中他調(diào)用了StandardService的init方法。我們繼續(xù)查看StandardService的init方法。
和StandardServer類(lèi)似,調(diào)用init方法實(shí)際上是調(diào)用了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內(nèi)部的container為空那么就初始化if (container != null) {container.init();}// Initialize any Executors//初始化executor,事實(shí)上在Service中代碼走到這里的時(shí)候,findExecutors會(huì)返回空數(shù)組,這里的代碼是不會(huì)執(zhí)行的。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內(nèi)部所有的connectors全部輪流調(diào)用init方法,是不是感覺(jué)很熟悉。是的!StandardService內(nèi)部所有的connectors正是在server.xml中定義的。那么默認(rèn)的就是有兩個(gè)了,分別對(duì)應(yīng)處理http和ajp請(qǐng)求,我們加點(diǎn)代碼打印下看看是不是這樣。
測(cè)試代碼// 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]好了,我們繼續(xù)看connector的init方法,需要注意的是,類(lèi)似server,service都是有指定標(biāo)準(zhǔn)實(shí)現(xiàn)類(lèi)的,而connector是沒(méi)有standardconnector這種實(shí)現(xiàn)類(lèi)的,這主要是因?yàn)閏onnector根據(jù)不同的協(xié)議是有多個(gè)對(duì)應(yīng)實(shí)現(xiàn)的,來(lái)一起看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(); }你會(huì)發(fā)現(xiàn),和StandardService類(lèi)似,Connector也實(shí)現(xiàn)LifeCycle接口,也實(shí)現(xiàn)了initInternal方法。
現(xiàn)在大家肯定都有疑問(wèn),為什么都要實(shí)現(xiàn)Lifecycle這個(gè)接口,都要實(shí)現(xiàn)initInternal方法,這個(gè)這里先說(shuō)一下。Lifecycle是管理所有tomcat組件的接口,只要這個(gè)組件實(shí)現(xiàn)了Lifecycle這個(gè)接口,那么這個(gè)組件就可以隨著tomcat啟動(dòng)而啟動(dòng),隨著tomcat停止而停止,一句話就是通過(guò)實(shí)現(xiàn)這個(gè)接口,tomcat可以統(tǒng)一管理所有組件的啟動(dòng)和停止,而組件啟動(dòng)的時(shí)候肯定會(huì)做一些通用的方法,所以有了init方法,而每個(gè)組件又會(huì)做一些自己的特有事情,所以init方法中又調(diào)用了initInternal方法來(lái)讓每個(gè)組件自己去實(shí)現(xiàn)自己特有的初始化事情,這種其實(shí)是一種設(shè)計(jì)模式,叫做模版設(shè)計(jì)模式,關(guān)于LifeCycle和tomcat中常用的設(shè)計(jì)模式我們會(huì)單獨(dú)寫(xiě)文章來(lái)說(shuō)明,這里只是提一下,方便大家理解。
在connector的initInternal方法中,可以看到除了一些常規(guī)的方法以外,有兩個(gè)方法需要關(guān)注一下,一個(gè)是protocolHandler.init(),另外個(gè)就是mapperListener.init(),而我們主要看一些關(guān)鍵組件,所以在這里就不講解mapperListener.init(),有興趣的可以自行查看,提示:這個(gè)是一個(gè)監(jiān)聽(tīng)器,監(jiān)聽(tīng)的是容器內(nèi)部的映射關(guān)系變化,我們主要看protocolHandler.init()。
先看protocolHandler這個(gè)東西是哪個(gè)類(lèi)的實(shí)例,找到初始化的地方,查看Connector的構(gòu)造函數(shù),可以看到:
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);} }實(shí)際上Connector是在解析server.xml的時(shí)候?qū)嵗?#xff0c;在<connector>標(biāo)簽上可以指定很多屬性,其中就包含該connector是哪個(gè)類(lèi)的實(shí)例。
查看protocolHandlerClassName可以看到
/*** Coyote Protocol handler class name.* Defaults to the Coyote HTTP/1.1 protocolHandler.*/ protected String protocolHandlerClassName ="org.apache.coyote.http11.Http11Protocol";可以看出如果是處理http請(qǐng)求的Connector在init的時(shí)候,調(diào)用的是 org.apache.coyote.http11.Http11Protocol這個(gè)類(lèi)的init方法,那我們繼續(xù)查看類(lèi)Http11Protocol這個(gè)類(lèi)的init方法。
可以查看Http11Protocol的繼承類(lèi)圖:
Http11Protocol本身沒(méi)有init方法,我們查看其父類(lèi),可以在類(lèi)AbstractProtocol中找到init方法,如下:
@Override public void init() throws Exception {//其他代碼...try {endpoint.init();} catch (Exception ex) {getLog().error(sm.getString("abstractProtocolHandler.initError",getName()), ex);throw ex;} }可以看到最后調(diào)用了endpoint.init(),這個(gè)endPoint指向的是哪個(gè)類(lèi)呢?其實(shí)我們之前看到過(guò),在Http11Protocol的構(gòu)造函數(shù)中有如下代碼:
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); }所以最后又調(diào)用了JIoEndpoint的init方法,我們先查看下JIoEndpoint,可以發(fā)現(xiàn)其中并沒(méi)有init方法,查看其父類(lèi)AbstractEndpoint,發(fā)現(xiàn)有init方法,方法如下:
public final void init() throws Exception {testServerCipherSuitesOrderSupport();if (bindOnInit) {bind();bindState = BindState.BOUND_ON_INIT;} }其中bind方法是個(gè)抽象方法,查看其子實(shí)現(xiàn)在類(lèi)JIoEndpoint中,
@Override public void bind() throws Exception {// Initialize thread count defaults for acceptor//為acceptor 初始化線程數(shù)量if (acceptorThreadCount == 0) {acceptorThreadCount = 1;}// Initialize maxConnections//初始化最大連接數(shù)if (getMaxConnections() == 0) {// User hasn't set a value - use the defaultsetMaxConnections(getMaxThreadsExecutor(true));}//如果線程工廠類(lèi)為空,初始化if (serverSocketFactory == null) {if (isSSLEnabled()) {serverSocketFactory =handler.getSslImplementation().getServerSocketFactory(this);} else {serverSocketFactory = new DefaultServerSocketFactory(this);}}//初始化接收請(qǐng)求的線程。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;}}}可以看出這個(gè)這個(gè)init方法就是初始化了幾個(gè)比較重要的屬性,包括線程數(shù)量,線程最大鏈接數(shù),線程工廠類(lèi)以及接收請(qǐng)求的線程。
到這里我們終于把connector的init方法查看完畢。
那么我們繼續(xù)回到StandServer內(nèi)部的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);}}}你會(huì)發(fā)現(xiàn),connector初始化完畢以后,StandardServer的init方法也調(diào)用結(jié)束了,也就是說(shuō)load方法到這里就結(jié)束了。
轉(zhuǎn)載于:https://www.cnblogs.com/coldridgeValley/p/5631610.html
總結(jié)
以上是生活随笔為你收集整理的Tomcat启动过程源码分析四的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: html与js加载的顺序问题defer
- 下一篇: 使用阿里云加速docker镜像的安装