- 浏览: 19991 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
yangqianyu222:
yaobiao753 写道有ModernServlet的源代码 ...
译How Tomcat Works(第一章) -
yangqianyu222:
gosin 写道这么厉害啊。
你把代码跑起来没有?
哈哈,没 ...
译How Tomcat Works(第四章) -
yaobiao753:
有ModernServlet的源代码么?我下的怎么没有这个了? ...
译How Tomcat Works(第一章) -
yaobiao753:
翻译的不错~刚看了英文的前四章。看看你的翻译复习一下,呵呵
译How Tomcat Works(第一章) -
gosin:
这么厉害啊。
你把代码跑起来没有?
译How Tomcat Works(第四章)
第二章 简单的 Servlet Container
概览
这章将通过两个应用程序介绍如何开发自己的servlet container。第一个应用程序设计的尽可能简单以便更容易理解servlet container是如何工作的。第二个servlet container, 稍微复杂些。
注:每章中的servlet container 应用程序都是在前一章的基础上逐渐改进, 直到第十七章一个功能完善的 Tomcat servlet 被构建。
servlet containers既能处理简单的Servlet也能处理静态资源。你可以使用PrimitiveServlet类测试此 container。PrimitiveServlet类的代码将在清单2.1中给出,它的class文件在 webroot下可以找到。处理更复杂的servlet超出这个 container的能力范围,但是在下一章你将学会如何创建更完善的servlet container 。
清单 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;
}
}
该类在ex02.pyrmont 包下。要理解该应用程序是如何工作的,你需要了解javax.servlet.Servlet 接口,该接口在本章的第一部分讲解。之后你将学习到servlet container 必须为HTTP 请求一个Servlet后做些什么。
javax.servlet.Servlet 接口
使用javax.servlet 和 javax.servlet.http 包下的类或接口才能进行Servlet 编程。其中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的生命周期方法。init方法在Servlet实例化后由 Servlet Container 调用。init 方法仅被servlet container调用一次,表明该Servlet已经处于服务中了。init方法必须在servlet接收请求之前被成功调用。Servlet程序员可以重写该方法,在该方法中写入仅需要运行一次的初始化代码,像是加载数据库驱动,初始化变量值等等。通常情况下该方法是空的。
只要接收到对servlet的请求, servlet container就会调用该servlet 的service方法(参数一个javax.servlet.ServletRequest和javax.servlet.ServletResponse 对象)。ServletRequest 对象包含客户端的HTTP 请求信息和装有servlet响应信息的ServletResponse 对象。在servlet 的生命周期中Service方法会被调用很多次。
从服务中移除该servlet对象前,servlet container会调用destroy方法。这通常是在servlet container被关闭或者需要释放内存时发生。这个方法仅当servlet中所有线程已经退出或者超时时被调用。servlet container 调用destroy 方法后,在同一servlet中将不会再调用service方法。destroy 方法可以清理被占用的资源,如内存,文件句柄和线程,并确认内存中的持久化状态与此时的servlet状态是一致的。
清单 2.1展示了一个叫 PrimitiveServlet的Servlet代码,这是一个非常简单的Servlet,你可以用它来测试本章的servlet container应用程序。PrimitiveServlet类实现了javax.servlet.Servlet 接口(所有的servlet都必须实现) 并实现了Servlet的五个方法。PrimitiveServlet 做的非常简单。每次 init, service, 或者destroy 方法被调用,Servlet就会往控制台打印该方法的名字。另外,service 方法从ServletResponse对象中获得java.io.PrintWriter 对象并发送字符串到浏览器。
应用程序 1
现在,让我们从servlet container来观察一下servlet 程序。简单的说,一个功能完善的servlet container 要为每个HTTP 请求Servlet后做如下事情:
当servlet 第一次被调用时,加载该servlet 类并调用servlet的init 方法 (仅一次)
对于每个请求,构造一个javax.servlet.ServletRequest和javax.servlet.ServletResponse实例。
调用servlet的service方法,需要传入该ServletRequest和ServletResponse对象作为参数。
当servlet类被关闭时,调用servlet的destroy方法并卸载该servlet类。
本章的第一个servlet container的功能是不完善的。因此不能调用servlet的 init 和 destroy方法。它只做以下事情:
等待HTTP 请求。
构造一个ServletRequest和ServletResponse对象。
如果请求的是一个静态资源,调用StaticResourceProcessor的process方法,所需参数为该ServletRequest和ServletRequest对象
如果请求的是一个servlet,加载该servlet类并调用service方法(所需参数为该ServletRequest和ServletRequest对象)
•
注:在该servlet container中,每次该servlet被请求时,该servlet 类都会被加载。
第一个应用程序包含六个类:
• HttpServer1
• Request
• Response
• StaticResourceProcessor
• ServletProcessor1
• Constants
图 2.1 展示了第一个servlet container的UML 图 。
图2.1:第一个servlet container的UML 图
程序的入口(main 方法)在HttpServer1类中。main方法创建一个 HttpServer1 的实例并调用其await方法。 await 方法等待 HTTP 请求,并为每个请求创建一个Request 对象和一个Response 对象,并分发它们到一个StaticResourceProcessor实例或者ServletProcessor 实例,这取决于请求的是一个静态资源还是一个servlet。包含static final WEB_ROOT的Constants类的WEB_ROOT变量被其他类所引用,它表示PrimitiveServlet类和静态资源的位置。
HttpServer1等待HTTP请求直到接收到关闭命令。你可以用第一章的方法发送一个关闭命令。下面将讨论这些类。
HttpServer1 类
HttpServer1 类与第一章的 HttpServer 类相似。但HttpServer1 类既适用于静态资源也适用于servlet。要请求一个静态资源,你可以输入如下地址:
http://machineName:port/staticResource
这是第一章中所介绍的内容。要请求一个servlet,你可以输入以下URL:
http://machineName:port/servlet/servletClass
所以,如果你使用本地主机浏览器请求一个叫PrimitiveServlet的servlet,你应当输入如下URL:
http://localhost:8080/servlet/PrimitiveServlet
该servlet container将会处理PrimitiveServlet。然而,如果你调用其他的servlet,如叫ModernServlet的servlet,该servlet container将抛出一个异常。在后面的章节里,你将学会如何写一个能处理所有Servlet的应用程序。
HttpServer1 类的代码清单如2.2所示。
清单2.2: HttpServer1 类的 await 方法
package ex02.pyrmont;
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
public class HttpServer1 {
/** WEB_ROOT is the directory where our HTML and other files reside.
* For this package, WEB_ROOT is the "webroot" directory under the
* working directory.
* The working directory is the location in the file system
* from where the java command was invoked.
*/
// shutdown command
private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
// the shutdown command received
private boolean shutdown = false;
public static void main (String [] args) {
HttpServer1 server = new HttpServer1();
server. 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);
}
// Loop waiting for a request
while (! shutdown) {
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try {
socket = serverSocket.accept ();
input = socket.getInputstream ();
output = socket.getOutputStream ();
// create Request object and parse
Request request = new Request (input);
request.parse ();
// create Response object
Response response = new Response (output);
response.setRequest(request);
// check if this is a request for a servlet or
// a static resource
// a request for a servlet begins with "/servlet/"
if (request.getUri().startsWith("/servlet/")) {
ServletProcessor1 processor = new ServletProcessor1 ();
processor.process(request, response);
}
else {
StaticResoureProcessor processor =
new StaticResourceProcessor();
processor.process(request, response);
}
// Close the socket
socket.close();
//check if the previous URI is a shutdown command
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
}
catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}
}
该类的await方法在收到关闭命令前会一直等待HTTP请求,提醒你一下,第一章中的await方法和清单2.2中的await方法的不同之处在于,后者请求会被分发到StaticResourceProcessor或者ServletProcessor。如果URI中包含字符串” /servlet/”,请求将被分发到ServletProcessor,否则将被分发到StaticResourceProcessor。
Request 类
Servlet的service 方法从servlet container 接收一个javax.servlet.ServletRequest 实例和一个javax.servlet.ServletResponse 实例。这就是说,对于每个HTTP 请求,servlet container 必须创建一个ServletRequest 对象和一个ServletResponse 对象并把它们传给servlet 的service 方法。
ex02.pyrmont.Request 类展示了一个传递给service方法的request对象。同样,它必须实现 javax.servlet.ServletRequest 接口。 该类实现了接口里的所有方法。如果你想要让它变得简单些的话,你可以写空的实现方法。如果你看到Request类的代码清单2.3 ,你发现方法的返回值是null。
清单 2.3: Request 类
package ex02.pyrmont;
import java.io.InputStream;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
public class Request implements ServletRequest {
private InputStream input;
private String uri;
public Request(InputStream input){
this.input = input;
}
public String getUri() {
return uri;
}
private String parseUri(String requestString) {
int index1, index2;
index1 = requestString.indexOf(' ');
if (index1 != -1) {
index2 = requestString.indexOf(' ', index1 + 1);
if (index2 > index1)
return requestString.substring(index1 + 1, index2);
}
return null;
}
public void parse() {
// Read a set of characters from the socket
StringBuffer request = new StringBuffer(2048);
int i;
byte[] buffer = new byte[2048];
try {
i = input.read(buffer);
}
catch (IOException e) {
e.printStackTrace();
i = -1;
}
for (int j=0; j<i; j++) {
request.append((char) buffer(j));
}
System.out.print(request.toString());
uri = parseUri(request.toString());
}
/* implementation of ServletRequest */
public Object getAttribute(String attribute) {
return null;
}
public Enumeration getAttributeNames() {
return null;
}
public String getRealPath(String path) {
return null;
}
public RequestDispatcher getRequestDispatcher(String path) {
return null;
}
public boolean isSecure() {
return false;
}
public String getCharacterEncoding() {
return null;
}
public int getContentLength() {
return 0;
}
public String getContentType() {
return null;
}
public ServletInputStream getInputStream() throws IOException {
return null;
}
public Locale getLocale() {
return null;
}
public Enumeration getLocales() {
return null;
}
public String getParameter(String name) {
return null;
}
public Map getParameterMap() {
return null;
}
public Enumeration getParameterNames() {
return null;
}
public String[] getParameterValues(String parameter) {
return null;
}
public String getProtocol() {
return null;
}
public BufferedReader getReader() throws IOException {
return null;
}
public String getRemoteAddr() {
return null;
}
public String getRemoteHost() {
return null;
}
public String getScheme() {
return null;
}
public String getServerName() {
return null;
}
public int getServerPort() {
return 0;
}
public void removeAttribute(String attribute) { }
public void setAttribute(String key, Object value) { }
public void setCharacterEncoding(String encoding)
throws UnsupportedEncodingException { }
}
该 Request类有parse和getUri方法,在第一章我们讨论过的。
Response 类
ex02.pyrmont.Response 类代码在清单2.4中给出,它实现了javax.servlet.ServletResponse. 接口。与Request 类类似,我们除了getWriter 方法外其他方法都返回null。
清单 2.4: Response 类
package ex02.pyrmont;
import java.io.OutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.File;
import java.io.PrintWriter;
import java.util.Locale;
import javax.servlet.ServletResponse;
import javax.servlet.ServletOutputStream;
public class Response implements ServletResponse {
private static final int BUFFER_SIZE = 1024;
Request request;
OutputStream output;
PrintWriter writer;
public Response(OutputStream output) {
this.output = output;
}
public void setRequest(Request request) {
this.request = request;
}
/* This method is used to serve static pages */
public void sendStaticResource() throws IOException {
byte[] bytes = new byte[BUFFER_SIZE];
FileInputstream fis = null;
try {
/* request.getUri has been replaced by request.getRequestURI */
File file = new File(Constants.WEB_ROOT, request.getUri());
fis = new FileInputstream(file);
/*
HTTP Response = Status-Line
*(( general-header | response-header | entity-header ) CRLF)
CRLF
[ message-body ]
Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
*/
int ch = fis.read(bytes, 0, BUFFER_SIZE);
while (ch!=-1) {
output.write(bytes, 0, ch);
ch = fis.read(bytes, 0, BUFFER_SIZE);
}
}
catch (FileNotFoundException e) {
String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: 23\r\n" +
"\r\n" +
"<h1>File Not Found</h1>";
output.write(errorMessage.getBytes());
}
finally {
if (fis!=null)
fis.close();
}
}
/** implementation of ServletResponse */
public void flushBuffer() throws IOException ( }
public int getBufferSize() {
return 0;
}
public String getCharacterEncoding() {
return null;
}
public Locale getLocale() {
return null;
}
public ServletOutputStream getOutputStream() throws IOException {
return null;
}
public PrintWriter getWriter() throws IOException {
// autoflush is true, println() will flush,
// but print() will not.
writer = new PrintWriter(output, true);
return writer;
}
public boolean isCommitted() {
return false;
}
public void reset() { }
public void resetBuffer() { }
public void setBufferSize(int size) { }
public void setContentLength(int length) { }
public void setContentType(String type) { }
public void setLocale(Locale locale) { }
}
getWriter方法中,PrintWriter构造函数中的第二个参数是一个boolean类型,它表示autoflush是否被允许,如果是true,调用println方法flush输出,但是print方法并不flush输出。
因此如果在servlet的service方法的最后一行调用的print方法,输出将不会被发送到浏览器。这个缺陷将在以后的程序中解决。Response类的sendStaticResource 方法在第一章已经讨论过了。(注:不知道如何准确的翻译flush这个词,但这段的意思就是,如果将autoflush为ture,缓存中已存的内容将会被发送到浏览器显示并清除缓存,但它只对println有效,如果在service的最后一行调用的是print方法,缓存中的内容将不会发送到浏览器)
StaticResourceProcessor 类
ex02.pyrmont.StaticResourceProcessor类用于处理静态资源。它只有一个process方法 。清单 2.5 提供了 StaticResourceProcessor类的代码。
清单 2.5:StaticResourceProcessor 类
package ex02.pyrmont;
import java.io.IOException;
public class StaticResourceProcessor {
public void process(Request request, Response response) {
try {
response.sendStaticResource();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
Process方法接收两个参数:一个ex02.pyrmont.Request实例,一个ex02.pyrmont.Response实例。这个方法只是简单的调用Response 类的sendStaticResource方法。
ServletProcessor1 类
清单2.6中的 ex02.pyrmont.ServletProcessor1 类是为servlet处理HTTP 请求的
清单2.6: ServletProcessor1 类
package ex02.pyrmont;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
import java.io.File;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class ServletProcessor1 {
public void process(Request request, Response response) {
String uri = request.getUri();
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
URLClassLoader loader = null;
try {
// create a URLClassLoader
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(Constants.WEB_ROOT);
// the forming of repository is taken from the
// createClassLoader method in
// org.apache.catalina.startup.ClassLoaderFactory
String repository =
(new URL("file", null, classPath.getCanonicalPath() +
File.separator)).toString() ;
// the code for forming the URL is taken from
// the addRepository method in
// org.apache.catalina.loader.StandardClassLoader.
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader(urls);
}
catch (IOException e) {
System.out.println(e.toString() );
}
Class myClass = null;
try {
myClass = loader.loadClass(servletName);
}
catch (ClassNotFoundException e) {
System.out.println(e.toString());
}
Servlet servlet = null;
try {
servlet = (Servlet) myClass.newInstance();
servlet.service((ServletRequest) request,
(ServletResponse) response);
}
catch (Exception e) {
System.out.println(e.toString());
}
catch (Throwable e) {
System.out.println(e.toString());
}
}
}
ServletProcessor1类非常简单,只有一个process方法,该方法需要两个参数:一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例。调用ServletRequest的 getRequestUri 方法可获得URI:
String uri = request.getUri();
回忆一下URI的格式,如下:
/servlet/servletName
servletName是servlet 类的名字。
要加载这个servlet类,我们需要知道该servlet得名字,从URI中我们能得到servlet的名字,使用process方法中下行代码:
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
接下来,process方法要加载该servlet。你必须创建一个加载类,并告诉它要加载的类的位置。对于这个servlet container来说,加载类直接到Constants.WEB_ROOT下寻找,它指向工作目录下webroot目录。
注:加载类将在第八章详细讨论。
可以使用java.net.URLClassLoader类加载一个servlet,它是java.lang.ClassLoader的一个子类。当你创建URLClassLoader类的一个实例后,可以使用loadClass方法加载一个servlet。实例化URLClassLoader类很简单,这个类有三个构造函数,最简单的就是这个:
public URLClassLoader (URL [] urls);
urls是一个java.net.URL数组,当加载一个类时,将在其中展开搜索。任何以“/”结尾的URL都被认为是一个路径。相反,则被认为是一个JAR包,如果需要的话可以下载或打开JAR包。
注:在servlet container中,加载器能找到servlet类的位置称为repository。
在我们的程序中,类加载器只搜索一个位置,就是工作目录下的webroot文件夹。所以我们只创建了一个长度为1的URL数组。URL类提供了很多种构造函数,因此我们有很多种方式实例化这个类。对于这个程序来讲,我们用和Tomcat中的其他类一样的构造方法。
public URL (URL context, java.lang.String spec, URLStreamHandler hander)
throws MalformedURLException
第一个和第三个参数都可以为空。还有一个构造函数也是三个参数:
public URL(java.lang.String protocol, java.lang.String host, java.lang.String file
) throws MalformedURLException
所以,如果你这样写:
new URL (null, aString, null);
编译器将不知道使用哪个构造函数,你可以告诉编译器第三个参数的类型,像这样写:
URLStreamHandler streamHandler = null;
new URL(null, aString, streamHandler);
第二个参数是一个包含repository(servlet的路径)的字符串:
String repository = (new URL("file", null,
classPath.getCanonicalPath () + File.separator)).toString () ;
连接起所有的代码片段,这是process方法构造一个适合的URLClassLoader实例的部分代码:
// create a URLClassLoader
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File (Constants.WEB_ROOT);
String repository = (new URL ("file", null,
classPath.getCanonicalPath () + File.separator)).toString ();
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader (urls);
注:repository处的代码是从org.apache.catalina.startup.ClassLoaderFactory 类的createClassLoader方法中摘取的,URL处的代码是从org.apache.catalina.loader.StandardClassLoader的addRepository方法摘取的。但是,不到最后一章,你不必考虑这些类。
有了类加载器后,就可以使用loadClass方法加载一个servlet类了:
Class myClass = null;
try {
myClass = loader.loadClass (servletName);
}
catch (ClassNotFoundException e) {
System.out.println (e.toString ());
}
接下来,process方法创建一个servlet实例,转型为 javax.servlet.Servlet类,调用 servlet的 service 方法:
Servlet servlet = null;
try {
servlet = (Servlet) myClass.newInstance ();
servlet.service ((ServletRequest) request,
(ServletResponse) response);
}
catch (Exception e) {
System.out.println (e.toString ());
}
catch (Throwable e) {
System.out.println (e.toString ());
}
运行程序
在Windows下运行该程序,敲入如下命令:
java -classpath ./lib/servlet.jar;./ ex02.pyrmont.HttpServer1
在Linux下,要使用冒号“:”分割两个包:
java -classpath ./lib/servlet.jar:./ ex02.pyrmont.HttpServer1
在浏览器中输入如下地址测试该程序:
http://localhost:8080/index.html
或者
http://localhost:8080/servlet/PrimitiveServlet
调用 PrimitiveServlet时,你将在浏览器中看到如下的文本:
Hello. Roses are red.
注:你看不到第二个字符串“Violets are blue”,因为只有第一个字符串是flush到浏览器,不过我们在第三章将处理这个问题。
Application 2
在第一个程序中,还遗留了一系列的问题。在ServletProcessor1类的process方法中,我们将ex02.pyrmont.Request实例向上转型为javax.servlet.ServletRequest,并把它传给servlet的service方法。又将ex02.pyrmont.Response实例向上转型为javax.servlet.ServletResponse,并也传递给servlet的service方法。
try {
servlet = (Servlet) myClass.newInstance();
servlet.service((ServletRequest) request,
(ServletResponse) response);
}
这是不安全的。知道servlet container 内部工作机制的Servlet编程人员可能会将ServletRequest实例和 ServletResponse实例重新向下转型回ex02.pyrmont.Request实例 和 ex02.pyrmont.Response实例。有了Request实例后,他们就可以调用parse方法。有了Response实例,就可以调用sendStaticResource方法。你不可能把parse和sendStaticResource方法写成private型的,因为其他类会调用到它们。有一个解决方法就是使Request和Response类都使用默认的访问修饰符。这样的话,它们就不能在ex02.pyrmont包外使用了。其实还有更好的方法可以解决这个问题,就是使用Façade类。让我们看一下图2.2的UML图:
图2.2 Façade类
在第二个程序中,我们增加两个类: RequestFacade 和 ResponseFacade。RequestFacade 实现了 ServletRequest 接口,要实例化该类需要传递一个Request实例,再构造方法中该Request实例将被赋值给一个ServleLRequest类的对象。该类实现了ServletRequest接口的所有方法,但是这个ServletRequest 对象是 private 类型的,在外部不能访问。我们不是将Request 向上转型为ServletRequest 类再传递给service 方法,而是创建一个RequestFacade 对象传给service方法。 Servlet程序员也可以将ServletRequest实例向下转型回RequestFacade,但是他们只能访问ServletRequest接口的方法。这样parseUri 方法就安全了。
清单 2.7展示了 RequestFacade 类的一部分代码:
清单 2.7:RequestFacade 类
package ex02.pyrmont;
public class RequestFacade implements ServletRequest {
private ServleLRequest request = null;
public RequestFacade(Request request) {
this.request = request;
}
/* implementation of the ServletRequest*/
public Object getAttribute(String attribute) {
return request.getAttribute(attribute);
}
public Enumeration getAttributeNames() {
return request.getAttributeNames();
}
...
}
注意RequestFacade的构造函数。它接收一个Request对象,并立刻将它赋值给一个私有的ServletRequest对象,再看一下该类的方法,调用的都是ServletRequest类的相应方法。ResponseFacade类的实现与RequestFacade的相同。
程序2中所用到的类如下:
• HttpServer2
• Request
• Response
• StaticResourceProcessor
• ServletProcessor2
• Constants
HttpServer2 类与 HttpServer1类似, 除了await 方法不同:
if (request.getUri().startWith("/servlet/")) {
servletProcessor2 processor = new ServletProcessor2();
processor.process(request, response);
}
else {
...
}
ServletProcessor2 类与ServletProcessor1类似,除了process方法有所不同:
Servlet servlet = null;
RequestFacade requestFacade = new RequestFacade(request);
ResponseFacade responseFacade = new ResponseFacade(response);
try {
servlet = (Servlet) myClass.newInstance();
servlet.service((ServletRequest) requestFacade,
(ServletResponse) responseFacade);
}
运行程序
在windows中运行该程序,在命令提示符下输入:
java -classpath ./lib/servlet.jar;./ ex02.pyrmont.HttpServer2
在 Linux下,则用冒号分割开两个包:
java -classpath ./lib/servlet.jar:./ ex02.pyrmont.HttpServer2
You can use the same URLs as in Application1 and you will get the same result.
总结
这章讨论了两个简单的servlet container,既可以用于静态资源,也可以用于servlet。
javax.servlet.Servlet接口的背景知识和相关类也给出了。
概览
这章将通过两个应用程序介绍如何开发自己的servlet container。第一个应用程序设计的尽可能简单以便更容易理解servlet container是如何工作的。第二个servlet container, 稍微复杂些。
注:每章中的servlet container 应用程序都是在前一章的基础上逐渐改进, 直到第十七章一个功能完善的 Tomcat servlet 被构建。
servlet containers既能处理简单的Servlet也能处理静态资源。你可以使用PrimitiveServlet类测试此 container。PrimitiveServlet类的代码将在清单2.1中给出,它的class文件在 webroot下可以找到。处理更复杂的servlet超出这个 container的能力范围,但是在下一章你将学会如何创建更完善的servlet container 。
清单 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;
}
}
该类在ex02.pyrmont 包下。要理解该应用程序是如何工作的,你需要了解javax.servlet.Servlet 接口,该接口在本章的第一部分讲解。之后你将学习到servlet container 必须为HTTP 请求一个Servlet后做些什么。
javax.servlet.Servlet 接口
使用javax.servlet 和 javax.servlet.http 包下的类或接口才能进行Servlet 编程。其中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的生命周期方法。init方法在Servlet实例化后由 Servlet Container 调用。init 方法仅被servlet container调用一次,表明该Servlet已经处于服务中了。init方法必须在servlet接收请求之前被成功调用。Servlet程序员可以重写该方法,在该方法中写入仅需要运行一次的初始化代码,像是加载数据库驱动,初始化变量值等等。通常情况下该方法是空的。
只要接收到对servlet的请求, servlet container就会调用该servlet 的service方法(参数一个javax.servlet.ServletRequest和javax.servlet.ServletResponse 对象)。ServletRequest 对象包含客户端的HTTP 请求信息和装有servlet响应信息的ServletResponse 对象。在servlet 的生命周期中Service方法会被调用很多次。
从服务中移除该servlet对象前,servlet container会调用destroy方法。这通常是在servlet container被关闭或者需要释放内存时发生。这个方法仅当servlet中所有线程已经退出或者超时时被调用。servlet container 调用destroy 方法后,在同一servlet中将不会再调用service方法。destroy 方法可以清理被占用的资源,如内存,文件句柄和线程,并确认内存中的持久化状态与此时的servlet状态是一致的。
清单 2.1展示了一个叫 PrimitiveServlet的Servlet代码,这是一个非常简单的Servlet,你可以用它来测试本章的servlet container应用程序。PrimitiveServlet类实现了javax.servlet.Servlet 接口(所有的servlet都必须实现) 并实现了Servlet的五个方法。PrimitiveServlet 做的非常简单。每次 init, service, 或者destroy 方法被调用,Servlet就会往控制台打印该方法的名字。另外,service 方法从ServletResponse对象中获得java.io.PrintWriter 对象并发送字符串到浏览器。
应用程序 1
现在,让我们从servlet container来观察一下servlet 程序。简单的说,一个功能完善的servlet container 要为每个HTTP 请求Servlet后做如下事情:
当servlet 第一次被调用时,加载该servlet 类并调用servlet的init 方法 (仅一次)
对于每个请求,构造一个javax.servlet.ServletRequest和javax.servlet.ServletResponse实例。
调用servlet的service方法,需要传入该ServletRequest和ServletResponse对象作为参数。
当servlet类被关闭时,调用servlet的destroy方法并卸载该servlet类。
本章的第一个servlet container的功能是不完善的。因此不能调用servlet的 init 和 destroy方法。它只做以下事情:
等待HTTP 请求。
构造一个ServletRequest和ServletResponse对象。
如果请求的是一个静态资源,调用StaticResourceProcessor的process方法,所需参数为该ServletRequest和ServletRequest对象
如果请求的是一个servlet,加载该servlet类并调用service方法(所需参数为该ServletRequest和ServletRequest对象)
•
注:在该servlet container中,每次该servlet被请求时,该servlet 类都会被加载。
第一个应用程序包含六个类:
• HttpServer1
• Request
• Response
• StaticResourceProcessor
• ServletProcessor1
• Constants
图 2.1 展示了第一个servlet container的UML 图 。
图2.1:第一个servlet container的UML 图
程序的入口(main 方法)在HttpServer1类中。main方法创建一个 HttpServer1 的实例并调用其await方法。 await 方法等待 HTTP 请求,并为每个请求创建一个Request 对象和一个Response 对象,并分发它们到一个StaticResourceProcessor实例或者ServletProcessor 实例,这取决于请求的是一个静态资源还是一个servlet。包含static final WEB_ROOT的Constants类的WEB_ROOT变量被其他类所引用,它表示PrimitiveServlet类和静态资源的位置。
HttpServer1等待HTTP请求直到接收到关闭命令。你可以用第一章的方法发送一个关闭命令。下面将讨论这些类。
HttpServer1 类
HttpServer1 类与第一章的 HttpServer 类相似。但HttpServer1 类既适用于静态资源也适用于servlet。要请求一个静态资源,你可以输入如下地址:
http://machineName:port/staticResource
这是第一章中所介绍的内容。要请求一个servlet,你可以输入以下URL:
http://machineName:port/servlet/servletClass
所以,如果你使用本地主机浏览器请求一个叫PrimitiveServlet的servlet,你应当输入如下URL:
http://localhost:8080/servlet/PrimitiveServlet
该servlet container将会处理PrimitiveServlet。然而,如果你调用其他的servlet,如叫ModernServlet的servlet,该servlet container将抛出一个异常。在后面的章节里,你将学会如何写一个能处理所有Servlet的应用程序。
HttpServer1 类的代码清单如2.2所示。
清单2.2: HttpServer1 类的 await 方法
package ex02.pyrmont;
import java.net.Socket;
import java.net.ServerSocket;
import java.net.InetAddress;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.IOException;
public class HttpServer1 {
/** WEB_ROOT is the directory where our HTML and other files reside.
* For this package, WEB_ROOT is the "webroot" directory under the
* working directory.
* The working directory is the location in the file system
* from where the java command was invoked.
*/
// shutdown command
private static final String SHUTDOWN_COMMAND = "/SHUTDOWN";
// the shutdown command received
private boolean shutdown = false;
public static void main (String [] args) {
HttpServer1 server = new HttpServer1();
server. 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);
}
// Loop waiting for a request
while (! shutdown) {
Socket socket = null;
InputStream input = null;
OutputStream output = null;
try {
socket = serverSocket.accept ();
input = socket.getInputstream ();
output = socket.getOutputStream ();
// create Request object and parse
Request request = new Request (input);
request.parse ();
// create Response object
Response response = new Response (output);
response.setRequest(request);
// check if this is a request for a servlet or
// a static resource
// a request for a servlet begins with "/servlet/"
if (request.getUri().startsWith("/servlet/")) {
ServletProcessor1 processor = new ServletProcessor1 ();
processor.process(request, response);
}
else {
StaticResoureProcessor processor =
new StaticResourceProcessor();
processor.process(request, response);
}
// Close the socket
socket.close();
//check if the previous URI is a shutdown command
shutdown = request.getUri().equals(SHUTDOWN_COMMAND);
}
catch (Exception e) {
e.printStackTrace();
System.exit(1);
}
}
}
}
该类的await方法在收到关闭命令前会一直等待HTTP请求,提醒你一下,第一章中的await方法和清单2.2中的await方法的不同之处在于,后者请求会被分发到StaticResourceProcessor或者ServletProcessor。如果URI中包含字符串” /servlet/”,请求将被分发到ServletProcessor,否则将被分发到StaticResourceProcessor。
Request 类
Servlet的service 方法从servlet container 接收一个javax.servlet.ServletRequest 实例和一个javax.servlet.ServletResponse 实例。这就是说,对于每个HTTP 请求,servlet container 必须创建一个ServletRequest 对象和一个ServletResponse 对象并把它们传给servlet 的service 方法。
ex02.pyrmont.Request 类展示了一个传递给service方法的request对象。同样,它必须实现 javax.servlet.ServletRequest 接口。 该类实现了接口里的所有方法。如果你想要让它变得简单些的话,你可以写空的实现方法。如果你看到Request类的代码清单2.3 ,你发现方法的返回值是null。
清单 2.3: Request 类
package ex02.pyrmont;
import java.io.InputStream;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.UnsupportedEncodingException;
import java.util.Enumeration;
import java.util.Locale;
import java.util.Map;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.ServletRequest;
public class Request implements ServletRequest {
private InputStream input;
private String uri;
public Request(InputStream input){
this.input = input;
}
public String getUri() {
return uri;
}
private String parseUri(String requestString) {
int index1, index2;
index1 = requestString.indexOf(' ');
if (index1 != -1) {
index2 = requestString.indexOf(' ', index1 + 1);
if (index2 > index1)
return requestString.substring(index1 + 1, index2);
}
return null;
}
public void parse() {
// Read a set of characters from the socket
StringBuffer request = new StringBuffer(2048);
int i;
byte[] buffer = new byte[2048];
try {
i = input.read(buffer);
}
catch (IOException e) {
e.printStackTrace();
i = -1;
}
for (int j=0; j<i; j++) {
request.append((char) buffer(j));
}
System.out.print(request.toString());
uri = parseUri(request.toString());
}
/* implementation of ServletRequest */
public Object getAttribute(String attribute) {
return null;
}
public Enumeration getAttributeNames() {
return null;
}
public String getRealPath(String path) {
return null;
}
public RequestDispatcher getRequestDispatcher(String path) {
return null;
}
public boolean isSecure() {
return false;
}
public String getCharacterEncoding() {
return null;
}
public int getContentLength() {
return 0;
}
public String getContentType() {
return null;
}
public ServletInputStream getInputStream() throws IOException {
return null;
}
public Locale getLocale() {
return null;
}
public Enumeration getLocales() {
return null;
}
public String getParameter(String name) {
return null;
}
public Map getParameterMap() {
return null;
}
public Enumeration getParameterNames() {
return null;
}
public String[] getParameterValues(String parameter) {
return null;
}
public String getProtocol() {
return null;
}
public BufferedReader getReader() throws IOException {
return null;
}
public String getRemoteAddr() {
return null;
}
public String getRemoteHost() {
return null;
}
public String getScheme() {
return null;
}
public String getServerName() {
return null;
}
public int getServerPort() {
return 0;
}
public void removeAttribute(String attribute) { }
public void setAttribute(String key, Object value) { }
public void setCharacterEncoding(String encoding)
throws UnsupportedEncodingException { }
}
该 Request类有parse和getUri方法,在第一章我们讨论过的。
Response 类
ex02.pyrmont.Response 类代码在清单2.4中给出,它实现了javax.servlet.ServletResponse. 接口。与Request 类类似,我们除了getWriter 方法外其他方法都返回null。
清单 2.4: Response 类
package ex02.pyrmont;
import java.io.OutputStream;
import java.io.IOException;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.File;
import java.io.PrintWriter;
import java.util.Locale;
import javax.servlet.ServletResponse;
import javax.servlet.ServletOutputStream;
public class Response implements ServletResponse {
private static final int BUFFER_SIZE = 1024;
Request request;
OutputStream output;
PrintWriter writer;
public Response(OutputStream output) {
this.output = output;
}
public void setRequest(Request request) {
this.request = request;
}
/* This method is used to serve static pages */
public void sendStaticResource() throws IOException {
byte[] bytes = new byte[BUFFER_SIZE];
FileInputstream fis = null;
try {
/* request.getUri has been replaced by request.getRequestURI */
File file = new File(Constants.WEB_ROOT, request.getUri());
fis = new FileInputstream(file);
/*
HTTP Response = Status-Line
*(( general-header | response-header | entity-header ) CRLF)
CRLF
[ message-body ]
Status-Line = HTTP-Version SP Status-Code SP Reason-Phrase CRLF
*/
int ch = fis.read(bytes, 0, BUFFER_SIZE);
while (ch!=-1) {
output.write(bytes, 0, ch);
ch = fis.read(bytes, 0, BUFFER_SIZE);
}
}
catch (FileNotFoundException e) {
String errorMessage = "HTTP/1.1 404 File Not Found\r\n" +
"Content-Type: text/html\r\n" +
"Content-Length: 23\r\n" +
"\r\n" +
"<h1>File Not Found</h1>";
output.write(errorMessage.getBytes());
}
finally {
if (fis!=null)
fis.close();
}
}
/** implementation of ServletResponse */
public void flushBuffer() throws IOException ( }
public int getBufferSize() {
return 0;
}
public String getCharacterEncoding() {
return null;
}
public Locale getLocale() {
return null;
}
public ServletOutputStream getOutputStream() throws IOException {
return null;
}
public PrintWriter getWriter() throws IOException {
// autoflush is true, println() will flush,
// but print() will not.
writer = new PrintWriter(output, true);
return writer;
}
public boolean isCommitted() {
return false;
}
public void reset() { }
public void resetBuffer() { }
public void setBufferSize(int size) { }
public void setContentLength(int length) { }
public void setContentType(String type) { }
public void setLocale(Locale locale) { }
}
getWriter方法中,PrintWriter构造函数中的第二个参数是一个boolean类型,它表示autoflush是否被允许,如果是true,调用println方法flush输出,但是print方法并不flush输出。
因此如果在servlet的service方法的最后一行调用的print方法,输出将不会被发送到浏览器。这个缺陷将在以后的程序中解决。Response类的sendStaticResource 方法在第一章已经讨论过了。(注:不知道如何准确的翻译flush这个词,但这段的意思就是,如果将autoflush为ture,缓存中已存的内容将会被发送到浏览器显示并清除缓存,但它只对println有效,如果在service的最后一行调用的是print方法,缓存中的内容将不会发送到浏览器)
StaticResourceProcessor 类
ex02.pyrmont.StaticResourceProcessor类用于处理静态资源。它只有一个process方法 。清单 2.5 提供了 StaticResourceProcessor类的代码。
清单 2.5:StaticResourceProcessor 类
package ex02.pyrmont;
import java.io.IOException;
public class StaticResourceProcessor {
public void process(Request request, Response response) {
try {
response.sendStaticResource();
}
catch (IOException e) {
e.printStackTrace();
}
}
}
Process方法接收两个参数:一个ex02.pyrmont.Request实例,一个ex02.pyrmont.Response实例。这个方法只是简单的调用Response 类的sendStaticResource方法。
ServletProcessor1 类
清单2.6中的 ex02.pyrmont.ServletProcessor1 类是为servlet处理HTTP 请求的
清单2.6: ServletProcessor1 类
package ex02.pyrmont;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
import java.io.File;
import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
public class ServletProcessor1 {
public void process(Request request, Response response) {
String uri = request.getUri();
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
URLClassLoader loader = null;
try {
// create a URLClassLoader
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File(Constants.WEB_ROOT);
// the forming of repository is taken from the
// createClassLoader method in
// org.apache.catalina.startup.ClassLoaderFactory
String repository =
(new URL("file", null, classPath.getCanonicalPath() +
File.separator)).toString() ;
// the code for forming the URL is taken from
// the addRepository method in
// org.apache.catalina.loader.StandardClassLoader.
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader(urls);
}
catch (IOException e) {
System.out.println(e.toString() );
}
Class myClass = null;
try {
myClass = loader.loadClass(servletName);
}
catch (ClassNotFoundException e) {
System.out.println(e.toString());
}
Servlet servlet = null;
try {
servlet = (Servlet) myClass.newInstance();
servlet.service((ServletRequest) request,
(ServletResponse) response);
}
catch (Exception e) {
System.out.println(e.toString());
}
catch (Throwable e) {
System.out.println(e.toString());
}
}
}
ServletProcessor1类非常简单,只有一个process方法,该方法需要两个参数:一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例。调用ServletRequest的 getRequestUri 方法可获得URI:
String uri = request.getUri();
回忆一下URI的格式,如下:
/servlet/servletName
servletName是servlet 类的名字。
要加载这个servlet类,我们需要知道该servlet得名字,从URI中我们能得到servlet的名字,使用process方法中下行代码:
String servletName = uri.substring(uri.lastIndexOf("/") + 1);
接下来,process方法要加载该servlet。你必须创建一个加载类,并告诉它要加载的类的位置。对于这个servlet container来说,加载类直接到Constants.WEB_ROOT下寻找,它指向工作目录下webroot目录。
注:加载类将在第八章详细讨论。
可以使用java.net.URLClassLoader类加载一个servlet,它是java.lang.ClassLoader的一个子类。当你创建URLClassLoader类的一个实例后,可以使用loadClass方法加载一个servlet。实例化URLClassLoader类很简单,这个类有三个构造函数,最简单的就是这个:
public URLClassLoader (URL [] urls);
urls是一个java.net.URL数组,当加载一个类时,将在其中展开搜索。任何以“/”结尾的URL都被认为是一个路径。相反,则被认为是一个JAR包,如果需要的话可以下载或打开JAR包。
注:在servlet container中,加载器能找到servlet类的位置称为repository。
在我们的程序中,类加载器只搜索一个位置,就是工作目录下的webroot文件夹。所以我们只创建了一个长度为1的URL数组。URL类提供了很多种构造函数,因此我们有很多种方式实例化这个类。对于这个程序来讲,我们用和Tomcat中的其他类一样的构造方法。
public URL (URL context, java.lang.String spec, URLStreamHandler hander)
throws MalformedURLException
第一个和第三个参数都可以为空。还有一个构造函数也是三个参数:
public URL(java.lang.String protocol, java.lang.String host, java.lang.String file
) throws MalformedURLException
所以,如果你这样写:
new URL (null, aString, null);
编译器将不知道使用哪个构造函数,你可以告诉编译器第三个参数的类型,像这样写:
URLStreamHandler streamHandler = null;
new URL(null, aString, streamHandler);
第二个参数是一个包含repository(servlet的路径)的字符串:
String repository = (new URL("file", null,
classPath.getCanonicalPath () + File.separator)).toString () ;
连接起所有的代码片段,这是process方法构造一个适合的URLClassLoader实例的部分代码:
// create a URLClassLoader
URL[] urls = new URL[1];
URLStreamHandler streamHandler = null;
File classPath = new File (Constants.WEB_ROOT);
String repository = (new URL ("file", null,
classPath.getCanonicalPath () + File.separator)).toString ();
urls[0] = new URL(null, repository, streamHandler);
loader = new URLClassLoader (urls);
注:repository处的代码是从org.apache.catalina.startup.ClassLoaderFactory 类的createClassLoader方法中摘取的,URL处的代码是从org.apache.catalina.loader.StandardClassLoader的addRepository方法摘取的。但是,不到最后一章,你不必考虑这些类。
有了类加载器后,就可以使用loadClass方法加载一个servlet类了:
Class myClass = null;
try {
myClass = loader.loadClass (servletName);
}
catch (ClassNotFoundException e) {
System.out.println (e.toString ());
}
接下来,process方法创建一个servlet实例,转型为 javax.servlet.Servlet类,调用 servlet的 service 方法:
Servlet servlet = null;
try {
servlet = (Servlet) myClass.newInstance ();
servlet.service ((ServletRequest) request,
(ServletResponse) response);
}
catch (Exception e) {
System.out.println (e.toString ());
}
catch (Throwable e) {
System.out.println (e.toString ());
}
运行程序
在Windows下运行该程序,敲入如下命令:
java -classpath ./lib/servlet.jar;./ ex02.pyrmont.HttpServer1
在Linux下,要使用冒号“:”分割两个包:
java -classpath ./lib/servlet.jar:./ ex02.pyrmont.HttpServer1
在浏览器中输入如下地址测试该程序:
http://localhost:8080/index.html
或者
http://localhost:8080/servlet/PrimitiveServlet
调用 PrimitiveServlet时,你将在浏览器中看到如下的文本:
Hello. Roses are red.
注:你看不到第二个字符串“Violets are blue”,因为只有第一个字符串是flush到浏览器,不过我们在第三章将处理这个问题。
Application 2
在第一个程序中,还遗留了一系列的问题。在ServletProcessor1类的process方法中,我们将ex02.pyrmont.Request实例向上转型为javax.servlet.ServletRequest,并把它传给servlet的service方法。又将ex02.pyrmont.Response实例向上转型为javax.servlet.ServletResponse,并也传递给servlet的service方法。
try {
servlet = (Servlet) myClass.newInstance();
servlet.service((ServletRequest) request,
(ServletResponse) response);
}
这是不安全的。知道servlet container 内部工作机制的Servlet编程人员可能会将ServletRequest实例和 ServletResponse实例重新向下转型回ex02.pyrmont.Request实例 和 ex02.pyrmont.Response实例。有了Request实例后,他们就可以调用parse方法。有了Response实例,就可以调用sendStaticResource方法。你不可能把parse和sendStaticResource方法写成private型的,因为其他类会调用到它们。有一个解决方法就是使Request和Response类都使用默认的访问修饰符。这样的话,它们就不能在ex02.pyrmont包外使用了。其实还有更好的方法可以解决这个问题,就是使用Façade类。让我们看一下图2.2的UML图:
图2.2 Façade类
在第二个程序中,我们增加两个类: RequestFacade 和 ResponseFacade。RequestFacade 实现了 ServletRequest 接口,要实例化该类需要传递一个Request实例,再构造方法中该Request实例将被赋值给一个ServleLRequest类的对象。该类实现了ServletRequest接口的所有方法,但是这个ServletRequest 对象是 private 类型的,在外部不能访问。我们不是将Request 向上转型为ServletRequest 类再传递给service 方法,而是创建一个RequestFacade 对象传给service方法。 Servlet程序员也可以将ServletRequest实例向下转型回RequestFacade,但是他们只能访问ServletRequest接口的方法。这样parseUri 方法就安全了。
清单 2.7展示了 RequestFacade 类的一部分代码:
清单 2.7:RequestFacade 类
package ex02.pyrmont;
public class RequestFacade implements ServletRequest {
private ServleLRequest request = null;
public RequestFacade(Request request) {
this.request = request;
}
/* implementation of the ServletRequest*/
public Object getAttribute(String attribute) {
return request.getAttribute(attribute);
}
public Enumeration getAttributeNames() {
return request.getAttributeNames();
}
...
}
注意RequestFacade的构造函数。它接收一个Request对象,并立刻将它赋值给一个私有的ServletRequest对象,再看一下该类的方法,调用的都是ServletRequest类的相应方法。ResponseFacade类的实现与RequestFacade的相同。
程序2中所用到的类如下:
• HttpServer2
• Request
• Response
• StaticResourceProcessor
• ServletProcessor2
• Constants
HttpServer2 类与 HttpServer1类似, 除了await 方法不同:
if (request.getUri().startWith("/servlet/")) {
servletProcessor2 processor = new ServletProcessor2();
processor.process(request, response);
}
else {
...
}
ServletProcessor2 类与ServletProcessor1类似,除了process方法有所不同:
Servlet servlet = null;
RequestFacade requestFacade = new RequestFacade(request);
ResponseFacade responseFacade = new ResponseFacade(response);
try {
servlet = (Servlet) myClass.newInstance();
servlet.service((ServletRequest) requestFacade,
(ServletResponse) responseFacade);
}
运行程序
在windows中运行该程序,在命令提示符下输入:
java -classpath ./lib/servlet.jar;./ ex02.pyrmont.HttpServer2
在 Linux下,则用冒号分割开两个包:
java -classpath ./lib/servlet.jar:./ ex02.pyrmont.HttpServer2
You can use the same URLs as in Application1 and you will get the same result.
总结
这章讨论了两个简单的servlet container,既可以用于静态资源,也可以用于servlet。
javax.servlet.Servlet接口的背景知识和相关类也给出了。
- How_Tomcate_works.rar (250.5 KB)
- 下载次数: 19
相关推荐
《How Tomcat Works》中文版一书详细剖析了Tomcat服务器的内部工作机制。该书基于Tomcat 4.1.12和5.0.18两个版本,深入讲解了其servlet容器的架构和运作原理,尤其是代号为Catalina的核心组件。 Tomcat是一个开源的...
《译How Tomcat Works(第一章)》这篇文章是关于Apache Tomcat服务器的工作原理的翻译,它主要探讨了Tomcat如何处理HTTP请求以及内部架构的关键组成部分。Tomcat是一款开源的Java Servlet容器,广泛用于部署Java Web...
《How Tomcat Works》是一份深入探讨Apache Tomcat工作原理的重要资源,包含了英文PDF文档、中文HTML翻译以及源代码,旨在帮助读者理解Tomcat服务器的内部运作机制。这份资料是IT从业者,特别是Java Web开发者、系统...
《译How Tomcat Works(第四章)》这篇文章深入解析了Apache Tomcat服务器的工作原理,主要聚焦于Tomcat的内部机制,对于理解Web应用容器的运行方式具有重要意义。Tomcat是Java Servlet和JavaServer Pages(JSP)...
《How Tomcat Works》是一本深入探讨Apache Tomcat工作原理的书籍,中文版的提供使得国内开发者能够更方便地理解这一流行的开源Java Servlet容器。这本书不仅涵盖了Tomcat的基础知识,还详细解析了其内部机制,对于...
《How Tomcat Work 第二章 实例应用》深入解析 在深入探讨Tomcat工作原理之前,我们先要了解什么是Tomcat。Apache Tomcat是一款开源的Java Servlet容器,它实现了Java Servlet和JavaServer Pages (JSP) 规范,是...
《How Tomcat Works》这本书是理解Apache Tomcat服务器工作原理的宝贵资源,它全面深入地讲解了这个流行的Java Servlet和JavaServer Pages(JSP)容器的内部机制。书中的20个章节涵盖了从基础概念到高级特性的广泛...
《How Tomcat Works》是一本深入解析Apache Tomcat工作原理的书籍,同时也包含了源码,为读者提供了理论与实践相结合的深入学习体验。Tomcat是一款广泛使用的开源Java Servlet容器,它是Apache软件基金会 Jakarta...
《译How Tomcat Works(第三章)》这篇文章深入解析了Apache Tomcat服务器的工作原理,Tomcat作为开源的Java Servlet容器,是许多Web应用程序的基础。在本章中,我们将聚焦于Tomcat如何处理HTTP请求,以及它如何加载和...
本篇文章将深入探讨Tomcat,一个广泛使用的Java Web服务器,通过翻译分析"How Tomcat Works"一书的前四章内容,来理解其工作机制。 首先,我们要了解Tomcat的基本架构。Tomcat是Apache软件基金会的Jakarta项目中的...
《How Tomcat Works中文版》这本书是一本深入探讨Apache Tomcat服务器工作原理的专著。Apache Tomcat服务器,或简称为Tomcat,是世界上广泛使用的Java Servlet容器和JavaServer Pages(JSP)引擎,负责处理基于Java...
### How Tomcat Works中文版深度解析 #### 一、引言与概述 《How Tomcat Works》是一本针对Apache Tomcat服务器内部工作机制进行深入剖析的专业书籍。本书详细介绍了Tomcat 4.1.12和5.0.18两个版本的内部结构与...
《How Tomcat Works》这本书深入浅出地介绍了Apache Tomcat这款广泛应用的Java Servlet容器的工作原理。Tomcat作为开源软件,是许多Web应用的基础,尤其在轻量级开发和测试环境中非常常见。以下是对Tomcat核心知识点...
《how tomcat works》是一本深入探讨Apache Tomcat内部工作原理的专业书籍。Apache Tomcat是一个开源的Java Servlet容器,它实现了Java Servlet和JavaServer Pages技术规范,提供了Java Web服务器的功能。对于Java ...
HowTomcatWorks书籍课程实例工程与代码 书籍剖析了Tomcat 4.1.12和Tomcat 5.0.18--一个免费的、开源的、深受大众欢迎的、代号为Catalina的servlet容器,并讲解其容器的内部运行机制。通过迭代实现一个简化版软件来...
《HowTomcatWorks》是一本深入解析Apache Tomcat工作原理的书籍,中文版的发布使得更多的中国开发者能够理解和掌握这款广泛应用的开源Java Servlet容器的工作机制。Tomcat是Apache软件基金会Jakarta项目的一部分,它...