Tomcat启动过程源码分析四

时间:2022-07-18 16:49:24

前言

上一篇文章中我们讨论了Bootstrap类中main方法中涉及到的init方法,今天这篇文章我们来查看下load方法。

daemon.setAwait(true);
daemon.load(args);

setAwait方法

load方法执行前,执行了setAwait方法,跟进去查看

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);

}

代码很简单,和上一篇文章中的类似,使用了反射调用了catalinaDaemon对象的setAwait方法,传递了参数true,而我们在上一篇文章中,init方法的最后讲解中可以看到catalinaDaemon对象是Catalina类的一个实例,所以查看Catalina对象的setAwait方法:

public void setAwait(boolean b) {
    await = b;
}

看来daemon.setAwait(true)这句代码很简单,就是使用了反射,设置了catalina实例的await属性为true

load方法

 /**
 * Load daemon.
 */
private void load(String[] arguments)
        throws Exception {
    // Call the load() method
    String 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);
}

这段代码有了前面的经验读起来很容易,Bootstrapload方法是使用了反射调用了Catalina类的load方法,我们继续查看Catalina类的load方法

 /**
 * Start a new server instance.
 */
public void load() {

    long t1 = System.nanoTime();
    //1 初始化相关属性
    initDirs();
    //2 初始化相关属性
    initNaming();
    //3 创建专门用来解析server.xml的Digester类,同时也隶属于Jakarta Commons项目,专门用来解析xml工具包
    Digester digester = createStartDigester();
    ......
    ......
    //到这里为止都是在解析server.xml

    getServer().setCatalina(this);

    // Stream redirection
    initStreams();

    // Start the new server
    try {
        //4 初始化一个Server实例 
        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");
    }

}

上面代码省略了部分,用中文注释注解出了相关含义,我们这里重点看下getServer().init(),getServer返回的是一个Server的实例,很明显这里返回的应该是一个StandardServer实例,我们继续查看StandardServerinit方法。

然而我们跟到StandardServer类中没有直接的init方法,查看其实现的接口发现他实现了Server接口,Server继承了LifeCycle接口,StandardServer继承了LifecycleMBeanBase,LifecycleMBeanBase继承了LifecycleBase,而LifecycleBase也实现了Lifecycle,所以调用的init方法应该是LifecycleBaseinit方法。

 @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中除了通用的代码还调用了initInternal方法。
我们看下在LifecycleBase类中initInternal方法做了什么。

protected abstract void initInternal() throws LifecycleException;

在抽象类LifeCycleBase中只定义了initInternal方法,并没有去实现而在StandardServer中实现了initInternal,所以调用了StandardServer对象的init方法实际上就是在调用initInternal方法,我们来看看StandardServerinitInternal方法做了什么。

 /**
 * 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
    // Initialize our defined Services
    for (int i = 0; i < services.length; i++) {
        services[i].init();
    }
}

前面的一篇文章我们说过Tomcat的架构是什么样子的,Server中包含多个Service
initInternal方法大末尾,我们看到了StandardServer获取到了他内部所有Service然后调用每个Serviceinit方法。那么这个service数组里面都包含了哪些service呢。其实在调用init之前这个service数组就已经初始化好了,那么是在哪里初始化的呢?大家应该还记得上面的load方法中有个类叫做Digester,相关的代码Digester digester = createStartDigester();,这个类在createStartDigester方法中通过解析server.xml文件,不仅来生成指定对象,更生成了不同对象之间的依赖关系,在这个方法内部,就将server内部的service全部都初始化了,其实StandardServer根据server.xml的格式默认只有一个service,他的指定实现类就是StandardService,关于digester这个类,有机会可以单独写一篇文章讲解下使用方法。

我们现在知道了,在StandardServerinit方法中他调用了StandardServiceinit方法。我们继续查看StandardServiceinit方法。

StandardServer类似,调用init方法实际上是调用了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内部的container为空那么就初始化
    if (container != null) {
        container.init();
    }

    // Initialize any Executors
    //初始化executor,事实上在Service中代码走到这里的时候,findExecutors会返回空数组,这里的代码是不会执行的。
    for (Executor executor : findExecutors()) {
        if (executor instanceof LifecycleMBeanBase) {
            ((LifecycleMBeanBase) executor).setDomain(getDomain());
        }
        executor.init();
    }

    // Initialize our defined Connectors
    //初始化 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);
            }
        }
    }
}

StandardServiceinit方法中可以看出init方法主要还是将Service内部所有的connectors全部轮流调用init方法,是不是感觉很熟悉。是的!StandardService内部所有的connectors正是在server.xml中定义的。那么默认的就是有两个了,分别对应处理http和ajp请求,我们加点代码打印下看看是不是这样。

测试代码
 // Initialize our defined Connectors
    synchronized (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]

好了,我们继续看connectorinit方法,需要注意的是,类似server,service都是有指定标准实现类的,而connector是没有standardconnector这种实现类的,这主要是因为connector根据不同的协议是有多个对应实现的,来一起看connectorinit方法。

 @Override
protected void initInternal() throws LifecycleException {

    super.initInternal();

    // Initialize adapter
    adapter = new CoyoteAdapter(this);
    protocolHandler.setAdapter(adapter);

    // Make sure parseBodyMethodsSet has a default
    if( 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 listener
    mapperListener.init();
}

你会发现,和StandardService类似,Connector也实现LifeCycle接口,也实现了initInternal方法。

现在大家肯定都有疑问,为什么都要实现Lifecycle这个接口,都要实现initInternal方法,这个这里先说一下。Lifecycle是管理所有tomcat组件的接口,只要这个组件实现了Lifecycle这个接口,那么这个组件就可以随着tomcat启动而启动,随着tomcat停止而停止,一句话就是通过实现这个接口,tomcat可以统一管理所有组件的启动和停止,而组件启动的时候肯定会做一些通用的方法,所以有了init方法,而每个组件又会做一些自己的特有事情,所以init方法中又调用了initInternal方法来让每个组件自己去实现自己特有的初始化事情,这种其实是一种设计模式,叫做模版设计模式,关于LifeCycle和tomcat中常用的设计模式我们会单独写文章来说明,这里只是提一下,方便大家理解。

connectorinitInternal方法中,可以看到除了一些常规的方法以外,有两个方法需要关注一下,一个是protocolHandler.init(),另外个就是mapperListener.init(),而我们主要看一些关键组件,所以在这里就不讲解mapperListener.init(),有兴趣的可以自行查看,提示:这个是一个监听器,监听的是容器内部的映射关系变化,我们主要看protocolHandler.init()

先看protocolHandler这个东西是哪个类的实例,找到初始化的地方,查看Connector的构造函数,可以看到:

    public Connector(String protocol) {
    setProtocol(protocol);
    // Instantiate protocol handler
    try {
        Class<?> clazz = Class.forName(protocolHandlerClassName);
        this.protocolHandler = (ProtocolHandler) clazz.newInstance();
    } catch (Exception e) {
        log.error(sm.getString(
                "coyoteConnector.protocolHandlerInstantiationFailed"), e);
    }
}

实际上Connector是在解析server.xml的时候实例化的,在<connector>标签上可以指定很多属性,其中就包含该connector是哪个类的实例。

查看protocolHandlerClassName可以看到

    /**
 * Coyote Protocol handler class name.
 * Defaults to the Coyote HTTP/1.1 protocolHandler.
 */
protected String protocolHandlerClassName =
    "org.apache.coyote.http11.Http11Protocol";

可以看出如果是处理http请求的Connector在init的时候,调用的是 org.apache.coyote.http11.Http11Protocol这个类的init方法,那我们继续查看类Http11Protocol这个类的init方法。
可以查看Http11Protocol的继承类图:

Tomcat启动过程源码分析四

Http11Protocol本身没有init方法,我们查看其父类,可以在类AbstractProtocol中找到init方法,如下:

@Override
public void init() throws Exception {
    //其他代码
    ...
    try {
        endpoint.init();
    } catch (Exception ex) {
        getLog().error(sm.getString("abstractProtocolHandler.initError",
                getName()), ex);
        throw ex;
    }
}

可以看到最后调用了endpoint.init(),这个endPoint指向的是哪个类呢?其实我们之前看到过,在Http11Protocol的构造函数中有如下代码:

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);
}

所以最后又调用了JIoEndpointinit方法,我们先查看下JIoEndpoint,可以发现其中并没有init方法,查看其父类AbstractEndpoint,发现有init方法,方法如下:

 public final void init() throws Exception {
    testServerCipherSuitesOrderSupport();
    if (bindOnInit) {
        bind();
        bindState = BindState.BOUND_ON_INIT;
    }
}

其中bind方法是个抽象方法,查看其子实现在类JIoEndpoint中,

@Override
public void bind() throws Exception {

    // Initialize thread count defaults for acceptor
    //为acceptor 初始化线程数量
    if (acceptorThreadCount == 0) {
        acceptorThreadCount = 1;
    }
    // Initialize maxConnections
    //初始化最大连接数
    if (getMaxConnections() == 0) {
        // User hasn't set a value - use the default
        setMaxConnections(getMaxThreadsExecutor(true));
    }

    //如果线程工厂类为空,初始化
    if (serverSocketFactory == null) {
        if (isSSLEnabled()) {
            serverSocketFactory =
                handler.getSslImplementation().getServerSocketFactory(this);
        } else {
            serverSocketFactory = new DefaultServerSocketFactory(this);
        }
    }
    //初始化接收请求的线程。
    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();
            else
                msg = orig.getMessage() + " " +
                        getAddress().toString() + ":" + getPort();
            BindException be = new BindException(msg);
            be.initCause(orig);
            throw be;
        }
    }

}

可以看出这个这个init方法就是初始化了几个比较重要的属性,包括线程数量,线程最大链接数,线程工厂类以及接收请求的线程。

到这里我们终于把connectorinit方法查看完毕。

那么我们继续回到StandServer内部的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);
            }
        }
    }

你会发现,connector初始化完毕以后,StandardServerinit方法也调用结束了,也就是说load方法到这里就结束了。