深入理解Tomcat系列之五 Context容器和Wrapper容器

                       

前言

Context容器是一个Web项目的代表,主要管理Servlet实例,在Tomcat中Servlet实例是以Wrapper出现的,现在问题是如何才能通过Context容器找到具体的Servlet呢?在解决这个问题之前,Context容器需要先启动,启动的过程就是加载个类资源文件以及打开子容器以及Pipeline管道的过程。启动Context容器后,就可以处理具体的请求了,具体是通过Request对象,从代码清单4-3的Wrapper wrapper = request.getWrapper()就可以看出来。
那么Context调用invoke方法后又发生什么了呢?具体执行的是org.apache.catalina.core.StandardContextValve的invoke方法。相当于进入了Context管道中,要开始通过管道中一个个闸门了。

StandardContext的处理流程可以用下面的图简化:

Context容器处理请求流程简化版

代码清单4-3:

/** * Select the appropriate child Wrapper to process this request, * based on the specified request URI.  If no matching Wrapper can * be found, return an appropriate HTTP error. * * @param request Request to be processed * @param response Response to be produced * * @exception IOException if an input/output error occurred * @exception ServletException if a servlet error occurred */@Overridepublic final void invoke(Request request, Response response)    throws IOException, ServletException {    // Disallow any direct access to resources under WEB-INF or META-INF    MessageBytes requestPathMB = request.getRequestPathMB();    if ((requestPathMB.startsWithIgnoreCase("/META-INF/", 0))            || (requestPathMB.equalsIgnoreCase("/META-INF"))            || (requestPathMB.startsWithIgnoreCase("/WEB-INF/", 0))            || (requestPathMB.equalsIgnoreCase("/WEB-INF"))) {        response.sendError(HttpServletResponse.SC_NOT_FOUND);        return;    }    // Select the Wrapper to be used for this Request    Wrapper wrapper = request.getWrapper();    if (wrapper == null || wrapper.isUnavailable()) {        response.sendError(HttpServletResponse.SC_NOT_FOUND);        return;    }    // Acknowledge the request    try {        response.sendAcknowledgement();    } catch (IOException ioe) {        container.getLogger().error(sm.getString(                "standardContextValve.acknowledgeException"), ioe);        request.setAttribute(RequestDispatcher.ERROR_EXCEPTION, ioe);        response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);        return;    }    if (request.isAsyncSupported()) {        request.setAsyncSupported(wrapper.getPipeline().isAsyncSupported());    }    wrapper.getPipeline().getFirst().invoke(request, response);}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  1. 禁止直接访问WEB-INF或者META-INF目录下的资源
  2. 选择具体的Wrapper处理请求
  3. 返回一个确认响应
  4. 调用Wrapper容器的invoke方法,把处理请求交给StandardWrapperValve处理

Wrapper容器
Wrapper容器负责管理一个Servlet,包括Servlet的装载、初始化、资源回收。Wrapper是最底层的容器,其不能在添加子容器了。Wrapper是一个接口,其标准实现类是StandardWrapper,下面是这两个类的结构:

Wrapper
StandardWrapper

上面的图都只是类的一部分,由于Wrapper与Servlet息息相关,其中的loadServlet方法负责装载Servlet,其源码如下:

代码清单4-4:

/** * Load and initialize an instance of this servlet, if there is not already * at least one initialized instance.  This can be used, for example, to * load servlets that are marked in the deployment descriptor to be loaded * at server startup time. */public synchronized Servlet loadServlet() throws ServletException {    if (unloading) {        throw new ServletException(                sm.getString("standardWrapper.unloading", getName()));    }    // Nothing to do if we already have an instance or an instance pool    if (!singleThreadModel && (instance != null))        return instance;    PrintStream out = System.out;    if (swallowOutput) {        SystemLogHandler.startCapture();    }    Servlet servlet;    try {        long t1=System.currentTimeMillis();        // Complain if no servlet class has been specified        if (servletClass == null) {            unavailable(null);            throw new ServletException                (sm.getString("standardWrapper.notClass", getName()));        }        InstanceManager instanceManager = ((StandardContext)getParent()).getInstanceManager();        try {            servlet = (Servlet) instanceManager.newInstance(servletClass);        } 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);            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<Servlet>();            }            singleThreadModel = true;        }        //init servlet instance        initServlet(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;}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88

该类主要负责初始化一个Servlet实例,并调用该实例的init方法,然后通知感兴趣的事件监听程序。代码清单4-3中调用了Wrapper的invoke方法,这个方法完成什么呢?

代码清单4-5:

/** * Invoke the servlet we are managing, respecting the rules regarding * servlet lifecycle and SingleThreadModel support. * * @param request Request to be processed * @param response Response to be produced * * @exception IOException if an input/output error occurred * @exception ServletException if a servlet error occurred */@Overridepublic 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++;    StandardWrapper wrapper = (StandardWrapper) getContainer();    Servlet servlet = null;    Context context = (Context) wrapper.getParent();    // Check for the application being marked unavailable    if (!context.getState().isAvailable()) {        response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,                       sm.getString("standardContext.isUnavailable"));        unavailable = true;    }    // Check for the servlet being marked unavailable    if (!unavailable && wrapper.isUnavailable()) {        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 = wrapper.allocate();        }    } 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(            Globals.COMET_SUPPORTED_ATTR) == Boolean.TRUE) {        comet = true;        request.setComet(true);    }    MessageBytes requestPathMB = request.getRequestPathMB();    DispatcherType dispatcherType = DispatcherType.REQUEST;    if (request.getDispatcherType()==DispatcherType.ASYNC) dispatcherType = DispatcherType.ASYNC;     request.setAttribute(Globals.DISPATCHER_TYPE_ATTR,dispatcherType);    request.setAttribute(Globals.DISPATCHER_REQUEST_PATH_ATTR,            requestPathMB);    // Create the filter chain for this request    ApplicationFilterFactory factory =        ApplicationFilterFactory.getInstance();    ApplicationFilterChain filterChain =        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()) {                        //TODO SERVLET3 - async                        ((AsyncContextImpl)request.getAsyncContext()).doInternalDispatch();                     } else if (comet) {                        filterChain.doFilterEvent(request.getEvent());                        request.setComet(true);                    } else {                        filterChain.doFilter(request.getRequest(),                                 response.getResponse());                    }                } finally {                    String log = SystemLogHandler.stopCapture();                    if (log != null && log.length() > 0) {                        context.getLogger().info(log);                    }                }            } else {                if (request.isAsyncDispatching()) {                    //TODO SERVLET3 - async                    ((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()) {            // If this is a Comet request, then the same chain will be used for the            // processing of all subsequent events.            filterChain.reuse();        } else {            filterChain.release();        }    }    // Deallocate the allocated servlet instance    try {        if (servlet != null) {            wrapper.deallocate(servlet);        }    } 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);        }    }}
   
   
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  1. 初始化一些本地变量
  2. 判断当前应用是否可用,就是判断是否确实有这个项目
  3. 分配一个Servlet实例
  4. 为请求创建一个过滤器链
  5. 过滤器过滤请求
  6. 关闭过滤器
  7. 重新委派原来委派的Servlet实例
  8. 释放资源

这个方法与上面的loadServlet关系如下:

关系

可以看出在调用loadServlet的allocate方法的时候调用了StandardWrapperValve的invoke方法,在Wrapper容器获得请求后,通过allocate方法从实例池栈中弹出一个servlet实例来处理这个请求,servlet实例被封装成filterChain对象,紧接着通过一系列的过滤器过滤到达servlet.service()方法。这个过程可以如下:

Wrapper容器处理请求过程简化版

           

再分享一下我老师大神的人工智能教程吧。零基础!通俗易懂!风趣幽默!还带黄段子!希望你也加入到我们人工智能的队伍中来!https://blog.csdn.net/jiangjunshow

猜你喜欢

转载自blog.csdn.net/qq_43667968/article/details/86412200
今日推荐