tomcat4标准包装器StandardWrapper

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,如

    
        filterName
        filterClass
    
    
        filterName
        /*
    

都会被先转化为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)