前言

Servlet实际上就是一个java类,只不过可以和浏览器进行一些数据的交换。有Servlet类就有管理Servlet的容器。

Servlet的工作过程大致可以分为以下几个阶段:

  1. 启动Tomcat容器
  2. Web应用初始化
  3. 创建Servlet实例
  4. 初始化Servlet
  5. 执行Servlet的service方法

Tomcat的启动过程

Web应用初始化

初始化工作是由ContextConfig类的configureStart方法完成的,该方法的主要任务是完成web.xml配置文件的解析。下面解析的关键代码:

    Set<WebXml> defaults = new HashSet<WebXml>();
defaults.add(getDefaultWebXmlFragment());
WebXml webXml = createWebXml();
// Parse context level web.xml
InputSource contextWebXml = getContextWebXmlSource();
parseWebXml(contextWebXml, webXml, false);
ServletContext sContext = context.getServletContext();
// Ordering is important here
// Step 1. Identify all the JARs packaged with the application
// If the JARs have a web-fragment.xml it will be parsed at this
// point.
Map<String,WebXml> fragments = processJarsForWebFragments(webXml);
// Step 2. Order the fragments.
Set<WebXml> orderedFragments = null;
orderedFragments =
WebXml.orderWebFragments(webXml, fragments, sContext);
// Step 3. Look for ServletContainerInitializer implementations
if (ok) {
processServletContainerInitializers(context.getServletContext());
}
if (!webXml.isMetadataComplete() || typeInitializerMap.size() > 0) {
// Step 4. Process /WEB-INF/classes for annotations
if (ok) {
// Hack required by Eclipse's "serve modules without
// publishing" feature since this backs WEB-INF/classes by
// multiple locations rather than one.
NamingEnumeration<Binding> listBindings = null;
try {
try {
listBindings = context.getResources().listBindings(
"/WEB-INF/classes");
} catch (NameNotFoundException ignore) {
// Safe to ignore
}
while (listBindings != null &&
listBindings.hasMoreElements()) {
Binding binding = listBindings.nextElement();
if (binding.getObject() instanceof FileDirContext) {
File webInfClassDir = new File(
((FileDirContext) binding.getObject()).getDocBase());
processAnnotationsFile(webInfClassDir, webXml,
webXml.isMetadataComplete());
} else {
String resource =
"/WEB-INF/classes/" + binding.getName();
try {
URL url = sContext.getResource(resource);
processAnnotationsUrl(url, webXml,
webXml.isMetadataComplete());
} catch (MalformedURLException e) {
log.error(sm.getString(
"contextConfig.webinfClassesUrl",
resource), e);
}
}
}
} catch (NamingException e) {
log.error(sm.getString(
"contextConfig.webinfClassesUrl",
"/WEB-INF/classes"), e);
}
}
// Step 5. Process JARs for annotations - only need to process
// those fragments we are going to use
if (ok) {
processAnnotations(
orderedFragments, webXml.isMetadataComplete());
}
// Cache, if used, is no longer required so clear it
javaClassCache.clear();
}
if (!webXml.isMetadataComplete()) {
// Step 6. Merge web-fragment.xml files into the main web.xml
// file.
if (ok) {
ok = webXml.merge(orderedFragments);
}
// Step 7. Apply global defaults
// Have to merge defaults before JSP conversion since defaults
// provide JSP servlet definition.
webXml.merge(defaults);
// Step 8. Convert explicitly mentioned jsps to servlets
if (ok) {
convertJsps(webXml);
}
// Step 9. Apply merged web.xml to Context
if (ok) {
webXml.configureContext(context);
}
} else {
webXml.merge(defaults);
convertJsps(webXml);
webXml.configureContext(context);
}
// Step 9a. Make the merged web.xml available to other
// components, specifically Jasper, to save those components
// from having to re-generate it.
// TODO Use a ServletContainerInitializer for Jasper
String mergedWebXml = webXml.toXml();
sContext.setAttribute(
org.apache.tomcat.util.scan.Constants.MERGED_WEB_XML,
mergedWebXml);
if (context.getLogEffectiveWebXml()) {
log.info("web.xml:\n" + mergedWebXml);
}
// Always need to look for static resources
// Step 10. Look for static resources packaged in JARs
if (ok) {
// Spec does not define an order.
// Use ordered JARs followed by remaining JARs
Set<WebXml> resourceJars = new LinkedHashSet<WebXml>();
if (orderedFragments != null) {
for (WebXml fragment : orderedFragments) {
resourceJars.add(fragment);
}
}
for (WebXml fragment : fragments.values()) {
if (!resourceJars.contains(fragment)) {
resourceJars.add(fragment);
}
}
processResourceJARs(resourceJars);
// See also StandardContext.resourcesStart() for
// WEB-INF/classes/META-INF/resources configuration
}
// Step 11. Apply the ServletContainerInitializer config to the
// context
if (ok) {
for (Map.Entry<ServletContainerInitializer,
Set<Class<?>>> entry :
initializerClassMap.entrySet()) {
if (entry.getValue().isEmpty()) {
context.addServletContainerInitializer(
entry.getKey(), null);
} else {
context.addServletContainerInitializer(
entry.getKey(), entry.getValue());
}
}
}

在代码已经清晰地说明了Web应用的初始化过程,由于在Tomcat7中增加了对注解(annotation)的支持,所以会对Servlet中的注解进行解析。首先查找jar包中的web-fragment.xml,并对其进行解析,接下来将对/WEB-INF/classes目录下的class进行注解的解析。之后,把web-fragment.xml文件合并到web.xml中,被解析后的web.xml文件的配置项将保存到WebXml对象中,然后把WebXml对象中的属性设置到Context容器中,这个过程是由configureContext方法来完成的。下面是其部分源码——完成Servlet的解析:

    for (ServletDef servlet : servlets.values()) {
Wrapper wrapper = context.createWrapper();
// Description is ignored
// Display name is ignored
// Icons are ignored
// jsp-file gets passed to the JSP Servlet as an init-param
if (servlet.getLoadOnStartup() != null) {
wrapper.setLoadOnStartup(servlet.getLoadOnStartup().intValue());
}
if (servlet.getEnabled() != null) {
wrapper.setEnabled(servlet.getEnabled().booleanValue());
}
wrapper.setName(servlet.getServletName());
Map<String,String> params = servlet.getParameterMap();
for (Entry<String, String> entry : params.entrySet()) {
wrapper.addInitParameter(entry.getKey(), entry.getValue());
}
wrapper.setRunAs(servlet.getRunAs());
Set<SecurityRoleRef> roleRefs = servlet.getSecurityRoleRefs();
for (SecurityRoleRef roleRef : roleRefs) {
wrapper.addSecurityReference(
roleRef.getName(), roleRef.getLink());
}
wrapper.setServletClass(servlet.getServletClass());
MultipartDef multipartdef = servlet.getMultipartDef();
if (multipartdef != null) {
if (multipartdef.getMaxFileSize() != null &&
multipartdef.getMaxRequestSize()!= null &&
multipartdef.getFileSizeThreshold() != null) {
wrapper.setMultipartConfigElement(new MultipartConfigElement(
multipartdef.getLocation(),
Long.parseLong(multipartdef.getMaxFileSize()),
Long.parseLong(multipartdef.getMaxRequestSize()),
Integer.parseInt(
multipartdef.getFileSizeThreshold())));
} else {
wrapper.setMultipartConfigElement(new MultipartConfigElement(
multipartdef.getLocation()));
}
}
if (servlet.getAsyncSupported() != null) {
wrapper.setAsyncSupported(
servlet.getAsyncSupported().booleanValue());
}
wrapper.setOverridable(servlet.isOverridable());
context.addChild(wrapper);
}

这段代码说明了把Servlet包装成StandardWrapper的过程,并把这个Wrapper实例设置到Context容器中。之所以要把Servlet封装成一个Wrapper,主要为了解耦,因为Wrapper是一个容器而Servlet是一个具体的类。

到目前为止,已经完成了web应用的初始化,其中将web.xml进行了解析并完成了注解的解析,还把配置的Servlet类封装成了一个Wrapper容器,接下来就是根据这个Wrapper创建Servlet实例了。

创建Servlet实例

创建Servlet实例要回到我们分析Wrapper容器中提到的loadServlet方法了,这个方法是由Wrapper容器的标准实现类StandardWrapper完成的,通过loadServlet方法获取servletClass,然后把这个servletClass交给实例管理器(InstanceManager)完成servletClass.class的对象创建。

初始化Servlet

初始化的工作是由StandardWrapper的initServlet方法完成的,这个方法主要就是调用Servlet的init方法,然后把包装了StandardWrapper的StandardWrapperFacade交给Servlet。下面是这个方法的源码:

    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);
}
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);
}
}

这样就完成了Servlet的初始化,下面就是执行Servlet的service方法了。

执行service方法

在分析Wrapper方法中提到,StandardWrapper会调用allocate方法从实例池栈中弹出一个Servlet处理请求,这个Servlet实例在完成初始化后,并经过一系列过滤器的过滤后就到达Servlet实例的service方法,这个过程就是过滤器执行的过程。这样Servlet实例就顺利调用到了service方法,之后发生的过程就是我们熟悉的request获取参数并用response进行响应的过程了。


原文博主地址:rhwayfunn

Tomcat Servlet工作原理的更多相关文章

  1. HTTP协议 Servlet入门 Servlet工作原理和生命周期 Servlet细节 ServletConfig对象

    1 HTTP协议特点   1)客户端->服务端(请求request)有三部份     a)请求行--请求行用于描述客户端的请求方式.请求的资源名称,以及使用的HTTP协议版本号 请求行中的GET ...

  2. Servlet工作原理

    Servlet生命周期分为三个阶段: 1,初始化阶段  调用init()方法 2,响应客户请求阶段 调用service()方法 3,终止阶段 调用destroy()方法 Servlet初始化阶段: 在 ...

  3. Servlet工作原理分析

    最近在看<Java Web技术内幕>的Servlet工作原理,有点深奥和难以理解,于是乎,想通过一个简单的Demo将书上的思路理一遍,对Servlet有个更透彻更深的了解. Servlet ...

  4. 你还记得 Tomcat 的工作原理么

    SpringBoot 就像一条巨蟒,慢慢缠绕着我们,使我们麻痹.不得不承认,使用了 SpringBoot 确实提高了工作效率,但同时也让我们遗忘了很多技能.刚入社会的时候,我还是通过 Tomcat 手 ...

  5. Unit02: Servlet工作原理

    Unit02: Servlet工作原理 点击注册按钮,返回注册信息 package web; import java.io.IOException; import java.io.PrintWrite ...

  6. JSP+JavaBean+Servlet工作原理实例…

    JSP+JavaBean+Servlet工作原理实例讲解 首先,JavaBean和Servlet虽都是Java程序,但是是完全不同的两个概念.引用mz3226960提出的MVC的概念,即M-model ...

  7. 【Tomcat】Servlet 工作原理解析

    Web 技术成为当今主流的互联网 Web 应用技术之一,而 Servlet 是 Java Web 技术的核心基础.因而掌握 Servlet 的工作原理是成为一名合格的 Java Web 技术开发人员的 ...

  8. Servlet 工作原理解析

    转自:http://www.ibm.com/developerworks/cn/java/j-lo-servlet/ Web 技术成为当今主流的互联网 Web 应用技术之一,而 Servlet 是 J ...

  9. Servlet工作原理(转)

    Servlet运行在Servlet容器中,由容器负责Servlet实例的查找及创建工作,并按照Servlet规范的规定调用Servlet的一组方法,这些方法也叫生命周期的方法.具体调用过程如下图所示: ...

随机推荐

  1. JavaScript--总结二(流程控制+调试)

    表达式和语句 表达式------ 一个表达式可以产生一个值,有可能式运算,函数调用,有可能是字面量.表达式可以放在任何需要值的地方 语句----- 语句可以理解为一个行为,循环语句和判断语句就是典型的 ...

  2. 第二章 进程同步(二)——> 重点

    2.4  进程同步 2.4.1  进程同步的基本概念 1.  两种形式的制约关系 (1)间接相互制约关系:互斥问题(往往是互斥设备)---是同步的特例 (2)直接相互制约关系:同步问题 注: 互斥问题 ...

  3. Linux 设置静态IP

    由于工作需要,安装一套Linux系统.安装完成后发现这个家伙居然不能上网,然后看了下IP,(命令 ip a)发现是127.0.0.1 下面是我的界面: inet 是127.0.0.1/8 还有6个网卡 ...

  4. .NET Core 处理 WebAPI JSON 返回烦人的null为空

    前言 项目开发中不管是前台还是后台都会遇到烦人的null,数据库表中字段允许空值,则代码实体类中对应的字段类型为可空类型Nullable<>,如int?,DateTime?,null值字段 ...

  5. Java实现RS485串口通信

    前言 前段时间赶项目的过程中,遇到一个调用RS485串口通信的需求,赶完项目因为楼主处理私事,没来得及完成文章的更新,现在终于可以整理一下当时的demo,记录下来. 首先说一下大概需求:这个项目是机器 ...

  6. 【SpringBoot1.x】SpringBoot1.x 安全

    SpringBoot1.x 安全 文章源码 环境搭建 SpringSecurity 是针对 Spring 项目的安全框架,也是 SpringBoot 底层安全模块默认的技术选型.他可以实现强大的 we ...

  7. 【Java】集合框架(List Set Map)

    文章目录 集合框架 List(列表) ArrayList 案例 Set HashSet 案例 iterator(迭代器) Map HashMap 案例 集合总结 参考资料 重新搞一波 复习巩固 简单记 ...

  8. RWCTF2020 DBaaSadge 复现

    数据库题目 2020RWCTF DBaaSadge WP 这是一个很有意思的题目,难到让我绝望,跟着大佬smity的思路跑一下,求大佬抱抱. https://mp.weixin.qq.com/s/jv ...

  9. 利用容器逃逸实现远程登录k8s集群节点

    某天, 某鱼说要吃瞄, 于是...... 李国宝:边缘计算k8s集群SuperEdge初体验 ​ zhuanlan.zhihu.com 图标 照着上一篇文章来说,我这边边缘计算集群有一堆节点. 每个节 ...

  10. Zabbix监控虚拟机服务-告警与自动恢复-模板化

    上一篇文章测试了服务的告警与自动恢复:Zabbix监控虚拟机服务-告警与自动恢复 但是我是直接为某一个主机增加的监控项和触发器, 如果要让某一个自定义的监控项和触发器被很多机器共用,则需要创建模板 1 ...