前言

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的处理流程能够用以下的图简化:

代码清单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*/
@Override
public final void invoke(Request request, Response response)throws IOException, ServletException {// Disallow any direct access to resources under WEB-INF or META-INFMessageBytes 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 RequestWrapper wrapper = request.getWrapper();if (wrapper == null || wrapper.isUnavailable()) {response.sendError(HttpServletResponse.SC_NOT_FOUND);return;}// Acknowledge the requesttry {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与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 poolif (!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 specifiedif (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 ClassLoaderthrow 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=36630if(log.isDebugEnabled()) {log.debug(sm.getString("standardWrapper.instantiate", servletClass), e);}// Restore the context ClassLoaderthrow 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 instancesif ((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 instanceinitServlet(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*/
@Override
public final void invoke(Request request, Response response)throws IOException, ServletException {// Initialize local variables we may needboolean 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 unavailableif (!context.getState().isAvailable()) {response.sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE,sm.getString("standardContext.isUnavailable"));unavailable = true;}// Check for the servlet being marked unavailableif (!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 requesttry {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 allocatedboolean 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 requestApplicationFilterFactory factory =ApplicationFilterFactory.getInstance();ApplicationFilterChain filterChain =factory.createFilterChain(request, wrapper, servlet);// Reset comet flag value after creating the filter chainrequest.setComet(false);// Call the filter chain for this request// NOTE: This also calls the servlet's service() methodtry {if ((servlet != null) && (filterChain != null)) {// Swallow output if neededif (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 requestif (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 instancetry {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 instancetry {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()方法。这个过程能够例如以下:

深入理解Tomcat系列之五:Context容器和Wrapper容器相关推荐

  1. 深入理解Tomcat系列之一:系统架构(转)

    前言 Tomcat是Apache基金组织下的开源项目,性质是一个Web服务器.下面这种情况很普遍:在eclipse床架一个web项目并部署到Tomcat中,启动tomcat,在浏览器中输入一个类似ht ...

  2. tomcat lifecyclelistener_大公司程序员带你死磕Tomcat系列(五)——容器

    死磕Tomcat系列(5)--容器 回顾 在死磕Tomcat系列(1)--整体架构中我们简单介绍了容器的概念,并且说了在容器中所有子容器的父接口是Container.在死磕Tomcat系列(2)--E ...

  3. 深入理解Tomcat和Jetty源码之第四篇tomcat系统架构上:容器是如何设计的

    今天来介绍一下Tomcat的容器机制: 1.Tomcat的四种容器 2.容器示意图 3.责任链模式介绍 4.Tomcat如何确定请求是哪个Wrapper处理的 5.Tomcat的Context和ser ...

  4. 深入理解Tomcat和Jetty源码之第二篇servlet规范和servlet容器

    深入理解Tomcat和Jetty源码之第二篇servlet规范和servlet容器 思维导图总览 这篇推送主要讲servlet的规范和什么是servlet容器? 1.先来讲讲servlet规范: 2. ...

  5. PostgreSQL数据库从入门到精通系列之五:深入理解lsn_proc、lsn_commit、lsn、txId、ts_usec

    PostgreSQL数据库从入门到精通系列之五:深入理解lsn_proc.lsn_commit.lsn.txId.ts_usec 一.深入理解lsn_proc 二.深入理解lsn_commit 三.深 ...

  6. Tomcat系列之Java技术详解

    一.概述 1.前言 在前面几篇博客中,我们和大家说了负载均衡器服务器.Web服务器.反向代理服务器.缓存服务器,从这篇博客开始我们和大家说说应用程序服务器,对于上述内容不了解的博友可以去参考一下我们前 ...

  7. 深入理解Spring系列之四:BeanDefinition装载前奏曲

    转载 https://mp.weixin.qq.com/s?__biz=MzI0NjUxNTY5Nw==&mid=2247483835&idx=1&sn=276911368d4 ...

  8. 深入理解 Tomcat(四)Tomcat 类加载器之为何违背双亲委派模型

    这是我们研究Tomcat的第四篇文章,前三篇文章我们搭建了源码框架,了解了tomcat的大致的设计架构, 还写了一个简单的服务器.按照我们最初订的计划,今天,我们要开始研究tomcat的几个主要组件( ...

  9. 【冰极峰教程系列之五】:无hack并支持透明圆角框的全兼容九宫格布局

    原创:冰极峰 转载请注明出处 时间:2009年7月19日 23:33:05 冰极峰教程系列之一:九宫格基本布局 冰极峰教程系列之二:牢不可破的九宫格布局 冰极峰教程系列之三:三层分离的完美九宫格 冰极 ...

最新文章

  1. 工艺技术:14nm与28nm工艺
  2. 线程的生命周期其实没有我们想象的那么简单!!
  3. linux filesystem_如何使用cgdb + qemu调试linux内核模块
  4. 邮件办公系统Exchange
  5. Outlook怎么打印日历 Outlook日历打印教程
  6. python3 unicodeencodeerror_解决 Python3 下 print 时出现 UnicodeEncodeError 的问题
  7. SIM: 基于搜索的超长行为序列上的用户兴趣建模
  8. 太原理工大学ACM队简介(2018版)
  9. (5)二进制文件方式部署Kubernetes高可用集群----------创建kubeconfig文件Token.csv随机数文件
  10. CentOS 7.6 安装 Sentaurus 2017.09 记录
  11. 桌面计算机找不到硬盘,bios找不到硬盘完美解决方法 选择STATConfigur
  12. 2.1.5 物理路径泄漏_最新的泄漏的Windows 8.1 Build显示出继续关注波兰语和改进功能...
  13. REST风格详细介绍
  14. #4【BZOJ5109】[CodePlus 2017]大吉大利,晚上吃鸡!(未完成)
  15. 对面向对象和面向过程的理解
  16. textContent 和 innerText
  17. 计算机硬盘清理,Win7电脑的磁盘满了怎么办?最简单的清理方法教给你!
  18. 制作ubuntu光盘启动盘
  19. 云服务器可以用来做什么?云服务器有什么用途?
  20. 用matlab实现运筹学最短路问题,运筹学最短路问题实验报告

热门文章

  1. MVC3中Ajax.ActionLink用法
  2. IS-IS数据包--包头结构
  3. 阿里面试回答的认真总结
  4. 买茶叶想到的哪个比较便宜 x1/y1 x2/y2 x代表多少钱 y代表 多少克 无聊的试炼...
  5. 磁盘阵列 (RAID)简介
  6. Xshell远程连接CentOS-7版的Linux
  7. 控制层@Value注解取不到值
  8. 使用 luajit的ffi 调用libcurl
  9. 查询SQL Server Index上次Rebuild时间
  10. Centos/Linux桌面偏移