Tomcat源码分析(七)--单一启动/关闭机制(生命周期)
在前面的大部分文章都是讲连接器和容器的,以后的内容会偏向写一些Tomcat的其他组件以及一些细节的东西。
Tomcat有很多组件,要一个一个启动组件难免有点麻烦。由于Tomcat的包含关系是Catalina->Server->Service->容器/连接器/日志器等,于是可通过父组件负责启动/关闭它的子组件,这样只要启动Catalina,其他的都自动启动了。这种单一启动和关闭的机制是通过实现Lifecycle接口来实现的。下面是Lifecycle接口的定义:
public interface Lifecycle { public static final String START_EVENT = "start"; //生命周期的六个事件类型! public static final String BEFORE_START_EVENT = "before_start"; public static final String AFTER_START_EVENT = "after_start"; public static final String STOP_EVENT = "stop"; public static final String BEFORE_STOP_EVENT = "before_stop"; public static final String AFTER_STOP_EVENT = "after_stop"; public void addLifecycleListener(LifecycleListener listener);//在此组件中添加一个监听器 public LifecycleListener[] findLifecycleListeners(); public void removeLifecycleListener(LifecycleListener listener); public void start() throws LifecycleException;//组件启动方法 public void stop() throws LifecycleException; }
当组件实现了Lifecycle接口,父组件启动的时候,即调用start方法时,只要在父组件的start方法中也调用子组件的start方法即可(只有实现统一的接口Lifecycle才能实现统一调用,如以下调用方式:(Lifecycle)子组件.start()),下面一步一步来看源代码,首先在Catalina启动start,部分代码如下:
// Start the new server if (server instanceof Lifecycle) { try { server.initialize(); ((Lifecycle) server).start();//启动server try { // Register shutdown hook Runtime.getRuntime().addShutdownHook(shutdownHook); } catch (Throwable t) { // This will fail on JDK 1.2. Ignoring, as Tomcat can run // fine without the shutdown hook. } // Wait for the server to be told to shut down server.await(); } catch (LifecycleException e) { System.out.println("Catalina.start: " + e); e.printStackTrace(System.out); if (e.getThrowable() != null) { System.out.println("----- Root Cause -----"); e.getThrowable().printStackTrace(System.out); } } }
关键看((Lifecycle) server).start();这样便在启动Catalina的时候启动了Server,再看StandardServer的start方法:
public void start() throws LifecycleException { // Validate and update our current component state if (started) throw new LifecycleException (sm.getString("standardServer.start.started")); // Notify our interested LifecycleListeners //发送这个事件 lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);//发送生命周期事件。 lifecycle.fireLifecycleEvent(START_EVENT, null); started = true; // Start our defined Services synchronized (services) { //由这里也可以看出一个server可以有多个services for (int i = 0; i < services.length; i++) { if (services[i] instanceof Lifecycle) ((Lifecycle) services[i]).start(); } } // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); }
主要做了两件事,1:发送生命周期事件给监听者;2:启动子组件services(至于server怎么关联上services请看前面的几篇文章,以后都不再题怎么关联上的了)。
这里先岔开一下,说一下监听器,lifecycle是一个工具类LifecycleSupport的实例,每一个组件都有这样一个工具类,这个工具类的作用就是帮助管理该组件上的监听器,包括添加监听器和群发事件给监听器,看LifecycleSupport类的一些关键代码:
public final class LifecycleSupport { public LifecycleSupport(Lifecycle lifecycle) { super(); this.lifecycle = lifecycle; } private LifecycleListener listeners[] = new LifecycleListener[0]; public void addLifecycleListener(LifecycleListener listener) { //向listeners添加监听器 synchronized (listeners) { LifecycleListener results[] = new LifecycleListener[listeners.length + 1]; for (int i = 0; i < listeners.length; i++) results[i] = listeners[i]; results[listeners.length] = listener; listeners = results; } } public void fireLifecycleEvent(String type, Object data) {//群发事件给监听器 LifecycleEvent event = new LifecycleEvent(lifecycle, type, data); LifecycleListener interested[] = null; synchronized (listeners) { interested = (LifecycleListener[]) listeners.clone(); } for (int i = 0; i < interested.length; i++) interested[i].lifecycleEvent(event);//发送组件生命周期事件。 } }
先看构造方法,传入一个lifecycle,因为每个组件都实现了lifecycle,所以这里传入的实际上是一个组件,即每个组件都有一个LifecycleSupport与之关联,当要在组件中添加一个监听器的时候,实际上是添加进工具类LifecycleSupport的一个监听器数组listeners中,当要发送一个组件生命周期的事件时,工具类就会遍历监听器数组,然后再一个一个的发送事件。这里需要先实现我们自己的监听器类并且添加进我们需要监听的组件当中。实现监听器类只要实现LifecycleListener接口就行,这个接口只有一个方法:
public interface LifecycleListener { public void lifecycleEvent(LifecycleEvent event); }
我们需要做的就是实现LifecycleListener接口来拥有自己的监听器,在lifecycleEvent方法里写自己监听到事件后该做的事情,然后添加进要监听的组件就行,比如当我们要看StandardServer是否启动了,在上面StandardServer的start方法有一句这样的代码:lifecycle.fireLifecycleEvent(START_EVENT, null);即发送StandardServer启动的事件给跟它关联的监听器。接下来回到一开始,当server启动后,接着启动它的子组件service,即调用StandardService的start方法,这个方法跟StandardServer的start方法差不多,只是启动了连接器和容器,连接器的start方法在前面的文章已经讲过了,主要是启动了n个处理器HttpProcessor组件。顶级容器是StandardEngine,它的start方法仅仅调用了父类ContainerBase的start方法,下面看ContainerBase的start方法:
public synchronized void start() throws LifecycleException { // Validate and update our current component state if (started) throw new LifecycleException (sm.getString("containerBase.alreadyStarted", logName())); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null); addDefaultMapper(this.mapperClass); started = true; // Start our subordinate components, if any if ((loader != null) && (loader instanceof Lifecycle)) //启动所有其他的组件 ((Lifecycle) loader).start(); if ((logger != null) && (logger instanceof Lifecycle)) ((Lifecycle) logger).start(); if ((manager != null) && (manager instanceof Lifecycle)) ((Lifecycle) manager).start(); if ((cluster != null) && (cluster instanceof Lifecycle)) ((Lifecycle) cluster).start(); if ((realm != null) && (realm instanceof Lifecycle)) ((Lifecycle) realm).start(); if ((resources != null) && (resources instanceof Lifecycle)) ((Lifecycle) resources).start(); // Start our Mappers, if any Mapper mappers[] = findMappers(); for (int i = 0; i < mappers.length; i++) { if (mappers[i] instanceof Lifecycle) ((Lifecycle) mappers[i]).start(); } // Start our child containers, if any Container children[] = findChildren(); for (int i = 0; i < children.length; i++) { if (children[i] instanceof Lifecycle) ((Lifecycle) children[i]).start(); } // Start the Valves in our pipeline (including the basic), if any if (pipeline instanceof Lifecycle) ((Lifecycle) pipeline).start(); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(START_EVENT, null); // Notify our interested LifecycleListeners lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null); }
这里代码比较丰富,由它启动了Tomcat其他所有的组件,包括加载器,映射器,日志记录器,管道等等,由这里也可以看出,他们都实现了Lifecycle接口。统一关闭跟统一启动的逻辑差不多,这里就不再说了。至此,我们对Tomcat怎么实现统一启动/关闭应该有一个比较清晰的认识了!