美文网首页
tomcat4标准包装器StandardWrapper

tomcat4标准包装器StandardWrapper

作者: 紫苓 | 来源:发表于2018-09-11 13:30 被阅读18次

    1、关于Mapper的补充

    对于Mapper,其有一个默认实现为StandardContextMapper


    image.png

    在StandardContext的start()方法中,这个默认的Mapper通过addDefaultMapper()方法会被初始化,然后通过Container的addMapper()方法加入到Container中。

    private String mapperClass =
            "org.apache.catalina.core.StandardContextMapper";
    
    // StandardContext.addDefaultMapper
    protected void addDefaultMapper(String mapperClass) {
            super.addDefaultMapper(this.mapperClass);
        }
    
    // ContainerBase.addDefaultMapper
    protected void addDefaultMapper(String mapperClass) {
            // Do we need a default Mapper?
            if (mapperClass == null)
                return;
            if (mappers.size() >= 1)
                return;
            // Instantiate and add a default Mapper
            try {
                Class clazz = Class.forName(mapperClass);
                Mapper mapper = (Mapper) clazz.newInstance();
    // 默认Mapper协议为HTTP
                mapper.setProtocol("http");
                addMapper(mapper);
            } catch (Exception e) {
                log(sm.getString("containerBase.addDefaultMapper", mapperClass),
                    e);
            }
        }
    

    2、关于SingleThreadModel

    对于一个实现了SingleThreadModel接口的servlet,其目的是保证servlet 一次只能有一个请求,也即保证不会有两个线程同是使用 servlet
    的 service 方法,该接口并不能避免同步而产生的问题,如访问静态
    类变量或该 servlet 以外的类或变量。

    3、StandardWrapper处理过程

    3、1 Context把请求交给Wrapper过程

    在tomcat中,Wrapper容器的父容器为Context,Context的默认实现为StandardContext,其Pipeline的basic Valve为StandardContextValve,当一个请求被Context的invoke方法接收到的时候,根据Pipeline的处理流程,最终交给了StandardContextValve的invoke(Request request, Response response,ValveContext valveContext)方法来处理,而StandardContextValve是通过Context.map()方法来找到对应的Wrapper,Context如何找Wrapper这里略去,比较简单,主要看看Wrapper处理请求的过程,在tomcat中,Wrapper的默认实现为StandardWrapper,其Pipeline的basic Valve为StandardWrapperValve,这里就从这里开始分析:

    public void invoke(Request request, Response response,
                           ValveContext valveContext){
            StandardWrapper wrapper = (StandardWrapper) getContainer();
            ServletRequest sreq = request.getRequest();
            ServletResponse sres = response.getResponse();
            try {
                if (!unavailable) {
    // 这里通过allocate()方法获取到一个Servlet实例
                    servlet = wrapper.allocate();
                }
            } catch (ServletException e) {
            }
            //......
    }
    
    3、2 StandardWrapper.allocate()
    public Servlet allocate() throws ServletException {
            // If not SingleThreadedModel, return the same instance every time
            if (!singleThreadModel) {
    // 如果Servlet没有实现SingleThreadModel,返回的是Servlet单例
    // 对于非SingleThreadModel的Servlet,在整个tomcat中只会有一个实例来处理客户端的请求,
    // 也就是说多个线程共享一个Servlet实例,这个要考虑资源共享的问题
                // Load and initialize our instance if necessary
                if (instance == null) {
                    synchronized (this) {
                        if (instance == null) {
                            try {
                                instance = loadServlet();
                            } catch (ServletException e) {
                                throw e;
                            } catch (Throwable e) {
                                throw new ServletException
                                        (sm.getString("standardWrapper.allocate"), e);
                            }
                        }
                    }
                }
    
                if (!singleThreadModel) {
                    if (debug >= 2)
                        log("  Returning non-STM instance");
                    countAllocated++;
                    return (instance);
                }
            }
    
            // 而对于SingleThreadModel,tomcat为了提高并发问题,
    // 创建了多个Servlet实例来处理请求,实例保存在一个Stack中。
            synchronized (instancePool) {
                while (countAllocated >= nInstances) {
                    // Allocate a new instance if possible, or else wait
                    if (nInstances < maxInstances) {
                        try {
                            instancePool.push(loadServlet());
                            nInstances++;
                        } catch (ServletException e) {
                            throw e;
                        } catch (Throwable e) {
                            throw new ServletException
                                    (sm.getString("standardWrapper.allocate"), e);
                        }
                    } else {
                        try {
                            instancePool.wait();
                        } catch (InterruptedException e) {
                            ;
                        }
                    }
                }
                countAllocated++;
                return (Servlet) instancePool.pop();
            }
        }
    

    不管是SingleThreadModel的Servlet还是非SingleThreadModel的Servlet,其创建的方法都为loadServlet()

    3、3 StandardWrapper.loadServlet()
    public synchronized Servlet loadServlet() throws ServletException {
      // 因为jsp最终是被tomcat编译成class文件的,tomcat也需要能处理JSP文件
    String actualClass = servletClass;
                if ((actualClass == null) && (jspFile != null)) {
                    Wrapper jspWrapper = (Wrapper)
                            ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
                    if (jspWrapper != null)
                        actualClass = jspWrapper.getServletClass();
                }
    // tomcat有自己的一套类加载机制,主要原因一是安全机制,
    // tomcat只允许访问WEB-INF/classes下的servlet,
    //二是tomcat要能够热加载servlet,即所谓热加载、动态部署
    Loader loader = getLoader();// 得到一个Loader
    // 再从loader中取得ClassLoader
    ClassLoader classLoader = loader.getClassLoader();
    
    // Special case class loader for a container provided servlet
    if (isContainerProvidedServlet(actualClass)) {
        // 查看目标是否为org.apache.catalina包下的Servlet或者是ContainerServlet子接口或实现
        classLoader = this.getClass().getClassLoader();
        log(sm.getString
                ("standardWrapper.containerServlet", getName()));
    }
    
    Class classClass = null;
    if (classLoader != null) {
      System.out.println("Using classLoader.loadClass");
      classClass = classLoader.loadClass(actualClass);
    } else {
        System.out.println("Using forName");
        classClass = Class.forName(actualClass);
    }
    
    servlet = (Servlet) classClass.newInstance();
    // 初始化操作
    // class StandardWrapperFacade implements ServletConfig
    // StandardWrapperFacade facade = new StandardWrapperFacade(this);
    // public StandardWrapperFacade(StandardWrapper config){}
    // 这里传的是包装类,使得servlet只能访问ServletConfig中的方法
    servlet.init(facade);
    // Invoke jspInit on JSP pages
    if ((loadOnStartup > 0) && (jspFile != null)) {
    // 如果是JSP文件,并且loadOnStartup 大于0,则立马就调用了service方法
        // Invoking jspInit
        HttpRequestBase req = new HttpRequestBase();
        HttpResponseBase res = new HttpResponseBase();
        req.setServletPath(jspFile);
        req.setQueryString("jsp_precompile=true");
        servlet.service(req, res);
    }
    // 如果是SingleThreadModel,则初始化一个Stack,用来存放servlet实例
    singleThreadModel = servlet instanceof SingleThreadModel;
    if (singleThreadModel) {
        if (instancePool == null){
            instancePool = new Stack();
        }
    }
    }
    
    3、4 ServletConfig对象

    在servlet初始化的时候,其需要一个参数为ServletConfig,先看下类图:


    image.png

    StandardWrapperFacade是一个包装类,内部对于ServletConfig的实现由StandardWrapper来完成,这里只看一下StandardWrapper是如何获取ServletContext 的:

    protected Container parent = null;
    
    public ServletContext getServletContext() {
            if (parent == null)
                return (null);
            else if (!(parent instanceof Context))
                return (null);
            else
                return (((Context) parent).getServletContext());
        }
    

    servlet运行的上下文环境由Context创建,从这里可以看到,一个单独的wrapper是不能单独部署的,单独部署的Wrapper是没法获取到servletcontext的。在StandardContext中的getServletContext方法中创建了servletcontext。

    public ServletContext getServletContext() {
           if (context == null){
               context = new ApplicationContext(getBasePath(), this);
           }
           return (context);
       }
    
    3、5 StandardWrapperValve后续处理流程

    在获取到servlet实例后,StandardWrapperValve继续对请求进行相关处理

    // StandardWrapperValve.invoke()
    // Create the filter chain for this request
     ApplicationFilterChain filterChain = createFilterChain(request, servlet);
    

    在分析ApplicationFilterChain的创建过程之前,先了解下filter关联的类图


    image.png
    private ApplicationFilterChain createFilterChain(Request request, Servlet servlet) {
    
            // If there is no servlet to execute, return null
            if (servlet == null)
                return (null);
    
            // Create and initialize a filter chain object
            // 构建了一个filterChain,
            ApplicationFilterChain filterChain = new ApplicationFilterChain();
            // 把servlet设置到filterChain中
            filterChain.setServlet(servlet);
            //
            StandardWrapper wrapper = (StandardWrapper) getContainer();
            filterChain.setSupport(wrapper.getInstanceSupport());
    
            // Acquire the filter mappings for this Context
            StandardContext context = (StandardContext) wrapper.getParent();
            FilterMap filterMaps[] = context.findFilterMaps();
    
            // If there are no filter mappings, we are done
            if ((filterMaps == null) || (filterMaps.length == 0)){
                return (filterChain);
            }
    
            // Acquire the information we will need to match filter mappings
            String requestPath = null;
            if (request instanceof HttpRequest) {
                HttpServletRequest hreq = (HttpServletRequest) request.getRequest();
                String contextPath = hreq.getContextPath();
                if (contextPath == null){
                    contextPath = "";
                }
                String requestURI = ((HttpRequest) request).getDecodedRequestURI();
                if (requestURI.length() >= contextPath.length()){
                    requestPath = requestURI.substring(contextPath.length());
                }
            }
            String servletName = wrapper.getName();
            int n = 0;
    
            // Add the relevant path-mapped filters to this filter chain
            // 根据url-pattern去添加过滤器
            for (int i = 0; i < filterMaps.length; i++) {
                if (!matchFiltersURL(filterMaps[i], requestPath)){
                    continue;
                }
                ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                    context.findFilterConfig(filterMaps[i].getFilterName());
                if (filterConfig == null) {
                    continue;
                }
                filterChain.addFilter(filterConfig);
                n++;
            }
    
            // Add filters that match on servlet name second
            for (int i = 0; i < filterMaps.length; i++) {
                if (!matchFiltersServlet(filterMaps[i], servletName))
                    continue;
                ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                    context.findFilterConfig(filterMaps[i].getFilterName());
                if (filterConfig == null) {
                    continue;
                }
                filterChain.addFilter(filterConfig);
                n++;
            }
            return (filterChain);
        }
    

    这个构建ApplicationFilterChain的逻辑还是比较简单的,主要看这句:

    ApplicationFilterConfig filterConfig = (ApplicationFilterConfig)
                    context.findFilterConfig(filterMaps[i].getFilterName());
    

    我们在web.xml中配置的filter,如

        <filter>
            <filter-name>filterName</filter-name>
            <filter-class>filterClass</filter-class>
        </filter>
        <filter-mapping>
            <filter-name>filterName</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    

    都会被先转化为FilterDef和FilterMap,这些FilterDef和FilterMap被保存在Context中,在Context的启动过程中会被转化为ApplicationFilterConfig:
    StandardContext.start():

    public synchronized void start() throws LifecycleException {
        if (ok) {
                if (!filterStart())
                    ok = false;
            }
    }
    

    StandardContext.filterStart():

    public boolean filterStart() {
            // Instantiate and record a FilterConfig for each defined filter
            boolean ok = true;
            synchronized (filterConfigs) {
                filterConfigs.clear();
                Iterator names = filterDefs.keySet().iterator();
                while (names.hasNext()) {
                    String name = (String) names.next();
                    if (debug >= 1)
                        log(" Starting filter '" + name + "'");
                    ApplicationFilterConfig filterConfig = null;
                    try {
                        filterConfig = new ApplicationFilterConfig
                          (this, (FilterDef) filterDefs.get(name));
                        filterConfigs.put(name, filterConfig);
                    } catch (Throwable t) {
                        log(sm.getString("standardContext.filterStart", name), t);
                        ok = false;
                    }
                }
            }
            return (ok);
        }
    

    所以StandardContext找FilterConfig的方式就比较简单了:

    public FilterConfig findFilterConfig(String name) {
            synchronized (filterConfigs) {
                return ((FilterConfig) filterConfigs.get(name));
            }
        }
    

    在获取到ApplicationFilterChain之后,StandardWrapperValve.invoke()中紧跟着就调用其doFilter方法,ApplicationFilterChain中doFilter最终调用了internalDoFilter()方法:

    if ((servlet != null) && (filterChain != null)) {
                    filterChain.doFilter(sreq, sres);
                }
    

    ApplicationFilterChain.internalDoFilter()

    private void internalDoFilter(ServletRequest request, ServletResponse response){
        if (this.iterator == null){
                this.iterator = filters.iterator();
        }
    // 如果还有拦截器
        if (this.iterator.hasNext()) {
                ApplicationFilterConfig filterConfig =
                  (ApplicationFilterConfig) iterator.next();
                Filter filter = null;
                filter = filterConfig.getFilter();
                filter.doFilter(request, response, this);
                return;
          }
    // 所有的拦截器都走完了,最后调用servlet的service()
          if ((request instanceof HttpServletRequest) &&
                    (response instanceof HttpServletResponse)) {
                    servlet.service((HttpServletRequest) request,
                                    (HttpServletResponse) response);
                } else {
                    servlet.service(request, response);
                }
    }
    

    从这段逻辑中可以看出,在自己定义拦截器的时候,如果最后没有调用ApplicationFilterChain.doFilter()方法,那后续所有的filter和servlet的service()都是没有机会被执行的,从而达到了拦截的目的。

    相关文章

      网友评论

          本文标题:tomcat4标准包装器StandardWrapper

          本文链接:https://www.haomeiwen.com/subject/zlogwftx.html