`
Tyrion
  • 浏览: 260908 次
  • 性别: Icon_minigender_1
  • 来自: 南京
社区版块
存档分类
最新评论

Tomcat7启动分析(四)各组件init、start方法调用

阅读更多

在正常启动Tomcat7的情况下,上篇文章分析到了执行org.apache.catalina.core.StandardServer的init和start方法这儿,那么就来看看这两个方法里面到底干了些什么。

但是在StandardServer类里面并没有发现这两个方法:


由此推知这两方法必定是在该类的父类中已实现了,在StandardServer类的父类LifecycleMBeanBase类的父类LifecycleBase类里面终于找到了这两个方法的实现,下面先来看下init方法:

    @Override
    public final synchronized void init() throws LifecycleException {
        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);
        }

        setStateInternal(LifecycleState.INITIALIZED, null, false);
    }
    
    
    protected abstract void initInternal() throws LifecycleException;

先将干扰程序阅读视线的setStateInternal方法调用忽略掉(下一篇文章会详细讲解该方法),发现这里面就做了一件事情,调用了一下接下来定义的抽象方法initInternal()(第21行)。实际上看下LifecycleBase的实现类就会发现,所有的组件类几乎都继承了LifecycleBase类,所以这些组件类一般只会有initInternal方法的定义。(这里所说的组件类就是前面我们分析Digester解析时发现的StandardServer、StandardService、StandardEngine、StandardHost、StandardContext等类)

 

这里所说的组件可以将其理解为我们最开始分析server.xml时xml文件里的各个节点,父子关系也即xml文件里的父子节点。浏览下LifecycleBase的子类就会发现节点的实现类都是这个类的子类(记住这点,后面会提到)。


 

同样分析start方法:

    /**
     * {@inheritDoc}
     */
    @Override
    public final synchronized void start() throws LifecycleException {
        
        if (LifecycleState.STARTING_PREP.equals(state) ||
                LifecycleState.STARTING.equals(state) ||
                LifecycleState.STARTED.equals(state)) {
            
            if (log.isDebugEnabled()) {
                Exception e = new LifecycleException();
                log.debug(sm.getString("lifecycleBase.alreadyStarted",
                        toString()), e);
            } else if (log.isInfoEnabled()) {
                log.info(sm.getString("lifecycleBase.alreadyStarted",
                        toString()));
            }
            
            return;
        }
        
        if (state.equals(LifecycleState.NEW)) {
            init();
        } else if (state.equals(LifecycleState.FAILED)){
            stop();
        } else if (!state.equals(LifecycleState.INITIALIZED) &&
                !state.equals(LifecycleState.STOPPED)) {
            invalidTransition(Lifecycle.BEFORE_START_EVENT);
        }

        setStateInternal(LifecycleState.STARTING_PREP, null, false);

        try {
            startInternal();
        } catch (Throwable t) {
            ExceptionUtils.handleThrowable(t);
            setStateInternal(LifecycleState.FAILED, null, false);
            throw new LifecycleException(
                    sm.getString("lifecycleBase.startFail",toString()), t);
        }

        if (state.equals(LifecycleState.FAILED) ||
                state.equals(LifecycleState.MUST_STOP)) {
            stop();
        } else {
            // Shouldn't be necessary but acts as a check that sub-classes are
            // doing what they are supposed to.
            if (!state.equals(LifecycleState.STARTING)) {
                invalidTransition(Lifecycle.AFTER_START_EVENT);
            }
            
            setStateInternal(LifecycleState.STARTED, null, false);
        }
    }


    /**
     * Sub-classes must ensure that the state is changed to
     * {@link LifecycleState#STARTING} during the execution of this method.
     * Changing state will trigger the {@link Lifecycle#START_EVENT} event.
     * 
     * If a component fails to start it may either throw a
     * {@link LifecycleException} which will cause it's parent to fail to start
     * or it can place itself in the error state in which case {@link #stop()}
     * will be called on the failed component but the parent component will
     * continue to start normally.
     * 
     * @throws LifecycleException
     */
    protected abstract void startInternal() throws LifecycleException;

第7到21行是start功能的前置校验,这里如果发现start方法已经调用过了,将会记录日志并直接返回。第23到30行如果发现start放的需要做的前置方法没有调用完,或者调用出错,将会先调用这些前置方法。第32行暂时先不管,不影响程序阅读,第35行是该方法的实质,将会调用本类中定义的抽象方法startInternal()(第71行)。下面的代码同上述一样,都是一些start方法调用过程中可能出现的错误的错误处理。

 

从以上init和start方法的定义可以看到这两个方法最终将会调用子类中定义的initInternal和startInternal。

 

接回本文开头,一开始在找StandardServer类中init和start方法的定义,看完了上面的分析发现最终会调用StandardServer类的initInternal和startInternal两个方法。那这两个方法里面干了些什么?

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

init方法里面做了好几件事情,牵涉的话题比较多,这里重点关注最后第53到55行的代码,这里将循环调用Server类里内置的Service数组的init方法。

 

startInternal方法:

    /**
     * Start nested components ({@link Service}s) and implement the requirements
     * of {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
     *
     * @exception LifecycleException if this component detects a fatal error
     *  that prevents this component from being used
     */
    @Override
    protected void startInternal() throws LifecycleException {

        fireLifecycleEvent(CONFIGURE_START_EVENT, null);
        setState(LifecycleState.STARTING);

        globalNamingResources.start();
        
        // Start our defined Services
        synchronized (services) {
            for (int i = 0; i < services.length; i++) {
                services[i].start();
            }
        }
    }

重点关注第17到21行,同上一段所分析的代码类似,将循环调用Sever类里内置的Service数组的start方法。

 

那么这里提到的Service的对象到底是什么?

上篇文章分析Digester时提到“经过对xml文件的解析将会产生org.apache.catalina.core.StandardServer、org.apache.catalina.core.StandardService、org.apache.catalina.connector.Connector、org.apache.catalina.core.StandardEngine、org.apache.catalina.core.StandardHost、org.apache.catalina.core.StandardContext等等一系列对象,这些对象从前到后前一个包含后一个对象的引用(一对一或一对多的关系)。”

所以正常情况下这里的Service将会是org.apache.catalina.core.StandardService的对象(该代码见org.apache.catalina.startup.Catalina类的339到341行)。

 

所以按上面的分析,接下来将会调用StandardService类的init和start方法,实际上这个类也是LifecycleBase类的子类,所以最终的也会调用本类中的initInternal和startInternal方法。

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 (container != null) {
            container.init();
        }

        // Initialize any Executors
        for (Executor executor : findExecutors()) {
            if (executor instanceof LifecycleMBeanBase) {
                ((LifecycleMBeanBase) executor).setDomain(getDomain());
            }
            executor.init();
        }

        // Initialize our defined Connectors
        synchronized (connectors) {
            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);
                }
            }
        }
    }

这里将会调用Service下的各类子组件中的init方法。

 

startInternal方法:

    /**
     * Start nested components ({@link Executor}s, {@link Connector}s and
     * {@link Container}s) and implement the requirements of
     * {@link org.apache.catalina.util.LifecycleBase#startInternal()}.
     *
     * @exception LifecycleException if this component detects a fatal error
     *  that prevents this component from being used
     */
    @Override
    protected void startInternal() throws LifecycleException {

        if(log.isInfoEnabled())
            log.info(sm.getString("standardService.start.name", this.name));
        setState(LifecycleState.STARTING);

        // Start our defined Container first
        if (container != null) {
            synchronized (container) {
                container.start();
            }
        }

        synchronized (executors) {
            for (Executor executor: executors) {
                executor.start();
            }
        }

        // Start our defined Connectors second
        synchronized (connectors) {
            for (Connector connector: connectors) {
                try {
                    // If it has already failed, don't try and start it
                    if (connector.getState() != LifecycleState.FAILED) {
                        connector.start();
                    }
                } catch (Exception e) {
                    log.error(sm.getString(
                            "standardService.connector.startFailed",
                            connector), e);
                }
            }
        }
    }

同理,将会调用service下各类子组件中的start方法。

 

StandardService的子容器是StandardEngine,看下StandardEngine的startInternal方法:

    protected synchronized void startInternal() throws LifecycleException {
        
        // Log our server identification information
        if(log.isInfoEnabled())
            log.info( "Starting Servlet Engine: " + ServerInfo.getServerInfo());

        // Standard container startup
        super.startInternal();
    }

这里不同于上面两级容器的实现,而是直接调用了父类的startInternal方法:

    protected synchronized void startInternal() throws LifecycleException {

        // Start our subordinate components, if any
        if ((loader != null) && (loader instanceof Lifecycle))
            ((Lifecycle) loader).start();
        logger = null;
        getLogger();
        if ((manager != null) && (manager instanceof Lifecycle))
            ((Lifecycle) manager).start();
        if ((cluster != null) && (cluster instanceof Lifecycle))
            ((Lifecycle) cluster).start();
        Realm realm = getRealmInternal();
        if ((realm != null) && (realm instanceof Lifecycle))
            ((Lifecycle) realm).start();
        if ((resources != null) && (resources instanceof Lifecycle))
            ((Lifecycle) resources).start();

        // Start our child containers, if any
        Container children[] = findChildren();
        List<Future<Void>> results = new ArrayList<Future<Void>>();
        for (int i = 0; i < children.length; i++) {
            results.add(startStopExecutor.submit(new StartChild(children[i])));
        }

        boolean fail = false;
        for (Future<Void> result : results) {
            try {
                result.get();
            } catch (Exception e) {
                log.error(sm.getString("containerBase.threadedStartFailed"), e);
                fail = true;
            }

        }
        if (fail) {
            throw new LifecycleException(
                    sm.getString("containerBase.threadedStartFailed"));
        }

        // Start the Valves in our pipeline (including the basic), if any
        if (pipeline instanceof Lifecycle)
            ((Lifecycle) pipeline).start();


        setState(LifecycleState.STARTING);

        // Start our thread
        threadStart();

    }

第19到34行即启动当前容器下的子容器的代码,这里采用了分线程分别启动的方式。核心的调用子容器的start方法的代码在StartChild类的call方法中:

    private static class StartChild implements Callable<Void> {

        private Container child;

        public StartChild(Container child) {
            this.child = child;
        }

        @Override
        public Void call() throws LifecycleException {
            child.start();
            return null;
        }
    }

这里使用了JDK5的新的执行线程的方式,不理解的话请参考相关文档说明。

 

StandardHost中的startInternal与StandardEngine类似,这里不再赘述,建议有兴趣的朋友逐一分析Review一遍,碰到组件里面嵌套的变量不知道具体实现类的就从上篇文章里面提到的createStartDigester那边开始找起,这里不能直接找到的就在里面提到的new *RuleSet的addRuleInstances方法里面找。通过这种调用将会最终执行完所有在server.xml里配置的节点的实现类中initInternal和startInternal方法。


最后上面提到的org.apache.catalina.core.StandardServer、org.apache.catalina.core.StandardService、org.apache.catalina.connector.Connector、org.apache.catalina.core.StandardEngine、org.apache.catalina.core.StandardHost、org.apache.catalina.core.StandardContext等等组件的这两个方法都会调用到。

 

就这样,Tomcat7在内存中为这一连串组件产生对象,建立对象调用关系,调用它们各自的初始化和启动方法,启动的总体过程就介绍完了,这些对象start之后将会响应客户端的请求,为用户服务了。当然,这里还没有涉及到对于具体的发布到tomcat里面的没有应用的载入过程,web应用中配置的servlet、filter、listener等的载入、启动服务过程,浏览器发起的一个请求如何经过Tomcat内各组件的流转调用到具体应用里去的,这一系列问题都还没谈到。因为Tomcat本身庞大繁杂,需要找一个视角切入进去,为了叙述的简单,先从整体上对Tomcat内包含的各组件产生机制有一个大体轮廓的了解,这样为后面的介绍提供一个统一的背景。

 

下一篇文章将分析本文开头遗留的一个问题——setStateInternal方法的作用,以及Tomcat中的Lifecycle实现原理。

  • 大小: 50.3 KB
  • 大小: 57 KB
  • 大小: 76.4 KB
2
1
分享到:
评论

相关推荐

    Tomcat 6.0启动过程分析

    - `Bootstrap` 的 `start` 方法最终调用 `Catalina` 的 `start` 方法来启动 Tomcat。 - 首先检查 `server` (`org.apache.catalina.Server`)是否为 `null`,如果是,则调用 `load()` 方法加载服务器配置。 - ...

    我的tomcat7源码手撕过程

    - 在`init`方法中,遍历所有的服务组件并调用它们的`init`方法。 - 在`StandardService`的`initInternal`方法中,会初始化`StandardEngine`、`StandardThreadExecutor`和`Connector`组件。 7. **Container初始化*...

    tomcat6启动脚本

    7. 修改`/etc/init.d/`目录下的启动脚本,比如创建一个名为`tomcat`的文件,将`Tomcat5.sh`的内容复制过去。 8. 为脚本添加执行权限:`chmod +x tomcat`。 为了使Tomcat能够以特定用户(如`tomcat`)运行,并避免...

    quartz 随tomcat启动执行一次

    通过对题目中提供的信息进行分析,我们可以了解到如何利用 Spring 与 Quartz 进行集成,从而实现 Tomcat 启动时调用 Quartz 执行一次任务的需求。通过合理配置 `SchedulerFactoryBean`、`CronTriggerBean` 和 `...

    Tomcat 6 启动过程分析.doc

    Bootstrap的`main`方法是程序的起点,通过`daemon.init()`初始化Tomcat环境,然后根据命令行参数决定启动或停止Tomcat服务。`initClassLoaders()`方法负责创建三个类加载器:commonLoader、catalinaLoader和...

    Linux 配置 tomcat 开机启动.txt

    - 先调用 `stop()` 函数停止 Tomcat,等待 1 秒后再调用 `start()` 函数重新启动 Tomcat。 5. **主逻辑控制**: - 通过分析传入的第一个参数 `$1` 来判断用户想要执行的操作(`start`, `stop`, 或 `restart`)。 ...

    tomcat启动服务运行servlet

    6. 装载Servlet:根据`web.xml`中的配置,Tomcat实例化Servlet类,并调用其`init()`方法进行初始化。 7. 服务器开始监听并处理请求:一旦启动完成,Tomcat就开始接受HTTP请求,将请求分发给相应的Servlet进行处理。 ...

    linux中设置tomcat自启动

    ### 四、编写Tomcat启动脚本 在创建的文件中,我们定义了一系列环境变量,包括 `CATALINA_BASE`, `CATALINA_HOME`, `CATALINA_TMPDIR`, 和 `JRE_HOME`。这些变量指向了Tomcat及其相关Java运行环境的具体位置。接着...

    Tomcat7+Spring3异常Failed to start component

    标题 "Tomcat7+Spring3异常Failed to start component" 描述了一个常见的问题,即在集成Tomcat7服务器和Spring3框架时,应用启动时出现了组件无法启动的错误。这个问题可能是由于多种原因引起的,包括但不限于配置...

    Spring Boot启动过程(五)之Springboot内嵌Tomcat对象的start教程详解

    Spring Boot启动过程(五)之Springboot内嵌Tomcat对象的start教程详解是Spring Boot启动过程的重要组件之一,我们需要详细了解TomcatEmbeddedServletContainer的start方法、LifecycleBase的init方法、MBean的强大...

    ant启动tomcat

    - **启动**:通过调用Tomcat的bootstrap.jar文件启动Tomcat。 - **停止**:向Tomcat发送停止命令,使服务器安全关闭。 - **调试**:配置Tomcat以支持远程调试,便于开发过程中对应用进行调试。 此外,脚本还提供了...

    linux中设置tomcat自启动.pdf

    6. **使用chkconfig命令**:`chkconfig --add tomcat`命令将Tomcat服务添加到系统启动服务列表中,这样在系统启动时会自动调用`/etc/init.d/tomcat`脚本来启动Tomcat。 7. **检查服务状态**:`chkconfig --list ...

    tomcat自启脚本

    tomcat自启动脚本,使用方法: 将tomcat脚本放到/etc.init.d目录下 使用servcie调用tomcat脚本进行启动停止重启 例如:service tomcat start service tomcat stop service tomcat restart

    linux设置tomcat开机自动启动1

    第一行设置了`JAVA_HOME`环境变量,第二行则调用Tomcat的启动脚本来启动服务。 4. **使rc.local文件可执行**: 修改`rc.local`文件的权限,使其具有执行权限。可以使用命令`chmod +x /etc/rc.d/rc.local`来完成。...

    tomcat源码分析

    - `start`方法启动Tomcat服务器。 2. **Catalina类**: - `load`方法中创建并解析`server.xml`配置文件。 - `start`方法继续启动流程。 3. **StandardServer类**: - `initialize`方法中调用`lifecycle....

    tomcat之Linux版

    在大多数Linux发行版中,可以将Tomcat的启动脚本`/apache-tomcat-7.0.57/bin/startup.sh`链接到`/etc/init.d`目录下,并设置相应的启动级别: ```bash sudo ln -s /path/to/apache-tomcat-7.0.57/bin/startup.sh /...

    Tomcat源码研究.pdf

    生命周期方法通常包括init(), start(), stop(), destroy()等,通过这些方法,可以对组件进行精确的控制。 通过对Tomcat源码的研究,我们可以更深入地了解Web服务器的工作原理,掌握其架构设计以及如何进行故障排查...

    linux as 5下把tomcat做成服务

    启动Tomcat服务,使用`service tomcat start`命令。如果一切正常,Tomcat应该已经开始并监听指定的端口(默认8080)。 8. **文档说明** 提供的`tomcatservices.doc`文件可能包含了更详细的步骤和注意事项,建议...

    centos6配置tomcat8开机自启动脚本

    要实现在CentOS 6系统中配置Tomcat 8服务器以开机自启动,我们需要编写一个自定义的系统启动脚本,该脚本会被init系统在启动时调用。这里涉及到的知识点包括Linux的启动过程、Shell脚本编写、环境变量配置以及如何...

Global site tag (gtag.js) - Google Analytics