Servlet容器的启动(Tomcat为例)
一.容器简介
在tomcat容器等级中,context容器直接管理servlet在容器中的包装类Wrapper,所以Context容器如何运行将直接影响servlet的工作方式。
tomcat容器模型如下:
一个context对应一个web工程,在tomcat的配置文件server.xml中,可以发现context的配置(在eclipse工程中,可在部署路径的conf文件夹zhoing找到)
Context docBase="/Users/wongrobin/all/projects/tech-test/java-test/.metadata/.plugins/org.eclipse.wst.server.core/tmp0/wtpwebapps/base-webapp" path="/base-webapp" reloadable="true" source="org.eclipse.jst.j2ee.server:base-webapp"/>
二.启动servlet
Tomcat7增加了一个启动类:
org.apache.catalina.startup.Tomcat
创建一个Tomcat的一个实例对象并调用start方法就可以启动tomcat。
还可以通过这个对象来增加和修改tomcat的配置参数,如可以动态增加context,servlet等。
在tomcat7中提供的example中,看是如何添加到context容器中:
Tomcat tomcat = getTomcatInstance();
File appDir = new File(getBuildDirectory(), "webapps/examples");
tomcat.addWebapp(null, "/examples", appDir.getAbsolutePath());
tomcat.start();
ByteChunk res = getUrl("http://localhost:" + getPort() +
"/examples/servlets/servlet/HelloWorldExample");
assertTrue(res.toString().indexOf("<h1>Hello World!</h1>") > 0);
这段代码创建了一个Tomcat实例并新增了一个WEB应用,然后启动Tomcat并调用其中的一个HelloWorldExampleServlet。
Tomcat的addWebap方法的代码如下:
public Context addWebapp(Host host, String url, String path) {
silence(url);
Context ctx = new StandardContext();
ctx.setPath( url );
ctx.setDocBase(path);
if (defaultRealm == null) {
initSimpleAuth();
}
ctx.setRealm(defaultRealm);
ctx.addLifecycleListener(new DefaultWebXmlListener());
ContextConfig ctxCfg = new ContextConfig();
ctx.addLifecycleListener(ctxCfg);
ctxCfg.setDefaultWebXml("org/apache/catalin/startup/NO_DEFAULT_XML");
if (host == null) {
getHost().addChild(ctx);
} else {
host.addChild(ctx);
}
return ctx;
}
一个WEB应用对应一个context容器,也就是servlet运行时的servlet容器。添加一个web应用时将会创建一个StandardContext容器,并且给这个context容器设置必要的参数,url和path分别代表这个应用在tomcat中的访问路径和这个应用实际的物理路径,这两个参数与tomcat配置中的两个参数是一致的。其中一个最重要的一个配置是ContextConfig,这个类会负责整个web应用配置的解析工作。
最后将这个context容器加入到父容器host中。
接下来会调用tomcat的start方法启动tomcat。
Tomcat的启动逻辑是基于观察者模式的,所有的容器都会继承Lifecycle接口,它管理着容器的整个生命周期,所有容器的修改和状态改变都会由它通知已经注册的观察者。
Tomcat启动的时序如下:
当context容器初始状态设置Init时,添加到context容器的listener将会被调用。ContextConfig继承了LifecycleListener接口,它是在调用Tomcat.addWebapp时被加入到StandardContext容器中的。ContextConfig类会负责整个WEB应用的配置文件的解析工作。
ContextConfig的init方法将会主要完成一下工作:
- 创建用于解析XML配置文件的contextDigester对象
- 读取默认的context.xml文件,如果存在则解析它
- 读取默认的Host配置文件,如果存在则解析它
- 读取默认的Context自身的配置文件,如果存在则解析它
- 设置Context的DocBase
ContextConfig的init方法完成后,Context容器会执行startInternal方法,这个方法包括如下几个部分:
- 创建读取资源文件的对象
- 创建ClassLoader对象
- 设置应用的工作目录
- 启动相关的辅助类,如logger,realm,resources等
- 修改启动状态,通知感兴趣的观察者
- 子容器的初始化
- 获取ServletContext并设置必要的参数
- 初始化“load on startuo”的Servlet
三.Web应用的初始化
WEB应用的初始化工作是在ContextConfig的configureStart方法中实现的,应用的初始化工作主要是解析web.xml文件,这个文件是一个WEB应用的入口。
Tomcat首先会找globalWebXml,这个文件的搜索路径是engine的工作目录下的org/apache/catalina/startup/NO-DEFAULT_XML或conf/web.xml。接着会找hostWebXml,这个文件可能会在System.getProperty("catalina.base")/conf/${EngineName}/${HostName}/web.xml.default中。接着寻找应用的配置文件examples/WEB-INF/web.xml,web.xml文件中的各个配置项将会被解析成相应的属性保存在WebXml对象中。接下来会讲WebXml对象中的属性设置到context容器中,这里包括创建servlet对象,filter,listerner等,这些在WebXml的configureContext方法中。
下面是解析servlet的代码对象:
for (ServletDef servlet : servlets.values()) {
Wrapper wrapper = context.createWrapper();
String jspFile = servlet.getJspFile();
if (jspFile != null) {
wrapper.setJspFile(jspFile);
}
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());
}
context.addChild(wrapper);
}
上面的代码将servlet容器包装成context容器中的StandardWrapper。StandardWrapper是tomcat容器中的一部分,它具有容器的特征,而Servlet作为一个独立的web开发标准,不应该强制耦合在tomcat中。
除了将Servlet包装成StandardWrapper并作为子容器添加到Context中外,其他所有的web.xml属性都被解析到Context中。
四.创建Servlet实例
前面完成了servlet的解析工作,并且被包装成了StandardWrapper添加到Context容器中,但是它仍然不能为我们工作,它还没有被实例化。
1.创建
如果Servlet的load-on-startup配置项大于0,那么在Context容器启动时就会被实例化。
前面提到的在解析配置文件时会读取默认的globalWebXml,在conf下的web.xml文件中定义了一些默认的配置项,其中定义了两个Servlet,分别是org.apache.catalina.servlets.DefaultServlet和org.apache.jsper.servlet.JspServelt,它们的load-on-startup分别是1和3,也就是当tomcat启动时这两个servlet就会被启动。
创建Servlet实例的方式是从Wrapper.loadServlet开始的,loadServlet方法要完成的就是获取servletClass,然后把它交给InstanceManager去创建一个基于servletClass.class的对象。如果这个Servlet配置了jsp-file,那么这个servletClass就是在conf/web.xml中定义的org.apache.jasper.servlet.JspServlet。
2.初始化
初始化Servlet在StandardWrapper的initServlet方法中,这个方法很简单,就是调用Servlet的init()方法,同时把包装了StandardWrapper对象的StandardWrapperFacade作为ServletConfig传给Servlet。
如果该Servlet关联的是一个JSP文件,那么前面初始化的就是JspServlet,接下来会模拟一次简单请求,请求调用这个JSP文件,以便编译这个JSP文件为类,并初始化这个类。
这样Servlet对象的初始化就完成了。
3.容器默认Servlet
每个servlet容器都有一个默认的servlet,一般都叫做default。
例如:tomcat中的 DefaultServlet 和 JspServlet (上面的部分)
Servlet容器的启动(Tomcat为例)的更多相关文章
- Servlet容器的启动过程
[http://book.51cto.com/art/201408/448854.htm] Tomcat的启动逻辑是基于观察者模式设计的,所有的容器都会继承Lifecycle接口,它管理着容器的整 ...
- docker 容器操作( 以 tomcat 为例 )
一.容器操作 一个镜像可以启动多个容器.比如一个 tomcat 镜像,可以启动多个tomcat 容器,启动后的这些 tomcat 都是各自独立的 二.步骤 1.搜索镜像 [root@localhost ...
- Docker Compose基本使用-使用Compose启动Tomcat为例
场景 Docker-Compose简介与Ubuntu Server 上安装Compose: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/deta ...
- 容器配置jndi Tomcat为例
context.xml 文件 <Resource name=" password="spsj" type="javax.sql.DataSource&qu ...
- 尚硅谷springboot学习26-嵌入式servlet容器自动配置、启动原理
EmbeddedServletContainerAutoConfiguration:嵌入式的Servlet容器自动配置 @AutoConfigureOrder(Ordered.HIGHEST_PREC ...
- 嵌入式Servlet容器自动配置和启动原理
EmbeddedServletContainerAutoConfiguration:嵌入式的Servlet容器自动配置? @AutoConfigureOrder(Ordered.HIGHEST_PRE ...
- 【串线篇】spring boot嵌入式Servlet容器启动原理;
什么时候创建嵌入式的Servlet容器工厂?什么时候获取嵌入式的Servlet容器并启动Tomcat: 获取嵌入式的Servlet容器工厂: 1).SpringBoot应用启动运行run方法 2).r ...
- 4_8.springboot2.x嵌入式servlet容器启动原理解析
问题描述: 什么时候创建嵌入式的Servlet容器工厂? 什么时候获取嵌入式的Servlet容器并启动Tomcat? *获取嵌入式的Servlet容器工厂: 1).SpringBoot应用启动运行ru ...
- 配置嵌入式Servlet容器
SpringBoot默认是用的是Tomcat作为嵌入式的Servlet容器:问题?1).如何定制和修改Servlet容器的相关配置:1.修改和server有关的配置(ServerProperties) ...
随机推荐
- $q -- AngularJS中的服务
此 承诺/延迟(promise/deferred)实现 的灵感来自于 Kris Kowal's QCommonJS Promise建议文档 将承诺(promise) 作为和 异步执行操作(action ...
- DevExpress TreeList 那些事儿
1:TreeList绑定数据源 当我们给予TreeList 的 parentFieldName 和 KeyFieldName 两个属性之后 会自动的生成树结构. 1 var sql = @" ...
- Python 融于ASP框架
一.ASP的平反 想到ASP 很多人会说 “asp语言很蛋疼,不能面向对象,功能单一,很多东西实现不了” 等等诸如此类. 以上说法都是错误的,其一ASp不是一种语言是 微软用来代替CGI的一种web框 ...
- hdu 4352 XHXJ's LIS 数位DP
数位DP!dp[i][j][k]:第i位数,状态为j,长度为k 代码如下: #include<iostream> #include<stdio.h> #include<a ...
- poj 2975 Nim 博弈论
令ans=a1^a2^...^an,如果需要构造出异或值为0的数, 而且由于只能操作一堆石子,所以对于某堆石子ai,现在对于ans^ai,就是除了ai以外其他的石子 的异或值,如果ans^ai< ...
- mysql使用过程中碰到的问题
start job failed to start mysql ubuntu 原因时我将var整个目录的组权限设置为www-data了, 试了网上的办法都不行(有个妥协方法是重新安装, 但很不好), ...
- Haproxy安装及配置
1.安装 # wget http://haproxy.1wt.eu/download/1.3/src/haproxy-1.3.20.tar.gz # tar zcvf haproxy-1.3.20.t ...
- ios开发--一个苹果证书怎么多次使用——导出p12文件
为什么要导出.p12文件 当我们用大于三个mac设备开发应用时,想要申请新的证书,如果在我们的证书里,包含了3个发布证书,2个开发证书,可以发现再也申请不了开发证书和发布证书了(一般在我们的证书界面中 ...
- JavaWeb项目开发案例精粹-第6章报价管理系统-04Service层
1. package com.sanqing.service; import com.sanqing.dao.DAO; import com.sanqing.po.Customer; /** * 客户 ...
- 也谈SWD接口协议分析
这几日看到坛里有几个关于SWD协议相关的文章,自己也尝试了下,有点体会,也有些疑惑,写出来与大家分享和交流下. 以下我的模拟SWD接口的板子简称为Host,目标MCU(即我要连接的板子)简称为T ...