【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启动过程简述(二)_第1张图片
图一 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 type  Event type
     * @param data  Data 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类。

看到这里,我们结合server.xml,对于tomcat的初始化流程和一些组件的关系,应该有了一个初步的了解了

你可能感兴趣的:(【Tomcat源码阅读分享】—(4)Tomcat启动过程简述(二))