前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从零开始手写Tomcat的教程12节----StandardContext

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

作者头像
大忽悠爱学习
发布2022-05-10 15:53:24
4600
发布2022-05-10 15:53:24
举报
文章被收录于专栏:c++与qt学习

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


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

StandardContext的配置

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

StandardContext类的构造函数

代码语言:javascript
复制
    /**
     * 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类的实例

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
  /**
     * 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方法

代码语言:javascript
复制
    /**
     * 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方法

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
 /**
     * 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方法:

代码语言:javascript
复制
    /**
     * 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的成员变量:

代码语言:javascript
复制
    /**
     * The Pipeline object with which this Container is associated.
     */
    protected Pipeline pipeline = new org.apache.catalina.core.StandardPipeline(this);

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

代码语言:javascript
复制
    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的成员属性:

代码语言:javascript
复制
    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)) {
                basic.invoke(request, response, this);
            } else {
                throw new ServletException
                    (sm.getString("standardPipeline.noValve"));
            }

        }


    }
在这里插入图片描述
在这里插入图片描述

这是11节末尾画的图,大家可以回顾一下:


StandardContextMapper类

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
    /**
     * Add the specified Mapper associated with this Container.
     */
    public void addMapper(Mapper mapper) {

        synchronized(mappers) {
            if (mappers.get(mapper.getProtocol()) != null)
                throw new IllegalArgumentException("addMapper:  Protocol '" +
                                                   mapper.getProtocol() +
                                                   "' is not unique");
            //设置映射器关联的容器对象                                       
            mapper.setContainer((Container) this);      // May throw IAE
            //如果是容器启动后才放入的映射器,则需要调用其start方法进行启动
            if (started && (mapper instanceof Lifecycle)) {
                try {
                    ((Lifecycle) mapper).start();
                } catch (LifecycleException e) {
                    log("ContainerBase.addMapper: start: ", e);
                    throw new IllegalStateException
                        ("ContainerBase.addMapper: start: " + e);
                }
            } 
            //放入映射器集合中
            mappers.put(mapper.getProtocol(), mapper);
            if (mappers.size() == 1)
                this.mapper = mapper;
            else
                this.mapper = null;
                //释放事件
            fireContainerEvent(ADD_MAPPER_EVENT, mapper);
        }

    }
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
                addDefaultMapper(this.mapperClass);
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
    /**
     * The Java class name of the default Mapper class for this Container.
     */
    private String mapperClass =
        "org.apache.catalina.core.StandardContextMapper";
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
    public void setContainer(Container container) {

        if (!(container instanceof StandardContext))
            throw new IllegalArgumentException
                (sm.getString("httpContextMapper.container"));
        context = (StandardContext) container;

    }
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
    public Container map(Request request, boolean update);
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
    public Container map(Request request, boolean update) {

        // Select the Mapper we will use
        Mapper mapper = findMapper(request.getRequest().getProtocol());
        if (mapper == null)
            return (null);

        // Use this Mapper to perform this mapping
        return (mapper.map(request, update));

    }
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
 public Container map(Request request, boolean update) {

        int debug = context.getDebug();

        // Has this request already been mapped?
        if (update && (request.getWrapper() != null))
            return (request.getWrapper());

        // Identify the context-relative URI to be mapped
       //1.标识出相对于Context的URI
         String contextPath =
            ((HttpServletRequest) request.getRequest()).getContextPath();
        String requestURI = ((HttpRequest) request).getDecodedRequestURI();
        String relativeURI = requestURI.substring(contextPath.length());

        if (debug >= 1)
            context.log("Mapping contextPath='" + contextPath +
                        "' with requestURI='" + requestURI +
                        "' and relativeURI='" + relativeURI + "'");

        // Apply the standard request URI mapping rules from the specification
        Wrapper wrapper = null;
        String servletPath = relativeURI;
        String pathInfo = null;
        String name = null;
          
        //下面有这几种匹配规则  
              
        // Rule 1 -- Exact Match ---精确匹配
        if (wrapper == null) {
            if (debug >= 2)
                context.log("  Trying exact match");
            if (!(relativeURI.equals("/")))
            //利用匹配规则找到一个当前请求URI对应的wrapper的name
                name = context.findServletMapping(relativeURI);
            if (name != null)
            //从context的子容器中找个找个wrapper实例
                wrapper = (Wrapper) context.findChild(name);
            if (wrapper != null) {
                servletPath = relativeURI;
                pathInfo = null;
            }
        }

        // Rule 2 -- Prefix Match --- 前缀匹配
        if (wrapper == null) {
            if (debug >= 2)
                context.log("  Trying prefix match");
            servletPath = relativeURI;
            while (true) {
                name = context.findServletMapping(servletPath + "/*");
                if (name != null)
                    wrapper = (Wrapper) context.findChild(name);
                if (wrapper != null) {
                    pathInfo = relativeURI.substring(servletPath.length());
                    if (pathInfo.length() == 0)
                        pathInfo = null;
                    break;
                }
                int slash = servletPath.lastIndexOf('/');
                if (slash < 0)
                    break;
                servletPath = servletPath.substring(0, slash);
            }
        }

        // Rule 3 -- Extension Match ----扩展匹配规则
        if (wrapper == null) {
            if (debug >= 2)
                context.log("  Trying extension match");
            int slash = relativeURI.lastIndexOf('/');
            if (slash >= 0) {
                String last = relativeURI.substring(slash);
                int period = last.lastIndexOf('.');
                if (period >= 0) {
                    String pattern = "*" + last.substring(period);
                    name = context.findServletMapping(pattern);
                    if (name != null)
                        wrapper = (Wrapper) context.findChild(name);
                    if (wrapper != null) {
                        servletPath = relativeURI;
                        pathInfo = null;
                    }
                }
            }
        }

        // Rule 4 -- Default Match ----默认匹配
        if (wrapper == null) {
            if (debug >= 2)
                context.log("  Trying default match");
            name = context.findServletMapping("/");
            if (name != null)
                wrapper = (Wrapper) context.findChild(name);
            if (wrapper != null) {
                servletPath = relativeURI;
                pathInfo = null;
            }
        }

        // Update the Request (if requested) and return this Wrapper
        if ((debug >= 1) && (wrapper != null))
            context.log(" Mapped to servlet '" + wrapper.getName() +
                        "' with servlet path '" + servletPath +
                        "' and path info '" + pathInfo +
                        "' and update=" + update);
           //将查询到的wrapper对象设置给当然request对象             
        if (update) {
            request.setWrapper(wrapper);
            ((HttpRequest) request).setServletPath(servletPath);
            ((HttpRequest) request).setPathInfo(pathInfo);
        }
        return (wrapper);

    }
在这里插入图片描述
在这里插入图片描述

对重载的支持

在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
    /**
     * Set the Container with which this Logger has been associated.
     *
     * @param container The associated Container
     */
    public void setContainer(Container container) {

        // Deregister from the old Container (if any)
        if ((this.container != null) && (this.container instanceof Context))
            ((Context) this.container).removePropertyChangeListener(this);

        // Process this property change
        Container oldContainer = this.container;
        this.container = container;
        support.firePropertyChange("container", oldContainer, this.container);

        // Register with the new Container (if any)
        if ((this.container != null) && (this.container instanceof Context)) {
            setReloadable( ((Context) this.container).getReloadable() );
            ((Context) this.container).addPropertyChangeListener(this);
        }

    }
在这里插入图片描述
在这里插入图片描述
代码语言:javascript
复制
    /**
     * Set the reloadable flag for this Loader.
     *
     * @param reloadable The new reloadable flag
     */
    public void setReloadable(boolean reloadable) {

        // Process this property change
        boolean oldReloadable = this.reloadable;
        this.reloadable = reloadable;
        support.firePropertyChange("reloadable",
                                   new Boolean(oldReloadable),
                                   new Boolean(this.reloadable));

        // Start or stop our background thread if required
        if (!started)
            return;
        if (!oldReloadable && this.reloadable)
            threadStart();
        else if (oldReloadable && !this.reloadable)
            threadStop();

    }
在这里插入图片描述
在这里插入图片描述

backgroundProcess()方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

小结

在这里插入图片描述
在这里插入图片描述

这里的源码分析针对的是tomcat4版本的

这里我先对StandardContext容器的start方法调用链做个总结:

在这里插入图片描述
在这里插入图片描述

ContainerBase的start方法:

代码语言:javascript
复制
    @Override
    public synchronized void start() throws LifecycleException {

        // Validate and update our current component state
        if (started)
            throw new LifecycleException
                (sm.getString("containerBase.alreadyStarted", logName()));

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

        addDefaultMapper(this.mapperClass);
        started = true;

        // Start our subordinate components, if any
        if ((loader != null) && (loader instanceof Lifecycle))
            ((Lifecycle) loader).start();
        if ((logger != null) && (logger instanceof Lifecycle))
            ((Lifecycle) logger).start();
        if ((manager != null) && (manager instanceof Lifecycle))
            ((Lifecycle) manager).start();
        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();

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

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

    }

StandardContext后面的start方法调用干了啥子,就是上面讲的过程了,这里没有调用其父类ContainerBase的start方法了注意

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-03-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 从零开始手写Tomcat的教程12节----StandardContext
  • StandardContext的配置
    • StandardContext类的构造函数
      • 启动StandardContext类的实例
        • invoke方法
        • StandardContextMapper类
        • 对重载的支持
        • backgroundProcess()方法
        • 小结
        相关产品与服务
        容器服务
        腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档