博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
深入理解Tomcat系列之五:Context容器和Wrapper容器
阅读量:5973 次
发布时间:2019-06-19

本文共 15949 字,大约阅读时间需要 53 分钟。

前言

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. 禁止直接訪问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
(); } 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;}

该类主要负责初始化一个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. 分配一个Servlet实例
  4. 为请求创建一个过滤器链
  5. 过滤器过滤请求
  6. 关闭过滤器
  7. 又一次委派原来委派的Servlet实例
  8. 释放资源

这种方法与上面的loadServlet关系例如以下:

关系

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

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

转载地址:http://ljbox.baihongyu.com/

你可能感兴趣的文章
HTTP协议详解
查看>>
自动生成 java 测试 mock 对象框架 DataFactory-01-入门使用教程
查看>>
Go语言开发(十六)、Go语言常用标准库六
查看>>
小梅科普:白帽子-高端信息安全培训
查看>>
JavaScript学习总结(9)——JS常用函数(一)
查看>>
Maven+SpringMVC+MyBatis实现系统(一)
查看>>
易宝典文章——如何在Exchange 2010中使用PowerShell文本文件批量移动邮箱
查看>>
智能dns 根据地区解析
查看>>
VS2012配置Git并连接到osc@git
查看>>
mysql完整备份,超过十天的自动删除
查看>>
索尼高清影视技术学院参观观后感
查看>>
jQuery 文本编辑器插件 HtmlBox 使用
查看>>
生活随笔
查看>>
多台服务器联合工作之samba+wordpress
查看>>
如何优化 Android Studio 启动、编译和运行速度?
查看>>
谈一谈 Gradle 的仓库(Repositories)
查看>>
内核物理地址和虚拟地址之间的静态映射过程
查看>>
【20180205】MySQL 1032和1062跳过错误总结
查看>>
进程管理常用命令
查看>>
『中级篇』如何在mac上通过vagrant安装虚拟机(七)
查看>>