how tomcat works 五 servlet容器 上
servlet容器是用来处理请求servlet资源,并为Web客户端填充response对象的模块。在上一篇文章(也就是书的第四章)我们设计了SimpleContainer类,让他实现Container接口,也基本完成了容器的作用。但是我们得知道在实际的tomcat中有4类容器:
Engine: 表示整个Catalina servlet引擎;
Host: 包含一个或多个Context容器的虚拟主机;
Context:表示一个Web应用程序,一个Context可以包含多个Wrapper
Wrapper: 表示一个独立的Servlet;
UML图如下:
Container容器中包含addChild(Container con),removeChild(Container con),findChild(String name),findChildren()四个方法,方法的作用就不解释了,Wrapper就表示一个独立的Servlet所以,它的addChild方法体为空。
在本节,我们主要展示Wrapper的特性,其他的容器以后再说。
首先我们看看时序图(第一次画 可能在时序图规则上有些问题)
管道任务
这节主要说明档连接器调用了servlet容器的invoke方法后发生的事情。
管道包含了一个servlet容器要执行的任务(和本身的servlet无关,是指额外执行的任务)
一个Wrapper会包含一个管道,而一个管道会包含若干个Valve(阀)
public class SimpleWrapper implements Wrapper, Pipeline {
// the servlet instance
private Servlet instance = null;
private String servletClass;
private Loader loader;
private String name;
private SimplePipeline pipeline = new SimplePipeline(this);
protected Container parent = null;
...
}
public class SimplePipeline implements Pipeline {
public SimplePipeline(Container container) {
setContainer(container);
}
// The basic Valve (if any) associated with this Pipeline.
protected Valve basic = null;
// The Container with which this Pipeline is associated.
protected Container container = null;
// the array of Valves
protected Valve valves[] = new Valve[0]; //所有的阀
}
在阀中,请求会像水一样经过管道中的每一个阀,上图中的阀n会调用我们请求的servlet类。
再调用容器的invoke方法后,会到如下的方法
invokeNext是StandardPipelineValveContext类中的方法(StandardPipelineValveContext是管道的一个内部类,因此可以访问管道的所有成员)
public void invokeNext(Request request, Response response)
throws IOException, ServletException {
int subscript = stage;
stage = stage + 1;
// Invoke the requested Valve for the current request thread
if (subscript < valves.length) {
valves[subscript].invoke(request, response, this);
}
else if ((subscript == valves.length) && (basic != null)) {
basic.invoke(request, response, this);
}
else {
throw new ServletException("No valve");
}
}
} // end of inner class
ok?如水般流过。下面这行代码值得我们仔细看看
valves[subscript].invoke(request, response, this);
也可参阅拙作
<<说说struts2中拦截器的请求流程一(模拟大致流程)>>
http://blog.csdn.net/dlf123321/article/details/40078583
public class HeaderLoggerValve implements Valve, Contained {
protected Container container;
public void invoke(Request request, Response response, ValveContext valveContext)
throws IOException, ServletException {
// Pass this request on to the next valve in our pipeline
valveContext.invokeNext(request, response);
System.out.println("Header Logger Valve");
ServletRequest sreq = request.getRequest();
if (sreq instanceof HttpServletRequest) {
HttpServletRequest hreq = (HttpServletRequest) sreq;
Enumeration<?> headerNames = hreq.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement().toString();
String headerValue = hreq.getHeader(headerName);
System.out.println(headerName + ":" + headerValue);
}
}
else
System.out.println("Not an HTTP Request");
System.out.println("------------------------------------");
}
}
这行代码 valveContext.invokeNext(request, response); 看懂了吧
另一方面 大家应该能看出来,所有的阀都是逆向执行的,每次执行一个阀的时候,阀都会先传递给后面的阀,等到后面的执行完毕后,才执行自己的业务逻辑。
当通道里的处了最后一个基础阀外,都走了一遍的时候自然就执行如下代码
basic.invoke(request, response, this);
问题是basic是谁?
这个咱们等会再说。
Pipeline接口
我们在这里使用的是SimplePipeline,它实现了Pipeline的接口。
public interface Pipeline {
public Valve getBasic();
public void setBasic(Valve valve);
public void addValve(Valve valve);
public Valve[] getValves();
public void invoke(Request request, Response response)
throws IOException, ServletException;
public void removeValve(Valve valve);
}
Pipeline里面有个基础阀,是最后调用的,负责处理request对象鱼response对象。因而这里有getset方法
通道里面存放的是阀,所以剩下的方法就不用多说了吧。
另外tomcat4中,还存在一个StandardPipelineValveContext,上面已经说它是SimplePipeline的一个内部类。
Valve接口
public interface Valve {
public String getInfo();
public void invoke(Request request, Response response,ValveContext context) throws IOException, ServletException;
}
很清晰,不是吗?
Contained接口
public interface Contained {
public Container getContainer();
public void setContainer(Container container);
}
实现这个接口的类,至多与一个servlet容器相关联。
Wrapper接口
首先我们在文章开始的时候就说了,Wrapper包装的是一个最基础的servlet
这个接口里面有两个方法比较重要
public javax.servlet.Servlet allocate() throws javax.servlet.ServletException;
public void load() throws javax.servlet.ServletException;
allocate会分配一个已经初始化的servlet实例,
load会载入它。
Wrapper应用程序
类图如下:
SimpleLoader类
public class SimpleLoader implements Loader {
public static final String WEB_ROOT =
System.getProperty("user.dir") + File.separator + "webroot";
ClassLoader classLoader = null;
Container container = null;
public SimpleLoader() {
try {
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(WEB_ROOT);
String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
urls[0] = new URL(null, repository, streamHandler);
classLoader = new URLClassLoader(urls);
}
catch (IOException e) {
System.out.println(e.toString() );
}
}
}
构造函数会初始会类加载器,供SimpleWrapper使用
SimpleWrapper类
该类实现了 org.apache.catalina.Wrapper 接口并且实现了 allocate 方法和load 方法。
器构造函数如下
public SimpleWrapper() {
pipeline.setBasic(new SimpleWrapperValve());
}
上文我们曾问过,basic从哪里来,从这里来!
SimpleWrapperValve类
通过pipeline.setBasic(new SimpleWrapperValve())我们知道SimpleWrapperValve类是一个基础阀,用来处理传递Servlet的参数,如下:
public void invoke(Request request, Response response, ValveContext valveContext)
throws IOException, ServletException {
SimpleWrapper wrapper = (SimpleWrapper) getContainer();
ServletRequest sreq = request.getRequest();
ServletResponse sres = response.getResponse();
Servlet servlet = null;
HttpServletRequest hreq = null;
if (sreq instanceof HttpServletRequest)
hreq = (HttpServletRequest) sreq;
HttpServletResponse hres = null;
if (sres instanceof HttpServletResponse)
hres = (HttpServletResponse) sres;
// Allocate a servlet instance to process this request
try {
servlet = wrapper.allocate();
if (hres!=null && hreq!=null) {
servlet.service(hreq, hres);
}
else {
servlet.service(sreq, sres);
}
}
catch (ServletException e) {
}
}
应该没有什么问题。
ClientIPLoggerValve类
是用来显示客户端的ip地址的,代码从略,另外还有HeaderLoggerValve,用来显示http的请求头,代码从略。
Bootstrap1启动类
public final class Bootstrap1 {
@SuppressWarnings("deprecation")
public static void main(String[] args) {
/* call by using http://localhost:8080/ModernServlet,
but could be invoked by any name */
HttpConnector connector = new HttpConnector();
Wrapper wrapper = new SimpleWrapper();
wrapper.setServletClass("ModernServlet");
Loader loader = new SimpleLoader();
Valve valve1 = new HeaderLoggerValve();
Valve valve2 = new ClientIPLoggerValve();
wrapper.setLoader(loader);
((Pipeline) wrapper).addValve(valve1);
((Pipeline) wrapper).addValve(valve2);
connector.setContainer(wrapper);
try {
connector.initialize();
connector.start();
// make the application wait until we press a key.
System.in.read();
}
catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果如下:
看到了,其实在部分里,不管你请求那个服务,返回的结果都一样,为什么?这个还用我来解释吗?
这一章东西比较多,关于context容器的使用,我们放在下一节中。
how tomcat works 五 servlet容器 上的更多相关文章
- how tomcat works 5 servlet容器 下
上一节,我们主要说的是Wrapper容器,这一节我们说Context容器. 再重申一遍,一个Context容器可以包含多个Wrapper容器; 一个Wrapper容器就表示一个独立的servlet. ...
- Tomcat是一个Servlet容器?
"Tomcat是一个Servlet容器",这句话对于2019年的程序员应该是耳熟能详的. 单纯的思考一下这句话,我们可以抽象出来这么一段代码: class Tomcat { Lis ...
- How Tomcat works — 五、tomcat启动(4)
前面摆了三节的姿势,现在终于要看到最终tomcat监听端口,接收请求了. 目录 Connector Http11Protocol JIoEndpoint 总结 在前面的初始化都完成之后,进行Conne ...
- Web服务器(Apache)与Servlet容器(Tomcat)
之前一直比较迷惑Apache与Tomcat的关系,通过查询资料,有所了解,现记录于此. Apache与Tomcat 两者定位:Apache是HTTP Web服务器,Tomcat是Web容器. 有一个非 ...
- httpServletRequest对象、filter、servlet、servlet容器、catalina、tomcat、以及web容器之间的关系
学习servlet的时候经常感到疑惑 HttpServletRequest是服务器创建的?还是servlet容器创建的? 过滤器是服务器创建的?还是servlet容器创建的? serlet容器和tom ...
- Servlet容器container
通俗点说,所谓容器,就是放东西的地方.Servlet容器自然就是放Servlet的地方.J2EE开发,是有分工的.一般的程序员,写得都是应用开发,我们会按照一定的规则,开发我们的系统,比如用Servl ...
- 为什么要有 Servlet ,什么是 Servlet 容器,什么是 Web 容器?
本文已收录至 https://github.com/yessimida/yes ,这里有我的所有文章分类汇总,欢迎 star! 以下代码相信大家都很熟悉,大学时学 Java Web 都写过这样的代码. ...
- tomcat与jboss等容器的区别
1.JBoss 是 J2EE 应用服务器,而 Tomcat 只是一个 Servlet 容器,或者说是一个简单的 J2EE 应用服务器. JBoss 中的 Servlet 容器还是 Tomcat. 与 ...
- springboot(七) 配置嵌入式Servlet容器
github代码地址:https://github.com/showkawa/springBoot_2017/tree/master/spb-demo/spb-brian-query-service ...
随机推荐
- Programming In Scala笔记-第六章、函数式对象
这一章主要是以定义和完善一个有理数类Rational为线索,分析和介绍有关类定义,构造函数,方法重写,变量定义和私有化,以及对操作符的定义等. 一.Rational类定义和构造函数 1.定义一个空类 ...
- Android属性动画完全解析(下),Interpolator和ViewPropertyAnimator的用法
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/44171115 大家好,欢迎继续回到Android属性动画完全解析.在上一篇文章当中 ...
- Cocos2D-ObjC:在RPG游戏中混合Swift代码
我之前写过一个RPG游戏<<熊猫之魂 SoulOfPanda>> 编译器使用的是SpriteBuilder,很好很强大!全部代码都由Objc完成,现在想尝试一下在其中混入Swi ...
- Android对话框Dialog深度剖析
对话框 对话框是提示用户作出决定或输入额外信息的小窗口. 对话框不会填充屏幕,通常用于需要用户采取行动才能继续执行的模式事件. 对话框设计 Dialog 类是对话框的基类,但您应该避免直接实例化 Di ...
- Ajax 异步加载
AJAX (Asynchronous JavaScript and XML,异步的 JavaScript 和 XML).它不是新的编程语言,而是一种使用现有标准的新方法,是在不重新加载整个页面的情况下 ...
- Redis 学习笔记4: Redis 3.2.1 集群搭建
在CenOS 6.7 linux环境下搭建Redis 集群环境 1.下载最新的Redis版本 本人下载的Redis版本是3.2.1版本,下载之后,解压,编译(make): 具体操作可以参考我的博文:R ...
- cocos2dx 3.2之Lua打飞机项目
1 创建lua打飞机项目 cocos new T32Lua -dE:\Installed\cocos2d-x-3.2\cocos2d-x-3.2\projects -l lua 2 ...
- UE4 读取本地图片
参考链接:https://answers.unrealengine.com/questions/235086/texture-2d-shows-wrong-colors-from-jpeg-on-ht ...
- Android简易实战教程--第二十八话《加载大图片》
Android系统以ARGB表示每个像素,所以每个像素占用4个字节,很容易内存溢出.假设手机内存比较小,而要去加载一张像素很高的图片的时候,就会因为内存不足导致崩溃.这种异常是无法捕获的 内存不足并不 ...
- 带你深入理解STL之Deque容器
在介绍STL的deque的容器之前,我们先来总结一下vector和list的优缺点.vector在内存中是分配一段连续的内存空间进行存储,其迭代器采用原生指针即可,因此其支持随机访问和存储,支持下标操 ...