Tomcat源码分析(十一) -- Wrapper

598 阅读7分钟

Tomcat 中的 Wrapper

Wrapper 定义

public interface Wrapper extends Container {

    public static final String ADD_MAPPING_EVENT = "addMapping";

    public static final String REMOVE_MAPPING_EVENT = "removeMapping";

    public long getAvailable();

    public void setAvailable(long available);

    public int getLoadOnStartup();

    public void setLoadOnStartup(int value);

    public String getRunAs();

    public void setRunAs(String runAs);

    public String getServletClass();

    public void setServletClass(String servletClass);

    public String[] getServletMethods() throws ServletException;

    public boolean isUnavailable();

    public Servlet getServlet();

    public void setServlet(Servlet servlet);

    public void addInitParameter(String name, String value);

    public void addMapping(String mapping);

    public void addSecurityReference(String name, String link);

    public Servlet allocate() throws ServletException;

    public void deallocate(Servlet servlet) throws ServletException;

    public String findInitParameter(String name);

    public String[] findInitParameters();

    public String[] findMappings();

    public String findSecurityReference(String name);

    public String[] findSecurityReferences();

    public void incrementErrorCount();

    public void load() throws ServletException;

    public void removeInitParameter(String name);

    public void removeMapping(String mapping);

    public void removeSecurityReference(String name);

    public void unavailable(UnavailableException unavailable);

    public void unload() throws ServletException;

    public MultipartConfigElement getMultipartConfigElement();

    public void setMultipartConfigElement(
            MultipartConfigElement multipartConfig);

    public boolean isAsyncSupported();

    public void setAsyncSupported(boolean asyncSupport);

    public boolean isEnabled();

    public void setEnabled(boolean enabled);

    @Deprecated
    public void setServletSecurityAnnotationScanRequired(boolean b);

    @Deprecated
    public void servletSecurityAnnotationScan() throws ServletException;

    public boolean isOverridable();

    public void setOverridable(boolean overridable);
}

从定义中可以看出

  • Wrapper 接口继承了 Container 接口
  • Wrapper 跟跟 Servlet 有关联
  • Wrapper 的实现类是 StandardWrapper

StandardWrapper 的构造方法

 public StandardWrapper() {
     super();
     //为 pipeline 添加基础阀 StandardWrapperValve
     pipeline.setBasic(new StandardWrapperValve());
 }

StandardWrapper 的 allocate 方法 -- 实例化Servlet

public Servlet allocate() throws ServletException {

    if (debug >= 1)
        log("Allocating an instance");

    // If we are currently unloading this servlet, throw an exception
    if (unloading)
        throw new ServletException
        (sm.getString("standardWrapper.unloading", getName()));

    // If not SingleThreadedModel, return the same instance every time
    if (!singleThreadModel) {

        // Load and initialize our instance if necessary
        if (instance == null) {
            synchronized (this) {
                if (instance == null) {
                    try {
                        //加载Servlet
                        instance = loadServlet();
                    } catch (ServletException e) {
                        throw e;
                    } catch (Throwable e) {
                        throw new ServletException
                            (sm.getString("standardWrapper.allocate"), e);
                    }
                }
            }
        }
        //如果不是 SingleThreadModel 只会存在一个实例,所以直接返回唯一的实例就行
        //SingleThreadModel 表示同一个对象在同一时刻只能被一个线程访问,所以会创建多个对象来处理多线程。这里会用对象池来管理对象
        if (!singleThreadModel) {
            if (debug >= 2)
                log("  Returning non-STM instance");
            countAllocated++;
            return (instance);
        }

    }

    synchronized (instancePool) {
        //活动数量大于实例数量,要一直创建新的 Servlet 对象
        //注意这个活动数量不是 Servlet 的数量,应该是活动线程的数量
        while (countAllocated >= nInstances) {
            // Allocate a new instance if possible, or else wait
            if (nInstances < maxInstances) {
                //如果实例数量小于最大实例数量,创建新的 Servlet 对象
                //注意这里最大数量默认 20
                try {
                    instancePool.push(loadServlet());
                    nInstances++;
                } catch (ServletException e) {
                    throw e;
                } catch (Throwable e) {
                    throw new ServletException
                        (sm.getString("standardWrapper.allocate"), e);
                }
            } else {
                //如果实例数量达到最大数量,等待其它 Servlet 结束
                try {
                    instancePool.wait();
                } catch (InterruptedException e) {
                    ;
                }
            }
        }
        if (debug >= 2)
            log("  Returning allocated STM instance");
        //如果活动线程数量小于实例数量,表示有空闲的 Servlet 对象来处理活动线程
        //这里直接返回空闲的 Servlet 对象
        countAllocated++;
        return (Servlet) instancePool.pop();

    }

}

StandardWrapper 的 loadServlet 方法

public synchronized Servlet loadServlet() throws ServletException {

    // Nothing to do if we already have an instance or an instance pool
    if (!singleThreadModel && (instance != null))
        return instance;

    PrintStream out = System.out;
    SystemLogHandler.startCapture();
    Servlet servlet = null;
    try {
        // If this "servlet" is really a JSP file, get the right class.
        // HOLD YOUR NOSE - this is a kludge that avoids having to do special
        // case Catalina-specific code in Jasper - it also requires that the
        // servlet path be replaced by the <jsp-file> element content in
        // order to be completely effective
        //获取servlet类的全路径
        String actualClass = servletClass;
        if ((actualClass == null) && (jspFile != null)) {
            Wrapper jspWrapper = (Wrapper)
                ((Context) getParent()).findChild(Constants.JSP_SERVLET_NAME);
            if (jspWrapper != null)
                actualClass = jspWrapper.getServletClass();
        }

        // Complain if no servlet class has been specified
        if (actualClass == null) {
            unavailable(null);
            throw new ServletException
                (sm.getString("standardWrapper.notClass", getName()));
        }

        // Acquire an instance of the class loader to be used
        //获取加载器
        Loader loader = getLoader();
        if (loader == null) {
            unavailable(null);
            throw new ServletException
                (sm.getString("standardWrapper.missingLoader", getName()));
        }
        //获取类加载器
        ClassLoader classLoader = loader.getClassLoader();

        // Special case class loader for a container provided servlet
        if (isContainerProvidedServlet(actualClass)) {
            classLoader = this.getClass().getClassLoader();
            log(sm.getString
                ("standardWrapper.containerServlet", getName()));
        }

        // Load the specified servlet class from the appropriate class loader
        Class classClass = null;
        try {
            //加载 Servlet 对象的类到jvm
            if (classLoader != null) {
                System.out.println("Using classLoader.loadClass");
                classClass = classLoader.loadClass(actualClass);
            } else {
                System.out.println("Using forName");
                classClass = Class.forName(actualClass);
            }
        } catch (ClassNotFoundException e) {
            unavailable(null);
            throw new ServletException
                (sm.getString("standardWrapper.missingClass", actualClass),
                 e);
        }
        if (classClass == null) {
            unavailable(null);
            throw new ServletException
                (sm.getString("standardWrapper.missingClass", actualClass));
        }

        // Instantiate and initialize an instance of the servlet class itself
        try {
            //通过反射实例化 Servlet 对象
            servlet = (Servlet) classClass.newInstance();
        } catch (ClassCastException e) {
            unavailable(null);
            // Restore the context ClassLoader
            throw new ServletException
                (sm.getString("standardWrapper.notServlet", actualClass), e);
        } catch (Throwable e) {
            unavailable(null);
            // Restore the context ClassLoader
            throw new ServletException
                (sm.getString("standardWrapper.instantiate", actualClass), e);
        }

        // Check if loading the servlet in this web application should be
        // allowed
        if (!isServletAllowed(servlet)) {
            throw new SecurityException
                (sm.getString("standardWrapper.privilegedServlet",
                              actualClass));
        }

        // Special handling for ContainerServlet instances
        if ((servlet instanceof ContainerServlet) &&
            isContainerProvidedServlet(actualClass)) {
            System.out.println("calling setWrapper");                  
            ((ContainerServlet) servlet).setWrapper(this);
            System.out.println("after calling setWrapper");                  
        }


        // Call the initialization method of this servlet
        try {
            //发送事件
            instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,
                                              servlet);
            //调用 Servlet 的init方法
            servlet.init(facade);
            // Invoke jspInit on JSP pages
            if ((loadOnStartup > 0) && (jspFile != null)) {
                // Invoking jspInit
                HttpRequestBase req = new HttpRequestBase();
                HttpResponseBase res = new HttpResponseBase();
                req.setServletPath(jspFile);
                req.setQueryString("jsp_precompile=true");
                servlet.service(req, res);
            }
            instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                                              servlet);
        } catch (UnavailableException f) {
            instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                                              servlet, f);
            unavailable(f);
            throw f;
        } catch (ServletException f) {
            instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                                              servlet, f);
            // If the servlet wanted to be unavailable it would have
            // said so, so do not call unavailable(null).
            throw f;
        } catch (Throwable f) {
            instanceSupport.fireInstanceEvent(InstanceEvent.AFTER_INIT_EVENT,
                                              servlet, f);
            // If the servlet wanted to be unavailable it would have
            // said so, so do not call unavailable(null).
            throw new ServletException
                (sm.getString("standardWrapper.initException", getName()), f);
        }

        // Register our newly initialized instance
        //如果当前 Servlet 实现了 SingleThreadModel 接口,加入对象池中
        singleThreadModel = servlet instanceof SingleThreadModel;
        if (singleThreadModel) {
            if (instancePool == null)
                instancePool = new Stack();
        }
        fireContainerEvent("load", this);
    } finally {
        String log = SystemLogHandler.stopCapture();
        if (log != null && log.length() > 0) {
            if (getServletContext() != null) {
                getServletContext().log(log);
            } else {
                out.println(log);
            }
        }
    }
    //返回创建的 Servlet 对象
    return servlet;
}

StandardWrapper 的基础阀 -- StandardWrapperValve

 public void invoke(Request request, Response response,
                       ValveContext valveContext)
        throws IOException, ServletException {
     // Initialize local variables we may need
     boolean unavailable = false;
     Throwable throwable = null;
     //获取 Wrapper 对象,要通过这个对象实例化 Servlet,获取 Servlet
     StandardWrapper wrapper = (StandardWrapper) getContainer();
     //获取请求体对象
     ServletRequest sreq = request.getRequest();
     //获取返回体对象
     ServletResponse sres = response.getResponse();
     Servlet servlet = null;
     HttpServletRequest hreq = null;
     //强转
     if (sreq instanceof HttpServletRequest)
         hreq = (HttpServletRequest) sreq;
     HttpServletResponse hres = null;
     if (sres instanceof HttpServletResponse)
         hres = (HttpServletResponse) sres;

     // Check for the application being marked unavailable
     if (!((Context) wrapper.getParent()).getAvailable()) {
         hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                        sm.getString("standardContext.isUnavailable"));
         unavailable = true;
     }

     // Check for the servlet being marked unavailable
     if (!unavailable && wrapper.isUnavailable()) {
         log(sm.getString("standardWrapper.isUnavailable",
                          wrapper.getName()));
         if (hres == null) {
             ;       // NOTE - Not much we can do generically
         } else {
             long available = wrapper.getAvailable();
             if ((available > 0L) && (available < Long.MAX_VALUE))
                 hres.setDateHeader("Retry-After", available);
             hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                            sm.getString("standardWrapper.isUnavailable",
                                         wrapper.getName()));
         }
         unavailable = true;
     }

     // Allocate a servlet instance to process this request
     try {
         if (!unavailable) {
             //调用 Wrapper 的 allocate 方法创建并获取 Servlet 对象
             servlet = wrapper.allocate();
         }
     } catch (ServletException e) {
         log(sm.getString("standardWrapper.allocateException",
                          wrapper.getName()), e);
         throwable = e;
         exception(request, response, e);
         servlet = null;
     } catch (Throwable e) {
         log(sm.getString("standardWrapper.allocateException",
                          wrapper.getName()), e);
         throwable = e;
         exception(request, response, e);
         servlet = null;
     }

     // Acknowlege the request
     try {
         //握手,返回Ack
         response.sendAcknowledgement();
     } catch (IOException e) {
         sreq.removeAttribute(Globals.JSP_FILE_ATTR);
         log(sm.getString("standardWrapper.acknowledgeException",
                          wrapper.getName()), e);
         throwable = e;
         exception(request, response, e);
     } catch (Throwable e) {
         log(sm.getString("standardWrapper.acknowledgeException",
                          wrapper.getName()), e);
         throwable = e;
         exception(request, response, e);
         servlet = null;
     }

     // Create the filter chain for this request
     //创建请求过滤链
     ApplicationFilterChain filterChain =
         createFilterChain(request, servlet);

     // Call the filter chain for this request
     // NOTE: This also calls the servlet's service() method
     try {
         String jspFile = wrapper.getJspFile();
         if (jspFile != null)
             sreq.setAttribute(Globals.JSP_FILE_ATTR, jspFile);
         else
             sreq.removeAttribute(Globals.JSP_FILE_ATTR);
         if ((servlet != null) && (filterChain != null)) {
             //Servlet 的 service 方法在这调用
             //这里调用的是 ApplicationFilterChain 的 doFilter 方法
             filterChain.doFilter(sreq, sres);
         }
         sreq.removeAttribute(Globals.JSP_FILE_ATTR);
     } catch (IOException e) {
         sreq.removeAttribute(Globals.JSP_FILE_ATTR);
         log(sm.getString("standardWrapper.serviceException",
                          wrapper.getName()), e);
         throwable = e;
         exception(request, response, e);
     } catch (UnavailableException e) {
         sreq.removeAttribute(Globals.JSP_FILE_ATTR);
         log(sm.getString("standardWrapper.serviceException",
                          wrapper.getName()), e);
         //            throwable = e;
         //            exception(request, response, e);
         wrapper.unavailable(e);
         long available = wrapper.getAvailable();
         if ((available > 0L) && (available < Long.MAX_VALUE))
             hres.setDateHeader("Retry-After", available);
         hres.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                        sm.getString("standardWrapper.isUnavailable",
                                     wrapper.getName()));
         // Do not save exception in 'throwable', because we
         // do not want to do exception(request, response, e) processing
     } catch (ServletException e) {
         sreq.removeAttribute(Globals.JSP_FILE_ATTR);
         log(sm.getString("standardWrapper.serviceException",
                          wrapper.getName()), e);
         throwable = e;
         exception(request, response, e);
     } catch (Throwable e) {
         sreq.removeAttribute(Globals.JSP_FILE_ATTR);
         log(sm.getString("standardWrapper.serviceException",
                          wrapper.getName()), e);
         throwable = e;
         exception(request, response, e);
     }

     // Release the filter chain (if any) for this request
     try {
         if (filterChain != null)
             filterChain.release();
     } catch (Throwable e) {
         log(sm.getString("standardWrapper.releaseFilters",
                          wrapper.getName()), e);
         if (throwable == null) {
             throwable = e;
             exception(request, response, e);
         }
     }

     // Deallocate the allocated servlet instance
     try {
         if (servlet != null) {
             //释放 Servlet 对象
             wrapper.deallocate(servlet);
         }
     } catch (Throwable e) {
         log(sm.getString("standardWrapper.deallocateException",
                          wrapper.getName()), e);
         if (throwable == null) {
             throwable = e;
             exception(request, response, e);
         }
     }

     // If this servlet has been marked permanently unavailable,
     // unload it and release this instance
     try {
         if ((servlet != null) &&
             (wrapper.getAvailable() == Long.MAX_VALUE)) {
             wrapper.unload();
         }
     } catch (Throwable e) {
         log(sm.getString("standardWrapper.unloadException",
                          wrapper.getName()), e);
         if (throwable == null) {
             throwable = e;
             exception(request, response, e);
         }
     }

 }

ApplicationFilterChain 的 doFilter 方法

public void doFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {

    if( System.getSecurityManager() != null ) {
        final ServletRequest req = request;
        final ServletResponse res = response;
        try {
            java.security.AccessController.doPrivileged(
                new java.security.PrivilegedExceptionAction()
                {
                    public Object run() throws ServletException, IOException {
                        //这里最重要,会调用Servlet的service方法
                        internalDoFilter(req,res);
                        return null;
                    }
                }
            );
        } catch( PrivilegedActionException pe) {
            Exception e = pe.getException();
            if (e instanceof ServletException)
                throw (ServletException) e;
            else if (e instanceof IOException)
                throw (IOException) e;
            else if (e instanceof RuntimeException)
                throw (RuntimeException) e;
            else
                throw new ServletException(e.getMessage(), e);
        }
    } else {
        internalDoFilter(request,response);
    }
}

ApplicationFilterChain 的 internalDoFilter方法

private void internalDoFilter(ServletRequest request, ServletResponse response)
        throws IOException, ServletException {

    // Construct an iterator the first time this method is called
    if (this.iterator == null)
        this.iterator = filters.iterator();

    // Call the next filter if there is one
    if (this.iterator.hasNext()) {
        ApplicationFilterConfig filterConfig =
            (ApplicationFilterConfig) iterator.next();
        Filter filter = null;
        try {
            filter = filterConfig.getFilter();
            support.fireInstanceEvent(InstanceEvent.BEFORE_FILTER_EVENT,
                                      filter, request, response);
            //调用过滤器的 doFilter 方法
            filter.doFilter(request, response, this);
            support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                      filter, request, response);
        } catch (IOException e) {
            if (filter != null)
                support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                          filter, request, response, e);
            throw e;
        } catch (ServletException e) {
            if (filter != null)
                support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                          filter, request, response, e);
            throw e;
        } catch (RuntimeException e) {
            if (filter != null)
                support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                          filter, request, response, e);
            throw e;
        } catch (Throwable e) {
            if (filter != null)
                support.fireInstanceEvent(InstanceEvent.AFTER_FILTER_EVENT,
                                          filter, request, response, e);
            throw new ServletException
                (sm.getString("filterChain.filter"), e);
        }
        return;
    }

    // We fell off the end of the chain -- call the servlet instance
    try {
        support.fireInstanceEvent(InstanceEvent.BEFORE_SERVICE_EVENT,
                                  servlet, request, response);
        //Servlet 的 service 方法在这调用
        if ((request instanceof HttpServletRequest) &&
            (response instanceof HttpServletResponse)) {
            servlet.service((HttpServletRequest) request,
                            (HttpServletResponse) response);
        } else {
            servlet.service(request, response);
        }
        support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
                                  servlet, request, response);
    } catch (IOException e) {
        support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
                                  servlet, request, response, e);
        throw e;
    } catch (ServletException e) {
        support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
                                  servlet, request, response, e);
        throw e;
    } catch (RuntimeException e) {
        support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
                                  servlet, request, response, e);
        throw e;
    } catch (Throwable e) {
        support.fireInstanceEvent(InstanceEvent.AFTER_SERVICE_EVENT,
                                  servlet, request, response, e);
        throw new ServletException
            (sm.getString("filterChain.servlet"), e);
    }

}

小结

  • Wrapper 中的 allocate 方法会通过反射实例化 Servlet 对象,注意这个方法在基础阀中调用
  • 基础阀会在请求到来后通过最顶层容器通过 invoke 方法来调用,基础阀是pipeline的最后一个valve
  • 基础阀会调用子容器的 pipeline 的 invoke 方法,在最底层容器Wrapper的基础阀中会实例化Servlet对象,并调用 Servlet 的 service 方法