Tomcat Context容器和Wrapper容器
前言
Context容器是一个Web项目的代表,主要管理Servlet实例,在Tomcat中Servlet实例是以Wrapper出现的,现在问题是如何才能通过Context容器找到具体的Servlet呢?在解决这个问题之前,Context容器需要先启动,启动的过程就是加载个类资源文件以及打开子容器以及Pipeline管道的过程。启动Context容器后,就可以处理具体的请求了,具体是通过Request对象。
从Wrapper wrapper = request.getWrapper()就可以看出来。
那么Context调用invoke方法后又发生什么了呢?具体执行的是org.apache.catalina.core.StandardContextValve的invoke方法。相当于进入了Context管道中,要开始通过管道中一个个闸门了。
StandardContext的处理流程可以用下面的图简化:
/**
1. Select the appropriate child Wrapper to process this request,
2. based on the specified request URI. If no matching Wrapper can
3. be found, return an appropriate HTTP error.
4. 5. @param request Request to be processed
6. @param response Response to be produced
7. 8. @exception IOException if an input/output error occurred
9. @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-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);
}
- 禁止直接访问WEB-INF或者META-INF目录下的资源
- 选择具体的Wrapper处理请求
- 返回一个确认响应
- 调用Wrapper容器的invoke方法,把处理请求交给StandardWrapperValve处理
Wrapper容器
Wrapper容器负责管理一个Servlet,包括Servlet的装载、初始化、资源回收。Wrapper是最底层的容器,其不能在添加子容器了。Wrapper是一个接口,其标准实现类是StandardWrapper,下面是这两个类的结构:
由于Wrapper与Servlet息息相关,其中的loadServlet方法负责装载Servlet。
/**
* 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;
}
该类主要负责初始化一个Servlet实例,并调用该实例的init方法,然后通知感兴趣的事件监听程序。
上面代码中的Wrapper的invoke方法,这个方法完成什么呢?
/**
1. Invoke the servlet we are managing, respecting the rules regarding
2. servlet lifecycle and SingleThreadModel support.
3. 4. @param request Request to be processed
5. @param response Response to be produced
6. 7. @exception IOException if an input/output error occurred
8. @exception ServletException if a servlet error occurred
*/
@Override
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++;
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);
}
}
}
- 初始化一些本地变量
- 判断当前应用是否可用,就是判断是否确实有这个项目
- 分配一个Servlet实例
- 为请求创建一个过滤器链
- 过滤器过滤请求
- 关闭过滤器
- 重新委派原来委派的Servlet实例
- 释放资源
这个方法与上面的loadServlet关系如下:
可以看出在调用loadServlet的allocate方法的时候调用了StandardWrapperValve的invoke方法,在Wrapper容器获得请求后,通过allocate方法从实例池栈中弹出一个servlet实例来处理这个请求,servlet实例被封装成filterChain对象,紧接着通过一系列的过滤器过滤到达servlet.service()方法。这个过程可以如下:
原文博主地址:rhwayfunn
Tomcat Context容器和Wrapper容器的更多相关文章
- 深入理解Tomcat系列之五:Context容器和Wrapper容器
前言 Context容器是一个Web项目的代表,主要管理Servlet实例,在Tomcat中Servlet实例是以Wrapper出现的.如今问题是怎样才干通过Context容器找到详细的Servlet ...
- 学习Tomcat(五)之Context和Wrapper容器
前面的文章中,我们介绍了Tomcat的Engine和Host容器,我们知道一个Tomcat最多只有一个Engine容器,一个Engine容器可以包含多个Host容器,请求中的不同的Host对应不用的H ...
- 学习Tomcat(一)之容器概览
Tomcat是Apache软件基金会的一个顶级项目,由Apache.Sun和其它一些公司及个人共同开发,是目前比较流行的Web服务器之一.Tomcat是一个开源的.小型的轻量级应用服务器,具有占用系统 ...
- tomcat源码解读(2)–容器责任链模式的实现
责任链模式:责任链模式可以用在这样的场景,当一个request过来的时候,需要对这个request做一系列的加工,使用责任链模式可以使每个加工组件化,减少耦合.也可以使用在当一个request过来的时 ...
- 学习Tomcat(二)之容器概览
Tomcat容器的Server模块有管理容器的启动和关闭.管理了容器内的服务组件Service.管理了全局JNDI资源的功能,对Tomcat容器的生命周期管理有重要意义.Tomcat的服务组件则是To ...
- docker 安装tomcat容器和mysql容器
1. docker pull mysql:5.6 2.docker run -p 3306:3306 --name mysql -v /data/mysql/conf:/etc/mysql/conf. ...
- 探秘Tomcat——一个简易的Servlet容器
即便再简陋的服务器也是服务器,今天就来循着书本的第二章来看看如何实现一个servlet容器. 背景知识 既然说到servlet容器这个名词,我们首先要了解它到底是什么. servlet 相比你或多或少 ...
- Web容器、Servlet容器、Spring容器、SpringMVC容器之间的关系
以下内容为个人理解,如有误还请留言指出,不胜感激! Web容器 web容器(web服务器)主要有:Apache.IIS.Tomcat.Jetty.JBoss.webLogic等,而Tomcat.Jet ...
- web容器、sevlet容器、spring容器、springmvc容器之间的关系
原文链接:http://www.cnblogs.com/jieerma666/p/10805966.html https://blog.csdn.net/zhanglf02/article/detai ...
随机推荐
- Kubernetes项目简介
Kubernetes项目简介 Kubernetes 是 Google 团队发起的开源项目,它的目标是管理跨多个主机的容器,提供基本的部署,维护以及运用伸缩,主要实现语言为 Go 语言.Kubernet ...
- String -- 从源码剖析String类
几乎所有的 Java 面试都是以 String 开始的,String 源码属于所有源码中最基础.最简单的一个,对 String 源码的理解也反应了你的 Java 基础功底. String 是如何实现的 ...
- 初识 D3.js :打造专属可视化
一.前言 随着现在自定义可视化的需求日益增长,Highcharts.echarts等高度封装的可视化框架已经无法满足用户各种强定制性的可视化需求了,这个时候D3的无限定制的能力就脱颖而出. 如果想要通 ...
- 卷积神经网络学习笔记——Siamese networks(孪生神经网络)
完整代码及其数据,请移步小编的GitHub地址 传送门:请点击我 如果点击有误:https://github.com/LeBron-Jian/DeepLearningNote 在整理这些知识点之前,我 ...
- newbee-mall 开源商城新计划:秒杀功能、优惠券、对接支付宝
新项目是 newbee-mall 的升级版本,暂时就叫它 newbee-mall-plus 吧,第一阶段会开发秒杀功能.优惠券.对接支付宝这些功能,也会慢慢加入 Redis. Elastic Sear ...
- --safe-user-create
此参数如果启用,用户将不能用grant语句创建新用户,除非用户有mysql数据库中user表的insert权限, ./mysqld_safe --safe-user-create & 用-- ...
- 【Samba】共享服务器的搭建和相关权限设置
1.查看防护墙 [root@zhang~ ]# /etc/init.d/iptables status iptables:Firewall is not running. 如果没有关闭的话将他 ...
- 【Oracle】instr()函数详解
1)instr()函数的格式 (俗称:字符查找函数) 格式一:instr( string1, string2 ) / instr(源字符串, 目标字符串) 格式二:instr( strin ...
- 网件wndr4300 ttl连接
路由成砖而还能进入cfe或uboot等情况下,可以通过ttl快速救砖. r4300主板有TTL的接线脚,脚的顺序可以找在OpenWrt的wiki上找到. 如下图4个TTL针在左下角,从下往上分别是GN ...
- zabbix-server安装部署配置
zabbix-server安装部署配置 zabbixLinux安装部署安装脚本 1 一步一步部署 1.1 安装zabbix仓库源 这里安装阿里的zabbix仓库地址 选用zabbix版本3.4 rpm ...