- Java Web开发技术教程
- 李西明 陈立为 邵艳玲主编
- 3279字
- 2025-04-01 14:59:55
2.3 初识Servlet
介绍了Tomcat后,下面我们在Web项目中创建一个能接收浏览器的HTTP请求并做出响应的Servlet。
2.3.1 Servlet简介
Servlet是运行在Web服务器上的小型Java程序,我们通常把实现了Servlet接口的Java类称为Servlet。Servlet通过HTTP(超文本传输协议)接收和响应来自Web客户端的请求,它是作为来自Web浏览器或其他HTTP客户端的请求和HTTP服务器上的数据库或应用程序之间的中间层。
Servlet应用在Web程序中的位置如图2.6所示。
由图2.6中可以看出Servlet在Web中主要是充当一个业务处理的角色。Servlet主要执行以下任务。
(1)读取客户端(浏览器)发送的显式的数据。包括网页上的HTML表单,或者是来自applet或自定义的HTTP客户端程序的表单。

图2.6 Servlet在Web中的位置
(2)读取客户端(浏览器)发送的隐式的HTTP请求数据。包括Cookies、媒体类型和浏览器能理解的压缩格式等。
(3)处理数据并生成结果。这个过程可能需要访问数据库,执行RMI或CORBA调用,调用Web服务,或者计算得出对应的响应。
(4)发送显式的数据(即文档)到客户端(浏览器)。该文档的格式可以是多种多样的,包括文本文件(HTML或XML)、二进制文件、Excel文件等。
(5)发送隐式的HTTP响应到客户端(浏览器)。包括告诉浏览器或其他客户端返回的文档类型(如HTML),设置Cookies和缓存参数,以及其他类似的任务。
2.3.2 第一个Servlet程序
开发一个Java程序向浏览器输出“My First Servlet”,需要完成以下两个步骤。
①创建Web项目,编写一个Java类,实现Servlet接口,重写其所有的抽象方法。
②把开发好的Web项目部署到Web服务器中。
还需要考虑两个问题,一是输出数据的Java代码应放在Servlet的哪个方法里,二是如何输出数据到客户端。可在以下内容中找到答案。
下面先来认识Servlet接口的抽象方法。
(1)init方法:初始化方法,可以利用该方法进行相应的初始化工作。当Servlet第一次被访问时,该方法被调用,而且整个Servlet生命周期该方法只被调用一次。
(2)service方法:用来处理客户端的请求,客户端每次请求Servlet时,都会调用这个方法。第一次访问Servlet时会调用init方法和service方法,后续的访问将只调用service方法。该方法的完整定义如下。
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { }
解释:对于每次访问请求,Servlet容器都会创建一个新的ServletRequest请求对象和一个新的ServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service方法,开发者可以在此方法中接收用户的请求数据,做出相应的响应返回给客户端。在service方法里面,利用servletResponse对象的getWriter方法,可以创建一个PrintWrite对象,再利用PrintWrite对象的print方法,即可向客户端输出数据。
(3)destroy方法:销毁方法,当卸载应用程序或关闭服务时会自动调用此方法。以上三个方法都是Servlet生命周期方法,下面即将介绍的getServletInfo和getServletConfig方法则是非生命周期方法。
(4)getServletInfo方法:返回Servlet的描述信息,可以返回有用的或为null的任意字符串。
(5)getServletConfig方法:返回由Servlet容器传给init方法的ServletConfig对象。但init方法的ServletConfig对象属于局部变量,无法在本方法中使用,所以要使用这个功能需要定义一个全局的ServletConfig变量。不使用的话返回null即可。
接下来编写一个简单的Servlet程序。
(1)在IDEA中创建Java EE项目TestServlet,在src下创建包com.lifeng.servlet。在包下创建一个Java Class,并将之命名为SimpleServlet,实现Servlet接口,代码如下(重点是service方法中的代码)。
package com.lifeng.servlet; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.*; @WebServlet("/simpletest") public class SimpleServlet implements Servlet { @Override public void init(ServletConfig servletConfig) throws ServletException { } @Override public ServletConfig getServletConfig() { return null; } @Override public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException { PrintWriter out=servletResponse.getWriter(); out.print("<h1>my first servlet</h1>"); } @Override public String getServletInfo() { return null; } @Override public void destroy() { } }
这只是实现Servlet的多种方式中的一种。实现Servlet有三种方式:实现Servlet接口、继承GenericServlet抽象类、继承HttpServlet抽象类。上述的示例代码就是第一种。
要使这段Servlet代码发挥作用,还得对请求路径进行配置,以便客户端能调用到它,这里采用的是比较方便的注解。上述代码中,注解@WebServlet("/simpletest")定义了访问该Servlet的URL,表示浏览器中使用http://localhost:8080/TestServlet/simpletest这个URL就可以调用(访问)到这个Servlet。同样也可以用XML方式来配置,例如在Web项目中加入下面的这段代码。
<servlet> <servlet-name>SimpleServlet</servlet-name> <servlet-class> com.lifeng.servlet.SimpleServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>SimpleServlet</servlet-name> <url-pattern>/simpletest</url-pattern> </servlet-mapping>
标签<servlet>可用来定义Servlet,指定Servlet的名称(别名)和实际路径,其子标签<servlet-class>指定了该Servlet的完整路径,子标签<servlet-name>指定了Servlet的自定义名称。标签<servlet-mapping>可用来指定Servlet名称与访问该Servlet的URL路径,其子标签<servlet-name>指定了Servlet名称,子标签<url-pattern>指定了URL访问路径(自定义,但不能重复)。使用注解和XML创建URL这两种方式不能同时使用,否则会报错。
(2)部署项目到Tomcat服务器,运行程序,在地址栏输入“http://localhost:8080/TestServlet/simpletest”,使用浏览器访问,效果如图2.7所示。

图2.7 第一个Servlet程序
2.3.3 Servlet执行过程
根据上述第一个Servlet程序来理解Servlet的执行过程。
(1)客户端发出请求http://localhost:8080/TestServlet/simpletest。
(2)服务器根据web.xml文件的配置,找到url-pattern子元素的值为“/simpletest”的servlet mapping元素。
(3)读取servlet-mapping元素的servlet-name子元素的值,由此确定要访问的Servlet的名字为“SimpleServlet”。
(4)找到<servlet-name>值为“SimpleServlet”的Servlet元素。
(5)读取Servlet元素的servlet-class子元素的值,由此确定Servlet的类名为com.lifeng.servlet.SimpleServlet。到Tomcat安装目录/webapps/TestServlet/WEB-INF/classes/com/lifeng/servlet下查找到SimpleServlet.class文件。最终找到要执行的目标Servlet。
【注意】如果URL不是在web.xml中配置,而是采用注解方式,道理仍然是一样的。
Servlet是通过实例对象来提供服务的,接下来介绍Servlet实例对象的创建过程。
Servlet程序由Web服务器调用,Web服务器收到客户端的Servlet访问请求后会执行以下操作。
①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,就直接执行第④步,否则,执行第②步。
②装载并创建该Servlet的一个实例对象。
③调用Servlet实例对象的init方法。
④创建一个用于封装HTTP请求消息的ServletRequest对象和一个代表HTTP响应消息的ServletResponse对象,然后调用Servlet的service方法,并将请求和响应对象作为参数传递进去。
⑤Web应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy方法。
2.3.4 HttpServlet类
前面提到,创建Servlet除了实现Servlet接口外,还有继承GenericServlet类,或者继承HttpServlet类也可以实现,所以共有三种创建Servlet的方法。其实GenericServlet类和HttpServlet类本身已经实现了Servlet接口,所以跟第一种方法比是间接实现了Servlet接口。
在这三种方法中,第一种方法必须给Servlet接口中的所有方法都提供实现,即使有些方法用不上,所以我们一般不用这种方法。GenericServlet类提供了Servlet接口的基本实现,其子类都必须实现service方法,GenericServlet并不常用。由于大多数Web应用都通过HTTP和客户端交互,所以最常用的是继承HttpServlet类的Servlet。
HttpServlet类扩展了GenericServlet并且提供了Servlet接口中应用于HTTP的实现,其中定义了两种形式的service方法。
public service(ServletRequest req,ServletResponse res)
其代码如下:
public service(ServletRequest req, ServletResponse res) throws ServletException, IOException { HttpServletRequest request; HttpServletResponse response; try { request=(HttpServletRequest)req; response=(HttpServletResponse)res; } catch(ClassCastException e) { throw new ServletException("non-HTTP"); } service(request, response); }
该方法用public修饰,是GenericServlet的service方法的实现,它把Servlet容器的request、response对象分别转化为HttpServletRequest和HttpServletResponse,并且调用下面重载的service方法。
protected void service(HttpServletRequest request, HttpservletResponse response) throws ServletException, java.IO.IOException
该方法用protected修饰,用HTTP的request、response对象作为参数,并且由上面的方法调用,HttpServlet实现这种方法后就成为一个HTTP请求的分发者,把请求代理给doGet、doPost等doXxx方法。
当容器为Servlet收到一个请求时,容器先调用公共的service方法把参数转换为HttpServletRequest,这个公共的service方法再调用受保护的service,根据HTTP请求方法的类型调用doXxx方法之一。其中最常用的有下面两种方法。
①doGet方法,是当得到一个GET类型的请求时调用的。
②doPost方法,是当得到一个POST类型的请求时调用的。
HttpServlet从GenericServlet继承而来,间接实现Servlet接口,因此HttpServlet也有init和destroy这两个生命周期函数以及service方法,但HttpServlet还有两个重要的doPost方法和doGet方法,并用它们来支持HTTP的POST和GET方法,由于HttpServlet中的service方法用来调用相应的doGet或doPost方法,所以一般不重写,只需重写doGet或doPost即可,如果service方法被开发者重写了,则访问该Servlet只会调用改写后的service方法,而不会去调用doGet或doPost方法。
2.3.5 Servlet的URL路径映射
1.多重映射
多重映射是指一个Servlet可以被多个URL路径访问。其实现方法就是在web.xml中配置Servlet的时候,在原来的基础上再多创建一个或多个<servlet-mapping>标签及其子标签,<servlet-mapping>的子标签<url-pattern>中使用不同的值即可。示例如下。
<servlet> <servlet-name>SimpleServlet</servlet-name> <servlet-class> com.lifeng.servlet.SimpleServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>SimpleServlet</servlet-name> <url-pattern>/simpletest</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>SimpleServlet</servlet-name> <url-pattern>/simpletest2</url-pattern> </servlet-mapping>
以上代码表示使用URL路径/simpletest或者/simpletest2都能访问到同一个SimpleServlet。
2.映射路径中通配符的使用
上述配置可以有多个路径访问到同一个Servlet,但有时希望某个目录下的所有任意路径都能访问到同一个Servlet,这时可以在映射路径中使用通配符(*)代表任意个字符,具体用法如下。
(1)“*.扩展名”,例如,“*.action”表示任意以“.action”结尾的URL均能访问到该Servlet。
(2)“/*”,例如,“/aaa/*”可以匹配以“/aaa”开头的所有URL。
【注意】上述两种格式不能混合使用,如/aaa/*.action就是错误的。
3.默认Servlet
如果浏览器访问了不存在的Servlet,服务器找不到该Servlet会反馈404错误页面。可以在web.xml中进行默认Servlet的配置,服务器会将找不到的请求交给默认的Servlet处理,在这个Servlet里面进行简单处理,就不会弹出404页面了。配置方法为,在web.xml中另配置一个URL为“/”的Servlet。示例代码如下。
<servlet> <servlet-name>DefaultServlet</servlet-name> <servlet-class> com.lifeng.servlet.DefaultServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>DefaultServlet</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping>
这样,其他找不到的请求,都会转到上述配置中的DefaultServlet进行处理,例如在DefaultServlet中输出一个界面温馨友好的找不到资源的提示。