前言

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. 无延时去斗按键实现方法(不好CPU)

    这一灵感来源于定时器计数的方法,最后可以实现的效果跟咱们电脑键盘按键的效果一样!我先来介绍下基本原理吧! 采用定时器中断的方法,比如定时器终端我们设置为5ms,我们需要按键按下超过40ms时才算有按键 ...

  2. Spring boot JPA读取数据库方法

    方法1: 1 StringBuffer sb = new StringBuffer(300); 2 sb.append("SELECT v.id, v.container_number, v ...

  3. linux hosts_access

    linux hosts_access 概要 tcp连接的访问控制功能,通过libwrap提供,即编译到代码里的访问控制功能 可以通过ldd 命令查看程序是否链接了libwrap库 主要根据程序名称,以 ...

  4. springboot源码解析-管中窥豹系列之项目类型(二)

    一.前言 Springboot源码解析是一件大工程,逐行逐句的去研究代码,会很枯燥,也不容易坚持下去. 我们不追求大而全,而是试着每次去研究一个小知识点,最终聚沙成塔,这就是我们的springboot ...

  5. RMI之由浅入深(一)

    0x01.什么是RMI RMI(Remote Method Invocation)即Java远程方法调用,RMI用于构建分布式应用程序,RMI实现了Java程序之间跨JVM的远程通信.顾名思义,远程方 ...

  6. WPF ToolTip 绑定

    Tool绑定: 绑定到ComboBox的SelectValue <ComboBox Name="cmb_WHSCD" VerticalAlignment="Cent ...

  7. 哎,这让人抠脑壳的 LFU。

    这是why哥的第 83 篇原创文章 让人抠脑壳的 LFU 前几天在某APP看到了这样的一个讨论: 看到一个有点意思的评论: LFU 是真的难,脑壳都给我抠疼了. 如果说 LRU 是 Easy 模式的话 ...

  8. 原生javascript制作省市区三级联动详细教程

    多级联动下拉菜单是前端常见的效果,省市区三级联动又属于其中最典型的案例.多级联动一般都是与数据相关联的,根据数据来生成和修改联动的下拉菜单.完成一个多级联动效果,有助于增强对数据处理的能力. 本实例以 ...

  9. VmwareTools显示灰色无法安装

    VMware不安装VMware Tools无法全屏,然后实机之间不能传输文件等. 安装Vmware Tools显示是灰色的,详细解决方案如下 打开虚拟机设置,CD/DVD 选择ISO映像文件 在Vmw ...

  10. ctfhub技能树—sql注入—Cookie注入

    手注 打开靶机 查看页面信息 查找cookie 测试是否为cookie注入 抓包 尝试注入 成功查询到数据库名 查询表名 查询字段名 查询字段信息 成功拿到flag sqlmap 查询数据库名 pyt ...