tomcat启动过程-load初始化

tomcat源码系列导航栏

    tomcat源码分析环境搭建

    tomcat启动过程-load初始化

目录

启动入口Main函数 

代码块一 load方法

代码块二 load方法

代码块三 init

代码块四 init 

代码块五 initInternal

整体load初始化流程

 总结


本篇文章主要是tomcat源码的load初始化流程,如果不知道tomcat源码如何启动请参考上一篇文章

启动入口Main函数 

      源码的启动函数在org.apache.catalina.startup.Bootstrap 中,以下是源代码。

    public static void main(String args[]) {

        Thread thread = Thread.currentThread();
        synchronized (daemonLock) {
            if (daemon == null) {
                // Don't set daemon until init() has completed
                Bootstrap bootstrap = new Bootstrap();
                try {
                    bootstrap.init();
                } catch (Throwable t) {
                    handleThrowable(t);
                    t.printStackTrace();
                    return;
                }
                daemon = bootstrap;
            } else {
                // When running as a service the call to stop will be on a new
                // thread so make sure the correct class loader is used to
                // prevent a range of class not found exceptions.
                thread.setContextClassLoader(daemon.catalinaLoader);
            }
        }

        try {
            String command = "start";
            if (args.length > 0) {
                command = args[args.length - 1];
            }
           ......

          if (command.equals("start")) {
                daemon.setAwait(true);
                daemon.load(args);
                daemon.start();
                if (null == daemon.getServer()) {
                    System.exit(1);
                }
            } 
        } catch (Throwable t) {
           ......
        }
    }

    为了代码看上去更加清晰明了,用省略号省去了部分和本次启动流程不相关的代码。后面的文章会讲到省去的代码的作用。

    启动main函数后默认command = "start" ,接下来执行daemon.load(args)。请看代码块一

代码块一 load方法

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

    可以看到这里使用了反射的技术执行了org.apache.catalina.startup.Catalina这个类的load方法。这里是使用之前创建好的对象来进行反射获取对象的方法来执行。为什么不直接通过对象调用方法来执行呢。应该是考虑到代码的可扩展型吧。接下来再看Catalina的load方法,代码块二 

代码块二 load方法

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

        if (loaded) {
            return;
        }
        loaded = true;
         
        //解析一 server.xml文件,将文件中的信息,比如根据类的全路径名称反射成对象然后将对象设置当this中去
        Digester digester = createStartDigester();

        ......

        getServer().setCatalina(this);
        getServer().setCatalinaHome(Bootstrap.getCatalinaHomeFile());
        getServer().setCatalinaBase(Bootstrap.getCatalinaBaseFile());

        // Stream redirection
        initStreams();

        // Start the new server
        try {
            //解析二 获取得到server对象然后调用其init方法
            getServer().init();
        } 
     ......
    }

    可以看到以上解析一的代码其实就是读取conf/server.xml 文件将文件中的信息给创建成我们需要的对象信息。后面的文章中的许多对象都是从这里创建的,有兴趣的话可以自己去研究一下,他是怎么解析文件和然后创建对象的。

    解析二获取的server对象就是解析一创建的,这里会调用server对象的init方法。请看 代码块三 init

代码块三 init

    我们会发现init这个方法其实是org.apache.catalina.Lifecycle 接口的一个方法。以下是这个接口的代码

public interface Lifecycle {


    // ----------------------------------------------------- Manifest Constants
    //解析 一 以下是定义的一些生命周期事件名称
     
    /**
     * The LifecycleEvent type for the "component before init" event.
     */
    public static final String BEFORE_INIT_EVENT = "before_init";


    /**
     * The LifecycleEvent type for the "component after init" event.
     */
    public static final String AFTER_INIT_EVENT = "after_init";


    /**
     * The LifecycleEvent type for the "component start" event.
     */
    public static final String START_EVENT = "start";


    /**
     * The LifecycleEvent type for the "component before start" event.
     */
    public static final String BEFORE_START_EVENT = "before_start";


    /**
     * The LifecycleEvent type for the "component after start" event.
     */
    public static final String AFTER_START_EVENT = "after_start";


    /**
     * The LifecycleEvent type for the "component stop" event.
     */
    public static final String STOP_EVENT = "stop";


    /**
     * The LifecycleEvent type for the "component before stop" event.
     */
    public static final String BEFORE_STOP_EVENT = "before_stop";


    /**
     * The LifecycleEvent type for the "component after stop" event.
     */
    public static final String AFTER_STOP_EVENT = "after_stop";


    /**
     * The LifecycleEvent type for the "component after destroy" event.
     */
    public static final String AFTER_DESTROY_EVENT = "after_destroy";


    /**
     * The LifecycleEvent type for the "component before destroy" event.
     */
    public static final String BEFORE_DESTROY_EVENT = "before_destroy";


    /**
     * The LifecycleEvent type for the "periodic" event.
     */
    public static final String PERIODIC_EVENT = "periodic";


    /**
     * The LifecycleEvent type for the "configure_start" event. Used by those
     * components that use a separate component to perform configuration and
     * need to signal when configuration should be performed - usually after
     * {@link #BEFORE_START_EVENT} and before {@link #START_EVENT}.
     */
    public static final String CONFIGURE_START_EVENT = "configure_start";


    /**
     * The LifecycleEvent type for the "configure_stop" event. Used by those
     * components that use a separate component to perform configuration and
     * need to signal when de-configuration should be performed - usually after
     * {@link #STOP_EVENT} and before {@link #AFTER_STOP_EVENT}.
     */
    public static final String CONFIGURE_STOP_EVENT = "configure_stop";

    // --------------------------------------------------------- Public Methods
    // 解析二 下面定义了一些生命周期方法。还有添加 查找 删除监听者的方法,以及获取当前生命周期状态的方法

    public void addLifecycleListener(LifecycleListener listener);

    public LifecycleListener[] findLifecycleListeners();

    public void removeLifecycleListener(LifecycleListener listener);

    public void init() throws LifecycleException;

    public void start() throws LifecycleException;


    public void stop() throws LifecycleException;


    public void destroy() throws LifecycleException;

    public LifecycleState getState();

    public String getStateName();
}

    这个接口其实就是定义了整个tomcat生命周期的接口。从 init->start->stop->destroy 其实前两个init和start方法在main函数中就可以看见,当执行了init方法紧接着就执行了start。

    这个时候我们再看server.init方法 做了什么见代码块四 init

代码块四 init 

    @Override
    public final synchronized void init() throws LifecycleException {
        if (!state.equals(LifecycleState.NEW)) {
            invalidTransition(Lifecycle.BEFORE_INIT_EVENT);
        }

        try {
            //注册一个事件,说server正在进行初始化中
            setStateInternal(LifecycleState.INITIALIZING, null, false);
            //在本抽象类中这是一个抽象方法,具体的实现。需要去找调用对象所属类。
            //这里是server对象调用的,他的全路径名称是org.apache.catalina.core.StandardServer
            //解析一 
            initInternal();
            //注册一个事件,说server初始化完成了
            setStateInternal(LifecycleState.INITIALIZED, null, false);
        } catch (Throwable t) {
            handleSubClassException(t, "lifecycleBase.initFail", toString());
        }
    }

    其实这个方法是在org.apache.catalina.util.LifecycleBase这个抽象类中实现的,其实tomcat源码作者对模板设计模式的使用,将公共的代码块部分全部抽离到抽象类中去实现。这样可以减少重复代码的开发。

    这里的公共代码就是注册事件。在这里表示server正在进行初始化,然后通知我的监听者们,你们之前定义好,对server正在初始化这个动作有其他操作的。现在该去做了。这里其实是监听者模式的一种使用。在整个源码中其实模板设计模式和监听者模式随处可见。读者要想更快速提升读源码的能力,这是必备技能。

    我们接着往下看解析一  代码块五 initInternal

代码块五 initInternal

protected void initInternal() throws LifecycleException {

        super.initInternal();
        ......
        
        // Initialize our defined Services
        for (Service service : services) {
            service.init();
        }
    }

    可以发现接下来就是service对象的init方法初始化。经过我们一直往下走发现这个其实是一个初始化链 先是server,接着是service,再是connector和engine,connector再到protocolHandler,再到endpoint。然后整个初始化load流程结束。

整体load初始化流程

流程图如下

 总结

     在tomcat初始化流程中,使用了反射解耦,使用模板设计模式提取公共代码提升代码可阅读性和简洁性,大量使用监听者模式,做一些初始化相关的事情。整个tomcat的生命周期都是围绕生命周期接口。整个初始化过程是一个链路,一环扣一环。下一篇文章将介绍start启动流程,重点会有tomcat的nio处理请求。对线程池的创建和使用。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值