JavaWeb从入门到精通(视频实战版)
上QQ阅读APP看书,第一时间看更新

2.6 Servlet

就当时功能来说,Servlet所提供的功能就是一个Java版本的CGI。它依旧是一个具有跨平台特性、100%纯Java的Server-Side程序。Java Servlet API定义了Servlet和服务器之间的一个标准接口,这使得Servlet具有跨服务器平台的特性。

2.6.1 Servlet简介

Servlet通过创建一个框架扩展服务器的能力,采用请求-响应模式提供Web服务。当客户机发送请求至服务器时,服务器将请求信息发送给Servlet,Servlet生成响应内容并将其传给服务器,然后再由服务器将响应返回给客户端。图2.22展示了Servlet的生命周期,定义了一个Servlet如何被加载、初始化,以及它怎样提供服务和最后销毁的过程。

图2.23展示了Servlet如何接收客户端请求、处理请求,最后返回响应的流程。

图2.22 Servlet生命周期

图2.23 Servlet响应请求序列图

2.6.2 创建Servlet

所有的Servlet必须直接或间接地实现javax.servlet.Servlet接口,这样才能在Servlet服务器或称为Servlet容器、Servlet引擎上运行,更简单的方法是继承HttpServlet类。实例2-13给出一个Servlet的类,功能是在用户请求中获取name参数,然后向客户端输出一个HTML网页。

【实例2-13】Servlet示例HelloServlet

01   package com.book.web3;
02   import java.io.*;
03   import javax.servlet.*;
04   import javax.servlet.http.*;
05
06   public class HelloServlet extends HttpServlet {
07     @Override
08     public void service(ServletRequest req, ServletResponse res)
09     throws IOException, ServletException {
10        String name = req.getParameter("name");    //获取传递过来的name属性的值
11        res.setContentType("text/html");
12        PrintWriter out = res.getWriter();       //输出html文件内容
13        out.println("<html>");
14        out.println("<body>");
15        out.println("<head>");
16        out.println("<title>Hello Reader!</title>");
17        out.println("</head>");
18        out.println("<body>");
19        out.println("<h1>Hello " + name + "!</h1>"); //显示相应的信息
20        out.println("</body>");
21        out.println("</html>");
22     }
23   }

【代码剖析】上面代码中首先在第10行获取参数name的值,然后在第11行通过方法setContentType()设置返回页面的类型,最后在第12行到21行间创建出out对象,并通过该对象把相应内容输出到返回页面。

还需要在web.xml中配置这个Servlet,让服务器可以根据用户请求的地址定位到这个Servlet类,如实例2-14。定义这个Servlet为“helloreader”,在mapping中定义了可以通过“/helloreader”这个URL来访问页面。

【实例2-14】Servlet在web.xml中的配置

01     <!--定义Servlet-->
02     <servlet>
03        <servlet-name>helloreader</servlet-name>
04        <servlet-class>com.book.web3.HelloServlet</servlet-class>
05     </servlet>
06     <!--定义Servlet与URL映射关系-->
07     <servlet-mapping>
08        <servlet-name>helloreader</servlet-name>
09        <url-pattern>/helloreader</url-pattern>
10     </servlet-mapping>

【代码剖析】上面代码中首先通过标签<servlet>来定义关于Servlet的类,然后通过标签<servlet-mapping>来设置Servlet类的映射。

【运行程序】浏览该页面,结果如图2.24所示。

2.6.3 过滤器

图2.24 Servlet结果图

一个过滤器(Filter)是一个可以传送请求或修改响应的对象。过滤器并不是Servlet,它们并不实际创建一个请求。它们是请求到达一个Servlet前的预处理程序,在响应离开Servlet后的后处理程序。一个过滤器能够做如下工作:

1)在一个Servlet被调用前截获该调用。

2)在一个Servlet被调用前检查请求。

3)修改在实际请求中提供了可定制请求对象的请求头和请求数据。

4)修改在实际响应中提供了可定制响应对象的响应头和响应数据。

5)在一个Servlet被调用之后截获该调用。

一个过滤器实现java.servlet.Filter接口并定义它的3个方法,见表2.9。

表2.9 Filter接口函数表

有一个实际的例子,在Tomcat 4.0发布中被命名为ExampleFilter,实例2-15使用这个过滤器可以计算执行某个Servlet所用时间并记录在日志中。

【实例2-15】Filter示例

01   package com.book.web3;
02   import java.io.*;
03   import javax.servlet.*;
04   import javax.servlet.http.*;
05   /*
06   * 实现一个过滤器
07   */
08   public class TimerFilter implements Filter {
09     private FilterConfig config = null;               //定义变量FilterConfig
10     public void init(FilterConfig config) throws ServletException { //初始化方法
11        this.config = config;                    //赋值变量config
12     }
13     public void destroy() {                     //销毁方法
14        config = null;
15     }
16     /*
17      * filter必须实现的方法
18      */
19     public void doFilter(ServletRequest request, ServletResponse response,
20           FilterChain chain) throws IOException, ServletException {
21        long before = System.currentTimeMillis();         //定义过滤前的时间
22        chain.doFilter(request, response);
23        long after = System.currentTimeMillis();          //定义过滤后的时间
24        String name = "";
25        if (request instanceof HttpServletRequest) {
26           name = ((HttpServletRequest) request).getRequestURI();
27        }
28        // 记录时间
29        config.getServletContext().log(name + ": " + (after - before) + "ms");
30     }
31   }

【代码剖析】如果要实现过滤器,则必须重写doFilter()方法。在该方法中,首先获取执行Servlet之前和之后的时间,然后获取请求的URI,最后输出相应的信息。

使用此过滤器,还要在web.xml文件中用<filter>标签部署它:

<filter>
          <filter-name>timerFilter</filter-name>
          <filter-class>TimerFilter</filter-class>
</filter>

过滤器被越来越广泛地应用,当前热门的开发组件webWork和后文将重点介绍的Struts2就大量使用了过滤器。

2.6.4 监听器

监听器(Listener)可以监听客户端的请求、服务端的操作等。通过监听器,可以自动激发一些操作,如监听在线用户数量,当增加一个HttpSession时,给在线人数加1。监听器还有一个好处是可以在不修改现有系统基础上增加Web应用程序生命周期事件的跟踪。

编写监听器需要实现相应的接口。常用的监听接口如下:

(1)ServletContextAttributeListener

监听对ServletContext属性的操作,比如增加、删除、修改。

(2)ServletContextListener

监听ServletContext。当创建ServletContext时,激发contextInitialized方法;当销毁ServletContext时,激发contextDestroyed方法。

(3)HttpSessionListener

监听HttpSession的操作。当创建一个Session时,激发sessionCreated方法;当销毁一个Session时,激发sessionDestroyed方法。

(4)HttpSessionAttributeListener

监听HttpSession中的属性的操作。当在Session中增加一个属性时,激发attributeAdded方法;当在Session中删除一个属性时,激发attributeRemoved方法;当在Session属性被重新设置时,激发attributeReplaced方法。

下面的例子是利用HttpSessionListener接口计算在线用户数量,实例2-16和实例2-17展示的是如何编写这个监听器。

【实例2-16】Listener示例OnlineCounter.java

01   package com.book.web3;
02   //一个简单的计数程序
03   public class OnlineCounter {
04     private static long online = 0;
05     public static long getOnline() {
06        return online;
07     }
08     // 累加
09     public static void raise() {
10        online++;
11     }
12     // 减1
13     public static void reduce() {
14        online--;
15     }
16   }

【代码剖析】在上述代码中创建了一个关于在线人数的类。在该类中有一个表示在线人数的变量online,然后又创建了两个方法raise()和reduce(),前者用来实现在线人数加1功能,后者实现在线人数减1功能。

【实例2-17】Listener示例OnlineCounterListener.java

01   package com.book.web3;
02   import javax.servlet.http.HttpSessionEvent;
03   import javax.servlet.http.HttpSessionListener;
04   //实现一个监听器程序
05   public class OnlineCounterListener implements HttpSessionListener {
06     public void sessionCreated(HttpSessionEvent hse) {
07        // 收到一个创建session事件, 计数器加1
08        OnlineCounter.raise();
09     }
10
11     public void sessionDestroyed(HttpSessionEvent hse) {
12        // 收到一个销毁session事件, 计数器减1
13        OnlineCounter.reduce();
14     }
15   }

【代码剖析】在上述代码中重写了sessionCreated()方法,在该方法中调用类OnlineCounter的raise()方法实现每创建一个Session对象,就让在线人数加1。同时也重写了sessionDestroyed()方法,在该方法中调用类OnlineCounter的reduce()方法,实现每销毁一个Session对象,就让在线人数减1。

使用此过滤器,还要在web.xml文件中用<listener>标签部署它。如下所示:

<listener>
    <listener-class>com.book.web3.OnlineCounterListener</listener-class>
</listener>