【Tomcat源码阅读分享】—(4)Tomcat启动过程简述(二)

上一篇写到Bootstrap类的启动流程,经过启动脚本,经历了一系列的初始化,类加载,最后通过不同的命令,执行到不同的操作,由于是启动过程,所以从执行load()方法开始。接下来我们简单梳理下load方法的主要流程和一些结构分析,从而了解到其中的启动流程,衍生出tomcat中主要的几个组件,以及这些组件之间的大概关系。为以后每个模块和组件的研究打下基础。
我们进入到daemon.load(args)方法,可以看到,也是使用反射调用了Catalina类的load(String args[])方法:
private void load(String[] arguments) throws Exception {// Call the load() method String methodName = "load"; ... method.invoke(catalinaDaemon, param); }

接下来进入到Catalina#load(String args[])
这个方法主要是对一些特殊命令进行处理,接下来便进入到重载的load()方法:
/** * Start a new server instance. */ public void load() { long t1 = System.nanoTime(); ... ... // Create and execute our Digester Digester digester = createStartDigester(); ... ...getServer().setCatalina(this); getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile()); getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile()); // Start the new server try { 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); } } }

这里只列出部分核心代码。
首先,使用Digester 工具预定义了一系列的conf/server.xml配置文件的解析规则,这个工具会在以后的章节中细说,这个工具主要是定义了xml转换为java对象的规则。
为了简洁,我将tomcat的默认配置文件的注释删掉了,如图:

【Tomcat源码阅读分享】—(4)Tomcat启动过程简述(二)
文章图片
图一 tomcat默认配置文件
通过Digester ,可以将Catalina中的server属性创建为默认的StandardServer类,并将其中的属性依赖关系也通过xml创建了。
从配置文件可以看到,Server中包含了一组Listener,用来监听生命周期内的事件,这里用到了观察者模式来做事件通知操作,后续的章节中也会详细说明。
Server中又包括了一个Service标签,在使用Digester 时,创建了默认的StandardService类,Service中包含了两个Connector和一个Engine,再往里面就是Realm和Host、Valve。初步就是这么个结构,后续章节会详细解说这些组件间的关系和作用。
解析完server.xml文件后,将当前的Catalina对象的信息传给server属性,让它们互相拥有彼此的引用,然后调用server的init()方法。
StandardServer中并没有init方法,所以我根据其继承关系,找到了其父类LifecycleBase的init方法,代码如下:
@Override public final synchronized void init() throws LifecycleException { if (!state.equals(LifecycleState.NEW)) { invalidTransition(Lifecycle.BEFORE_INIT_EVENT); }try { setStateInternal(LifecycleState.INITIALIZING, null, false); initInternal(); setStateInternal(LifecycleState.INITIALIZED, null, false); } catch (Throwable t) { ExceptionUtils.handleThrowable(t); setStateInternal(LifecycleState.FAILED, null, false); throw new LifecycleException( sm.getString("lifecycleBase.initFail",toString()), t); } }

为了防止其他线程同时调用这个方法,导致生命周期的状态不正常,这个方法加了关键字synchronized 。这个方法其实用了模板方法设计模式,留了一个initInternal抽象钩子方法让子类去实现。
setStateInternal方法设置了当前的state为初始化对应的状态,我们打开期内部,会在方法最后看到如下代码:
String lifecycleEvent = state.getLifecycleEvent(); if (lifecycleEvent != null) { fireLifecycleEvent(lifecycleEvent, data); }

/** * Allow sub classes to fire {@link Lifecycle} events. * * @param typeEvent type * @param dataData associated with event. */ protected void fireLifecycleEvent(String type, Object data) { LifecycleEvent event = new LifecycleEvent(this, type, data); for (LifecycleListener listener : lifecycleListeners) { listener.lifecycleEvent(event); } }

fireLifecycleEvent方法中其实就是通知所有的监听者本次发生了初始化的事件,监听者会根据event的类型进行不同的操作,在这里,各个Listener充当了观察者模式中的监听者,Server是监听目标,后续我们将会看到许多类似这样的写法。想深入观察者模式的请点击这里。
接下来我们再进入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(); // Register global String cache // Note although the cache is global, if there are multiple Servers // present in the JVM (may happen when embedding) then the same cache // will be registered under multiple names onameStringCache = register(new StringCache(), "type=StringCache"); // Register the MBeanFactory MBeanFactory factory = new MBeanFactory(); factory.setContainer(this); onameMBeanFactory = register(factory, "type=MBeanFactory"); // Register the naming resources globalNamingResources.init(); // Populate the extension validator with JARs from common and shared // class loaders if (getCatalina() != null) { ClassLoader cl = getCatalina().getParentClassLoader(); // Walk the class loader hierarchy. Stop at the system class loader. // This will add the shared (if present) and common class loaders while (cl != null && cl != ClassLoader.getSystemClassLoader()) { if (cl instanceof URLClassLoader) { URL[] urls = ((URLClassLoader) cl).getURLs(); for (URL url : urls) { if (url.getProtocol().equals("file")) { try { File f = new File (url.toURI()); if (f.isFile() && f.getName().endsWith(".jar")) { ExtensionValidator.addSystemResource(f); } } catch (URISyntaxException e) { // Ignore } catch (IOException e) { // Ignore } } } } cl = cl.getParent(); } } // Initialize our defined Services for (int i = 0; i < services.length; i++) { services[i].init(); } }

这里前面一大段是JMX中的注册MBean等操作,最后循环调用了它所有service的init方法。
同理进入到init方法,还是跟Server一样,又跳转到了LifecycleBase的init方法,同样地,也是进入到StandardService中的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(); if (engine != null) { engine.init(); }// Initialize any Executors for (Executor executor : findExecutors()) { if (executor instanceof JmxEnabled) { ((JmxEnabled) executor).setDomain(getDomain()); } executor.init(); }// Initialize mapper listener mapperListener.init(); // Initialize our defined Connectors 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); } } } }

首先调用了engine的init方法,在Digester 解析过程中配置了默认的StandardEngine,老规矩,从继承关系就可以肯定又是调用到了StandardEngine的initInternal这个钩子方法
@Override protected void initInternal() throws LifecycleException { // Ensure that a Realm is present before any attempt is made to start // one. This will create the default NullRealm if necessary. getRealm(); super.initInternal(); }

这里是为了避免realm对象为空,如果为null,会新建一个NullRealm对象来初始化。
然后调用了父类ContainerBase的initInternal方法,初始化了启动和停止的线程池startStopExecutor。
再回到StandardService中的initInternal方法,这里初始化了executors,等,最后初始化了connectors,也是默认的Connector类。
【【Tomcat源码阅读分享】—(4)Tomcat启动过程简述(二)】看到这里,我们结合server.xml,对于tomcat的初始化流程和一些组件的关系,应该有了一个初步的了解了

    推荐阅读