Tomcat源码阅读之StandarWrapper源码分析

这几天有点杂事,没有怎么看代码了,今天来把最下层的Container对象看了吧,这里之所以说它是最下层的container对象,是因为它不能在调用addChild方法来添加其他的子container对象了。。。

StandarWrapper,嗯,其实可以将其理解为对servlet对象的包装。。。先来看看简单的继承结构吧:

Tomcat源码阅读之StandarWrapper源码分析


这里的继承体系还算是比较简单吧,首先是继承了ContainerBase,让StandarWrapper有了基本的对Container的管理能力,其实是先了ServletConfig接口,这个接口应该比较熟悉吧,在servlet的初始化的时候,传进去的就是这个参数,另外就是还有实现了Wrapper接口和NotificationEmitter。。。

从前面可以知道,Wrapper对象是在ContextConfig对象中解析web应用程序的配置的时候创建的,它会读取配置文件里面申明的class属性,name,以及一些参数等。。。

这里先来看看Wrapper接口的定义吧:

//wrapper接口的定义,这里可以理解为对servlet的包装
public interface Wrapper extends Container {
	//一些事件的定义,主要是添加和移除map的信息
    public static final String ADD_MAPPING_EVENT = "addMapping";  
    public static final String REMOVE_MAPPING_EVENT = "removeMapping";


    // 返回这个servlet还需要多长时间可以用
    public long getAvailable();


    // 
    public void setAvailable(long available);

    //是否要在加载的时候启动,如果是负的话,那么表示在第一次调用的时候启动
    public int getLoadOnStartup();

    public void setLoadOnStartup(int value);
    public String getRunAs();

    public void setRunAs(String runAs);

    //servlet的class
    public String getServletClass();


    // 设置servlet的class
    public void setServletClass(String servletClass);

    //servlet支持的所有方法
    public String[] getServletMethods() throws ServletException;

    //现在是否不能用
    public boolean isUnavailable();

    //返回servlet对象
    public Servlet getServlet();

    //设置关联的servlet对象
    public void setServlet(Servlet servlet);

    //添加servlet的初始化参数
    public void addInitParameter(String name, String value);

    //添加监听
    public void addInstanceListener(InstanceListener listener);

    //为当前servlet添加map信息
    public void addMapping(String mapping);

    public void addSecurityReference(String name, String link);

    //这里表示分配一个servlet对象,这里会区分当前servlet的环境,如果不是单线程模式的吧,那么所有的都返回同一个对象
    public Servlet allocate() throws ServletException;


    //表示返回一个servlet到pool里面,只有在servlet配置为单线程模式的时候才有用
    public void deallocate(Servlet servlet) throws ServletException;

    //获取一个初始化的参数的值
    public String findInitParameter(String name);


    //返回所有的初始化参数的名字
    public String[] findInitParameters();


    //返回这个servlet所有的map信息
    public String[] findMappings();

    public String findSecurityReference(String name);

    public String[] findSecurityReferences();

    //增加错误计数
    public void incrementErrorCount();

    //加载并初始化servlet对象
    public void load() throws ServletException;

    //移除一个初始化的参数
    public void removeInitParameter(String name);

    //移除listener
    public void removeInstanceListener(InstanceListener listener);

    //移除一个mapping信息
    public void removeMapping(String mapping);

    public void removeSecurityReference(String name);

    public void unavailable(UnavailableException unavailable);

    //这里表示卸载所有的servlet实例
    public void unload() throws ServletException;

    public MultipartConfigElement getMultipartConfigElement();


    public void setMultipartConfigElement(
            MultipartConfigElement multipartConfig);

    //该servlet是否支持异步
    public boolean isAsyncSupported();

    public void setAsyncSupported(boolean asyncSupport);

    //当前关联的servlet是否可用
    public boolean isEnabled();

    public void setEnabled(boolean enabled);

    public void setServletSecurityAnnotationScanRequired(boolean b);

    public void servletSecurityAnnotationScan() throws ServletException;

    public boolean isOverridable();

    public void setOverridable(boolean overridable);
}

嗯,方法的定义虽然还比较的多,但其实都还算是比较简答的,首先是两个事件的定义,分别是添加和移除map的信息,然后就还有一些属性的设置和获取的方法,最后还有比较重要的就是load方法,用于加载servlet对象。。


这里先来看看构造方法和一些重要的属性申明吧:

    protected static final String[] DEFAULT_SERVLET_METHODS = new String[] {
                                                    "GET", "HEAD", "POST" };
    public StandardWrapper() {

        super();
        swValve=new StandardWrapperValve();  //创建pipeline上的basic的valve对象
        pipeline.setBasic(swValve);
        broadcaster = new NotificationBroadcasterSupport();

    }

    protected long available = 0L;  //还需要多少事件当前servlet可以用,如果是0的话,那么表示servlet现在就可以用了


    protected final NotificationBroadcasterSupport broadcaster;


    protected final AtomicInteger countAllocated = new AtomicInteger(0);  //已经分配了servlet对象的数目,单线程模式下


    protected final StandardWrapperFacade facade = new StandardWrapperFacade(this);  //servletConfig对象,用于维护servlet的初始化参数什么的,其实是对standerWrapper进行了包装



    protected volatile Servlet instance = null;  //共享模式下的对象

    protected volatile boolean instanceInitialized = false;  //用于标记servlet对象是否初始化

    protected final InstanceSupport instanceSupport = new InstanceSupport(this);   //用于处理一些实例的事件

    protected int loadOnStartup = -1;  //是否在加载的时候启动servlet,默认不是

    protected final ArrayList<String> mappings = new ArrayList<>();    //这个servlet的所有mapping信息

    protected HashMap<String, String> parameters = new HashMap<>();  //初始化参数

    protected HashMap<String, String> references = new HashMap<>();

    protected String runAs = null;

    protected long sequenceNumber = 0;  //通知的序列号

    protected String servletClass = null;  //servlet对象的class

    protected volatile boolean singleThreadModel = false;   //是否是单线程模型,如果是的话,每一个线程将会独占一个servlet对象

    protected boolean unloading = false;  //当前是否在卸载servlet对象

    protected int maxInstances = 20;  //最大的servlet对象数量

    protected int nInstances = 0;  //当前已经实例化的servlet对象数量(在单线程模式下用)

    protected Stack<Servlet> instancePool = null;   //在单线程模式下,将会维护一个servlet的对象池

    protected long unloadDelay = 2000;

    protected boolean isJspServlet;  //是不是jsp的servlet

    protected ObjectName jspMonitorON;

    protected boolean swallowOutput = false;  //是否要重定向servlet的system.out到log

    // To support jmx attributes
    protected StandardWrapperValve swValve;  //pipeline上面的基本valve对象
    protected long loadTime=0;
    protected int classLoadTime=0;

    protected MultipartConfigElement multipartConfigElement = null;   //multipart的配置

    protected boolean asyncSupported = false;   //是否支持异步

    protected boolean enabled = true;  //是否可用

    protected volatile boolean servletSecurityAnnotationScanRequired = false;  //安全注释扫描

    private boolean overridable = false;

这里构造方法其实主要是在pipeline上面添加一个basic类型的valve对象。。然后对于下面属性的申明,应该注释也说的比较清楚吧。。。


这里再来看看比较重要的load方法吧,看看是如何加载servlet对象的。。。

    //加载并初始化servlet,还要在jmx上面注册
    public synchronized void load() throws ServletException {
        instance = loadServlet();

        if (!instanceInitialized) {
            initServlet(instance);
        }

        // 如果是jsp的servlet的话,那么需要进行jmx上面的注册
        if (isJspServlet) {
            StringBuilder oname = new StringBuilder(getDomain());

            oname.append(":type=JspMonitor");

            oname.append(getWebModuleKeyProperties());

            oname.append(",name=");
            oname.append(getName());

            oname.append(getJ2EEKeyProperties());

            try {
                jspMonitorON = new ObjectName(oname.toString());
                Registry.getRegistry(null, null)
                    .registerComponent(instance, jspMonitorON, null);
            } catch( Exception ex ) {
                log.info("Error registering JSP monitoring with jmx " +
                         instance);
            }
        }
    }

这里好像没啥意思吧,首先是调用loadServlet方法来加载servlet对象,用instance来引用。。然后调用initServlet方法来初始化servlet.好了,那就先来看看loadServlet方法吧:

    // 加载servlet对象
    public synchronized Servlet loadServlet() throws ServletException {

        // Nothing to do if we already have an instance or an instance pool
        if (!singleThreadModel && (instance != null))  //如果不是单线程模式,而且有instanc了,那么直接返回吧
            return instance;

        PrintStream out = System.out;
        if (swallowOutput) {  //是否将servle的system.out输出定向到log
            SystemLogHandler.startCapture();
        }

        Servlet servlet;
        try {
            long t1=System.currentTimeMillis();  //获取当前时间
            // Complain if no servlet class has been specified
            if (servletClass == null) {  //这里都没有class,那么返回错误吧
                unavailable(null);
                throw new ServletException
                    (sm.getString("standardWrapper.notClass", getName()));
            }
            //其实最终还是调用webappclassLoader来载入class,然后再创建对象的
            InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();  //获取所属context的instanceManager对象
            try {
                servlet = (Servlet) instanceManager.newInstance(servletClass);   //调用instanceManager来加载servlet的class,并创建
            } catch (ClassCastException e) {  
                unavailable(null);
                // Restore the context ClassLoader
                throw new ServletException
                    (sm.getString("standardWrapper.notServlet", servletClass), e);
            } catch (Throwable e) {
                e = ExceptionUtils.unwrapInvocationTargetException(e);
                ExceptionUtils.handleThrowable(e);
                unavailable(null);

                // Added extra log statement for Bugzilla 36630:
                // http://issues.apache.org/bugzilla/show_bug.cgi?id=36630
                if(log.isDebugEnabled()) {
                    log.debug(sm.getString("standardWrapper.instantiate", servletClass), e);
                }

                // Restore the context ClassLoader
                throw new ServletException
                    (sm.getString("standardWrapper.instantiate", servletClass), e);
            }

            if (multipartConfigElement == null) {
                MultipartConfig annotation =
                        servlet.getClass().getAnnotation(MultipartConfig.class);  //获取multipart的注释申明
                if (annotation != null) {
                    multipartConfigElement =
                            new MultipartConfigElement(annotation);
                }
            }

            processServletSecurityAnnotation(servlet.getClass());  //处理安全注释

            // Special handling for ContainerServlet instances
            if ((servlet instanceof ContainerServlet) &&
                    (isContainerProvidedServlet(servletClass) ||
                            ((Context) getParent()).getPrivileged() )) {
                ((ContainerServlet) servlet).setWrapper(this);  //这个一般很少
            }

            classLoadTime=(int) (System.currentTimeMillis() -t1);  //加载对象所用的时间

            if (servlet instanceof SingleThreadModel) {  //如果是单线程模式,那么这里可能还要初始化池
                if (instancePool == null) {
                    instancePool = new Stack<>();
                }
                singleThreadModel = true;
            }

            initServlet(servlet);  // 初始化servlet

            fireContainerEvent("load", this);

            loadTime=System.currentTimeMillis() -t1;
        } finally {
            if (swallowOutput) {
                String log = SystemLogHandler.stopCapture();
                if (log != null && log.length() > 0) {
                    if (getServletContext() != null) {
                        getServletContext().log(log);
                    } else {
                        out.println(log);
                    }
                }
            }
        }
        return servlet;

    }

代码应该很简单吧,无非是调用context的instanceManager对象来创建servlet对象,当然这里还要区分servlet是否是单线程模式。。。其他的代码上面注释应该也还算比较清楚吧。。都比较的简单。。。


那么再来看看如何初始化servlet吧:

    //初始化serlvet
    private synchronized void initServlet(Servlet servlet)
            throws ServletException {

        if (instanceInitialized && !singleThreadModel) return;

        // Call the initialization method of this servlet
        try {
            instanceSupport.fireInstanceEvent(InstanceEvent.BEFORE_INIT_EVENT,
                                              servlet);

            if( Globals.IS_SECURITY_ENABLED) {
                boolean success = false;
                try {
                    Object[] args = new Object[] { facade };
                    SecurityUtil.doAsPrivilege("init",
                                               servlet,
                                               classType,
                                               args);
                    success = true;
                } finally {
                    if (!success) {
                        // destroy() will not be called, thus clear the reference now
                        SecurityUtil.remove(servlet);
                    }
                }
            } else {
                servlet.init(facade);  //初始化servlet
            }

            instanceInitialized = true;

            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) {
            ExceptionUtils.handleThrowable(f);
            getServletContext().log("StandardWrapper.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);
        }
    }

这里其实init方法传入的是facade对象,它是一个门面模式吧,其实是对当前standarWrapper对象的一个包装。。。


好了,其实到这里standardWrapper的主要的东西都差不多了。。这里再来看看StandardWrapperValve类型吧,其实最终处理请求还是调用它的invoke方法来处理的。。。

先来看看一些基本的属性的定义:

    private volatile long processingTime;   //请求的处理事件
    private volatile long maxTime;  //处理这里http请求的最大耗费的时间
    private volatile long minTime = Long.MAX_VALUE;   //处理这次http请求最短耗费的时间
    private final AtomicInteger requestCount = new AtomicInteger(0);   //用于请求的计数
    private final AtomicInteger errorCount = new AtomicInteger(0);   //用于记录错误数量

嗯,其实从名字就基本上就能知道这些属性是干嘛的。。。

接下来来看看最重要的invoke方法吧:

  // 在context的pipeline里面会调用wrapper对象的pipeline来处理请求
    public final void invoke(Request request, Response response)
        throws IOException, ServletException {

        // Initialize local variables we may need
        boolean unavailable = false;  //初始化的时候可用
        Throwable throwable = null;
        // This should be a Request attribute...
        long t1=System.currentTimeMillis();   //当前时间
        requestCount.incrementAndGet();  //请求计数加1
        StandardWrapper wrapper = (StandardWrapper) getContainer();  //获取所属的container对象,其实也就是wrapper
        Servlet servlet = null;
        Context context = (Context) wrapper.getParent();  //获取context

        // Check for the application being marked unavailable
        if (!context.getState().isAvailable()) {  //如果context不可用,那么表示当前服务不可用,返回错误就好了
            response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                           sm.getString("standardContext.isUnavailable"));
            unavailable = true;
        }

        // Check for the servlet being marked unavailable
        if (!unavailable && wrapper.isUnavailable()) {  //当前servlet暂时不可用
            container.getLogger().info(sm.getString("standardWrapper.isUnavailable",
                    wrapper.getName()));
            long available = wrapper.getAvailable();  //还要多久可用
            if ((available > 0L) && (available < Long.MAX_VALUE)) {
                response.setDateHeader("Retry-After", available);  //返回重试报文
                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                        sm.getString("standardWrapper.isUnavailable",
                                wrapper.getName()));
            } else if (available == Long.MAX_VALUE) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND,
                        sm.getString("standardWrapper.notFound",
                                wrapper.getName()));
            }
            unavailable = true;
        }

        // Allocate a servlet instance to process this request
        try {
            if (!unavailable) { //当前的servlet可以用
                servlet = wrapper.allocate();  //获取servlet对象
            }
        } catch (UnavailableException e) {
            container.getLogger().error(
                    sm.getString("standardWrapper.allocateException",
                            wrapper.getName()), e);
            long available = wrapper.getAvailable();
            if ((available > 0L) && (available < Long.MAX_VALUE)) {
                response.setDateHeader("Retry-After", available);
                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                           sm.getString("standardWrapper.isUnavailable",
                                        wrapper.getName()));
            } else if (available == Long.MAX_VALUE) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND,
                           sm.getString("standardWrapper.notFound",
                                        wrapper.getName()));
            }
        } catch (ServletException e) {
            container.getLogger().error(sm.getString("standardWrapper.allocateException",
                             wrapper.getName()), StandardWrapper.getRootCause(e));
            throwable = e;
            exception(request, response, e);
        } catch (Throwable e) {
            ExceptionUtils.handleThrowable(e);
            container.getLogger().error(sm.getString("standardWrapper.allocateException",
                             wrapper.getName()), e);
            throwable = e;
            exception(request, response, e);
            servlet = null;
        }

        // Identify if the request is Comet related now that the servlet has been allocated
        boolean comet = false;
        if (servlet instanceof CometProcessor && request.getAttribute(  //看当前servlet是否支持comet
                Globals.COMET_SUPPORTED_ATTR) == Boolean.TRUE) {
            comet = true;
            request.setComet(true);
        }

        MessageBytes requestPathMB = request.getRequestPathMB();  //获取当前请求的path  /examples  啥的
        DispatcherType dispatcherType = DispatcherType.REQUEST;   //设置当前请求的类型
        if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;
        request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);  //设置派遣类型,是否是异步的啥的,或者就是普通的request
        request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,
                requestPathMB);  //设置path属性
        // Create the filter chain for this request
        ApplicationFilterFactory factory =
            ApplicationFilterFactory.getInstance();  //创建filter
        ApplicationFilterChain filterChain = //创建filterChain,先调用filter来处理请求,在调用servlet的servie方法
            factory.createFilterChain(request, wrapper, servlet);

        // Reset comet flag value after creating the filter chain
        request.setComet(false);

        // Call the filter chain for this request
        // NOTE: This also calls the servlet‘s service() method
        try {
            if ((servlet != null) && (filterChain != null)) {
                // Swallow output if needed
                if (context.getSwallowOutput()) {
                    try {
                        SystemLogHandler.startCapture();
                        if (request.isAsyncDispatching()) {  // 如果是异步的执行
                            ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();
                        } else if (comet) {   //如果是comet的话,执行这里
                            filterChain.doFilterEvent(request.getEvent());
                            request.setComet(true);
                        } else {
                            filterChain.doFilter(request.getRequest(),  //一般情况下是坐这里,在filter调用完了之后,会调用servlet的service方法
                                    response.getResponse());
                        }
                    } finally {
                        String log = SystemLogHandler.stopCapture();
                        if (log != null && log.length() > 0) {
                            context.getLogger().info(log);
                        }
                    }
                } else {
                    if (request.isAsyncDispatching()) {
                        ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();
                    } else if (comet) {
                        request.setComet(true);
                        filterChain.doFilterEvent(request.getEvent());
                    } else {
                        filterChain.doFilter
                            (request.getRequest(), response.getResponse());
                    }
                }

            }
        } catch (ClientAbortException e) {
            throwable = e;
            exception(request, response, e);
        } catch (IOException e) {
            container.getLogger().error(sm.getString(
                    "standardWrapper.serviceException", wrapper.getName(),
                    context.getName()), e);
            throwable = e;
            exception(request, response, e);
        } catch (UnavailableException e) {
            container.getLogger().error(sm.getString(
                    "standardWrapper.serviceException", wrapper.getName(),
                    context.getName()), e);
            //            throwable = e;
            //            exception(request, response, e);
            wrapper.unavailable(e);
            long available = wrapper.getAvailable();
            if ((available > 0L) && (available < Long.MAX_VALUE)) {
                response.setDateHeader("Retry-After", available);
                response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,
                           sm.getString("standardWrapper.isUnavailable",
                                        wrapper.getName()));
            } else if (available == Long.MAX_VALUE) {
                response.sendError(HttpServletResponse.SC_NOT_FOUND,
                            sm.getString("standardWrapper.notFound",
                                        wrapper.getName()));
            }
            // Do not save exception in ‘throwable‘, because we
            // do not want to do exception(request, response, e) processing
        } catch (ServletException e) {
            Throwable rootCause = StandardWrapper.getRootCause(e);
            if (!(rootCause instanceof ClientAbortException)) {
                container.getLogger().error(sm.getString(
                        "standardWrapper.serviceExceptionRoot",
                        wrapper.getName(), context.getName(), e.getMessage()),
                        rootCause);
            }
            throwable = e;
            exception(request, response, e);
        } catch (Throwable e) {
            ExceptionUtils.handleThrowable(e);
            container.getLogger().error(sm.getString(
                    "standardWrapper.serviceException", wrapper.getName(),
                    context.getName()), e);
            throwable = e;
            exception(request, response, e);
        }

        // Release the filter chain (if any) for this request
        if (filterChain != null) {
            if (request.isComet()) {   //如果是comet类型的话,
                // If this is a Comet request, then the same chain will be used for the
                // processing of all subsequent events.
                filterChain.reuse();  //那么可以重用这个filter链
            } else {
                filterChain.release();  //如果不是comet的话,那么可以释放了
            }
        }

        // Deallocate the allocated servlet instance
        try {
            if (servlet != null) {
                wrapper.deallocate(servlet);  // 相当于释放servlet,只有在配置servlet为单线程的时候才有用,将会返还到pool
            }
        } catch (Throwable e) {
            ExceptionUtils.handleThrowable(e);
            container.getLogger().error(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) {
            ExceptionUtils.handleThrowable(e);
            container.getLogger().error(sm.getString("standardWrapper.unloadException",
                             wrapper.getName()), e);
            if (throwable == null) {
                throwable = e;
                exception(request, response, e);
            }
        }
        long t2=System.currentTimeMillis();

        long time=t2-t1;
        processingTime += time;  //处理耗费时间
        if( time > maxTime) maxTime=time;  //设置最大处理时间
        if( time < minTime) minTime=time;  //设置最小处理时间

    }

代码够长吧,不过注释应该也交代清楚了。。首先调用allocate方法来获取一个servlet对象,然后在最终调用servlet对象的service方法来处理之前还需要先ApplicationFilterChain对象来包装filter对象,然后再调用doFilter方法,先执行filter,然后在里面才回调用servlet来处理。。


这里发现,其实每次都要构造新的ApplicationFilterChain对象,并没有做缓存。。是不是不太好呢。。?起码在jetty中这部分是做了缓存的。。。


好了,到这里standarwrapper就差不多了吧。。写的比较粗糙。。

Tomcat源码阅读之StandarWrapper源码分析,布布扣,bubuko.com

Tomcat源码阅读之StandarWrapper源码分析

上一篇:The specified executable is not a validapplication for this OS platform.


下一篇:在iOS上实现一个简单的日历控件