第20章 JSP/Servlet技术

在传统的网页HTML文件(htm,html)中加入Java程序片段和JSP标记,就构成了JSP网页。Web服务器在遇到访问JSP网页的请求时,首先执行其中的程序片段,然后将执行结果以HTML格式返回给客户。程序片段有操作数据库、重新定向网页以及发送电子邮件等功能,所有程序操作都在服务器端执行,网络上传送给客户端的仅是得到的HTML结果,无须客户端的浏览器做任何扩展。

Servlet是用Java编写的服务端程序,它与协议和平台无关,运行在支持Java的Web服务器中。Java Servlet可以动态地扩展服务器的能力,并采用请求—响应模式提供Web服务。

实例199 JSP与Servlet之间的跳转

本实例主要讲解Servlet和JSP之间的跳转,主要的文件有StudentDataBean.java、AddStudentServlet.java、DeleteStudentServlet.java、Addstudent.jsp、addstudentsuccess.jsp、deletestudent.jsp和deleteFailure.jsp。

技术要点

JSP与Servlet之间的跳转的技术要点如下:

• Servlet是通过request.getRequestDispatcher()来实现与JSP页面的跳转的。

• Servlet通过request. getParameter(文本框的名字)来获取JSP页面中录入的值。

• 创建一个数据Bean类,包含相应属性名字的get和set方法。

• JSP页面显示的数据是在Servlet中,将数据放入Bean类的相应的set方法中,然后将Bean对象通过setAttribute()放入request中。在JSP页面获取该Bean对象,调用其get方法,将数据取出。

实现步骤

(1)新建一个类名为StudentDataBean.java。

(2)代码如下所示。

StudentDataBean.java含有表单项的get和set方法的Bean类,具体代码如下:

package chp20;
public class StudentDataBean {
    private String id = "";                      //学号
    private String name = "";                    //学生姓名
    private String address = "";                 //学生居住地址
    private String age = "";                     //学生年龄
    private int Math_score = 0;                  //数学成绩
    private int Eng_score = 0;                   //英语成绩
    private int chinese_score = 0;               //语文成绩
    //下面是相应字段的get和set方法
    public int getChinese_score() {
        return chinese_score;
    }
    public void setChinese_score(int chinese_score) {
        this.chinese_score = chinese_score;
    }
    public int getEng_score() {
        return Eng_score;
    }
    public void setEng_score(int eng_score) {
        Eng_score = eng_score;
    }
    public int getMath_score() {
        return Math_score;
    }
    public void setMath_score(int math_score) {
        Math_score = math_score;
    }
    public String getAddress() {
        return address;
    }
    public void setAddress(String address) {
        this.address = address;
    }
    public String getAge() {
        return age;
    }
    public void setAge(String age) {
        this.age = age;
    }
    public String getid() {
        return id;
    }
    public void setId(String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

AddStudentServlet.java处理如何将表单中的内容添加到StudentDataBean中,并显示出来,具体代码如下:

package chp20;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class AddStudentServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
              throws IOException, ServletException {
          response.setContentType("text/html;charset=gb2312");
          String contextPath = request.getContextPath();        //取得请求的上下文
          HttpSession session = request.getSession();           //取得session
          String requestPath = request.getServletPath();        //取得servlet的路径
          System.out.println("request.getServletPath()=" + requestPath);
          if (requestPath.equals("/AddStudentServlet")) {
              String id = request.getParameter("id"); //从request中取出name为id的值
              String name = request.getParameter("name");//从request中取出name为name的值
              String address = request.getParameter("address");
              //从request中取出name为address的值
              String age = request.getParameter("age");//从request中取出name为age的值
              String msc = request.getParameter("Math_score");
              //从request中取出name为Math_score的值
              String esc = request.getParameter("Eng_score");
              //从request中取出name为Eng_score的值
              String csc = request.getParameter("chinese_score");
              //从request中取出name为chinese_score的值
              StudentDataBean sdb = new StudentDataBean(); //创建StudentDataBean对象
              //将获得的参数放入DataBean sdb中
              sdb.setId(id);
              sdb.setName(name);
              sdb.setAddress(address);
              sdb.setAge(age);
              sdb.setChinese_score(Integer.parseInt(csc));
              sdb.setEng_score(Integer.parseInt(esc));
              sdb.setMath_score(Integer.parseInt(msc));
              //将sdb对象放入请求的属性"student"中
              request.setAttribute("student", sdb);
              //将sdb对象放入session的属性"student"中
              session.setAttribute("student", sdb);
              //根据请求将页面跳转到指定的页面
              request.getRequestDispatcher("/lession20/addstudentsuccess.jsp")
                        .forward(request, response);
          }
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
              throws IOException, ServletException {
          doGet(request, response);
    }
}

DeleteStudentServlet.java处理如何删除信息,具体代码如下:

package chp20;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
public class DeleteStudentServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
              throws IOException, ServletException {
          response.setContentType("text/html;charset=gb2312");
          String servletPath = request.getServletPath(); //取得servlet请求的路径信息
          HttpSession session = request.getSession();    //获取session
          if (servletPath.equals("/DeleteStudentServlet")) {
          //在这里可以通过session来实现两个Servlet之间的通信
              Object o = session.getAttribute("student"); //从session中获取Object对象
              StudentDataBeansdb = (StudentDataBean)o;//将Object对象转换成StudentDataBean对象
              if (o == null) {          //如果对象为空则跳转deleteFailure.jsp页面
                    System.out.println("session.getAttribute(\"studnet\")=null");
                    request.getRequestDispatcher("/lession20/deleteFailure.jsp")
                              .forward(request, response);
              } else {                  //否则跳转deletestudent.jsp页面
                    System.out.println(sdb.getId());
                    System.out.println(sdb.getName());
                    System.out.println(sdb.getAddress());
                    System.out.println(sdb.getAge());
                    System.out.println(sdb.getChinese_score());
                    System.out.println(sdb.getEng_score());
                    System.out.println(sdb.getMath_score());
                    session.removeAttribute("student");
                    System.out.println("'student'对象已经被删除");
                    request.getRequestDispatcher("/lession20/deletestudent.jsp")
                              .forward(request, response);
              }
          }
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
              throws IOException, ServletException {
          doGet(request, response);
    }
}

Addstudent.jsp是录入信息的JSP页面,具体代码如下:

<%@ page contentType="text/html;charset=gb2312" language="java" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
    <title>     登录页面</title>
</head>
<%
    String contextPath = request.getContextPath();
%>
<body>
        <form method="post" action="<%=contextPath %>/AddStudentServlet">
              <table border="1" cellpadding="1" cellspacing="0" width="50%"
height="40%" bgcolor="#FDF5E6" bordercolor="#6B8E23">
                  <tr>
                        <th>ID号:</th>
                        <td><input type="text" name="id"></td>
                  </tr>
                  <tr>
                        <th>姓名:</th>
                        <td><input type="text" name="name"></td>
                  </tr>
                  <tr>
                        <th>年龄:</th>
                        <td><input type="text" name="age"></td>
                  </tr>
                  <tr>
                        <th>地址:</th>
                        <td><input type="text" name="address"></td>
                  </tr>
                  <tr>
                        <th>数学成绩:</th>
                        <td><input type="text" name="Math_score"></td>
                  </tr>
                  <tr>
                        <th>语文成绩:</th>
                        <td><input type="text" name="chinese_score"></td>
                  </tr>
                  <tr>
                        <th>英语成绩:</th>
                        <td><input type="text" name="Eng_score"></td>
                  </tr>
                  <tr>
                        <td colspan="2"><input type="submit" name="submit" value="
提交">&nbsp;&nbsp;
                        <input type="reset" name="reset" value="重填"></td>
                  </tr>
              </table>
        </form>
</body>
</html>

addstudentsuccess.jsp是添加成功后显示信息的页面,具体代码如下:

<%@ page contentType="text/html;charset=gb2312" language="java"%>
<%@ page import="chp20.*"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<jsp:useBean id="sdb" class="chp20.StudentDataBean" />
<jsp:useBean id="student" type="chp20.StudentDataBean" scope="request"/>
<html>
<head>
    <title>     显示添加信息的页面</title>
</head>
<%
    String contextPath = request.getContextPath();
    sdb = (StudentDataBean)request.getAttribute("student");
    //从request中取得StudentDataBean对象
%>
<body>
    <fontstyle="FONT-SIZE: 120pt; COLOR: red"><B>用JavaBean的ID:student得到的数据::</B></font>
              <table border="1" cellpadding="1" cellspacing="0" bgcolor="#FCE6C9"
bordercolor="#FF6347" align="center">
              <tr>
                    <td>ID号:</td>
                    <td><%=student.getId()%></td>
              </tr>
              <tr>
                    <td>姓名:</td>
                    <td><%=student.getName()%></td>
              </tr>
              <tr>
                    <td>年龄:</td>
                    <td><%=student.getAge()%></td>
              </tr>
              <tr>
                    <td>地址:</td>
                    <td><%=student.getAddress()%></td>
              </tr>
              <tr>
                    <td>数学成绩:</td>
                    <td><%=student.getMath_score()%></td>
              </tr>
              <tr>
                    <td>语文成绩:</td>
                    <td><%=student.getChinese_score()%></td>
              </tr>
              <tr>
                    <td>英语成绩:</td>
                    <td><%=student.getEng_score()%></td>
              </tr>
</table> <font style="FONT-SIZE: 120pt; COLOR: blue"><B>通过request取到的:</B></font> <br> <table border="1" cellpadding="1" cellspacing="0" bgcolor="#F0FFFF" bordercolor="#9933FA" align="center"> <tr> <td>ID号:</td> <td><%=sdb.getId()%></td> </tr> <tr> <td>姓名:</td> <td><%=sdb.getName()%></td> </tr> <tr> <td>年龄:</td> <td><%=sdb.getAge()%></td> </tr> <tr> <td>地址:</td> <td><%=sdb.getAddress()%></td> </tr> <tr> <td>数学成绩:</td> <td><%=sdb.getMath_score()%></td> </tr> <tr> <td>语文成绩:</td> <td><%=sdb.getChinese_score()%></td> </tr> <tr> <td>英语成绩:</td> <td><%=sdb.getEng_score()%></td> </tr> </table> <% sdb = (StudentDataBean)session.getAttribute("student"); //从session中取得StudentDataBean对象 %> <font style="FONT-SIZE: 120pt; COLOR: pink"><B>通过session取到的:</B></font> <br> <table border="1" cellpadding="1" cellspacing="0" bgcolor="#F0FFF0" bordercolor="#32CD32" align="center"> <tr> <td>ID号:</td> <td><%=sdb.getId()%></td> </tr> <tr> <td>姓名:</td> <td><%=sdb.getName()%></td> </tr> <tr> <td>年龄:</td> <td><%=sdb.getAge()%></td> </tr> <tr> <td>地址:</td> <td><%=sdb.getAddress()%></td> </tr> <tr> <td>数学成绩:</td> <td><%=sdb.getMath_score()%></td> </tr> <tr> <td>语文成绩:</td> <td><%=sdb.getChinese_score()%></td> </tr> <tr> <td>英语成绩:</td> <td><%=sdb.getEng_score()%></td> </tr> </table> <br> <hr width="100%"> <a href="<%=contextPath%>/DeleteStudentServlet">删除"student"对象</a> </body> </html>

deletestudent.jsp和deleteFailure.jsp是delete操作后的提示页面,具体代码如下:

<%@ page contentType="text/html;charset=gb2312" language="java" %>
<%@ page import="chp20.*"%>
<jsp:useBean id="sdb" class="chp20.StudentDataBean" />
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
    <head>
          <title>Delete Servlet</title>
    </head>
<%
    //String contextPath = request.getContextPath();
    sdb = (StudentDataBean)session.getAttribute("student");
%>
<body>
<%
    if (sdb==null){
%>
    <h2><font style="FONT-SIZE: 20pt; COLOR: green"><B>操作成功!!!"student"对象
被删除。</B></font></h2>
<%
    }else{
%>
    <h2><font style="FONT-SIZE: 20pt; COLOR: green"><B>操作失败!!!"student"对象
并没有被删除。</B></font></h2>
<%
    }
%>
    </body>
</html>

配置文件WEB-INF/web.xml,具体代码如下:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
    <display-name>My Web Application</display-name>
    <description>A application for test.</description>
    <servlet>
          <servlet-name>AddStudentServlet</servlet-name>
          <servlet-class>chp20.AddStudentServlet</servlet-class>
    </servlet>
    <servlet>
          <servlet-name>DeleteStudentServlet</servlet-name>
          <servlet-class>chp20.DeleteStudentServlet</servlet-class>
    </servlet>
    <servlet-mapping>
          <servlet-name>AddStudentServlet</servlet-name>
          <url-pattern>/AddStudentServlet</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
          <servlet-name>DeleteStudentServlet</servlet-name>
          <url-pattern>/DeleteStudentServlet</url-pattern>
    </servlet-mapping>
</web-app>

(3)运行结果:登录页面如图20-1所示。

(4)显示相应的添加信息,如图20-2所示。

图20-1 登录页面

图20-2 显示添加信息页面

(5)将对象删除,成功后的页面如图20-3所示。

图20-3 删除页面

源程序解读

1. StudentDataBean类

该类中主要封装了表单中相应文本输入框的name属性的get和set方法,其作用是实现Servlet与JSP页面间的数据传递。例如,在Servlet中创建StudentDataBean对象,然后调用相应值的set方法,就可以对StudentDataBean类中的private变量赋值,在JSP页面就可以调用相应的get方法,将数据取出,然后显示在页面上。

2. AddStudentServlet类

该类的主要作用是从添加信息的表单中获取录入的信息,然后将这些信息显示在另一个JSP页面上。若取值成功,则跳转到addstudentsuccess.jsp页面。

JSP与Servlet之间跳转的流程如下:

(1)通过request.getParameter()方法在JSP页面中获取信息。

(2)将值放入StudentDataBean的set方法中,再将对象放入request中。

(3)通过request.getRequestDispatcher()方法指定要跳转的JSP页面。

(4)在JSP页面中通过request.getAttribute()获取StudentDataBean对象。

3. DeleteStudentServlet类

该类的主要作用是删除StudentDataBean对象,即将填加的所有信息全部删除掉。通过session.removeAttribute()可以实现其功能。

实例200 简单的JSP多人聊天室

在客户端与服务器的交互过程中,经常会使用Session记录用户的有关信息,以供用户再次以此身份对Web服务器提供要求时作确认。一个Session的会话期从用户登录某网站开始,到该用户离开这个网站时结束。本实例使用Session机制,实现一个无刷新的聊天室,多个用户可以登录到聊天室服务器,共享聊天信息。

技术要点

实现无刷新聊天室的技术要点如下:

• session的getAttribute方法获得session中的某个属性值。

• application的getAttribute方法获得application中的某个属性值。

• request的getParameter方法获得请求表单中的参数值。

• request的sendRedirect方法实现重定向功能,将请求重定向到另外一个页面。

• 采用了JavaScript的setTimeOut方法,实现每隔2秒就从服务器取最新聊天信息。

实现步骤

(1)新建一个HTML为log.html。

(2)代码如下所示。

log.html登录界面,具体代码如下:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
    <head>
          <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
          <title>登录聊天室</title>
    </head>
    <body>
        <form name="form1" method="post"
            action="/My_Servlet/lession20/loadMes.jsp?action=login">
            <center>
                  <font style="FONT-SIZE:20px;COLOR:#FF6347"><B>登录聊天室</B> </font>
            </center>
            <table width="50%" height="20%" border="1" align="center"
                  cellpadding="0" cellspacing="0" bgcolor="#FDF5E6"
                  bordercolor="#6B8E23">
                  <tr>
                      <th>
                            您的昵称:
                      </th>
                      <td>
                            <input name="username" type="text" id="username">
                      </td>
                  </tr>
                  <tr>
                      <th>
                            输入密码:
                      </th>
                      <td>
                            <input name="password" type="password" id="password">
                      </td>
                  </tr>
                  <tr>
                      <td colspan="2" align="center">
                            <input type="submit" name="Submit" value="进入">
                            &nbsp;&nbsp;
                            <input type="reset" name="reset" value="重填">
                      </td>
                  </tr>
            </table>
        </form>
    </body>
</html>

main.html进入聊天室的主界面,具体代码如下:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
"http://www.w3.org/TR/html4/frameset.dtd">
<html>
    <head>
          <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
          <title>聊天室主页面</title>
    </head>
    <frameset rows="*,100" cols="*" frameborder="yes" border="1" framespacing=
"2" bordercolor="#40E0D0">
          <frame src="show.html" name="mainFrame">
          <frame src="writeMes.jsp" name="bottomFrame" scrolling="NO" noresize>
    </frameset>
    <body>
    </body>
</html>

show.html显示聊天内容的界面,具体代码如下:

<html><head>
<script language="JavaScript" type="text/javascript">
function Timeout(){
          url="/My_Servlet/lession20/method.jsp?action=show";              //调用页面
          try {
              LoadTime.src = url;
          } catch(e) {
          return false;
          }
          {var timeoutid = setTimeout("Timeout()",2000)}
}
</script>
<script id="LoadTime" language="JavaScript" type="text/javascript" defer></script>
</head>
<body onLoad="javascript:Timeout();" bgcolor="#F0FFFF">
<font style="FONT-SIZE:20px;COLOR:#FF00FF"><span id=loadData>正在加载数据,请稍
候...</span></font>
</body>
</html>

loadMes.jsp装载消息的JSP页面,具体代码如下:

<%@ page contentType="text/html; charset=GB2312" language="java"%>
<%@ page import ="java.util.*" %>
<jsp:useBean id="msgs" class="java.util.HashMap" scope="application" />
<%
    request.setCharacterEncoding("GBK");
    String action = request.getParameter("action");
if(action.equals("login")){ //用户登录,获得用户名。然后创建两个session变量,保存用户登录信息和聊天信息 String username = request.getParameter("username"); String msg="欢迎 "+username+" 光临本聊天室!<br>"; session.setAttribute("username",username); msgs.put(username, msg); response.sendRedirect("main.html"); } if(action.equals("write")){ String newMsg = session.getAttribute("username")+ ": " +(String)request.getParameter("msg"); //发送消息时,将聊天室所有人的消息都加上新的发言内容 Iterator it = msgs.keySet().iterator(); String username = null; String msg = null; while (it.hasNext()){ username = (String)it.next(); msg = (String)msgs.get(username); msg = msg+"<br>"+ newMsg; msgs.put(username, msg); } response.sendRedirect("writeMes.jsp"); } if(action.equals("show")){ //显示某个用户的消息 String username = (String)session.getAttribute("username"); String msg = (String)msgs.get(username); out.println("loadData.innerHTML=\""+msg+"\";"); } %>

writeMes.jsp写消息的JSP页面,具体代码如下:

<%@ page contentType="text/html; charset=gb2312" language="java"
pageEncoding="GB2312"%>
<html>
<head>
<title></title>
</head>
<body bgcolor="#FCE6C9">
<form action="/My_Servlet/lession20/loadMes.jsp?action=write" method="post" name="form1">
<font style="FONT-SIZE:18px;COLOR:#FF6347"><%=session.getAttribute("username")%>:</font>
<input name="msg" type="text" id="msg" size="60">
  <input type="submit" name="Submit" value="发 言">
</form>
</body>
</html>

图20-4 登录界面

(3)运行结果:将以上5个文件放在lession20文件夹下,再把lession20文件夹放在Tomcat安装目录的/webapps/My_Servle目录下,在浏览器的地址栏中输入http://localhost:8080/My_Servlet/lession20/log.html,访问聊天室登录页面,得到的界面如图20-4所示。

(4)登录聊天室后,在下面的输入框中输入聊天信息,单击“发言”按钮,开始聊天,如图20-5所示。

(5)新开一个浏览器,在地址栏中同样输入http://localhost:8080/My_Servlet/lession20/log.html,用不同的用户名登录聊天室,得到的界面如图20-6所示。

图20-5 进入聊天室主界面

图20-6 另一用户登录聊天室

(6)后进来的用户发言,如图20-7所示。

(7)此时先进来的用户能看到后进来的用户的聊天信息,如图20-8所示。

图20-7 另一用户聊天记录

图20-8 多人聊天记录

源程序解读

(1)login.html的作用是使用户通过用户名和密码登录聊天室,由于表单name属性为form1的action指向了/My_Servlet/lession20/loadMes.jsp?action=login,表示当用户单击“进入”按钮进入聊天室时,会自动跳转到loadMes页面,除了把username文本框的值传给loadMes.jsp外,还传入了action参数login。

(2)loadMes.jsp页面的作用是专门处理聊天室的各种请求。<jsp:useBean id="msgs"class="java.util.HashMap"scope="application" />语句表示在整个应用程序范围内,id为msgs的HashMap对象都有效,HashMap的主要作用是存储所有用户的聊天信息。其中用户名为key,聊天信息为value。

• request的setCharacterEncoding方法将请求消息中的字符集编码设为GBK,目的是能够正确地解析中文。

• 通过request的getParameter方法获得请求的action参数,如果action为login,则表示这是一个登录请求,再次通过getParameter方法获得login.html表单中的username参数,把username保存到session变量中,属性名为username。然后调用request的sendRedirect方法将请求重定向给main.html。

• 如果请求的action参数为send,则表示这是一个发言的请求,首先通过request的getParameter方法获得发言的内容,然后通过session的getAttribute方法获得发言者的username,接着将发言者和发言内容组成一条完整的信息,添加到所有用户的聊天内容中,最后把请求重定向到inputMsg.jsp。

• 如果请求的action参数为show,则表示这是一个显示聊天信息的请求,从session中取得用户的username,把以它为key,从id为msgs的HashMap中取出它的聊天信息。

(3)main.html是聊天室的用户界面,使用frame技术把页面分为上下两部分,中间用一个分隔栏隔开,可以调整上下两个页面的高度。上一部分为show.html,显示聊天内容;下一部分为writeMes.jsp,输入聊天信息,然后发言。

(4)writeMes.jsp页面很简单,由一个名称和一个文本输入框以及一个“发言”按钮组成。由于在该JSP页面form表单中的action指向了“/My_Servlet/lession20/loadMes.jsp?action=write" method="post" name="form1”,所以当用户单击“发言”按钮时,会将请求交给loadMes.jsp页面去处理。

(5)show.html则负责显示用户的聊天内容,通过JavaScript函数,由span控件的src属性去调用loadMes.jsp,通过setTimeout函数定期调用GetData函数,便实现了聊天内容的自动刷新。

实例201 Servlet生成的动态图片

本实例利用Servlet随机生成不同颜色的图像,并且能够不断地变化着,形成一幅类似于gif格式的动态图片。启动Tomcat后,在IE地址栏里输入http://localhos:3306t/My_Servlet/D_servlet.jsp之后就可以看到相应的JSP页面。

技术要点

Servlet生成的动态图片的技术要点如下:

• JPEGImageEncoder接口的使用。

• BufferedImage类的使用。

• Graphics类的常见方法的使用。

• 利用随机数动态地获取颜色的方式。

实现步骤

(1)新建一个类名为DongTaiServlet.java。

(2)代码如下所示。

DongTaiServlet.java处理如何生成动态图片信息,具体代码如下:

package chp20;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGImageEncoder;
public class DongTaiServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
              throws ServletException, IOException {
          //首先设置页面不缓存
          response.setHeader("Pragma", "No-cache");
          response.setHeader("Cache-Control", "no-cache");
          response.setDateHeader("Expires", 0);
          response.setContentType("image/jpeg");
          //注意这里的MIME 类型
          ServletOutputStream out = response.getOutputStream();
          //构造一个缓冲图像
          BufferedImage image = new BufferedImage(750, 30,
                    BufferedImage.TYPE_INT_RGB);
          int num = (int) (Math.random() * 50) + 1;
          int a = (int) (Math.random() * 300) + 1;
          int b = (int) (Math.random() * 300) + 1;
        Graphics graphics = image.getGraphics();
        graphics.setColor(getRandColor(Math.min(a, b), Math.max(a, b)));
        graphics.fillRect(0, 0, 750, 30);
        graphics.setColor(getRandColor(Math.min(a, b), Math.max(a, b)));
        graphics.fillRect(0, 0, 750 * num / 100, 30);
        JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
        encoder.encode(image);
        out.close();
    }
        //处理 Http Post request和doGet一样
    public void doPost(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
        doGet(request, response);
    }
        //动态获取颜色
    public Color getRandColor(int min, int max) {
        Random random = new Random();
        if (min > 255) {
            min = 255;
        }
        if (max > 255) {
            max = 255;
        }
        int R = min + random.nextInt(max - min);
        int G = min + random.nextInt(max - min);
        int B = min + random.nextInt(max - min);
        return new Color(R, G, B);
    }
}

D_servlet.jsp将图片显示出来的JSP文件,具体代码如下:

<%@ page contentType="text/html;charset=gb2312"%>
<html>
    <head>
          <title>Servlet生成图片</title>
          <meta http-equiv="content-type" content="text/html; charset=gb2312">
    </head>
    <%
    String contextPath = request.getContextPath();
    %>
    <body>
          <hr width="100%">
          <center>
              <font style="FONT-SIZE:30px;COLOR:#FF8000"><B>Servlet生成的动态图片</B>
              </font>
          </center>
          <br>
          <br>
          <br>
          <center>
              <img id="qq" src="<%=contextPath%>/DongTaiServlet" height=30% width=50%>
        </center>
    </body>
    <script type="text/javascript">
        setInterval(bb,1000)                    //隔1000毫秒执行一次
    function bb(){
          document.getElementById("qq").src="<%=contextPath%>/DongTaiServlet";
    }
    </script>
    </html>

配置文件web.xml,具体代码如下:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
    <display-name>My Web Application</display-name>
    <description>A application for test.</description>
          <servlet>
          <servlet-name>DongTaiServlet</servlet-name>
          <servlet-class>
              chp20.DongTaiServlet
          </servlet-class>
    </servlet>
    <servlet-mapping>
          <servlet-name>DongTaiServlet</servlet-name>
          <url-pattern>/DongTaiServlet</url-pattern>
</servlet-mapping>
</web-app>

图20-9 动态图片

(3)运行结果如图20-9所示。

源程序解读

(1)doGet()方法的主要作用是获取HTTP的请求,通过response.setContentType("image/jpeg");语句来设置将发送到客户端的响应的内容类型,注意:mage/jpeg是MIME类型。由于servlet容器本身是不会编码二进制数据的,所以ServletOutputStream out = response.getOutputStream();语句的作用是返回适用于在响应中编写二进制数据的ServletOutputStream,ServletOutputStream类是一个提供用于将二进制数据发送到客户端的输出流。然后构造一个缓冲图像BufferedImage。利用Graphics将图像画出。

(2)getRandColor()的作用是动态获取颜色。利用Random类随机产生两个随机数,然后利用Math类的min()和max()方法,求出两数之间的最大数和最小数,接着通过Random类的nextInt方法返回下一个int型的伪随机数。最后利用产生的3个随机数作为Color类构造方法的参数,产生一个随机的颜色。

(3)JPEGImageEncoder接口的使用。JPEGImageEncoder是将图像缓冲数据编码为JPEG数据流。JPEGImageEncoder 接口可将用户在Raster或BufferedImage生成的图像数据编码转换成JPEG数据流,该数据流将写入提供给编码器的OutputStream中。

实例202 简单的JSP上传文件

jspSmartUpload是由www.jspsmart.com网站开发的一个可免费使用的全功能的文件上传下载组件,适于嵌入执行上传下载操作的jsp文件中。该组件具有使用简单、能全程控制上传、能对上传文件的大小和类型做限制、下载灵活和可以将文件上传到数据库并可以从数据库中下载出来5个特点。

本实例将演示如何使用jspSmartUpload组件,实现在JSP页面中上传文件的功能。在演示此功能前,需到www.jspsmart.com网站上自由下载jspSmartUpload组件,压缩包的名字是jspSmartUpload.zip。下载后,将解压后的jspsmartupload.jar放入Tomcat服务器/自定义工程/WEB-INF/lib下,则此工程中的所有Java程序都能使用jspSmartUpload组件了。

技术要点

用JSP实现上传文件的技术要点如下:

(1)File类:该类包装了一个上传文件的所有信息。通过它,可以得到上传文件的文件名、文件大小、扩展名、文件数据等信息。常用的方法如下:

• saveAs(java.lang.String destFilePathName, int optionSaveAs)方法将文件以另外一个名字或目录存储。其中,destFilePathName表示需要指定另存的文件名,optionSaveAs表示另存的选项。

• optionSaveAs有三个值,分别是SAVEAS_PHYSICAL、SAVEAS_VIRTUAL和SAVEAS_AUTO。SAVEAS_PHYSICAL表示以操作系统的根目录为文件根目录另存文件,SAVEAS_VIRTUAL表示以Web应用程序的根目录为文件根目录另存文件, SAVEAS_AUTO则表示让组件决定,当Web应用程序的根目录存在另存文件的目录时,它会选择SAVEAS_VIRTUAL,否则会选择SAVEAS_PHYSICAL。

• isMissing()方法用于判断用户是否选择了文件,也即对应的表单项是否有值。选择了文件时,它返回false。未选文件时,它返回true。

• getFileName()方法获得上传文件的名字。

• getFilePathName()方法获得上传文件的全名(包括路径)。

• getFileExt()方法获得上传文件的扩展名。

• getSize()方法获得上传文件的大小。

• getBinaryData(int index)。其中,index表示位移,其值在0到getSize()-1之间。

(2)Files类:表示所有上传文件的集合,通过它可以得到上传文件的数目、大小等信息。常用的方法如下:

• getCount()方法获得上传文件的数目。

• getFile()方法获得某个被上传的文件。

• getSize()方法获得所有上传文件的总长度。

• getCollection()方法将所有上传文件对象以Collection的形式返回。

• getEnumeration()方法将所有上传文件对象以Enumeration(枚举)的形式返回。

(3)Request类:该类与JSP内置对象request的作用相同,用于获取表单项的值。常用的方法如下:

• getParameter(String name)方法获取指定参数的值。其中,name为参数的名字,当参数不存在时,返回值为null。

• getParameterValues(String name)方法。当一个参数可以有多个值时,用此方法来取其值。它返回的是一个字符串数组。其中,name为参数的名字,当参数不存在时,返回值为null。

• getParameterNames()方法获取Request对象中所有参数的名字,用于遍历所有参数。它返回的是一个枚举型(Enumeration)的对象。

(4)SmartUpload类:该类是jspSmartUpload组件的核心,主要完成上传下载工作。常用的方法如下:

• initialize(javax.servlet.jsp.PageContext pageContext)方法执行上传的初始化工作,必须第一个执行。其中,参数pageContext为JSP页面内置对象。

• upload()方法上传文件数据,对于上传操作,先执行initialize方法,紧接着就要执行该方法。

• save()方法将全部上传文件保存到指定目录下,并返回保存的文件个数。参数类型与saveAs相同。

• getSize()方法获取上传文件数据的总长度。

• getFiles()方法获取全部上传文件,以Files对象形式返回。

• getRequest()方法取得Request对象,以便由Request对象获得上传表单参数的值。

• setAllowedFilesList(String allowedFilesList)方法设定允许上传带有指定扩展名的文件,当上传过程中有文件名不允许时,组件将抛出异常。其中,allowedFilesList为允许上传的文件扩展名列表,各个扩展名之间以逗号分隔。如果想允许上传那些没有扩展名的文件,可以用两个逗号表示。例如,setAllowedFilesList("jpg,txt,")将允许上传带jpg和txt扩展名的文件以及没有扩展名的文件。

• setDeniedFilesList(String deniedFilesList)方法用于限制上传那些带有指定扩展名的文件。若有文件扩展名被限制,则上传时组件将抛出异常。其中,deniedFilesList为禁止上传的文件扩展名列表,各个扩展名之间以逗号分隔。如果想禁止上传那些没有扩展名的文件,可以用两个逗号来表示。例如,setDeniedFilesList("exe,")将禁止上传带exe扩展名的文件以及没有扩展名的文件。

• setMaxFileSize(long maxFileSize)方法设定每个文件允许上传的最大长度。当文件超出此长度时,将不被上传。其中,参数maxFileSize为每个文件允许上传的最大长度,当文件超出此长度时,将不被上传。

• setTotalMaxFileSize(long totalMaxFileSize)方法设定允许上传的文件的总长度,用于限制一次性上传的数据量大小。其中,参数totalMaxFileSize为允许上传的文件的总长度。

实现步骤

(1)新建一个类名为send.jsp。

(2)代码如下所示。

send.jsp选择上传文件的界面,具体代码如下:

<%@ page contentType="text/html; charset=gb2312" language="java"%>
<html>
    <head>
        <title>文件上传</title>
        <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
    </head>
    <body>
        <p>
            &nbsp;
        </p>
        <p align="center">
            <font style="FONT-SIZE:20px;COLOR:#B03060">选择上传文件</font>
        </p>
        <FORM METHOD="POST" ACTION="downlog.jsp" ENCTYPE="multipart/form-data">
            <table width="75%" border="1" align="center" bgcolor="#FDF5E6"
                  bordercolor="#6B8E23">
                  <tr>
                      <td>
                            <div align="center">
                                <font style="FONT-SIZE:15px;COLOR:#FF8000">上传文件1:</font>
                                <input type="File" name="file_1" size="40">
                            </div>
                      </td>
                  </tr>
                  <tr>
                      <td>
                            <div align="center">
                                <font style="FONT-SIZE:15px;COLOR:#FF8000">上传文件2:</font>
                                <input type="File" name="file_2" size="40">
                            </div>
                      </td>
                  </tr>
                  <tr>
                      <td>
                            <div align="center">
                                <font style="FONT-SIZE:15px;COLOR:#FF8000">上传文件3:</font>
                                <input type="File" name="file_3" size="40">
                            </div>
                      </td>
                  </tr>
                  <tr>
                      <td>
                            <div align="center">
                                <font style="FONT-SIZE:15px;COLOR:#FF8000">上传文件4:</font>
                                <input type="File" name="file_4" size="40">
                            </div>
                      </td>
                  </tr>
                  <tr>
                      <td>
                            <div align="center">
                                <font style="FONT-SIZE:15px;COLOR:#FF8000">上传文件5:</font>
                                <input type="File" name="file_5" size="40">
                            </div>
                        </td>
                    </tr>
                    <tr>
                        <td>
                            <div align="center">
                                  <font style="FONT-SIZE:15px;COLOR:#0000FF"><B>上传账户:</B>
                                  </font>
                                  <input name="username" type="text">
                                  <input type="submit" name="Submit" value="上 传">
                            </div>
                        </td>
                    </tr>
              </table>
          </FORM>
    </body>
</html>

downlog.jsp处理上传操作后的界面,具体代码如下:

<%@ page contentType="text/html; charset=gb2312" language="java"
import="com.jspsmart.upload.*"%>
<html>
    <head>
          <title>文件上传</title>
          <meta http-equiv="Content-Type" content="text/html; charset=gb2312">
    </head>
    <body>
          <% //新建一个SmartUpload对象
              SmartUpload la= new SmartUpload();
              //上传初始化
              la.initialize(pageContext);
              //设定上传限制
              //1.限制每个上传文件的最大长度15MB
              la.setMaxFileSize(15 * 1024 * 1024);
              //2.限制总上传数据的长度为100 MB
              la.setTotalMaxFileSize(100 * 1024 * 1024);
              //3.设定允许上传的文件(通过扩展名限制),仅允许txt,mp3,wmv,jpg,doc文件
              la.setAllowedFilesList("txt,mp3,wmv,jpg,doc");
              //4.设定禁止上传的文件(通过扩展名限制),禁止上传带有exe,bat,
              //jsp,htm,html扩展名的文件和没有扩展名的文件
              la.setDeniedFilesList("exe,bat,jsp,htm,html,,");
              //上传文件
              la.upload();
              //将上传文件全部保存到指定目录
              //注意这个目录是虚拟目录,相对于Web应用的根目录
              int count = la.save("/lession20");
              out.println("<font style='FONT-SIZE:35px;COLOR:#FF4500'><B>"+count
+ "个文件上传成功!</B></font><br>");
              //利用Request对象获取参数之值
              out.println("<BR><font style='FONT-SIZE:25px;COLOR:#FF4500'><B>上传账户:"
                                  + la.getRequest().getParameter("uploadername")
                                  + "</B></font><BR><BR>");
              //逐一提取上传文件信息,同时可保存文件
              for (int i = 0; i < la.getFiles().getCount(); i++) {
                    com.jspsmart.upload.File file = la.getFiles().getFile(i);
              //若文件不存在则继续
                    if (file.isMissing()) {
                        continue;
                    }
              //显示当前文件信息
                    out.println("<TABLE BORDER=1 width=40% bgcolor=#FDF5E6
bordercolor=#6B8E23 >");
                    out.println("<TR><Th><font style='FONT-SIZE:15px;COLOR:# FA8072'>
表单项名</font></Th><TD>"
                              + file.getFieldName() + "</TD></TR>");
                    out.println("<TR><Th><font style='FONT-SIZE:15px;COLOR:# FA8072'>
文件长度</font></Th><TD>" + file.getSize()
                              + " Byte</TD></TR>");
                    out.println("<TR><Th><font style='FONT-SIZE:15px;COLOR:# FA8072'>
文件名</font></Th><TD>"
                              + file.getFileName() + "</TD></TR>");
                    out.println("<TR><Th><font style='FONT-SIZE:15px;COLOR:#FA8072'>
文件扩展名</font></Th><TD>"
                              + file.getFileExt() + "</TD></TR>");
                    out.println("<TR><Th><font style='FONT-SIZE:15px;COLOR:#FA8072'>
文件来源</font></Th><TD>"
                              + file.getFilePathName() + "</TD></TR>");
                    out.println("</TABLE><BR>");
              //将文件另存为路径是相对于Web应用的根目录
                    file.saveAs("/lession20/saveas/" + file.getFileName(),
                              SmartUpload.SAVE_VIRTUAL);
              //另存到操作系统的根目录为文件根目录的目录下
              //SmartUpload.SAVE_PHYSICAL指定了采用物理路径
                    file.saveAs("D:/temp/upload/" + file.getFileName(),
                              SmartUpload.SAVE_PHYSICAL);
              }
          %>
    </body>
</html>

(3)运行结果如下所示。

启动Tomcat后,在输入栏中输入http://127.0.0.1:8080/My_Servlet/lession20/send.jsp,便出现相应界面,如图20-10所示。上传之后的页面如图20-11所示。

图20-10 上传文件到服务器

图20-11 上传文件的结果界面

源程序解读

(1)send.jsp页面中,包含5个File控件,通过File控件让用户选择本地的文件,然后在文本框中输入上传的账户,单击“上传”按钮,将请求交给downlog.jsp去处理。

(2)downlog.jsp页面处理文件的上传。新建一个SmartUpload对象,通过initialize方法初始化。

• SmartUpload对象的setMaxFileSize()方法的作用是设置它能上传的单个文件的最大字节数为15MB。

• SmartUpload对象的setTotalMaxFileSize()方法的作用是设置它能上传的所有文件的总上传数据的最大字节数为100MB。

• SmartUpload对象的setAllowedFilesList()方法的作用是仅允许上传扩展名为txt、mp3、wmv、jpg、doc的文件。

• SmartUpload对象的upload()方法的作用是上传文件的数据。

• SmartUpload对象的save ()方法的作用是完成文件的保存,返回保存成功的文件数。

(3)对于上传文件的FORM表单,有三个要求:

• METHOD必须使用POST,即METHOD="POST"。

• 增加属性:ENCTYPE="multipart/form-data"。

• 使用File控件,方便用户选择待上传的文件。

除了通过SmartUpload的save方法一次保存全部文件外,还可以使用File的saveas方法保存文件,File的isMissing方法判断待上传的文件是否还存在。

实例203 用Servlet获取Web服务器信息

本实例将演示如何利用Servlet来获取Web服务器的相应信息,包括用户发送给服务器的请求行和头部信息,以及一些可以访问的HTTP信息。

技术要点

用Servlet获取Web服务器信息的技术要点如下:

• javax.servlet.http.HttpServlet类的应用。javax.servlet.http包中包含许多类和接口,这些类和接口描述并定义了HTTP协议下运行的Servlet类与相应Servlet容器为此类的实例提供的运行时环境之间的协定。其中接口包括HttpServletRequest、HttpServletResponse和HttpSession等;类包括Cookie和HttpServlet等。

• Javax.servlet类包的应用。

• 远程主机服务器的访问。

实现步骤

(1)新建一个类名为GetWEBMessServlet.java。

(2)代码如下所示。

GetWEBMessServlet.java处理获取Web服务器信息,具体代码如下:

package chp20;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import javax.servlet.*;
import javax.servlet.http.*;
public class GetWEBMessServlet extends HttpServlet {
    public void doGet(HttpServletRequest request, HttpServletResponse response)
              throws ServletException, IOException {
          response.setContentType("text/plain;charset=GB2312");
          request.setCharacterEncoding("GB2312");
          PrintWriter out = response.getWriter();
          out.println("用Servlet获取Web服务器信息");
          out.println();
          out.println("Servlet参数初始化:");
          Enumeration e = getInitParameterNames();
          while (e.hasMoreElements()) {
              String key = (String) e.nextElement();
              String value = getInitParameter(key);
              out.println(" " + key + " = " + value);
          }
          out.println();
          out.println("Context参数初始化:");
          ServletContext context = getServletContext();
          Enumeration num1 = context.getInitParameterNames();
          while (num1.hasMoreElements()) {
              String key = (String) num1.nextElement();
              Object value = context.getInitParameter(key);
              out.println(" " + key + " = " + value);
          }
          out.println();
          out.println("Context属性:");
          num1 = context.getAttributeNames();
          while (num1.hasMoreElements()) {
              String key = (String) num1.nextElement();
              Object value = context.getAttribute(key);
              out.println(" " + key + " = " + value);
          }
          out.println();
          out.println("Request属性:");
          e = request.getAttributeNames();
          while (e.hasMoreElements()) {
              String key = (String) e.nextElement();
              Object value = request.getAttribute(key);
              out.println(" " + key + " = " + value);
          }
          out.println();
          out.println("Servlet名称: " + getServletName());
          out.println("协议: " + request.getProtocol());
          out.println("配置: " + request.getScheme());
          out.println("Server名称: " + request.getServerName());
          out.println("Server端口: " + request.getServerPort());
          out.println("Server信息: " + context.getServerInfo());
          out.println("远程地址: " + request.getRemoteAddr());
        out.println("远程主机: " + request.getRemoteHost());
        out.println("编码方式: " + request.getCharacterEncoding());
        out.println("内容长度: " + request.getContentLength());
        out.println("内容类型: " + request.getContentType());
        out.println("本地机: " + request.getLocale());
        out.println("默认缓冲区大小: " + response.getBufferSize());
        out.println();
        out.println("本次请求的参数名称:");
        e = request.getParameterNames();
        while (e.hasMoreElements()) {
            String key = (String) e.nextElement();
            String[] values = request.getParameterValues(key);
            out.print(" " + key + " = ");
            for (int i = 0; i < values.length; i++) {
                  out.print(values[i] + " ");
            }
            out.println();
        }
        out.println();
        out.println("本次请求的头部信息:");
        e = request.getHeaderNames();
        while (e.hasMoreElements()) {
            String key = (String) e.nextElement();
            String value = request.getHeader(key);
            out.println(" " + key + ": " + value);
        }
        out.println();
        out.println("本次请求中的Cookies:");
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (int i = 0; i < cookies.length; i++) {
                  Cookie cookie = cookies[i];
                  out.println(" " + cookie.getName() + " = " + cookie.getValue());
            }
        }
        out.println();
        out.println("Request Is Secure: " + request.isSecure());
        out.println("Auth类型: " + request.getAuthType());
        out.println("HTTP方法: " + request.getMethod());
        out.println("远程用户: " + request.getRemoteUser());
        out.println("请求URI: " + request.getRequestURI());
        out.println("Context路径: " + request.getContextPath());
        out.println("Servlet路径: " + request.getServletPath());
        out.println("路径信息: " + request.getPathInfo());
        out.println("路径转化: " + request.getPathTranslated());
        out.println("查询串: " + request.getQueryString());
        out.println();
        HttpSession session = request.getSession();
        out.println("请求会话Id: " + request.getRequestedSessionId());
        out.println("当前会话Id: " + session.getId());
        out.println("会话创建时间: " + session.getCreationTime());
        out.println("会话最后访问时间: " + session.getLastAccessedTime());
        out.println("会话最大停止时间间隔: " + session.getMaxInactiveInterval());
        out.println();
        out.println("会话值: ");
        Enumeration names = session.getAttributeNames();
        while (names.hasMoreElements()) {
            String name = (String) names.nextElement();
            out.println(" " + name + " = " + session.getAttribute(name));
        }
    }
}

配置文件web.xml,具体代码如下:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
    <display-name>My Web Application</display-name>
    <description>A application for test.</description>
    <servlet>
          <servlet-name>GetWEBMessServlet</servlet-name>
          <servlet-class>
              chp20.GetWEBMessServlet
          </servlet-class>
    </servlet>
    <servlet-mapping>
          <servlet-name>GetWEBMessServlet</servlet-name>
          <url-pattern>/GetWEBMessServlet</url-pattern>
    </servlet-mapping>
</web-app>

(3)运行结果如图20-12所示。

图20-12 获取Web信息

源程序解读

GetWEBMessServlet类的主要作用是利用HttpServletRequest接口处理一个对Servlet的HTTP格式的请求信息。客户请求的信息是由HttpServletRequest类提供的方法返回的。能够获取Web服务器信息的方法如下:

• getInitParameterNames():Servlet参数初始化,返回一个Enumeration对象。此对象的作用与Iterator接口相同。

• getServletContext():Context参数初始化。

• getAttributeNames():获取属性,同样返回一个Enumeration对象。

• request.getHeaderNames():本次请求的头部信息。

• getServletName():Servlet名称。

• request.getProtocol():协议。

• request.getScheme():配置。

• request.getServerName():Server名称。

• request.getServerPort():Server端口。

• context.getServerInfo():Server信息。

• request.getRemoteAddr():远程地址。

• request.getRemoteHost():远程主机。

• request.getCharacterEncoding():编码方式。

• request.getContentLength():内容长度。

• request.getContentType():内容类型。

• request.getLocale():本地机。

• response.getBufferSize():默认缓冲区大小。

• request.getRemoteHost():远程主机服务器的访问。

• request.getMethod():HTTP方法。

• request.getRemoteUser():远程用户。

• request.getRequestedSessionId():请求会话。

• ession.getId():当前会话。

• session.getCreationTime():会话创建时间。

• session.getLastAccessedTime():会话最后访问时间。

• session.getMaxInactiveInterval():会话最大停止时间间隔。

实例204 可选择的图形验证码

本实例将演示如何使用Servlet技术生成图形的验证码,当用户访问JSP时,会出现一个图形验证码,如果感觉看不清楚,可以单击“换一个”按钮,重新生成图形的验证码。

技术要点

用Servlet生成图形验证码的技术要点如下:

(1)HttpServlet类是一个抽象类,扩展了GenericServlet类。HttpServlet类用于处理HTTP请求的Servlet。一个HttpServlet的子类必须至少重载以下方法中的一个。

• doGet()方法,适用于HTTP GET请求。

• doPost()方法,适用于HTTP POST请求。

• doPut()方法,适用于HTTP PUT请求。

• doDelete()方法,适用于HTTP DELETE请求。

• init()和destroy()方法,管理Servlet生命周期中的资源。

• getServletInfo()方法,提供Servlet本身的信息。

(2)Web服务器在调用HttpServlet的doGet和doPost方法时,会传入两个参数:

• HttpServletRequest:封装了HTTP的请求消息,相当于JSP内置的request对象。

• HttpServletResponse:封装了HTTP的响应消息,相当于JSP内置的response对象。

(3)使用HTML页面中的IMG标签,通过它的src属性访问Servlet,获得验证码的图片。

(4)可以使用BufferedImage类保存图像,调用该类的createGraphics方法创建一个Graphics对象,利用Graphics在图像上画四位数字和干扰线。

(5)用Random类生成随机的数字,将生成的数字作为Color类的参数,生成RGB颜色。

(6)ImageIO的write方法将生成好的BufferedImage对象保存到Response对象的输出流中。

(7)重新获取验证码时,则再次请求Servlet。

实现步骤

(1)新建一个类名为Code_MakerServlet.java。

(2)代码如下所示。

Code_MakerServlet.java生成图形验证码的Servlet,具体代码如下:

package chp20;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Code_MakerServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
              throws ServletException, IOException {
          doPost(request, response);
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
              throws ServletException, IOException {
          //首先设置页面不缓存
          response.setHeader("Pragma", "No-cache");
          response.setHeader("Cache-Control", "no-cache");
          response.setDate
              int x = random.nextInt(width);
              int y = random.nextInt(height);
              g.drawOval(x, y, 5, 5);
          }
        g.setFont(new Font("", Font.PLAIN, 40));      //设置字体,下面准备画随机数
        String sRand = ""; //随机数字符串
        for (int i = 0; i < 4; i++) {
                                                      //生成四个数字字符
            String rand = String.valueOf(random.nextInt(10));
            sRand += rand;
                                                      //生成随机颜色
            g.setColor(new Color(20 + random.nextInt(80), 20 + random
                      .nextInt(100), 20 + random.nextInt(90)));
                                                      //将随机数字画在图像上
            g.drawString(rand, (17 + random.nextInt(3)) * i + 8, 34);
                                                      //生成干扰线
            for (int k = 0; k < 12; k++) {
                  int x = random.nextInt(width);
                  int y = random.nextInt(height);
                  int xl = random.nextInt(9);
                  int yl = random.nextInt(9);
                  g.drawLine(x, y, x + xl, y + yl);
            }
        }
                                                      //将生成的随机数字字符串写入session
        request.getSession().setAttribute("randomNumber", sRand);
        g.dispose();                                  //使图像生效
        ImageIO.write(image, "JPEG", response.getOutputStream()); //输出图像到页面
    }
    //产生一个随机的颜色 其中,min表示颜色分量最小值;max表示颜色分量最大值
    public Color getRandColor(int min, int max) {
        Random random = new Random();
        if (min > 255) {
            min = 255;
        }
        if (max > 255) {
            max = 255;
        }
        int R = min + random.nextInt(max - min);
        int G = min + random.nextInt(max - min);
        int B = min + random.nextInt(max - min);
        return new Color(R, G, B);
    }
}

ImageCode.jsp显示图形验证码的JSP,具体代码如下:

<%@ page language="java" contentType="text/html; charset=GB2312"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
          <title>验证码示例</title>
    </head>
    <%
    String contextPath = request.getContextPath();
    %>
    <body>
        <form name="form1" method="post" action="">
              <center>
                  <font style="FONT-SIZE:20px;COLOR:#FF6347"><B>登录</B> </font>
              </center>
              <table width="50%" height="30%" border="1" align="center"
                  cellpadding="0" cellspacing="0" bgcolor=#F8F8FF bordercolor=#A020F0>
                  <tr>
                        <th>
                            <font style="FONT-SIZE:15px;COLOR:#FF8000">用户名:</font>
                        </th>
                        <td align="left">
                            <input name="username" type="text">
                        </td>
                  </tr>
                  <tr>
                        <th>
                            <font style="FONT-SIZE:15px;COLOR:#FF8000">登录密码:</font>
                        </th>
                        <td align="left">
                            <input name="password" type="text">
                        </td>
                  </tr>
                  <tr>
                        <th>
                            <font style="FONT-SIZE:15px;COLOR:#FF8000">输入验证码:</font>
                        </th>
                        <td align="left">
                            <input name="randomNumber" type="text">
                            <IMG id="img1" src="<%=contextPath%> /Code_MakerServlet"
                                  height=40 width=90>&nbsp;
                            <input type="button" name="Submit" value="换一个" onclick="aa()">
                        </td>
                  </tr>
                  <tr>
                        <td align="center" colspan="2">
                            <input type="submit" name="Submit" value="进 入">
                        </td>
                  </tr>
              </table>
        </form>
        <script type="text/javascript">
        function aa(){
              d ocument.getElementById("img1").src="<%=contextPath%> /Code_MakerServlet";
        }
        </script>
    </body>
</html>

配置文件Web-INF/web.xml,具体代码如下:

<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
    <display-name>My Web Application</display-name>
    <description>A application for test.</description>
    <servlet>
          <servlet-name>Code_MakerServlet</servlet-name>
          <servlet-class>
              chp20.Code_MakerServlet
          </servlet-class>
    </servlet>
    <servlet-mapping>
          <servlet-name>Code_MakerServlet</servlet-name>
          <url-pattern>/Code_MakerServlet</url-pattern>
    </servlet-mapping>
</web-app>

(3)本程序第一次运行时出现的页面,如图20-13所示。

(4)若感觉图形验证码模糊不清,单击“换一个”按钮就可以更换,如图20-14所示。

图20-13 具有图形验证码的登录界面

图20-14 更换图形验证码

源程序解读

(1)Code_MakerServlet类的作用是生成图形验证码。Code_MakerServlet继承了HttpServlet,实现了doGet和doPost方法,来处理Get请求和Post请求。

• Response对象的setHeader方法设置响应消息的头信息,即不让浏览器缓存该页面,每次访问该页面时,都会用新的图形验证码。

• 新建一个BufferedImage对象,用它来保存生成的验证码图片。通过BufferedImage的createGraphics方法获得一个Graphics对象,通过Graphics为图片添加内容。如验证码、颜色,以及干扰线的位置都是用Random类随机生成的。图片生成完后,调用Graphics的dispose方法使图片生效,最后调用ImageIO的write方法把图片保存到Response对象的输出流中,保存的格式为JPEG。

• 通过Request对象的getSession方法获取Session对象,把生成的验证码保存到Session中,在进行登录验证时使用。

(2)在ImageCode.jsp页面中,使用IMG标签,设置它的src为<%=contextPath%>/Code_MakerServlet,表示该IMG的图像来自Servlet,Servlet的路径是Web工程的上下文与Servlet的URL的组合。<%=contextPath%> 表示的是“http://loacalhost:8080/工程名”Servlet的URL配置在web.xml文件中,为“/Code_MakerServle”,所以访问Code_MakerServle的路径是“<%=contextPath%>/Code_MakerServlet”。

(3)在WEB-INF/web.xml中,<servlet>标签用于配置Servlet的名字和实现类的类名,<servlet-name>定义Servlet的名称,<servlet-class>指的是Servlet类文件所在的位置,如果使用包则需加上包名,<servlet-mapping>标签用于配置Servlet的映射。其中<servlet-name>子标签的值必须和在<servlet>标签的<servlet-name>中定义过的Servlet名相同,<url-pattern>标签定义访问Servlet的URL。当用户访问的Servlet路径中包含该URL时,Web服务器便把请求交给该Servlet,根据Servlet名找到Servlet的实现类,最终由Servlet实现类完成程序功能。

实例205 简单的页面注册

本实例将演示如何通过JSP页面及Servlet制作一个简单的页面注册,如果用户访问login.jsp后不能成功登录,则会自动跳转到zhuce.jsp注册页面。注册成功后,可出现提示注册是否成功的页面;如果登录成功,则会转向显示会员个人注册时填写的详细信息页面。而loginServlet.java、zhuceServlet.java这两个文件主要是处理登录和注册信息的。

技术要点

在Servlet中实现JSP页面注册的技术要点如下:

• HttpServlet中的doGet()和doPost()方法的使用。

• 在Servlet中的数据库的连接方法。

• 添加记录函数及应用。

• 直接通过URL调用Servlet。

实现步骤

(1)新建一个类名为loginServlet.java。

(2)代码如下所示。

loginServlet.java处理登录信息的Servlet,具体代码如下:

package chp20;
//用servlet登录程序
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import java.sql.*;
public class loginServlet extends HttpServlet {
    static final private String CONTENT_TYPE = "text/html; charset=GBK";
    Connection connection;                                  //创建连接对象
    Vector userlogin;                                       //定义向量存入个人注册信息
    Vector userMessage;                                     //定义向量存入用户名及密码
    public void init() throws ServletException {
          connection = null;
          userlogin = new Vector();
          userMessage = new Vector();
    }
    public void doGet(HttpServletRequest request, HttpServletResponse response)
              throws ServletException, IOException {
          doPost(request, response);
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
              throws ServletException, IOException {
        String contextPath = request.getContextPath();
        String name = request.getParameter("username");     //获得登录用户名
        String password = request.getParameter("password"); //获得登录密码
        response.setContentType(CONTENT_TYPE);
        PrintWriter out = response.getWriter();
        String name1 = new String(name.getBytes("iso-8859-1"), "gb2312");
        out.println("<html>");
        out.println("<head><title>用户登录</title></head>");
        out.println("<body>");
        boolean loginning = false;
        if ((loginning = login(name, password)) == true) //判断数据库中是否有用户名
        {
              boolean right_pass = validate(password);      //检测用户密码是否正确
              if (right_pass) {
                  getUserInfo(name, password); //调用个人注册信息,分别赋值给字符串变量
                  System.out.println(userlogin.elementAt(0) + " "
                            + userlogin.elementAt(1));
                  String nich = String.valueOf(userlogin.elementAt(0));
                  String sex = String.valueOf(userlogin.elementAt(1));
                  String address = String.valueOf(userlogin.elementAt(2));
                  String mail = String.valueOf(userlogin.elementAt(3));
                  String phone = String.valueOf(userlogin.elementAt(4));
                  //显示个人注册信息
                  out
                            .println("<p align=center><FONT style='FONT-SIZE:25px;
COLOR:#FF0000'><B>会员个人信息</B></FONT></p>");
                  out
                            .println("<p align=center><FONT style='FONT-SIZE:23px;
COLOR:#FFA07A'><B>用户名:</B></FONT><FONT style='FONT-SIZE:20px;COLOR:#B0C4DE'>"
                                      + name1 + "</FONT></p>");
                  out
                            .println("<p align=center><FONT style='FONT-SIZE:23px;COLOR:
#FFA07A'><B>昵称:</B></FONT><FONT style='FONT-SIZE:20px;COLOR:#B0C4DE'>"
                                      + nich + "</FONT></p>");
                  out
                            .println("<p align=center><FONT style='FONT-SIZE:23px;COLOR:
#FFA07A'><B>性别:</B></FONT><FONT style='FONT-SIZE:20px;COLOR:#B0C4DE'>"
                                      + sex + "</FONT></p>");
                  out
                            .println("<p align=center><FONT style='FONT-SIZE:23px;COLOR:
#FFA07A'><B>居住地:</B></FONT><FONT style='FONT-SIZE:20px;COLOR:#B0C4DE'>"
                                      + address + "</FONT></p>");
                  out
                            .println("<p align=center><FONT style='FONT-SIZE:23px;COLOR:
#FFA07A'><B>E-mail:</B></FONT><FONT style='FONT-SIZE:20px;COLOR:#B0C4DE'>"
                                      + mail + "</FONT></p>");
                  out
                            .println("<p align=center><FONT style='FONT-SIZE:23px;COLOR:
#FFA07A'><B>电话:</B></FONT><FONT style='FONT-SIZE:20px;COLOR:#B0C4DE'>"
                                      + phone + "</FONT></p>");
              } else                 //处理密码不正确情况
              {
                  out.println("密码不正确!<br>");
                  out.println("<p align=center><a href='" + contextPath
                            + "/lession/login.jsp'>重新登录</a></p>");
              }
        } else                       //处理用户名不存在情况
        {
              out.println("用户名不存在!<br>");
              out.println("<p align=center><a href='" + contextPath
                        + "/lession/login.jsp'>重新登录</a></p>");
        }
        out.println("</body></html>");
    }
    public void destroy() {
    }
    public boolean login(String name, String password) {
        boolean loginning = false;
        try {
              Class.forName("com.mysql.jdbc.Driver");   //实例化JDBC-ODBC桥的驱动
              connection = DriverManager.getConnection( //连接数据库
                        "jdbc:mysql://localhost:3306/myuser", "root", "root");
              Statement statement = connection.createStatement();
              String sql = "select * from login where name = '" + name + "'";
              ResultSet resultset = statement.executeQuery(sql);
              int recordCount = 0;
              while (resultset.next()) {
                  recordCount++;
              }
              if (recordCount > 0)                //判断数据库表userbase中是否有用户信息
              {
                  userMessage.clear();
                  resultset.first();
                  userMessage.addElement(resultset.getString("name"));
                                                  //将个人信息存入向量对象userMessage中
                  userMessage.addElement(resultset.getString("password"));
                  loginning = true;
              }
        } catch (Exception ex) {
              ex.printStackTrace();
        }                                                       //捕捉异常
        return loginning;
    }
    public void getUserInfo(String name, String password) {
        try {
              Class.forName("com.mysql.jdbc.Driver");           //实例化JDBC-ODBC桥的驱动
              connection = DriverManager.getConnection(         //连接数据库
                        "jdbc:mysql://localhost:3306/myuser", "root", "root");
                                                                //创建Statement接口对象
              Statement statement = connection.createStatement();
              String sql = "select * from UserMess where name = '" + name
                        + "' and password = '" + password + "'";
              ResultSet resultset = statement.executeQuery(sql);
              resultset.next();
              userlogin.clear();
                                                        //将个人注册信息存入向量用户信息中
              userlogin.addElement(new String(resultset.getString("nich")
                        .getBytes("iso-8859-1"), "gb2312"));              //昵称0
              userlogin.addElement(new String(resultset.getString("sex")
                        .getBytes("iso-8859-1"), "gb2312"));              //性别1
              userlogin.addElement(new String(resultset.getString("address")
                        .getBytes("iso-8859-1"), "gb2312"));              //居住地2
              userlogin.addElement(new String(resultset.getString("mail")
                        .getBytes("iso-8859-1"), "gb2312"));              //E-mail3
              userlogin.addElement(new String(resultset.getString("phone")
                        .getBytes("iso-8859-1"), "gb2312"));              //电话4
          } catch (Exception ex)                                          //捕捉异常
          {
              ex.printStackTrace();
          }
    }
    public boolean validate(String password)                    //定义验证密码函数
    {
          boolean pass = false;
          if (password.equals(String.valueOf(userMessage.elementAt(1)))) {
              pass = true;
          }
          return pass;
    }
}

zhuceServlet.java处理注册信息的Servlet,具体代码如下:

package chp20;
//用servlet登录注册
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
import java.sql.*;
public class zhuceServlet extends HttpServlet {
    static final private String CONTENT_TYPE = "text/html; charset=GBK";
    Connection connection;
    //定义Connection接口对象connection
    public void init() throws ServletException {
    }
    public void doGet(HttpServletRequest request, HttpServletResponse response)
              throws ServletException, IOException {
          doPost(request, response);
    }
    public void doPost(HttpServletRequest request, HttpServletResponse response)
              throws ServletException, IOException {
    //获得个人注册信息并存入对应的字符串变量中
          String contextPath = request.getContextPath();
          String name = request.getParameter("name");
          String password = request.getParameter("password");
          String compass = request.getParameter("compass");
          String nich = request.getParameter("nich");
          String address = request.getParameter("address");
          String sex = request.getParameter("sex");
          String mail = request.getParameter("mail");
          String phone = request.getParameter("phone");
          String name1 = new String(name.getBytes("iso-8859-1"), "gb2312");
          response.setContentType(CONTENT_TYPE);
          PrintWriter out = response.getWriter();
          System.out.println(sex + " sex " + request.getParameter("sex"));
          out.println("<html>");
          out.println("<head><title>用户注册</title></head>");
          out.println("<body>");
          boolean existed = checkUser(name);          //检测数据库中是否存在重名
          if (existed)                                //有重名处理情况
          {
              out.println("<p align=center>对不起,此用户名已经存在!</p>");
              out.println("<p align=center><a href='" + contextPath
                        + "/lession/zhuce.jsp'>返回</a></p>");            //返回注册页
          } else                                       //不存在重名处理情况
          {
              boolean identical = checkPass(password, compass);
                                                       //检测密码和密码确认是否一致
              if (!identical)                          //处理密码不一致情况
              {
                  out.println("<p align=center>密码不一致!</p>");
                  out.println("<p align=center><a href='" + contextPath
                            + "/lession/zhuce.jsp'>返回</a></p>");
              } else                                   //处理密码一致情况
              {
                  String sql_base = "insert into login(name, password) values ('"
                            + name + "', '" + password + "')";
                  String sql_info = "insert into UserMess(name, password, nich,
address, sex, mail, phone) values ";
                  sql_info += "('" + name + "', ";
                  sql_info += "'" + password + "', ";
                  sql_info += "'" + nich + "', ";
                  sql_info += "'" + address + "', ";
                  sql_info += "'" + sex + "', ";
                  sql_info += "'" + mail + "', ";
                  sql_info += "'" + phone + "')";
                  boolean success = addRecord(sql_base, sql_info);
                  //向表userbase和userinfo中添加个人注册信息
                  if (success) {
                        out
                                  .println("<CENTER><FONT style='FONT-SIZE:30px;
COLOR:#FF0000'>恭喜,"
                                            + name1 + " 注册成功!</FONT></CENTER><br>");
                    } else {
                        out
                                  .println("<CENTER><FONT style='FONT-SIZE:30px;
COLOR:#808080'>注册失败,请稍后再试!</FONT></CENTER><br>");
                    }
                    out.println("<p align=center><a href='" + contextPath
                              + "/lession/login.jsp'>返回主页</a></p>");
              }
          }
          out.println("</body></html>");
    }
    public void destroy() {
    }
    public boolean checkUser(String name)          //检测是否存在用户名函数
    {
          boolean existed = false;
          try {
              connection = this.getConnection();
                                                   //创建Statement接口对象
              Statement statement = connection.createStatement();
              String sql = "select * from login where name = '" + name + "'";
              ResultSet rs = statement.executeQuery(sql);
              int recordCount = 0;
              while (rs.next()) {
                    recordCount++;
              }
              if (recordCount > 0) {
                    existed = true;
              }
          } catch (Exception ex)                    //处理异常
          {
              ex.printStackTrace();
          }
          return existed;
    }
    public boolean checkPass(String password, String pass_confirm)
                                                    //检测密码和密码确认是否一致函数
    {
          boolean identical = true;
          if (!password.equals(pass_confirm)) {
              identical = false;
          }
          return identical;
    }
    public boolean addRecord(String sql_base, String sql_info)   //添加记录函数
    {
          boolean made = false;
          try {
              connection = this.getConnection();//连接数据库
                                                              //创建Statement接口对象
                Statement statement = connection.createStatement();
                statement.executeUpdate(sql_base);            //向login表中插入数据
                System.out.println(statement + " statement");
                statement.close();                            //关闭statement对象
                Statement statement1 = connection.createStatement();
                statement1.executeUpdate(sql_info);           //向userinfo表中插入数据
                System.out.println(statement1 + "   statement1");
                statement1.close();                           //关闭statement对象
                made = true;
          } catch (Exception ex)                              //异常处理
          {
                ex.printStackTrace();
          }
          return made;
    }
    public Connection getConnection() {
          try {
                Class.forName("com.mysql.jdbc.Driver");        //实例化JDBC-ODBC桥的驱动
                connection = DriverManager.getConnection(      //连接数据库
                          "jdbc:mysql://localhost:3306/myuser", "root", "root");
          } catch (Exception e) {
                System.out.println("数据库连接失败");
          }
          return connection;
    }
}

login.jsp显示登录信息的JSP页面,具体代码如下:

<%@ page contentType="text/html; charset=GBK" language="java"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
          <title>会员登录</title>
    </head>
    <%
    String contextPath = request.getContextPath();
    %>
    <body>
          <center>
              <FONT style="FONT-SIZE:25px;COLOR:#87CEFA"><B>会员登录</B>
              </FONT>
          </center>
          <form name="form1" method="post"
              action="<%=contextPath%>/loginServlet">
              <table width="47%" border="0" align="center" bordercolor="#FFDEAD"
                    bgcolor="#FFE4E1" cellspacing="0" cellpadding="2">
                    <tr>
                        <th>
                              用户名:
                        </th>
                        <td>
                            <input type="text" name="username">
                        </td>
                    </tr>
                    <tr>
                        <th>
                            密码:
                        </th>
                        <td>
                            <input type="password" name="password">
                        </td>
                    </tr>
                    <tr>
                        <td colspan="2">
                            <input type="submit" name="Submit" value=" 登 录 ">
                            &nbsp;&nbsp;
                            <input type="reset" name="Reset" value=" 取 消 ">
                        </td>
                    </tr>
              </table>
          </form>
          <p align="center">
              <a href="zhuce.jsp">新用户注册</a>
          </p>
    </body>
</html>

zhuce.jsp显示注册信息的JSP页面,具体代码如下:

<%@ page contentType="text/html; charset=GBK" language="java"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <head>
          <title>新用户注册</title>
    </head>
    <%
    String contextPath = request.getContextPath();
    %>
    <body>
          <center>
              <FONT style="FONT-SIZE:25px;COLOR:#87CEFA"><B>新用户注册</B>
              </FONT>
          </center>
          <form name="form1" method="post"
              action="<%=contextPath%>/zhuceServlet">
              <div align="center">
                    <table width="75%" border="0">
                        <tr>
                              <th bgcolor="#FFB6C1">
                                  用户名:
                              </th>
                              <td bgcolor="#FFE4C4">
                                  <input type="text" name="name" size="30">
                              </td>
                        </tr>
                        <tr>
                              <th bgcolor="#FFB6C1">
                                  登录密码:
                              </th>
                              <td bgcolor="#FFE4C4">
                                  <input type="password" name="password" size="30">
                              </td>
                        </tr>
                        <tr>
                              <th bgcolor="#FFB6C1">
                                  密码确认:
                              </th>
                              <td bgcolor="#FFE4C4">
                                  <input type="password" name="compass" size="30">
                              </td>
                        </tr>
                        <tr>
                              <th bgcolor="#FFB6C1">
                                  昵称:
                              </th>
                              <td bgcolor="#FFE4C4">
                                  <input type="text" name="nich" size="30">
                              </td>
                        </tr>
                        <tr>
                              <th bgcolor="#FFB6C1">
                                  性 别:
                              </th>
                              <td bgcolor="#FFE4C4">
                                  <select name="sex">
                                      <option value="男">
                                            男
                                      </option>
                                      <option value="女">
                                            女
                                      </option>
                                  </select>
                              </td>
                        </tr>
                        <tr>
                              <th bgcolor="#FFB6C1">
                                  居住地址:
                              </th>
                              <td bgcolor="#FFE4C4">
                                  <input type="text" name="address" size="30">
                              </td>
                        </tr>
                        <tr>
                            <th bgcolor="#FFB6C1">
                                E_Mail:
                            </th>
                            <td bgcolor="#FFE4C4">
                                <input type="text" name="mail" size="30">
                            </td>
                        </tr>
                        <tr>
                            <th bgcolor="#FFB6C1">
                                联系电话:
                            </th>
                            <td bgcolor="#FFE4C4">
                                <input type="text" name="phone" size="30">
                            </td>
                        </tr>
                        <tr>
                            <td colspan="2">
                                <input type="submit" name="Submit" value=" 注 册 ">
                                &nbsp;&nbsp;
                                <input type="reset" name="Reset" value=" 取 消 ">
                            </td>
                        </tr>
                  </table>
              </div>
        </form>
    </body>
</html>

配置文件web.xml,具体代码如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <servlet>
        <servlet-name>loginServlet</servlet-name>
        <servlet-class>chp20.loginServlet</servlet-class>
    </servlet>
    <servlet>
        <servlet-name>zhuceServlet</servlet-name>
        <servlet-class>chp20.zhuceServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>loginServlet</servlet-name>
        <url-pattern>/loginServlet</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>zhuceServlet</servlet-name>
        <url-pattern>/zhuceServlet</url-pattern>
    </servlet-mapping>
</web-app>

图20-15 新用户注册

(3)运行结果:打开一个浏览器,在地址栏中输入http://localhost:8080/My_Servlet/lession/zhuce.jsp,用不同的用户名登录聊天室,得到的界面如图20-15所示。

(4)在填写好注册的信息,单击“注册”按钮,将信息存入数据库,并转向提示信息页面,如图20-16所示。

(5)注册完毕以后,就可以使用用户名和密码登录,如图20-17所示。

(6)成功登录后,会转向显示会员个人注册时填写的详细信息,如图20-18所示。

图20-16 注册提示信息

图20-17 会员登录信息

图20-18 会员个人信息

源程序解读

1. loginServlet类

• init()初始化Connection和Vector对象。

• 如果HTTP请求方法为Get,则默认清况下就调用doGet()。当一个客户通过HTTP表单发出一个HTTP Post请求时, doPost()方法被调用。与doPost请求相关的参数作为一个单独的HTTP请求从浏览器发送到服务器。当需要修改服务器端的数据时,应再使用doPost方法。

• destroy()方法仅执行一次,即在服务器停止且卸载servlet时执行该方法。当服务器卸载Servlet时,将在所有service()方法调用完成后,或在指定的时间间隔过后调用destroy方法。一个Servlet在运行service方法时可能会产生其他的线程,因此在调用destroy方法时,必须确认这些线程已终止或完成。

• login()通过用户名和密码连接数据库,判断数据库表login中是否有用户信息,如果有则将个人信息存入向量对象userMessage中。

• getUserInfo()连接数据库,根据Statement接口对象将个人注册信息存入userMessage表中。

2. zhuceServlet类

• doPost()的作用是获得个人注册信息并存入对应的字符串变量中。

• checkUser()的作用是检测数据库中是否存在重名,如果有重名则提示。

• checkPass()的作用是检测密码和密码确认是否一致,如果不一致则提示。

• addRecord()的作用是如果用户名不重复,密码和密码确认一致,则将表单中填好的注册信息插入数据库中并返回boolean类数据,如果为true,则注册成功,否则失败。

实例206 用Servlet实现分页查看数据库

在We b程序中,常常需要在网页上进行分页功能,网页上的数据一般来自数据库,如购物车中的分页。在Java API中,JDBC不支持分页功能,但为了实现这个需求,很多人都有自己的解决方案,如使用JSP页面实现分页操作,但这种方法的可用性很差,使大量运算代码嵌在页面中不便于维护也影响访问速度。本实例介绍一种可以与JDBC相兼容的、操作方便的分页方式。

技术要点

本实例设计的分页方案的技术要点如下:

• 创建一个继承ResultSet的接口,其作用是可以对ResultSet进行分页。

• 提供接口的实现类,其作用是实现接口中所定义的方法,将接口实现类的对象存放在Session变量中。

• 在JSP页面中获取ResultSet,将数据取出并提供当前页的页码和每页的记录数,在翻页时,数据库结果集的对象不变,页码变。

• 创建一个可以将数据库结果集转换成HTML表格显示的类,在此类中可以指定表格的行数,表列数可以由结果集的列数决定。

实现步骤

(1)新建四个类名为QueryDataBaseServlet.java、PageResultSet.java、PageResultSetImpl.java和TableResultSet.java。

(2)代码如下所示。

QueryDataBaseServlet.java连接数据库的Servlet,具体代码如下:

package chp20;
import java.io.IOException;
import java.sql.*;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.sql.rowset.CachedRowSet;
import com.sun.rowset.CachedRowSetImpl;
public class QueryDataBaseServlet extends HttpServlet {
    //数据库连接变量
    Connection con = null;
    Statement sm = null;
    //用于连接数据库
    public Connection getconnection() {
          try {
              Class.forName("com.mysql.jdbc.Driver");
              con = DriverManager.getConnection(
                        "jdbc:mysql://localhost:3306/myuser", "root", "root");
          } catch (ClassNotFoundException e) {
              e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return con;
    }
    //处理doGet请求,当JSP表单中的method为get时,会调用该方法
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
    //处理doPost请求,当JSP表单中的method为post时,会调用该方法
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        try {
            con = this.getconnection();
            sm = con.createStatement();
        } catch (SQLException e1) {
            System.out.println("数据库连接失败");
        }
    //从request中获取JSP页面上表单的输入参数
        String sql = request.getParameter("querySQL");
        int pageIndex = Integer.parseInt((String) request
                  .getParameter("pageIndex"));
        int pageSize = Integer.parseInt((String) request
                  .getParameter("pageSize"));
        String pathurl = request.getParameter("pathurl");
        String errorurl = request.getParameter("errorurl");
        try {
            sql = sql.trim();            //判断SQL语句是否为查询语句,以放置修改数据库
            if (!sql.toUpperCase().startsWith("SELECT ")) {
                  throw new Exception("只能输入select查询语句!");
            }
            ResultSet rs = sm.executeQuery(sql);      //执行查询
    //为了防止数据库连接被关闭时,ResultSet中的数据无法读取的问题
            CachedRowSet crs = new CachedRowSetImpl();
          //在这里将ResultSet转换成CachedRowSet,它存在于内存中,不依赖于数据库
            crs.populate(rs); //populate方法把ResultSet中的数据导入到CachedRowSet中
            PageResultSet pageResultSet = new PageResultSetImpl(crs);
    //构造能够分页的pageResultSet对象
    //设置每页的记录数和当前页码
            pageResultSet.setPageSize(pageSize);
            pageResultSet.gotoPage(pageIndex);
            //将结果放入session中
            HttpSession session = request.getSession();
            session.setAttribute("pageResultSet", pageResultSet);
            session.setAttribute("rs", rs);
            request.getRequestDispatcher(pathurl).forward(request, response);
//本Servlet处理完毕,将请求转发给下一页面
        } catch (Exception e) {
            //查询错误时,清除session的信息
            HttpSession session = request.getSession();
              session.removeAttribute("pageResultSet");
              request.setAttribute("errormsg", e.getMessage());
              //出现异常时,记录异常信息
              request.getRequestDispatcher(errorurl).forward(request, response);
              //将请求转发给错误页面
          }
    }
    //销毁Servlet实例时,调用该方法
    public void destroy() {
          super.destroy();
          try {
              if (sm != null) {//关闭Statement
                  sm.close();
              }
          } catch (Exception e1) {
          }
          try {
              if (con != null) {//关闭Connection
                  con.close();
              }
          } catch (Exception e1) {
          }
    }
    public String getServletInfo() {
          return "Servlet查询数据库";
    }
}

PageResultSet.java支持分页的结果集的接口,具体代码如下:

package chp20;
import java.sql.ResultSet;
public interface PageResultSet extends ResultSet {
int getPageCount();                             //返回总页数
int getPageRowsCount();                         //返回当前页的记录条数
int getPageSize();                              //返回分页大小
void gotoPage(int page);                        //转到指定页
    void setPageSize(int pageSize);             //设置分页大小
    int getRowsCount();//返回总记录行数
    void pageFirst() throws java.sql.SQLException;    //转到当前页的第一条记录
    void pageLast() throws java.sql.SQLException;     //转到当前页的最后一条记录
    int getCurPage();                                 //返回当前页号
}

PageResultSetImpl.java支持分页结果集的实现类,具体代码如下:

package chp20;
import java.io.InputStream;
import java.io.Reader;
import java.math.BigDecimal;
import java.net.URL;
import java.sql.*;
import java.util.Calendar;
import java.util.Map;
public class PageResultSetImpl implements PageResultSet {
    private ResultSet rest;                        //存储数据的结果集
    private int Rows; //记录的总行数
    protected int pageSize;      //每页的记录条数,如果为0,则不分页,即一页显示所有记录
    protected int curPage;                         //当前页码,从1开始
    //构造函数
    public PageResultSetImpl(java.sql.ResultSet rs) throws SQLException {
          if (rs == null) {
              throw new SQLException("传入的ResuleSet为null");
          }
          rs.last();                               //定位到最后一条记录
          Rows = rs.getRow();                      //获取结果集的记录数
          rs.beforeFirst();                        //将指针移动到此 ResultSet 对象的开头
this.rest = rs; pageSize = 0; curPage = 1; } public int getPageCount() { //获得总的页数 if(Rows==0){ //没有记录则没有页数 return 0; } if(pageSize==0){ //如果每页记录数为0,表示用一页显示所有记录,所以页数就为1 return 1; } double tmpD=(double)Rows/pageSize; //计算总页数、总行数/页记录数 int tmpI=(int)tmpD; if(tmpD>tmpI){ //如果不能整除,则加1,最后一页没满 tmpI++; } return tmpI; }
public int getPageRowsCount() { //获取当前页的记录数 if(pageSize==0){ //如果不分页,则当前页的记录数为所有记录 return Rows; } if(Rows==0){ //如果没有记录,则返回0 return 0; } if(curPage!=getPageCount()){ //如果当前页不是最后一页,则返回页记录数 return pageSize; } return Rows-(getPageCount()-1)*pageSize; //否则为最后一页,则返回多余的记录数 } //获得每页的最大记录数 public int getPageSize() { return pageSize; } //获得总的记录数 public int getRowsCount() { return Rows; } //获得当前的页码 public int getCurPage() { return curPage; } //转入到某页 public void gotoPage(int page) { if (rest == null){ return; } //如果参数页码小于1或者大于总页数,则自动调整参数页码的值 if (page < 1){ page = 1; } else if (page > getPageCount()){ page = getPageCount(); } //定位到参数页。将结果集指针移到参数页的开始位置 int row = (page - 1) * pageSize + 1; try { rest.absolute(row); curPage = page; } catch (java.sql.SQLException e) { } } //转到当前页的第一条记录 public void pageFirst() throws java.sql.SQLException { int row=(curPage-1)*pageSize+1; rest.absolute(row); } //转到当前页的最后一条记录 public void pageLast() throws java.sql.SQLException { int row=(curPage-1)*pageSize+getPageRowsCount(); rest.absolute(row); } //设置页面最大记录的大小 public void setPageSize(int pageSize) { if(pageSize>=0){ this.pageSize = pageSize; curPage=1; } } //下面的方法都是实现ResultSet接口定义的方法,使用装饰模式,调用rs的相应方法即可 public boolean next() throws SQLException { return rest.next(); } public void close() throws SQLException { rest.close(); } public boolean wasNull() throws SQLException { return rest.wasNull(); } public String getString(int columnIndex) throws SQLException { return rest.getString(columnIndex); } public boolean getBoolean(int columnIndex) throws SQLException { return rest.getBoolean(columnIndex); } //下面还有好多其他的类似方法,这里不再一一列出...

TableResultSet.java将一个SQL查询结果集以表格的形式输出,具体代码如下:

package chp20;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
public class TableResultSet {
    private ResultSet rs; //结果集
    public TableResultSet(ResultSet t) {
          this.rs = t;
    }
    //生成整个结果集的HTML
    public String TableData(){
          try {
              rs.last();
              int numrows = rs.getRow();
              return TableData(1, numrows);
          } catch (SQLException e) {
              StringBuffer out = new StringBuffer();
              out.append("<TABLE border=\"1\">\n");
              out.append("</TABLE><H1>ERROR:</H1> " + e.getMessage() + "\n");
              return out.toString();
          }
    }
    //只为结果集部分记录生成HTML
    public String TableData(int begin, int end) {
          StringBuffer out = new StringBuffer();
    //结果集的数据显示在HTML的Table中
          out.append("<TABLE border=\"1\" bordercolor=\"#87CEEB\" bgcolor=\"#FFEFD5\"
width=\"60%\" >\n");
          try {
              if ((begin <= 0)|| (end <= 0) ||(begin > end)){
                    throw new Exception("参数错误!");
              }
              ResultSetMetaData rsmd = rs.getMetaData();//创建ResultSetMetaData对象
                                                                //构建表的头
              int numcols = rsmd.getColumnCount();
              System.out.println(numcols+" numcols");
              out.append("<TR>");
              for (int i = 1; i <= numcols; i++) {
                  out.append("<TH align=\"center\"><FONT style=\"FONT-SIZE:20px;
COLOR:#9ACD32\">" + rsmd.getColumnLabel(i)+"<FONT>");
              }
              out.append("</TR>\n");
rs.absolute(begin); //构建表的内容 do { //每条记录占据一行 out.append("<TR>"); for (int i = 1; i <= numcols; i++) { out.append("<TDalign=\"center\"><FONTstyle=\"FONT-SIZE:15px;COLOR:#F4A460\">"); Object obj = rs.getObject(i); if (obj != null) { out.append(new String(obj.toString().getBytes("ISO-8859-1"), "GBK")+"<FONT>"); System.out.println(obj.toString()+" obj.toString()"); } else { out.append("&nbsp;"); } } out.append("</TR>\n"); if (rs.getRow() == end){ break; } } while (rs.next()); //表格结束 out.append("</TABLE>\n"); } catch (Exception e) { System.out.println(); out.append("</TABLE><H1>ERROR:</H1> " + e.getMessage() + "\n"); } return out.toString(); } }

query.jsp用于显示查询结果集的JSP页面,具体代码如下:

<%@ page contentType="text/html; charset=GBK" language="java"
    import="chp20.*,java.sql.*"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
    <%
          //获得session,参数为false,表示如果session不存在,则不创建
          session = request.getSession(false);
          PageResultSet pageResultSet = (PageResultSet) session
                    .getAttribute("pageResultSet");
          ResultSet res = (ResultSet) session.getAttribute("rs");
          boolean hasResult = (pageResultSet == null) ? false : true;
          System.out.println(hasResult + " hasResult");
          int pageIndex = 0;
        int pageCount = 0;
        if (hasResult) {
            pageIndex = Integer.parseInt(request.getParameter("pageIndex"));
            System.out.println(pageIndex + "   pageIndex");
            pageResultSet.gotoPage(pageIndex);
            pageCount = pageResultSet.getPageCount();
        }
    %>
    <head>
        <%
        String contextPath = request.getContextPath();
        %>
        <title>数据库查看器</title>
        <script language="javascript">
    <%if (hasResult){%>
    function firstpage(){
        fobj = document.mainForm;
        fobj.pageIndex.value = "1";
        fobj.submit();
    }
    function lastpage(){
        fobj = document.mainForm;
        fobj.pageIndex.value = "<%=pageCount%>";
        fobj.submit();
    }
    function prepage(){
        fobj = document.mainForm;
        fobj.pageIndex.value = "<%=(pageIndex - 1)%>";
        fobj.submit();
    }
    function nextpage(){
        fobj = document.mainForm;
        fobj.pageIndex.value = "<%=(pageIndex + 1)%>";
        fobj.submit();
    }
    <%}%>
    </script>
    </head>
    <body>
        <%@ include file="header.jsp"%>
        <br />
        <center>
            <FORM name="queryForm" METHOD="POST"
                  action="<%=contextPath%>/queryDB">
                  请输入SQL语句:
                  <input type="text" name="querySQL" size="50">
                  <input type="hidden" name="pathurl" value="/lession/query.jsp">
                  <input type="hidden" name="errorurl" value="/lession/query.jsp">
                  <input type="hidden" name="pageIndex" value="1">
                  <input type="hidden" name="pageSize" value="5">
                  <input type="Submit" name="Submit" value="查 询" />
              </FORM>
              <%
                  //如果存在错误信息,则用error.jsp显示错误信息
                  String errormsg = (String) request.getAttribute("errormsg");
                  if (errormsg != null) {
              %>
              <jsp:include page="error.jsp" />
              <%
              }
              %>
              <%
              if (hasResult) {
              %>
              <FORM name="mainForm" METHOD="POST"
                  action="<%=contextPath%>/lession/query.jsp">
                  <%
                            int begin = (pageResultSet.getCurPage() - 1)
                            * pageResultSet.getPageSize() + 1;
                            int end = (pageResultSet.getCurPage() - 1)
                            * pageResultSet.getPageSize()
                            + pageResultSet.getPageRowsCount();
                            out.println(new TableResultSet(res).TableData(begin, end));
                  %>
                  <BR />
                  <FONT style="FONT-SIZE:20px;COLOR:#4169E1">当前第&nbsp;</FONT><font
                        color="red"><%=pageIndex%> </font><FONT
                        style="FONT-SIZE:20px;COLOR:#4169E1">&nbsp;</FONT>&nbsp;
                  &nbsp;&nbsp;&nbsp;
                  <FONT style="FONT-SIZE:20px;COLOR:#4169E1">共&nbsp;</FONT><font
                        color="red"><%=pageCount%> </font><FONT
                        style="FONT-SIZE:20px;COLOR:#4169E1">&nbsp;页</FONT>
                  <BR />
                  <input type="hidden" name="pageIndex" value="" />
              </FORM>
              <input type="button" name="firstpage" value="第一页"
                  onclick="javascript:firstpage()" <%if (pageIndex == 1){%> disabled
                  <%}%> />
              <input type="button" name="prepage" value="上一页"
                  onclick="javascript:prepage()" <%if (pageIndex <= 1){%> disabled
                  <%}%> />
              <input type="button" name="nextpage" value="下一页"
                  onclick="javascript:nextpage()"
                  <%if (pageIndex >= pageCount){out.print(" disabled");}%> />
              <input type="button" name="lastpage" value="最后一页"
                  onclick="javascript:lastpage()" <%if (pageIndex == pageCount){%>
                  disabled <%}%> />
              <%
              }
              %>
          </center>
    </body>
</html>

header.jsp和error.jsp分别是头文件和错误文件,具体代码如下:

<!---------header.jsp------->
<%@ page contentType="text/html; charset=GBK" language="java" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head> </head>
  <body>
      <center><FONT style="FONT-SIZE:30px;COLOR:#FFA07A"><B>Servlet动态数据查询功
能</B></FONT></center><br/><br/>
  </body>
</html>
<!---------error.jsp------->
<%@ page contentType="text/html; charset=gb2312" language="java" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head> </head>
  <body>
      <BR/><font color = red bold>错误信息:</font><BR/>
    <font color = red><%= request.getAttribute("errormsg")%></font><br>
  </body>
</html>

配置文件web.xml,具体代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <servlet>
          <servlet-name>QueryDB</servlet-name>
          <servlet-class>chp20.QueryDataBaseServlet</servlet-class>
    </servlet>
    <servlet-mapping>
          <servlet-name>QueryDB</servlet-name>
          <url-pattern>/queryDB</url-pattern>
    </servlet-mapping>
</web-app>

(3)运行结果后,SQL的查询页面如图20-19所示。

(4)输入相应的SQL语句,单击“查询”按钮后的页面如图20-20所示。

图20-19 SQL查询页面

图20-20 单击“查询”按钮后的页面

(5)单击“下一页”按钮,可以进行翻页,如图20-21所示。

图20-21 单击“下一页”按钮后的页面

源程序解读

1. PageResultSet类

该类是一个接口,继承ResultSet,定义了用于分页的接口。在该接口中定义的方法有:getPageSize方法返回分页的大小,getPageCount返回总的页数,getRowsCount返回总的记录数, getPageRowsCount方法返回当前页的记录数, getCurPage方法获得当前页的页号,setPageSize方法设置页的大小,gotoPage方法跳转到指定的页,pageFirst方法跳转到第一页,pageLast方法跳转到最后一页。

2. PageResultSetImpl类

该类是PageResultSet接口的实现类,在实现PageResultSet接口定义的方法时,主要采用了ResultSet的游标功能,通过absolute方法将结果集的指针移到相应的位置,在实现Result接口定义的方法时,通过调用rs变量的同名方法实现。rest变量是一个ResultSet对象,用于存放结果集的数据。Rows变量用于存放结果集的记录总数,pageSize变量用于存放每页的记录数, curPage变量保存当前的页号。

3. TableResultSet类

该类将ResultSet整个结果集以HTML中的表格的形式输出。TableData方法完成转换功能,有两种形式,无参数的TableData方法将整个ResultSet输出为HTML表格。带参数的TableData方法只将某一段ResultSet的记录输出为HTML表格。通过ResultSetMetaData类获得ResultSet包含的列信息,将列信息当作表头,遍历ResultSet,通过getObject方法获得每条记录的值,构成表格的一行数据,最后把生成的HTML代码返回。

4. QueryDataBaseServlet类

(1)该类是查看数据库的Servlet类。对于HTTP的Get和Post请求,都使用同样的方法进行处理。首先从Request对象中获得HTTP请求的参数,包括querySQL、pageIndex和pageSize等信息。

(2)调用Statement的executeQuery方法执行请求中的SQL语句,得到一个ResultSet对象,创建一个新的CachedRowSet对象,调用它的populate方法将ResultSet中的数据全部导入到CachedRowSet对象中,CachedRowSet是将结果集保存在内存中,即使与数据库断开连接,数据也不会消失。所以用CachedRowSet来实现分页是明智的选择。

(3)通过调用Request的getRequestDispatcger获得一个RequestDispatcher对象,然后调用它的forward方法将请求和响应对象转发给servleturl指向的页面。如果访问数据库失败,则将请求转发给errorurl指向的页面。

(4)destroy方法,由Servlet容器调用,表示该Servlet已从服务中退出。在调用此方法之后,Servlet容器不会再对此Servlet调用service方法。

5. query.jsp页面

该页面是查看数据库的主页面。首先从Session中取出数据库结果集,得到一个PageResultSet对象,如果PageResultSet不为空,则根据页号显示结果集,首先获得该页第一条和最后一条记录所在的行标,调用TableResultSet的带参数的TableData方法,获得该页数据的HTML表格代码的字符串,通过out的println方法显示HTML表格。最后根据当前的页号和总的页号判断是否允许翻页。

该页中使用了两个特殊的JSP标签。<%@ include file="header.jsp"%>和<jsp:include page="error.jsp" />标签,<%@ include file="header.jsp"%>标签表示当前JSP页面静态包含header.jsp,即该标签始终都是当前JSP页面的一部分,无论该标签处于何处;<jsp:include>标签表示当前JSP页面动态包含error.jsp,即error.jsp不一定是当前JSP页面的一部分,是否包含error.jsp取决于标签所在的位置。

实例207 简单的BBS论坛

本实例将演示如何使用Servlet实现一个简单的留言条的功能,主要包括添加留言、保存留言和查看留言。在启动Tomcat之后输入相应的Servlet的地址,就可以看到留言板的页面了。

技术要点

使用Servlet实现一个简单的留言条的功能的技术要点如下:

• 对Servlet的Service()方法的使用。

• 针对留言内容对数据库进行的操作(如添加、保存、查看等)。

• Servlet页面出现乱码时的解决方法。

实现步骤

(1)新建一个类名为IndexPage.java。

(2)代码如下所示。

IndexPage.java是以HTML的格式,输出一个可以录入留言的表单,具体代码如下:

package chp20;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
public class IndexPage extends HttpServlet {
    protected void service(HttpServletRequest request,
              HttpServletResponse response) throws ServletException, IOException {
          java.io.PrintWriter out = new java.io.PrintWriter(response
                    .getOutputStream());
          String contextPath = request.getContextPath();
          out.print("<head><title>Servlet留言条</title></head>");
          out.print("<body>");
          out
                    .print("<center><FONT style='FONT-SIZE:25px;COLOR:#F08080'>Servlet留
言条</FONT>");
          out.print("<form name=\"form2\" action=\"" + contextPath
                    + "/InputMessage\" method=\"POST\">");
          System.out.println("dddeeee");
          out
                    .print("<table border=\"1\" cellpadding=\"1\" cellspacing=\"1\"
width=\"65%\" height=\"15%\" bgcolor=\"#EEE8AA\" bordercolor=\"#7B68EE\">");
          out.print("<tr>");
          out
                    .print("<th align=\"right\"><FONT style='FONT-SIZE:20px;COLOR:
#6B8E23'><b>姓名:</b></FONT></th>");
          out.print("<td align=\"left\">");
          out
                    .print("<input type=\"text\" name=\"name\" SIZE=\"20\" value=\"\"
required=\"\">");
          out.print("</td><br>");
          out
                    .print("<th align=\"right\"><FONT style='FONT-SIZE:20px;COLOR:
#6B8E23'><B>Email地址:</B> </FONT></th>");
          out.print("<td align=\"left\">");
          out
                    . print("<input type=\"text\" name=\"Email\" SIZE=\"20\" value=\"\">");
          out.print("</td><br>");
          out.print("</tr>");
          out.print("<tr><th align=\"right\">");
          out
                    .print("<b><FONT style='FONT-SIZE:20px;COLOR:#6B8E23'>主题:</FONT>
</b></th><td colspan=\"3\">");
          out
                    .print("<input type=\"text\" name=\"Topic\" SIZE=\"40\" value=
\"\"></td><br></tr>");
          out.print("<tr><td colspan=\"4\" align=\"center\">");
          out
                    .print("<textarea name=\"message\" rows=\"10\" cols=\"80%\"></textarea>");
          out.print("</td><br></tr></table></center>");
          out
                    .print("<center><input type=\"submit\" name=\"b2\" value=\"确认留言\">
&nbsp;&nbsp;");
          out.print("<input type=\"reset\" name=\"b3\" value=\"重新留言\"></center>");
          out.print("</form>");
          out.print("</body></html>");
          out.flush();
    }
}

InputMessage.java处理保存留言信息,具体代码如下:

package chp20;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.sql.*;
public class InputMessage extends HttpServlet {
    public void init() throws ServletException {
    }
    protected void doPost(HttpServletRequest request,
              HttpServletResponse response) throws ServletException, IOException {
        java.io.PrintWriter out = new java.io.PrintWriter(response
                  .getOutputStream());
        String contextPath = request.getContextPath();
        out.print("<html>");
        out.print("<head><title>保存留言</title></head>");
        out.print("<body>");
        out
                  .print("<center><h3><FONT style='COLOR:#87CEFA'><B>谢谢使用留言条
</B></FONT></h3></center>");
        out.print("<hr>");
        out
                  .print("<center><h4><FONT style='COLOR:#7B68EE'><B>留言信息
</B></FONT></h4>");
        String getDate = new java.util.Date().toString();
        String names = request.getParameter("name");
        String Emails = request.getParameter("Email");
        String messages = request.getParameter("message");
        String Topics = request.getParameter("Topic");
        //对中文数据进行乱码处理
        String name = new String(names.getBytes("iso-8859-1"), "gb2312");
        String Email = new String(Emails.getBytes("iso-8859-1"), "gb2312");
        String message = new String(messages.getBytes("iso-8859-1"), "gb2312");
        String Topic = new String(Topics.getBytes("iso-8859-1"), "gb2312");
        System.out.println(name);
        //检查客户的留言信息是否完整
        if (names.length() < 1) {
            out
                      .print("<FONT style='FONT-SIZE:20px;COLOR:#6495ED'>请输入姓名</FONT>");
        } else {
            if (Emails.length() < 3) {
                  out
                            .print("<FONT style='FONT-SIZE:20px;COLOR:#FF00FF'>请输
入正确的Email地址</FONT>");
            } else {
                  if (Topics.length() < 1) {
                      out
                                .print("<FONT style='FONT-SIZE:20px;COLOR:#9ACD32'>
请输入主题</FONT>");
                  } else {
                      if (messages.length() < 1) {
                            out
                                      .print("<FONT style='FONT-SIZE:20px;COLOR:
#FF0000'>没有留言</FONT>");
                      } else { //客户输入完整的信息则响应,在HTML中让客户看到自己的留言
                            out
                                      .print("<table border=\"1\" cellpadding=\"1\"
cellspacing=\"1\"width=\"60%\" height=\"35%\" bgcolor=\"#F0F8FF\" bordercolor=\"#FF8C00\">");
                            out
                                      .print("<tr><td align=\"right\"><FONT style=
'FONT-SIZE:22px;COLOR:#DAA520'>姓名:</FONT></td><td>");
                            out.print("<FONT style='FONT-SIZE:20px;COLOR:#FFD700'>"
                                      + name + "</FONT>");
                            out.print("</td></tr>");
                            out
                                      .print("<tr><td align=\"right\"><FONT style=
'FONT-SIZE:22px;COLOR:#DAA520'>Email地址:</FONT></td><td>");
                            out.print("<FONT style='FONT-SIZE:20px;COLOR:#FFD700'>"
                                      + Email + "</FONT>");
                            out.print("</td></tr>");
                            out
                                      .print("<tr><td align=\"right\"><FONT style='FONT-SIZE:
22px;COLOR:#DAA520'>主题:</FONT></td><td>");
                            out.print("<FONT style='FONT-SIZE:20px;COLOR:#FFD700'>"
                                      + Topic + "</FONT>");
                            out.print("</td></tr>");
                            out
                                      .print("<tr><td valign=\"top\"
align=\"right\"><FONT style='FONT-SIZE:22px;COLOR:#DAA520'>留言:</FONT></td><td>");
                            out.print("<FONT style='FONT-SIZE:20px;COLOR:#FFD700'>"
                                      + message + "</FONT>");
                            out.print("</td></tr>");
                            out
                                      .print("<tr><td align=\"right\"><FONT style='FONT-SIZE:
22px;COLOR:#DAA520'>日期:</FONT></td><td>");
                            out.print("<FONT style='FONT-SIZE:20px;COLOR:#FFD700'>"
                                      + getDate + "</FONT>");
                            out.print("</td></tr></table></center>");
                            try {     //加载数据驱动,连接数据库
                                  Class.forName("com.mysql.jdbc.Driver");
                                  Connection cn = DriverManager.getConnection(
                                          "jdbc:mysql://localhost:3306/myuser",
                                          "root", "root");
                                  //将从表单中取得的数据存入数据库中
                                  String str = "INSERT INTO BBS (name, date ,Email,Topic ,
message) VALUES('";
                                  String data = names + "','" + getDate + "','"
                                          + Emails + "','" + Topics + "','"
                                          + messages + "')";
                                  Statement st = cn.createStatement();
                                  st.executeUpdate(str + data);
                                  st.close();
                                  cn.close();
                                  out
                                          .print("<center><h3><FONT style='COLOR:
#F08080'><B>留言成功</B></FONT><h3></center>");
                            } catch (Exception e) {
                                  out
                                          .print("<center><h3><FONT style= 'COLOR:
#808080'><B>留言已存在</B></FONT><h3></center>");
                            }
                        }
                  }
              }
          }
          out.print("<form name=\"form3\" action=\"" + contextPath
                  + "/ShowMessage\" method=\"get\">");
          out
                  .print("<center><input type=\"submit\" name=\"we\" value=\"查看
留言\"></center>");
          out.print("<hr>"); //输出响应HTML文件的尾部信息
          out.print("</form>");
          out.print("</body></html>");
          out.flush();
    }
}

ShowMessage.java将数据库中的所有留言显示在网页上,具体代码如下:

package chp20;
import javax.servlet.*;
import javax.servlet.http.*;
import java.io.*;
import java.sql.*;
import java.util.Vector;
public class ShowMessage extends HttpServlet {
    protected void doGet(HttpServletRequest request,
              HttpServletResponse response) throws ServletException, IOException {
          java.io.PrintWriter out = new java.io.PrintWriter(response
                    .getOutputStream());
          String contextPath = request.getContextPath();
          out.print("<html>");
          //输出响应的HTML 文件头部信息
          out.print("<head><title>查看留言</title></head>");
          out.print("<body>");
          out
                    .print("<center><h2><FONTstyle='COLOR:#F08080'><B>查看留言</B></FONT></h2>");
          out.print("<hr>");
          //加载数据驱动,创建数据库连接取得库中保存的留言信息
          try {
              Class.forName("com.mysql.jdbc.Driver");
              Connection cn = DriverManager.getConnection(
                        "jdbc:mysql://localhost:3306/myuser", "root", "root");
              String str = "SELECT * FROM BBS";
              Statement statement = cn.createStatement();
              ResultSet resultSet = statement.executeQuery(str);
              String names, dates, emails, topics, messages;
              out
                        .print("<table border=\"1\" cellpadding=\"1\" cellspacing=\"1\"
width=\"60%\" height=\"30%\" bgcolor=\"#F0F8FF\" bordercolor=\"#FF8C00\">");
              while (resultSet.next()) {
                    //对数据进行乱码处理
                    names = new String(resultSet.getString("name").getBytes(
                              "iso-8859-1"), "gb2312");
                    dates = new String(resultSet.getString("date").getBytes(
                              "iso-8859-1"), "gb2312");
                    emails = new String(resultSet.getString("Email").getBytes(
                              "iso-8859-1"), "gb2312");
                    topics = new String(resultSet.getString("Topic").getBytes(
                              "iso-8859-1"), "gb2312");
                    messages = new String(resultSet.getString("message").getBytes(
                              "iso-8859-1"), "gb2312");
                    //输出相关信息
                    out.print("<tr><td align=\"right\">");
                    out
                              .print("<FONT style='FONT-SIZE:22px;COLOR:#D2691E'><b>
主题:</b></FONT></td><td colspan=\"3\"><b>");
                    out.print("<FONT style='FONT-SIZE:20px;COLOR:#BDB76B'>"
                              + topics + "</FONT>");
                    out.print("</b></td></tr>");
                    out
                              .print("<tr><td align=\"right\"><FONT style='FONT-SIZE:22px;
COLOR:#DAA520'>姓名:</FONT></td><td colspan=\"3\" >");
                    out.print("<FONT style='FONT-SIZE:20px;COLOR:#BDB76B'>" + names
                              + "</FONT>");
                    out.print("</td></tr>");
                    out
                              . print("<tr><td align=\"right\"><FONT style='FONT-SIZE:22px;
COLOR:#DAA520'>Email地址:</FONT></td><td colspan=\"3\" >");
                    out.print("<FONT style='FONT-SIZE:20px;COLOR:#BDB76B'>"
                              + emails + "</FONT>");
                    out.print("</td></tr>");
                    out
                              .print("<tr><td valign=\"top\" align=\"right\"><FONT style=
'FONT-SIZE:22px;COLOR:#DAA520'>留言:</FONT></td><td colspan=\"3\" >");
                    out.print("<FONT style='FONT-SIZE:20px;COLOR:#BDB76B'>"
                              + messages + "</FONT>");
                    out.print("</td></tr>");
                    out
                              .print("<tr><td align=\"right\"><FONT style='FONT-SIZE:22px;
COLOR:#DAA520'>日期:</FONT></td><td>");
                    out.print("<FONT style='FONT-SIZE:20px;COLOR:#BDB76B'>" + dates
                              + "</FONT>");
                    out.print("</td></tr>");
                }
                out.print("</table>");
                statement.close();
                cn.close();
                //关闭
          } catch (Exception e) {
                out.print(e.getMessage());
          }
          out.print("<form names=\"form1\" action=\"" + contextPath
                    + "/IndexPage\" method=\"POST\">");
          out.print("<input type=\"submit\" names=\"a1\" value=\"继续留言\">");
          out.print("</form>");
        out.print("</body></html>");
        out.flush();
    }
}

Tomcat配置Servlet的配置文件web.xml,具体代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
    http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
    <servlet>
          <servlet-name>InputMessage</servlet-name>
          <servlet-class>chp20.InputMessage</servlet-class>
    </servlet>
    <servlet>
          <servlet-name>IndexPage</servlet-name>
          <servlet-class>chp20.IndexPage</servlet-class>
    </servlet>
    <servlet>
          <servlet-name>ShowMessage</servlet-name>
          <servlet-class>chp20.ShowMessage</servlet-class>
    </servlet>
    <servlet-mapping>
          <servlet-name>InputMessage</servlet-name>
          <url-pattern>/InputMessage</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
          <servlet-name>IndexPage</servlet-name>
          <url-pattern>/IndexPage</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
          <servlet-name>ShowMessage</servlet-name>
          <url-pattern>/ShowMessage</url-pattern>
    </servlet-mapping>
</web-app>

(3)运行结果:打开一个浏览器,在地址栏中输入http://localhost:8080/My_Servlet/IndexPage,用不同的用户名登录聊天室,得到的界面如图20-22所示。

(4)录入信息后,单击“确认留言”按钮,将信息存入数据库并显示信息,如图20-23所示。

图20-22 录入留言页面

图20-23 录入留言页面

(5)将信息存入数据库后,单击“查看留言”按钮,将查看所有留言信息,如图20-24所示。

图20-24 查看留言页面

源程序解读

总体的流程共包含三个Servlet类,分别是IndexPage(首页)、InputMessage(保存留言)和ShowMessage(查看留言)。

1. IndexPage类

此类的主要功能就是以HTML的格式,输出一个可以录入留言的表单,表单中的内容包括留言人的姓名、Email地址、留言的主题、留言的时间和留言的内容。Service()方法:Servlet的主要功能是接收从浏览器发送过来的HTTP请求(request),并返回HTTP响应(response)。这个工作是在service() 方法中完成的。service()方法包括从request对象获得客户端数据和向response对象创建输出service()方法,默认的服务功能是调用与HTTP请求的方法相应的doXX()功能。在Servlet中主要用方法控制页面的跳转的语句为<form name=\"form2\" action=\"" + contextPath+ "/InputMessage\"method=\"POST\">"。

2. InputMessage类

此类的主要功能是保存留言。doPost():利用request.getParameter("");将数据从表单中取出,对表单中的中文汉字通过new String(names.getBytes("iso-8859-1"), "gb2312");语句来处理出现的乱码。然后对其获取的数据进行校验。若校验成功,则连接MySQL数据库,将数据存入库中,并提示。

3. ShowMessage类

此类的主要功能是将数据库中的所有留言显示在网页上。在doGet()方法中,先创建相关的数据连接,将数据从表中全部取出放入ResultSet结果集中待用。用同样方法对从数据库中取出的汉字进行乱码处理。最后将这些数据通过Servlet的out.print输出。