在前面的大部分文章都是讲连接器和容器的,以后的内容会偏向写一些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);
- }
- }
- }
- 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 );
- }
这里先岔开一下,说一下监听器,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); //发送组件生命周期事件。
- }
- }
- 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怎么实现统一启动/关闭应该有一个比较清晰的认识了!
更多文章、技术交流、商务合作、联系博主
微信扫码或搜索:z360901061
微信扫一扫加我为好友
QQ号联系: 360901061
您的支持是博主写作最大的动力,如果您喜欢我的文章,感觉我的文章对您有帮助,请用微信扫描下面二维码支持博主2元、5元、10元、20元等您想捐的金额吧,狠狠点击下面给点支持吧,站长非常感激您!手机微信长按不能支付解决办法:请将微信支付二维码保存到相册,切换到微信,然后点击微信右上角扫一扫功能,选择支付二维码完成支付。
【本文对您有帮助就好】元