您的位置:首页 > 运维架构 > Tomcat

how tomcat works 五 servlet容器 上

2017-05-03 20:24 489 查看
servlet容器是用来处理请求servlet资源,并为Webclient填充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类

是用来显示client的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容器的使用,我们放在下一节中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: