Web服务器的基本工作方式是请求-处理-回复。请求和回复是在网络上,以HTTP协议为基础的通信(参考HTTP协议)。请求是客户点菜,回复像是服务员上菜,而处理则是在后厨中,厨师根据请求的菜单,准备菜品的过程。
厨师通常是服务器上的一个应用程序。这个应用程序可以提取请求中的信息,并根据这些信息准备回复。这样的应用程序可以是许多中语言写成的,比如C, C++, Perl, Ruby, Python, Ruby, PHP等等。由于不同语言的设计理念和编译器特征的不同,这些语言写成的应用程序也有不同的特点(各种各样的厨师)。比如C和C++语言会有比较高的运行效率,PHP的Web应用广泛,Ruby和Python开发方便等等。
(在语言之争中,Web服务器的“后厨”是一块兵家必争之地)
Servlet是Java语言提供的的“厨师”。在Java中,“一切皆对象”。Servlet是一类特殊的Java对象。Java语言利用Servlet对象,来实现对HTTP请求的处理。
样例
下面是一个简单的Servlet例子,源文件为TestPage.java:
package foo; import javax.servlet.*; import javax.servlet.http.*; import java.io.*; public class TestPage extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { PrintWriter out = response.getWriter(); out.println("<html><body>" + "<p>Hello World!</p>" + "</body></html>"); } }
这个Servlet的功能是回复字符串"<html><body><p>Hello World!</p></body></html>"。这一段字符串实际上是一个HTML文本。它将作为HTTP回复的主体部分,发回给客户端。
这里定义了一个TestPage类,该类继承HttpServlet抽象类(参考HttpServlet)。我们覆盖了doGet()方法。doGet()方法处理GET方法的HTTP请求。如果HTTP请求的方法为POST,则应该覆盖doPost()方法。每个Servlet都应该覆盖doGet()和doPost()之一。doGet()和doPost()方法接收两个参数request和response,分别为HttpServletRequest类型和HttpServletResponse类型。这两个参数分别代表该次HTTP通信的请求和回复。
HttpServletRequest和HttpServletResponse为两个接口。在方法的内部,我们可以操作request和response对象。比如,通过response的getWriter方法,来获得Writer,从而写入HTML文本。这些写入的文本将作为HTTP回复的主体传递回客户端。再比如,我们可以使用request的getMethod()方法来获知HTTP请求的方法。可以参考下面的官方文档:
HttpServletRequest的更多方法
HttpServletResponse的更多方法
此外,PrintWriter类来自java.io包。参考Java IO
doGet()是真正的工作室。我们通过定义该方法(或者doPost())方法,来让厨师做特定的事情。Servlet编写的关键在于写出一个符合需求的doGet()方法或者doPost()方法。
正如第一行package语句所说明的(参考Java包),TestPage类被放入到foo包中。我们还import了其他的Java包。Servlet是基于Java的,所以它可以借助Java语言所提供的丰富的工具。
编译
使用下面命令编译TestPage.java
$javac -classpath /path-to-tomcat/lib/servlet-api.jar:classes:. TestPage.java
上面的path-to-tomcat是Tomcat的安装路径。我们需要包括servlet-api.jar,原因是,javax.servlet包属于J2EE,它并不属于JSE。
将编译生成的TestPage.class文件放入到下面的文件夹中。(如果文件夹不存在,需要自行创建):
/path-to-tomcat/webapps/test/WEB-INF/classes/foo
(在TesPage.java中,利用package语句,说明该类在foo包中。这里的路径符合之前的package定义。)
修改或者新建web.xml
<?xml version="1.0" encoding="ISO-8859-1"?> <!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0" metadata-complete="true"> <servlet> <servlet-name>Test</servlet-name> <servlet-class>foo.TestPage</servlet-class> </servlet> <servlet-mapping> <servlet-name>Test</servlet-name> <url-pattern>/MyServlet</url-pattern> </servlet-mapping> </web-app>
这样的一个xml文件被称作部署描述(DD, Deployment Descriptor)。DD告诉Container如何运行Servlet和JSP。
上面的<servlet>标签是将该Servlet(foo.TestPage.class)命名为Test。
在<servlet-mapping>标签中,我们将Test这个Servlet对应于URL: /MyServlet。这说明,当有请求访问该URL时,则将请求传递给Test这个Servlet处理。
上面修改的主要目的,是说明Servlet与URL的对应关系。
随后,可以访问localhost:8080/test/MyServlet,来查看访问页面。
过程
看一下Servlet完成一次请求处理的过程:
从客户端(Guest)向服务器发送HTTP请求,该HTTP请求传递给Servlet Container。该Container负责:
分析HTTP请求的信息,并新建request对象,将HTTP请求中的信息放入request对象
新建response对象
根据web.xml,查找URL对应的Servlet对象。如果Servlet对象不存在,则新建相应Servlet对象。
创建新的线程,用于处理本次请求。线程拥有指向request和response对象的引用。
线程将调用Servlet的doGet()或者doPost()方法。线程运行结束后,response对象将传回给Container。Container根据response对象中的信息,生成一个符合HTTP协议的回复,传回给客户端。
注意,即使是新建的Servlet对象,它也不会随着线程的结束而结束。Servlet将继续存在。下次相同的URL访问将不必新建Servlet。
总结
上面制作了一个简单的Servlet,主要用于说明制作Servlet的过程。Servlet的许多强大功能还有待以后展开。
每个Servlet就像是后厨的一个厨师。我们可以用不同的URL对应来选择不同的厨师。一个复杂的网站往往需要许多个不同功能的Servlet。