本文介绍servlet 容器的基本原理。现有两个servlet容器,第一个很简单,第二个则是根据第一个写出。为了使第一个容器尽量简单,所以没有做得很完整。复杂一些的 servlet 容器 (包括 tomcat 4 和 5) 可以参考其他资料。
两个servlet容器都处理简单的servlet及staticresource。您可以使用 webroot/ 目录下的 primitiveservlet 来测试它。复杂一些的 servlet会超出这些容器的容量,创建复杂servlet容器不是本文的内容,所以在这里就不详细介绍了。
两个应用程序的类都封装在ex02.pyrmont 包下。在理解应用程序如何运作之前,您必须熟悉 javax.servlet.servlet 接口。首先就来介绍这个接口。随后,就介绍 servlet 容器服务servlet 的具体内容。
javax.servlet.servlet 接口
servlet编程,需要引用以下两个类和接口:javax.servlet 和 javax.servlet.http,在这些类和接口中,javax.servlet.servlet接口尤为重要。所有的 servlet 必须实现这个接口或继承已实现这个接口的类。
servlet 接口有五个方法,如下:
| public void init(servletconfig config) throws servletexception public void service(servletrequest request, servletresponse response) throws servletexception, java.io.ioexception public void destroy() public servletconfig getservletconfig() public java.lang.string getservletinfo() |
init、service和 destroy 方法是 servlet 生命周期的方法。当 servlet 类实例化后,容器加载 init,以通知 servlet 它已进入服务行列。init 方法必须被加载,servelt 才能接收和请求。如果要载入数据库驱动程序、初始化一些值等等,程序员可以重写这个方法。在其他情况下,这个方法一般为空。
service 方法由 servlet 容器调用,以允许 servlet 响应一个请求。servlet 容器传递 javax.servlet.servletrequest 对象和 javax.servlet.servletresponse 对象。servletrequest 对象包含客户端 http 请求信息,servletresponse 则封装servlet 响应。通过这两个对象,您可以写一些需要 servlet怎样服务和客户怎样请求的代码。
从service中删除servlet实例之前,容器调用destroy方法。在servlet容器关闭或servlet 容器需要更多的内存时,就调用它。这个方法只有在servlet 的service 方法内的所有线程都退出的时候,或在超时的时候才会被调用。在 servlet 容器调用 destroy方法之后,它将不再调用 servlet的 service方法。
destroy 方法给了servlet机会,来清除所有空闲资源(比如:内存,文件处理和线程),以确保在内存的持续状态和 servlet的当前状态是同步的。listing 2.1 包含了primitiveservlet 的代码,此servlet非常简单,可以用它来测试本文的servlet容器应用程序。
primitiveservlet 类实现了javax.servlet.servlet 并提供了五个servlet方法的接口。它做的事情也很简单:每次调用 init、service 或 destroy方法的时候,servlet就向控制口写入方法名。service 方法也从servletresponsec对象中获得java.io.printwriter 对象,并发送字符串到浏览器。
listing 2.1.primitiveservlet.java
| import javax.servlet.*; import java.io.ioexception; import java.io.printwriter; public class primitiveservlet implements servlet { public void init(servletconfig config) throws servletexception { system.out.println("init"); } public void service(servletrequest request, servletresponse response) throws servletexception, ioexception { system.out.println("from service"); printwriter out = response.getwriter(); out.println("hello.roses are red."); out.print("violets are blue."); } public void destroy() { system.out.println("destroy"); } public string getservletinfo() { return null; } public servletconfig getservletconfig() { return null; } } |
现在,我们从 servlet容器的角度来看看 servlet 编程。一个功能健全的 servlet容器对于每个 servlet 的 http请求会完成以下事情:
1、当 servlet 第一次被调用的时候,加载了 servlet类并调用它的init方法(仅调用一次)
2、响应每次请求的时候 ,构建一个javax.servlet.servletrequest 和 javax.servlet.servletresponse实例。
3 、激活 servlet 的 service 方法,传递 servletrequest 和 servletresponse 对象。
4、当servlet 类关闭的时候,调用 servlet 的destroy 方法,并卸载 servlet 类。
发生在 servlet 容器内部的事就复杂多了。只是这个简单的 servlet 容器的功能不很健全,所以,这它只能运行非常简单的servelt ,并不能调用 servlet 的 init 和destroy 方法。然而,它也执行了以下动作:
1、等待 http 请求。
2、构建 servletrequest 和 servletresponse 对象
3、如果请求的是一个staticresource,就会激活staticresourceprocessor实例的 process方法,传递servletrequest 和 servletresponse 对象。
4、如果请求的是一个servlet ,载入该类,并激活它的service 方法,传递servletrequest 和servletresponse 对象。注意:在这个servlet 容器,每当 servlet被请求的时候该类就被载入。
在第一个应用程序中,servlet容器由六个类组成 。
httpserver1
request
response
staticresourceprocessor
servletprocessor1
constants
这个程序的进入口(静态 main 方法)是httpserver 类。这个方法创建了httpserver实例,并调用它的await方法等待 http 请示,然后创建一个 request 对象和 response对象,根据请求是否是staticresource还是 servlet 来分派它们到 staticresourceprocessor实例或servletprocessor实例。
constants 类包含 static find web_root,它是从其他类引用的。 web_root 指明 primitiveservlet 位置 和容器服务的staticresource。
httpserver1 实例等待 http 请求,直到它收到一个 shutdown 命令。发布 shutdown命令和前文是一样的。
httpserver1 类
此应用程序内的 httpserver1类 与前文简单的 web 服务器应用程序中的httpserver 十分相似。但是,此应用程序内的 httpserver1 能服务静态资源和 servlet。如果要请求一个静态资源,请输入以下 url:
http://machinename:port/staticresource 。如果要请求一个 servlet,请输入以下 url:
| http://machinename:port/servlet/servletclass |
如果您想在本地浏览器请求一个 primitiveservle servlet ,请输入以下 url:
| http://localhost:8080/servlet/primitiveservlet |
下面listing 2.2类的await方法,是等待一个http请求,直到一个发布shutdown命令。与前文的await 方法相似。
listing 2.2. httpserver1 类的 await 方法
| public void await() { serversocket serversocket = null; int port = 8080; try { serversocket = new serversocket(port, 1, inetaddress.getbyname("127.0.0.1")); } catch (ioexception e) { e.printstacktrace(); system.exit(1); } // 循环,等待一个请求 while (!shutdown) { socket socket = null; inputstream input = null; outputstream output = null; try { socket = serversocket.accept(); input = socket.getinputstream(); output = socket.getoutputstream(); // 创建请求对象并解析 request request = new request(input); request.parse(); // 创建回应对象 response response = new response(output); response.setrequest(request); //检测是否是 servlet 或静态资源的请求 //servlet 请求以 "/servlet/" 开始 if (request.geturi().startswith("/servlet/")) { servletprocessor1 processor = new servletprocessor1(); processor.process(request, response); } else { staticresourceprocessor processor = new staticresourceprocessor(); processor.process(request, response); } // 关闭socket socket.close(); //检测是否前面的 uri 是一个 shutdown 命令 shutdown = request.geturi().equals(shutdown_command); } catch (exception e) { e.printstacktrace(); system.exit(1); } } } |
此文 await 方法和前文的不同点就是,此文的 await 方法中的请求调度到staticresourceprocessor 或 ervletprocessor 。
如果 uri中包含 "/servlet/.",请求推进到后面,否则,请求传递到 staticresourceprocessor 实例。 request 类
servlet service 方法接受 servlet 容器的 javax.servlet.servletrequest 和javax.servlet.servletresponse 实例。因此,容器必须构建 servletrequest和servletresponse对象,然后将其传递到正在被服务的service 方法。
ex02.pyrmont.request 类代表一个请求对象传递到 service 方法。同样地,它必须实现 javax.servlet.servletrequest 接口。这个类必须提供接口内所有方法的实现。这里尽量简化它并只实现几个方法。要编译 request 类的话,必须提供这些方法的空实现。再来看看 request 类,内部所有需要返回一个对象实例都返回null,如下:
| public object getattribute(string attribute) { return null; } public enumeration getattributenames() { return null; } public string getrealpath(string path) { return null; } |
另外,request 类仍需有前文有介绍的 parse 和geturi 方法。
response 类
response 类实现 javax.servlet.servletresponse,同样,该类也必须提供接口内所有方法的实现。类似于 request 类,除 getwriter 方法外,其他方法的实现都为空。
| public printwriter getwriter() { // autoflush is true, println() will flush, // but print() will not. writer = new printwriter(output, true); return writer; } |
printwriter 类构建器的第二个参数是一个代表是否启用 autoflush 布尔值 ,如果为真,所有调用println 方法都 flush 输出。而 print 调用则不 flush 输出。因此,如果在servelt 的service 方法的最后一行调用 print方法,则从浏览器上看不到此输出 。这个不完整性在后面的应用程序内会有调整。
response 类也包含有前文中介绍的 sendstaticresource方法。
staticresourceprocessor 类
staticresourceprocessor 类用于服务静态资源的请求。它唯一的方法是 process。
listing 2.3.staticresourceprocessor 类的 process方法。
| public void process(request request, response response) { try { response.sendstaticresource(); } catch (ioexception e) { e.printstacktrace(); } } |
process 方法接受两个参数:request 和 response 实例。它仅仅是调用 response 类的sendstaticresource 方法。
闽公网安备 35060202000074号