Tomcat源码分析之Context的创建与启动分析

在前面的文章中,知道在HostConfig对象中,会在host启动的时候开始部署当前host下面的web应用程序,一般情况下就是扫描webapps文件夹,然后为每个web应用程序创建Context对象,一般情况下也就是StandardContext。。

这里其实一个Context也就代表了一个web应用程序,它具体来维护应用程序定义的servlet,filter,资源。。。

但是这里由于Context接口实在比较大,不好挨个挨个字段都贴出来,这里就先来看看StandardContext的创建与启动的过程吧,先来看看构造函数:

    public StandardContext() {

        super();
        pipeline.setBasic(new StandardContextValve());  //在pipeline上添加basic的valve
        broadcaster = new NotificationBroadcasterSupport();
        // Set defaults
        if (!Globals.STRICT_SERVLET_COMPLIANCE) {
            // Strict servlet compliance requires all extension mapped servlets
            // to be checked against welcome files
            resourceOnlyServlets.add("jsp");
        }
    }

这里主要是在当前pipeline上面添加了一个basic格式的valve对象。。。还有其他的一些事情。。。接下来来看看初始化吧:

    protected void initInternal() throws LifecycleException {
        super.initInternal();

        // Register the naming resources
        if (namingResources != null) {
            namingResources.init();
        }

        if (resources != null) {
            resources.start();  //启动本地资源
        }

        // Send j2ee.object.created notification
        if (this.getObjectName() != null) {
            Notification notification = new Notification("j2ee.object.created",
                    this.getObjectName(), sequenceNumber.getAndIncrement());
            broadcaster.sendNotification(notification);
        }
    }

这里主要还是进行资源的初始化吧。。。那么接下来来看看启动的方法吧:

    //启动context,这里可以理解为启动一个应用程序
    protected synchronized void startInternal() throws LifecycleException {

        if(log.isDebugEnabled())
            log.debug("Starting " + getBaseName());

        // Send j2ee.state.starting notification
        if (this.getObjectName() != null) {
            Notification notification = new Notification("j2ee.state.starting",
                    this.getObjectName(), sequenceNumber.getAndIncrement());
            broadcaster.sendNotification(notification);
        }

        setConfigured(false);  //还没有开始config
        boolean ok = true;  //必须保证每一步都ok

        // Currently this is effectively a NO-OP but needs to be called to
        // ensure the NamingResources follows the correct lifecycle
        if (namingResources != null) {
            namingResources.start();
        }

        //resource部分
        // Add missing components as necessary
        if (getResources() == null) {   // (1) Required by Loader  //可以理解为当前应用程序的资源引用
            if (log.isDebugEnabled())
                log.debug("Configuring default Resources");

            try {
                setResources(new StandardRoot(this));  //创建资源引用
            } catch (IllegalArgumentException e) {
                log.error("Error initializing resources: " + e.getMessage());
                ok = false;
            }
        }
        if (ok) {
            resourcesStart(); 
        }
        //loader部分
        if (getLoader() == null) { //这一步用于创建loader对象
            WebappLoader webappLoader = new WebappLoader(getParentClassLoader());    //这里用于创建当前context用的classLoader,这里会将parent设置为sharedclassLoader
            webappLoader.setDelegate(getDelegate());  
            setLoader(webappLoader);  //保存创建的loader
        }

        // Initialize character set mapper
        getCharsetMapper();  //charsetmapper

        // Post work directory
        postWorkDirectory();

        // Validate required extensions
        boolean dependencyCheck = true;
        try {  //扩展名检查
            dependencyCheck = ExtensionValidator.validateApplication
                (getResources(), this);
        } catch (IOException ioe) {
            log.error("Error in dependencyCheck", ioe);
            dependencyCheck = false;
        }

        if (!dependencyCheck) {
            // do not make application available if depency check fails
            ok = false;
        }

        // Reading the "catalina.useNaming" environment variable
        String useNamingProperty = System.getProperty("catalina.useNaming");
        if ((useNamingProperty != null)
            && (useNamingProperty.equals("false"))) {
            useNaming = false;
        }

        if (ok && isUseNaming()) {
            if (getNamingContextListener() == null) {
                NamingContextListener ncl = new NamingContextListener();  //创建一个NamingContextListener,并将其加入到listeneres里面去
                ncl.setName(getNamingContextName());
                ncl.setExceptionOnFailedWrite(getJndiExceptionOnFailedWrite());
                addLifecycleListener(ncl);
                setNamingContextListener(ncl);
            }
        }

        // Standard container startup
        if (log.isDebugEnabled())
            log.debug("Processing standard container startup");


        // Binding thread
        ClassLoader oldCCL = bindThread();  //这个是干啥的。。?

        try {
            if (ok) {
                // Start our subordinate components, if any
                Loader loader = getLoader();  //获取loader
                if ((loader != null) && (loader instanceof Lifecycle))
                    ((Lifecycle) loader).start();   //启动当前web应用程序的loader

                // since the loader just started, the webapp classloader is now
                // created.
                //设置loader的一些参数
                setClassLoaderProperty("clearReferencesStatic",
                        getClearReferencesStatic());
                setClassLoaderProperty("clearReferencesStopThreads",
                        getClearReferencesStopThreads());
                setClassLoaderProperty("clearReferencesStopTimerThreads",
                        getClearReferencesStopTimerThreads());
                setClassLoaderProperty("clearReferencesHttpClientKeepAliveThread",
                        getClearReferencesHttpClientKeepAliveThread());

                // By calling unbindThread and bindThread in a row, we setup the
                // current Thread CCL to be the webapp classloader
                unbindThread(oldCCL);
                oldCCL = bindThread();

                // Initialize logger again. Other components might have used it
                // too early, so it should be reset.
                logger = null;
                getLogger();

                Cluster cluster = getClusterInternal();
                if ((cluster != null) && (cluster instanceof Lifecycle))
                    ((Lifecycle) cluster).start();
                Realm realm = getRealmInternal();
                if ((realm != null) && (realm instanceof Lifecycle))
                    ((Lifecycle) realm).start();

                // Notify our interested LifecycleListeners
                fireLifecycleEvent(Lifecycle.CONFIGURE_START_EVENT, null);  //通知监听,激活config启动的事件,这里会在contextConfig里面执行xml文件的解析等工作

                // Start our child containers, if not already started
                for (Container child : findChildren()) {  //启动所有的child,其实这里主要是启动wrapper对象
                    if (!child.getState().isAvailable()) {
                        child.start();
                    }
                }

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

                // Acquire clustered manager
                Manager contextManager = null;
                Manager manager = getManager();  //创建manager对象,这个主要是跟session相关的吧
                if (manager == null) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("standardContext.cluster.noManager",
                                Boolean.valueOf((getCluster() != null)),
                                Boolean.valueOf(distributable)));
                    }
                    if ( (getCluster() != null) && distributable) {  //这里要根据是否有集群的配置来设置manager对象
                        try {
                            contextManager = getCluster().createManager(getName());   
                        } catch (Exception ex) {
                            log.error("standardContext.clusterFail", ex);
                            ok = false;
                        }
                    } else {
                        contextManager = new StandardManager();
                    }
                }

                // Configure default manager if none was specified
                if (contextManager != null) {
                    if (log.isDebugEnabled()) {
                        log.debug(sm.getString("standardContext.manager",
                                contextManager.getClass().getName()));
                    }
                    setManager(contextManager);
                }

                if (manager!=null && (getCluster() != null) && distributable) {
                    //let the cluster know that there is a context that is distributable
                    //and that it has its own manager
                    getCluster().registerManager(manager);
                }
            }

            if (!getConfigured()) {
                log.error( "Error getConfigured");
                ok = false;
            }

            // We put the resources into the servlet context
            if (ok)
                getServletContext().setAttribute
                    (Globals.RESOURCES_ATTR, getResources());   //在servletcontext里面保存整个web应用程序的rootresource,可以用于加载文件什么的

            if (ok ) {  //接下来主要是创建instancemanager,它用于负责管理当前web应用程序对象的实例化
                if (getInstanceManager() == null) {
                    javax.naming.Context context = null;
                    if (isUseNaming() && getNamingContextListener() != null) {
                        context = getNamingContextListener().getEnvContext();
                    }
                    Map<String, Map<String, String>> injectionMap = buildInjectionMap(
                            getIgnoreAnnotations() ? new NamingResourcesImpl(): getNamingResources());
                    setInstanceManager(new DefaultInstanceManager(context,    //创建instancemanager
                            injectionMap, this, this.getClass().getClassLoader()));
                    getServletContext().setAttribute(
                            InstanceManager.class.getName(), getInstanceManager());
                }
            }

            // Create context attributes that will be required
            if (ok) {  //设置jar包的扫描
                getServletContext().setAttribute(
                        JarScanner.class.getName(), getJarScanner());
            }

            // Set up the context init params
            mergeParameters();  //合并context的一些初始化参数

            // Call ServletContainerInitializers
            for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry :
                initializers.entrySet()) {
                try {
                    entry.getKey().onStartup(entry.getValue(),
                            getServletContext());
                } catch (ServletException e) {
                    log.error(sm.getString("standardContext.sciFail"), e);
                    ok = false;
                    break;
                }
            }

            // Configure and call application event listeners
            if (ok) {
                if (!listenerStart()) {  //启动contextListener啥的
                    log.error( "Error listenerStart");
                    ok = false;
                }
            }

            // Check constraints for uncovered HTTP methods
            // Needs to be after SCIs and listeners as they may programatically
            // change constraints
            if (ok) {
                checkConstraintsForUncoveredMethods(findConstraints());
            }

            try {
                // Start manager
                Manager manager = getManager();
                if ((manager != null) && (manager instanceof Lifecycle)) {
                    ((Lifecycle) getManager()).start();   //启动manager对象
                }
            } catch(Exception e) {
                log.error("Error manager.start()", e);
                ok = false;
            }

            // Configure and call application filters
            if (ok) {
                if (!filterStart()) {
                    log.error("Error filterStart");
                    ok = false;
                }
            }

            // Load and initialize all "load on startup" servlets
            if (ok) {
                loadOnStartup(findChildren());  //如果有load on startup的话,需要启动
            }

            // Start ContainerBackgroundProcessor thread
            super.threadStart();  //启动后台线程,用于执行一些周期事物
        } finally {
            // Unbinding thread
            unbindThread(oldCCL);
        }

        // Set available status depending upon startup success
        if (ok) {
            if (log.isDebugEnabled())
                log.debug("Starting completed");
        } else {
            log.error(sm.getString("standardContext.startFailed", getName()));
        }

        startTime=System.currentTimeMillis();

        // Send j2ee.state.running notification
        if (ok && (this.getObjectName() != null)) {
            Notification notification =
                new Notification("j2ee.state.running", this.getObjectName(),
                                 sequenceNumber.getAndIncrement());
            broadcaster.sendNotification(notification);
        }

        // Reinitializing if something went wrong
        if (!ok) {
            setState(LifecycleState.FAILED);
        } else {
            setState(LifecycleState.STARTING);
        }
    }

这部分代码够长的吧,这里将要做的工作归纳如下:

(1)设置当前web应用程序的资源引用,StandardRoot对象,可以用它来载入当前应用程序的资源。例如文件什么的。

(2)创建WebappLoader对象,内部还会创建WebAppClassLoader对象,具体这个是干嘛的,应该从名字猜也能猜出来吧,并启动

(3)激活CONFIGURE_START_EVENT事件,前面的文章已经交代过在context创建时会附带给其加一个listener,这个listener就是ContextConfig,它会响应CONFIGURE_START_EVENT事件,其实就是开始读取配置,包括servlet的定义什么,并将读取到的信息加入到当前context对象中。。

(4)启动刚刚通过处理配置文件得到的对象。例如wrapper,用于包装servlet。

(5)创建manager并启动,它用于处理session相关的工作

(6)在servletContext对象中设置一些属性

(7)初始化各种listener,例如定义的contextListener啥的


上面就是Context的启动要做的主要的事情。。。


好像这里说的比较粗糙吧,因为这部分涉及到的东西实在太多了。。以后再来一点一点的分析吧。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值