Servlet基本工作原理
?
?
本 文介绍一个简单 servlet 容器的基本原理。现有两个servlet容器,第一个很简单,第二个则是根据第一个写出。为了使第一个容器尽量简单,所以没有做得很完整。复杂一些的 servlet容器(包括TOMCAT4和5)在TOMCAT运行内幕的其他章节有介绍。
两个servlet容器都处理简单的 servlet及staticResource。您可以使用 webroot/ 目录下的 PrimitiveServlet 来测试它。复杂一些的 servlet会超出这些容器的容量,您可以从 TOMCAT 运行内幕 一书学习创建复杂的 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 ServletExceptionpublic void service(ServletRequest request, ServletResponse response)throws ServletException, jsp servlet ejb .io.IOException public void destroy() public ServletConfig getServletConfig() public jsp servlet ejb .lang.String getServletInfo()
Listing 2.1.PrimitiveServlet.javaimport javax.servlet.*;import jsp servlet ejb .io.IOException;import jsp servlet ejb .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; }}
Application 1
现在,我们从 servlet容器的角度来看看 servlet 编程。一个功能健全的 servlet容器对于每个 servlet 的HTTP请求会完成以下事情:
当servlet 第一次被调用的时候,加载了 servlet类并调用它的init方法(仅调用一次)
响应每次请求的时候 ,构建一个javax.servlet.ServletRequest 和 javax.servlet.ServletResponse实例。
激活servlet的service方法,传递 ServletRequest 和 ServletResponse 对象。
当servlet类关闭的时候,调用servlet的destroy方法,并卸载servlet类。
发生在 servlet 容器内部的事就复杂多了。只是这个简单的servlet容器的功能不很健全,所以,这它只能运行非常简单的servelt ,并不能调用servlet的init和destroy方法。然而,它也执行了以下动作:
等待HTTP请求。
构建ServletRequest和ServletResponse对象
如果请求的是一个staticResource,就会激活StaticResourceProcessor实例的 process方法,传递ServletRequest 和 ServletResponse 对象。
如果请求的是一个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
它就是前文中提到的怎样在 WEB 服务器应用程序里请求静态资源。如果要请求一个 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); }// 关闭socketsocket.close();//检测是否前面的 URI 是一个 shutdown 命令shutdown = request.getUri().equals(SHUTDOWN_COMMAND); }catch (Exception e) {e.printStackTrace();System.exit(1); } }}
public Object getAttribute(String attribute) {return null; }public Enumeration getAttributeNames() {return null; }public String getRealPath(String path) {return null; }
public PrintWriter getWriter() {// autoflush is true, println() will flush,// but print() will not.writer = new PrintWriter(output, true);return writer;}
Listing 2.3.StaticResourceProcessor 类的 process方法。public void process(Request request, Response response) {try {response.sendStaticResource(); }catch (IOException e) {e.printStackTrace(); }}
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 URLStreamHandler streamHandler = null; URL[] urls = new URL[1]; 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); } catch (IOException e) { System.out.println(e.toString()); } Class myClass = null; try { myClass = loader.loadClass(servletName); } catch (Exception 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()); }}
// create a URLClassLoaderURLStreamHandler streamHandler = null;URL[] urls = new URL[1];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);
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());}
try { servlet = (Servlet) myClass.newInstance(); servlet.service((ServletRequest) request, (ServletResponse) response);}
package ex02.pyrmont;public class RequestFacade implements ServletRequest { private ServletRequest 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(); } ...}
HttpServer2 Request Response StaticResourceProcessor ServletProcessor2 Constants HttpServer2 类类似于 HttpServer1,只是它在 await 方法内使用了 ServletProcessor2 而不是ServletProcessor1。if (request.getUri().startsWith("/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);}
编译并运行该应用程序
如果要编译该应用程序,在工作目录下键入以下命令:
javac -d . -classpath ./lib/servlet.jar src/ex02/pyrmont/*.java
如果要在 windows 下运行该应用程序,在工作目录下键入以下命令:
jsp servlet ejb -classpath ./lib/servlet.jar;./ ex02.pyrmont.HttpServer2
在linux环境下,使用分号来隔开类库:
jsp servlet ejb -classpath ./lib/servlet.jar:./ ex02.pyrmont.HttpServer2
您可以使用和 application 1 相同的 URL 以收到同样的结果。
总结
本文讨论了简单的能够用于服务静态资源,以及处理如 PrimitiveServlet 一样简单的 servlet 的 servlet 容器。 同时也提供 javax.servlet.Servlet 的背景信息。
?
?
转自:http://lxs9731.blog.163.com/blog/static/34793702007112263521960/
?
?
资料:http://www.ibm.com/developerworks/cn/java/j-lo-servlet/