从零开始手写Tomcat的教程12节----StandardContext

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始手写Tomcat的教程12节----StandardContext相关的知识,希望对你有一定的参考价值。

从零开始手写Tomcat的教程12节----StandardContext




StandardContext的配置




StandardContext类的构造函数

    /**
     * Create a new StandardContext component with the default basic Valve.
     */
    public StandardContext() 

        super();
        pipeline.setBasic(new org.apache.catalina.core.StandardContextValve());
        namingResources.setContainer(this);

    


启动StandardContext类的实例

  /**
     * Start this Context component.
     *
     * @exception LifecycleException if a startup error occurs
     */
    public synchronized void start() throws LifecycleException 
         //防止重复启动 
        if (started)
            throw new LifecycleException
                (sm.getString("containerBase.alreadyStarted", logName()));

        if (debug >= 1)
            log("Starting");

        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(BEFORE_START_EVENT, null);

        if (debug >= 1)
            log("Processing start(), current available=" + getAvailable());
        setAvailable(false);
        setConfigured(false);
        boolean ok = true;
         
        //设置资源
        // Add missing components as necessary
        if (getResources() == null)    // (1) Required by Loader
            if (debug >= 1)
                log("Configuring default Resources");
            try 
            //当前应用程序是一个war包还是一个目录---联想tomcat会自动解压webapp下面的war包
                if ((docBase != null) && (docBase.endsWith(".war")))
                    setResources(new WARDirContext());
                else
                    setResources(new FileDirContext());
             catch (IllegalArgumentException e) 
                log("Error initializing resources: " + e.getMessage());
                ok = false;
            
        
        if (ok && (resources instanceof ProxyDirContext)) 
            DirContext dirContext = 
                ((ProxyDirContext) resources).getDirContext();
            if ((dirContext != null) 
                && (dirContext instanceof BaseDirContext)) 
                ((BaseDirContext) dirContext).setDocBase(getBasePath());
                ((BaseDirContext) dirContext).allocate();
            
        
         
         //设置加载器 
        if (getLoader() == null)       // (2) Required by Manager
            if (getPrivileged()) 
                if (debug >= 1)
                    log("Configuring privileged default Loader");
                //该方法会设置载入器,然后发出firePropertyChange事件    
                setLoader(new WebappLoader(this.getClass().getClassLoader()));
             else 
                if (debug >= 1)
                    log("Configuring non-privileged default Loader");
                setLoader(new WebappLoader(getParentClassLoader()));
            
        
        
        //设置session管理器
        if (getManager() == null)      // (3) After prerequisites
            if (debug >= 1)
                log("Configuring default Manager");
                 //该方法会设置session管理器,然后发出firePropertyChange事件    
            setManager(new StandardManager());
        
        
        //初始化字符集映射器
        // Initialize character set mapper
        getCharsetMapper();

        
        // Post work directory
        postWorkDirectory();

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

        if (ok && isUseNaming()) 
            if (namingContextListener == null) 
                namingContextListener = new NamingContextListener();
                namingContextListener.setDebug(getDebug());
                namingContextListener.setName(getNamingContextName());
                addLifecycleListener(namingContextListener);
            
        

        // Binding thread
        ClassLoader oldCCL = bindThread();

        // Standard container startup
        if (debug >= 1)
            log("Processing standard container startup");

        if (ok) 

            try 
                //添加默认映射器
                addDefaultMapper(this.mapperClass);
                started = true;
                
                //下面是启动与该Context容器相关联的组件
                  
                // Start our subordinate components, if any
                //启动载入器组件
                if ((loader != null) && (loader instanceof Lifecycle))
                    ((Lifecycle) loader).start();
                if ((logger != null) && (logger instanceof Lifecycle))
                    ((Lifecycle) logger).start();

                // Unbinding thread
                unbindThread(oldCCL);

                // Binding thread
                oldCCL = bindThread();
                
                //启动相关组件 
                if ((cluster != null) && (cluster instanceof Lifecycle))
                    ((Lifecycle) cluster).start();
                if ((realm != null) && (realm instanceof Lifecycle))
                    ((Lifecycle) realm).start();
                if ((resources != null) && (resources instanceof Lifecycle))
                    ((Lifecycle) resources).start();
               
               //启动相关映射器
                // Start our Mappers, if any
                Mapper mappers[] = findMappers();
                for (int i = 0; i < mappers.length; i++) 
                    if (mappers[i] instanceof Lifecycle)
                        ((Lifecycle) mappers[i]).start();
                
                
                //启动子容器
                // Start our child containers, if any
                Container children[] = findChildren();
                for (int i = 0; i < children.length; i++) 
                    if (children[i] instanceof Lifecycle)
                        ((Lifecycle) children[i]).start();
                
 
                // Start the Valves in our pipeline (including the basic), 
                // if any
                //启动管道对象
                if (pipeline instanceof Lifecycle)
                    ((Lifecycle) pipeline).start();
                   
                  //释放启动事件信号----这里ContextConfig接收并响应该事件
                // Notify our interested LifecycleListeners
                lifecycle.fireLifecycleEvent(START_EVENT, null);
                
                //启动session管理器
                if ((manager != null) && (manager instanceof Lifecycle))
                    ((Lifecycle) manager).start();

             finally 
                // Unbinding thread
                unbindThread(oldCCL);
            

        
        //是否配置好了
        if (!getConfigured())
            ok = false;

        // We put the resources into the servlet context
        if (ok)
            getServletContext().setAttribute
                (Globals.RESOURCES_ATTR, getResources());

        // Binding thread
        oldCCL = bindThread();

        // Create context attributes that will be required
        if (ok) 
            if (debug >= 1)
                log("Posting standard context attributes");
            postWelcomeFiles();
        

        // Configure and call application event listeners and filters
        if (ok) 
            if (!listenerStart())
                ok = false;
        
        if (ok) 
            if (!filterStart())
                ok = false;
        

        // Load and initialize all "load on startup" servlets
        if (ok)
           //调用下面的方法来载入哪些需要在启动就载入的子容器,例如Wrapper实例
            loadOnStartup(findChildren());

        // Unbinding thread
        unbindThread(oldCCL);

        // Set available status depending upon startup success
        if (ok) 
            if (debug >= 1)
                log("Starting completed");
            setAvailable(true);
         else 
            log(sm.getString("standardContext.startFailed"));
            try 
                stop();
             catch (Throwable t) 
                log(sm.getString("standardContext.startCleanup"), t);
            
            setAvailable(false);
        

        // Notify our interested LifecycleListeners
        lifecycle.fireLifecycleEvent(AFTER_START_EVENT, null);

    



上面在13处有问题,应该是调用loadOnStartup方法

    /**
     * Load and initialize all servlets marked "load on startup" in the
     * web application deployment descriptor.
     *
     * @param children Array of wrappers for all currently defined
     *  servlets (including those not declared load on startup)
     */
    public void loadOnStartup(Container children[]) 

        // Collect "load on startup" servlets that need to be initialized
        TreeMap map = new TreeMap();
        for (int i = 0; i < children.length; i++) 
            Wrapper wrapper = (Wrapper) children[i];
            int loadOnStartup = wrapper.getLoadOnStartup();
            if (loadOnStartup < 0)
                continue;
            if (loadOnStartup == 0)     // Arbitrarily put them last
                loadOnStartup = Integer.MAX_VALUE;
            Integer key = new Integer(loadOnStartup);
            ArrayList list = (ArrayList) map.get(key);
            if (list == null) 
                list = new ArrayList();
                map.put(key, list);
            
            list.add(wrapper);
        

        // Load the collected "load on startup" servlets
        Iterator keys = map.keySet().iterator();
        while (keys.hasNext()) 
            Integer key = (Integer) keys.next();
            ArrayList list = (ArrayList) map.get(key);
            Iterator wrappers = list.iterator();
            while (wrappers.hasNext()) 
                Wrapper wrapper = (Wrapper) wrappers.next();
                try 
                    wrapper.load();
                 catch (ServletException e) 
                    log(sm.getString("standardWrapper.loadException",
                                     getName()), e);
                    // NOTE: load errors (including a servlet that throws
                    // UnavailableException from tht init() method) are NOT
                    // fatal to application startup
                
            
        

    

从零开始手写Tomcat的教程11节----StandardWrapper

wrapper的load方法会去调用loadServelt方法生成并返回一个与当前wrapper关联的servlet对象


invoke方法

 /**
     * Process the specified Request, and generate the corresponding Response,
     * according to the design of this particular Container.
     *
     * @param request Request to be processed
     * @param response Response to be produced
     *
     * @exception IOException if an input/output error occurred while
     *  processing
     * @exception ServletException if a ServletException was thrown
     *  while processing this request
     */
    public void invoke(Request request, Response response)
        throws IOException, ServletException 

        // Wait if we are reloading
        while (getPaused()) 
            try 
                Thread.sleep(1000);
             catch (InterruptedException e) 
                ;
            
        

        // Normal request processing
        if (swallowOutput) 
            try 
                SystemLogHandler.startCapture();
                super.invoke(request, response);
             finally 
                String log = SystemLogHandler.stopCapture();
                if (log != null && log.length() > 0) 
                    log(log);
                
            
         else 
            super.invoke(request, response);
        

    

父类ContainerBase的invoke方法:

    /**
     * Process the specified Request, to produce the corresponding Response,
     * by invoking the first Valve in our pipeline (if any), or the basic
     * Valve otherwise.
     *
     * @param request Request to be processed
     * @param response Response to be produced
     *
     * @exception IllegalStateException if neither a pipeline or a basic
     *  Valve have been configured for this Container
     * @exception IOException if an input/output error occurred while
     *  processing
     * @exception ServletException if a ServletException was thrown
     *  while processing this request
     */
    public void invoke(Request request, Response response)
        throws IOException, ServletException 

        pipeline.invoke(request, response);

    

ContainerBase的成员变量:

    /**
     * The Pipeline object with which this Container is associated.
     */
    protected Pipeline pipeline = new org.apache.catalina.core.StandardPipeline(this);

最终调用的是StandardPipeline的invoke方法:

    public void invoke(Request request, Response response)
        throws IOException, ServletException 

        // Invoke the first Valve in this pipeline for this request
        (new StandardPipelineValveContext()).invokeNext(request, response);
    

StandardPipelineValveContext是StandardPipeline的一个内部类,可以访问StandardPipeline的成员属性:

    protected class StandardPipelineValveContext
        implements ValveContext 
        protected int stage = 0;

        public void invokeNext(Request request, Response response)
            throws IOException, ServletException 

            int subscript = stage;
            stage = stage + 1;

            // Invoke the requested Valve for the current request thread
            if (subscript < valves.length) 
                valves[subscript].invoke(request, response, this);
             else if ((subscript == valves.length) && (basic != null从零开始手写Tomcat的教程6节----生命周期

从零开始手写Tomcat的教程9节---Session管理

从零开始手写Tomcat的教程5节---servlet容器

从零开始手写Tomcat的教程10节---安全性

从零开始手写Tomcat的教程4节---Tomcat默认连接器

从零开始手写Tomcat的教程7节---日志记录器