教 学 活 动 首 页
基 本 内 容 |
第 7 章 Java Servlet |
教学目的与要求:通过本章的学习让学生了解如何用 servlet 读写文件,用 servlet 访问数据库;理解servlet 工作原理,servlet共享变量的使用;掌握编译和安装 servlet,通过 JSP 页面调用 servlet,HttpServlet 类,掌握会话管理。 |
教学内容: 7.1 servlet 工作原理 7.2 编译和安装 servlet 7.3 通过 JSP 页面调用 servlet 7.4 servlet共享变量 7.5 HttpServlet 类 7.6 用 servlet 读写文件 7.7 用 servlet 访问数据库 7.8 会话管理 |
教学基本要求: 了解:用 servlet 读写文件,用 servlet 访问数据库 理解:servlet 工作原理,servlet共享变量 掌握:编译和安装 servlet,通过 JSP 页面调用 servlet,HttpServlet 类,会话管理 |
教学重点教学难点: servlet 工作原理,编译和安装 servlet,通过 JSP 页面调用 servlet,HttpServlet 类,会话管理 |
教学方法: 教学手段:多媒体教学和计算机程序演示 |
教学小结: (见教学进程) |
作业与思考:见课后习题 |
课后记载:
|
教 学 进 程
第7章 Java Servlet 我们已经知道,SUN公司以Java Servlet为基础,推出了Java Server Page。JSP提供了Java Servlet的几乎所有好处,当一个客户请求一个JSP页面时,JSP引擎根据JSP页面生成一个Java文件,即一个servlet。这一章,将对servlet做一个较详细的介绍,这不仅对于深刻理解JSP有一定的帮助,而且通过学习servlet,还能使我们选择使用JSP+javabeans+servlet的模式来开发我们的Web应用程序。 我们已经知道,用JSP支持JavaBeans这一特点,可以有效的管理页面的静态部分和页面的动态部分。另外,我们也可以在一个JSP页面中调用一个servlet完成动态数据的处理,而让JSP页面本身处理静态的信息。因此,开发一个Web应用有两种模式可以选择: (1) JSP+javabeans (2) JSP+javabeans+servlet 7.1 Servlet工作原理 servlet由支持servlet的服务器:servlet引擎,负责管理运行。当多个客户请求一个servlet时,引擎为每个客户启动一个线程而不是启动一个进程,这些线程由servlet引擎服务器来管理,与传统的CGI为每个客户启动一个进程相比较,效率要高的多。 7.1.1 Servlet 的生命周期 学习过Java 语言的人对Java Applet(Java小应用程序)都很熟悉,一个Java Applet是java.applet.Applet类的子类,该子类的对象由客户端的浏览器负责初始化和运行。servlet的运行机制和Applet类似,只不过它运行在服务器端。一个servlet是javax.servlet包中HttpServlet类的子类,由支持servlet的服务器完成该子类的对象,即servlet的初始化。 Servlet的生命周期主要有下列三个过程组成: (1)初始化servlet。servlet第一次被请求加载时,服务器初始化这个servlet,即创建一个servlet对象,这对象调用init方法完成必要的初始化工作。 (2)诞生的servlet对象再调用service方法响应客户的请求。 (3)当服务器关闭时,调用destroy方法,消灭servlet对象。 init方法只被调用一次,即在servlet第一次被请求加载时调用该方法。当后续的客户请求servlet服务时,Web服务将启动一个新的线程,在该线程中,servlet调用service方法响应客户的请求,也就是说,每个客户的每次请求都导致service方法被调用执行。 7.1.2 init方法 该方法是HttpServlet类中的方法,我们可以在servlet中重写这个方法。 方法描述:
public void init(ServletConfig config) throws ServletException
servlet第一次被请求加载时,服务器初始化一个servlet,即创建一个servlet对象,这个对象调用init方法完成必要的初始化工作。该方法在执行时,servlet引擎会把一个SevletConfig类型的对象传递给init()方法,这个对象就被保存在servlet对象中,直到servlet对象被消灭,这个ServletConfig对象负责向servlet传递服务设置信息,如果传递失败就会发生ServeletException,servlet就不能正常工作。 我们已经知道,当多个客户请求一个servlet时,引擎为每个客户启动一个线程,那么servlet类的成员变量被所有的线程共享。 7.1.3 service方法 该方法是HttpServlet类中的方法,我们可以在servlet中直接继承该方法或重写这个方法。 方法描述:
public void service(HttpServletRequest request HttpServletResponse response) throw ServletException,IOException
当servlet成功创建和初始化之后,servlet就调用service方法来处理用户的请求并返回响应。Servlet引擎将两个参数传递给该方法,一个HttpServletRequest类型的对象,该对象封装了用户的请求信息,此对象调用相应的方法可以获取封装的信息,即使用这个对象可以获取用户提交的信息。另外一个参数对象是HttpServletResponse类型的对象,该对象用来响应用户的请求。和init方法不同的是,init方法只被调用一次,而service方法可能被多次的调用,我们已经知道,当后续的客户请求servlet服务时,Servlet引擎将启动一个新的线程,在该线程中,servlet调用service方法响应客户的请求,也就是说,每个客户的每次请求都导致service方法被调用执行,调用过程运行在不同的线程中,互不干扰。 7.1.4 destroy方法 该方法是HttpServlet类中的方法。servlet可直接继承这个方法,一般不需要重写。 方法描述:
public destroy()
当Servlet引擎终止服务时,比如关闭服务器等,destroy()方法会被执行,消灭servlet对象。 7.2 编译和安装servlet 7.2.1 简单的servlet例子 在下面的例子1中,Hello扩展了HttpServlet。 例子1 servlet源文件 Hello.java: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Hello extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void service(HttpServletRequest reqest,HttpServletResponse response) throws IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(" ");out.println("Simple servlet"); out.println(" "); } } 7.2.2 编译servlet 为了编译servlet源文件,需要HttpServlet、HttpServletRequest等类,JDK内置包中并不包含这些类文件。为了能编译servlet源文件得到创建servlet用的字节码文件,需要在环境变量中包含servlet.jar,这个jar文件在TOMCAT安装目录的common\lib文件下,如图7.1所示(也可以到sun公司网站下载编译servlet所需要的类)。 对于window2000,用鼠标右键点击“我的电脑”,弹出菜单,然后选择属性,弹出“系统特性”对话筐,再单击该对话框中的高级选项,然后点击按钮“环境变量”,编辑classpath,添加新的环境变量的值:
D:\Tomcat\jakarta-tomcat-4.0\common\lib\servlet.jar;
我们将上述servlet的源文件Hello.java保存到F:\2000,然后编译生成字节码文件Hello.class 7.2.3 存放servlet的目录 (1)所有web服务目录可使用的servlet的存放位置 如果让所有web服务目录都可以使用该servlet,那么创建这个servlet的字节码文件需存放在Tomcat安装目录的classes目录中,例如,本书所用机器的目录就是:D:tomcat\Jakarta-tomcat-4.0\classes,如图7.1所示。 我们已经知道,servlet第一次被请求加载时,服务器初始化一个servlet,即创建一个servlet对象,这对象调用init方法完成必要的初始化工作。如果你对servlet的源文件进行了修改,并将新的字节码文件存放到classes中,如果服务器没有关闭的话,新的servlet不会被创建,因为,当后续客户请求servlet服务时,已初始化的servlet将调用service方法响应客户。 (2)只对examples服务目录可用的seclet的存放目录 examples是TOMCAT引擎的默认web服务目录之一。 如果想让某个servlet只对examples目录可用,那么创建该servlet的字节码文件只需存放在webapps/example/Web-inf/classes目录中。 存放在该目录中的servlet和存放在上面(1)中所述目录中的servlet有所不同,服务器引擎首先检查webapps/example/Web-inf/classes目录中的创建该servlet的字节码文件是否被修改过,如果重新修改过,就会用消灭servlet,用新的字节码重新初始化servlet。 如果经常调试servlet,可以把servlet放在webapps/example/Web-inf/classes。需要注意的是,当用户请求servlet服务时,由于服务器引擎每次都要检查字节码文件是否被修改过,导致服务器的运行效率降低。 7.2.4 运行servlet 如果一个servlet对所有的web服务目录可用,那么只要在服务器引擎启动后,在浏览器地址栏键入:
http://localhost:8080/web服务目录/servlet/创建servlet类的名字
即可,例如,对于用上述Hello创建的servlet, (1) Root 服务目录
http://localhost:8080/servlet/Hello
(2) friend目录(我们自定义的一个web服务目录)
http://localhost:8080/friend/servlet/Hello
如果是只对examples服务目录可用的servelt,那么只要在服务器引擎启动后,在浏览器地址栏键入:
http://localhost:8080/examples/servlet/创建servlet类的名字
我们将Hello.class文件保存到Tomcat引擎的classes文件夹中。图7.2和7.3是在不同的web目录下运行servlet的效果。
在写一个servlet的java文件时,可以使用package语句给servlet一个包名。包名可以是一个合法的标识符,也可以是若干个标识符加“.”分割而成,如:
package gping; package tom.jiafei;
程序如果使用了包语句,例如
package tom.jiafei;
那么在classes目录下需有如下的子目录,例如,在D:\Tomcat\jakarta-tomcat-4.0\classes下再建立如下的目录结构。
\tom\jiafei
并将servlet的字节码文件存在该目录中,如图7.4所示。
如果servlet有包名,比如,Hello的包名是tom.jiafei,那么调用该servlet的URL是:
http://localhost:8080/web服务目录/servlet/tom.jiafei.Hello
因为起了包名,Hello的全名是tom.jiafei.Hello(就好比大连的全名是:中国.辽宁.大连)。 7.3 通过JSP页面调用servlet 7.3.1 通过表单向servlet提交数据 任何一个Web服务目录下的JSP页面都可以通过表单或超链接访问某个servlet。通过JSP页面访问servlet的好处是,JSP页面可以负责页面的静态信息处理,动态信息处理交给servlet去完成。 在下面的例子中,JSP页面通过表单向servlet提交一个正实数,servlet负责计算这个数的平方根返回给客户。 为了方便地调试servlet,本书中,servlet的字节码文件存放在D:\Tomcat\jakarta-tomcat-4.0\webapps\example\Web-inf\classes中,那么在JSP页面中调用servlet时,servlet的URL是:
/examples/servlet/servletName
在下面的例子2中,JSP页面通过表单提交一个正数,servlet负责计算这个数的平方根。
例子2 调用servlet的页面(该页面存放在web服务的根目录Root中) givenumber.jsp(效果如图7.5所示) <%@ page contentType="text/html;charset=GB2312" %> 输入一个数,servlet求这个数的平方根:
Sqrt.java import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Sqrt extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void service(HttpServletRequest request,HttpServletResponse response) throws IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(" ");String number=request.getParameter("number"); //获取客户提交的信息。 double n=0; try{ n=Double.parseDouble(number); out.print(" } catch(NumberFormatException e) { out.print(" input number letter please!");} out.println(" "); } } 7.3.2 通过超链接访问servlet 我们可以在JSP页面中,点击一个超链接,访问servlet。 例子3 connection.jsp: <%@ page contentType="text/html;charset=GB2312" %> 7.4 servlet的共享变量 我们已经知道,在servlet被加载之后,当后续的客户请求servlet服务时,引擎将启动一个新的线程,在该线程中,servlet调用service方法响应客户的请求,而且servlet类中定义的成员变量,被所有的客户线程共享。在下面的例子4中,利用共享变量实现了一个计数器。 例子4(效果如图7.7所示) Count.java: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Count extends HttpServlet { int count; public void init(ServletConfig config) throws ServletException {super.init(config); count=0; } public synchronized void service(HttpServletRequest request,HttpServletResponse response) throws IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(" ");count++; //增加计数。 out.println("you are "+count+"th"+" people"); out.println(" "); } } 注:在处理多线程问题时,我们必须注意这样一个问题:当两个或多个线程同时访问同一个变量,并且一个线程需要修改这个变量。我们应对这样的问题作出处理,否则可能发生混乱.所以上述例子中的service方法是一个synchronized方法。 数学上有一个计算π的公式:
π/4=1-1/3+1/5-1/7+1/9-1/11… …
下面的例子中利用成员变量被所有客户共享这一特性实现客户帮助计算 的值,即每当客户请求访问servlet时都参与了一次的计算。 客户通过点击一个JSP页面的超链接访问一个计算 的servlet 例子5(效果如图7.8所示) JSP页面 Example7_5.jsp: <%@ page contentType="text/html;charset=GB2312" %>
servlet源文件 import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class ComputerPI extends HttpServlet { double sum=0,i=1,j=1; int number=0; public void init(ServletConfig config) throws ServletException {super.init(config); } public synchronized void service(HttpServletRequest request,HttpServletResponse response) throws IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/plain");//设置响应的MIME类型为纯文本。 number++; sum=sum+i/j; j=j+2; i=-i; out.println("You are "+number+"th People coming to here"); out.println("Now PI= "+4*sum); } } 7.5 HttpServlet 类 7.5.1 doGet方法和doPost方法 HttpServlet除了init、service、destroy方法外,该类还有两个很重要的方法:doGet和doPost,用来处理客户的请求并作出响应。 当服务器引擎第一次接受到一个servlet请求时,会使用init方法初始化一个servlet,以后每当服务器再接受到一个servlet请求时,就会产生一个新线程,并在这个线程中调用service方法检查HTTP请求类型(Get 、Post等),并在service方法中根据用户的请求方式,对应地再调用doGet或doPost方法。因此,在servlet类中,我们不必重写service方法来响应客户,直接继承service方法即可。我们可以在servlet类中重写doPost或doGet方法来响应用户的请求,这样可以增加响应的灵活性,并降低服务器的负担。 如果不论用户请求类型是Post还是Get,服务器的处理过程完全相同,那么我们可以只在doPost方法中编写处理过程,而在doGet方法中再调用doPost方法即可,或只在doGet方法中编写处理过程,而在doPost方法中再调用doGet方法(见例子6)。 如果根据请求的类型进行不同的处理,就需在两个方法中编写不同的处理过程(见例子7)。 在下面的例子6中,用户可以通过两个表单向servlet提交一个正数,其中一个表单的提交方式是post,另一个表单的方式是get。无论用户用那种方式,服务器的servlet都计算这个数的全部因数,返回给用户。而在下面的例子7中,如果使用post方式提交正数,servlet计算这个数的全部因数,如果使用get方式,servlet求出小于这个数的全部素数。
例子6(效果如图7.9所示) 提交正数的JSP页面 Example7_6.jsp: <%@ page contentType="text/html;charset=GB2312" %> 输入一个数,提交给servlet(Post方式): 输入一个数,提交给servlet(Get方式): sevlet源文件 ComputerFacor.java: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class ComputerFactor extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");
String number=request.getParameter("number"); //获取客户提交的信息。 double n=0; try{ n=Double.parseDouble(number); out.println(" factors of "+n+" :");//求n的全部因数: for(int i=1;i<=n;i++) { if(n%i==0) out.println(i); } } catch(NumberFormatException e) { out.print(" input number letter please!");} } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } }
提交正数的JSP页面 Example7_7.jsp: <%@ page contentType="text/html;charset=GB2312" %> 输入一个数,提交给servlet(Post方式): 输入一个数,提交给servlet(Get方式):
sevlet源文件 ComputerFacorandPrimNumber.java: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class ComputerFactorandPrimNumber extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");
String number=request.getParameter("number"); //获取客户提交的信息。 double n=0; try{ n=Double.parseDouble(number); out.println(" factors of "+n+" :");//求n的全部因数: for(int i=1;i<=n;i++) { if(n%i==0) out.println(i); } } catch(NumberFormatException e) { out.print(" input number letter please!");} } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");
String number=request.getParameter("number"); //获取客户提交的信息。 double n=0; try{ n=Double.parseDouble(number); out.println(" Primnumbers less "+n+" :");//求小于n的全部素数: int j=1; for(int i=1;i { for(j=2;j {if(i%j==0) break; } if(j>=i) { out.println(i); } } } catch(NumberFormatException e) { out.print(" }
} } 7.5.2 处理HTTP请求头及表单信息 有关HTTP请求头的和表单的介绍,可参见第3章。 在下面的例子8中,servlet显示请求的HTTP头的值和表单提交的信息(可参考对比第3章例子4)。 例子8 提交信息的JSP页面 Example7_8.jsp: <%@ page contentType="text/html;charset=GB2312" %>
处理HTTP请求头的sevlet源文件 GetMessages.java: import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class GetMessages extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");// 客户使用的协议是: out.println(" String protocol=request.getProtocol(); out.println(protocol); //获取接受客户提交信息的servlet: out.println(" String path=request.getServletPath(); out.println(path); //客户提交的信息的长度: out.println(" int length=request.getContentLength(); out.println(length); // 客户提交信息的方式: out.print(" String method=request.getMethod(); out.println(method); //获取HTTP头文件中User-Agent的值: out.println(" String header1=request.getHeader("User-Agent"); out.println(header1); //获取HTTP头文件中accept的值: out.println(" String header2=request.getHeader("accept"); out.println(header2); // 获取HTTP头文件中Host的值: out.println(" String header3=request.getHeader("Host"); out.println(header3); //获取HTTP头文件中accept-encoding的值: out.println(" String header4=request.getHeader("accept-encoding"); out.println(header4); //获取客户的IP地址: out.println(" String IP=request.getRemoteAddr(); out.println(IP); // 获取客户机的名称: out.println(" String clientName=request.getRemoteHost(); out.println(clientName); // 获取服务器的名称: out.println(" String serverName=request.getServerName(); out.println(serverName); // 获取服务器的端口号: out.println(" int serverPort=request.getServerPort(); out.println(serverPort); //获取客户端提交的所有参数的名字: out.println(" Enumeration enum=request.getParameterNames(); while(enum.hasMoreElements()) {String s=(String)enum.nextElement(); out.println(s); } // 文本框text提交的信息: out.println(" String str=request.getParameter("boy"); out.println(str); out.println(""); out.println(""); } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } }
下面的例子9用servlet实现用户注册。用户通过一个JSP页面提交姓名和email地址实现注册。当servlet获取这些信息后,首先检查散列表对象中是否已经存在这个名字,该散列表存储了已经注册的用户的名字。如果目前准备注册的用户提交的名字在散列表中已经存在,就提示客户更换名字,否则将检查客户是否提供了书写正确的email地址,如果提供了书写正确email地址将允许注册(仅仅要求email地址中不允许出现空格)。
例子9(效果如图7.11所示) 提交注册名字的JSP页面 Example7_9.jsp: <%@ page contentType="text/html;charset=GB2312" %>
sevlet源文件 LoginByServlet.java: import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class LoginByServlet extends HttpServlet { Hashtable hashtable=new Hashtable();
public void init(ServletConfig config) throws ServletException {super.init(config); } public synchronized void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");OutputStream(); response.setContentType("text/html");//设置响应的MIME类型。 out.println(""); out.println(" ");//获取用户提交的名字: String person_name=request.getParameter("name"), name_found=null; if(person_name==null) {person_name=""; } //在散列表查找是否已存在该名字: name_found=(String)hashtable.get(person_name); if(name_found==null) { String person_email=request.getParameter("address"); if(person_email==null) {person_email=""; } StringTokenizer fenxi=new StringTokenizer(person_email," @"); int n=fenxi.countTokens(); if(n>=3) {out.print(" } else { hashtable.put(person_name,person_name); out.print(" out.print(" } } else {out.print(" } out.println(""); out.println(""); }
public synchronized void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } } 7.5.3 设置响应的HTTP头 有关响应的HTTP头介绍,可参见第3章。 在下面的例子10中,servlet设置响应头:Refresh的头值是2,那么该servlet在2秒钟后自动刷新,即servlet在2秒钟后重新调用service方法响应用户。
例子10(效果如图7.12所示) 调用servlet的JSP页面 Example7_10: <%@ page contentType="text/html;charset=GB2312" %> sevlet源文件 DateServlet.java: import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class DateServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");response.setHeader("Refresh","2"); //设置Refresh的值。 out.println("Now Time:"); out.println(" out.println(""); out.println(""); } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } } 下面例子11实现servlet的重定向,客户访问servlet:Day;如果访问的时间是在22点之后,就被重定向到servlet:Night,提醒用户休息。Day和Night被存放在examples/Web-inf/classes中。 例子11(效果如图7.13所示) Day.java: import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class Day extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");Calendar calendar=Calendar.getInstance(); //创建一个日历对象。 calendar.setTime(new Date());//用当前时间初始化日历时间。 int hour=calendar.get(Calendar.HOUR_OF_DAY), minute=calendar.get(Calendar.MINUTE), second=calendar.get(Calendar.SECOND); if(hour>=22) {response.sendRedirect("Night"); //重定向。 } else { out.print("Now time :"); out.print(hour+":"+minute+":"+second); } out.println(""); out.println(""); }
public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } }
Night.java: import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class Night extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");out.println(" It is time to sleep");out.println(""); out.println(""); } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } } 7.6 用servlet读写文件 这节内容涉及到的文件操作及输入、输出流的内容可参见第4章。 7.6.1 读取文件的内容 在下面的例子12中,通过一个JSP页面显示给用户一些JSP文件的名字,该JSP文件存放在Root服务目录下。用户可以通过Post或Get方式将文件的名字提交给一个servlet,该servelt存放在服务目录examples下的Web-inf/classes中。这个servlet将根据提交方式的不同,分别读取JSP文件的源代码给客户,或显示该JSP文件的运行效果给客户。
例子12(效果如图7.14、7.15、7.16所示) 提交文件名字的JSP页面 read.jsp: <%@ page contentType="text/html;charset=GB2312" %> <%@ page import ="java.io.*" %> <%! class FileJSP implements FilenameFilter { String str=null; FileJSP(String s) {str="."+s; } public boolean accept(File dir,String name) { return name.endsWith(str); } } %> 下面列出了服务器上的一些jsp文件 <% File dir=new File("d:/Tomcat/Jakarta-tomcat-4.0/webapps/root/"); FileJSP file_jsp=new FileJSP("jsp"); String file_name[]=dir.list(file_jsp); for(int i=0;i<5;i++) {out.print(" } %>
读取文件的servlet源文件 ReadFileServlet: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class ReadFileServlet extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } //doPost方法使用了回压流来读取JSP文件的源代码: public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { //获取提交的文件的名字: String name=request.getParameter("name"); //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");File f=new File("d:/Tomcat/Jakarta-tomcat-4.0/webapps/root",name); try{ FileReader in=new FileReader(f) ; PushbackReader push=new PushbackReader(in); int c; char b[]=new char[1]; while ( (c=push.read(b,0,1))!=-1)//读取1个字符放入字符数组b。 { String s=new String(b); if(s.equals("<")) //回压的条件 { push.unread('&'); push.read(b,0,1); //push读出被回压的字符字节,放入数组b. out.print(new String(b)); push.unread('L'); push.read(b,0,1); //push读出被回压的字符字节,放入数组b. out.print(new String(b)); push.unread('T'); push.read(b,0,1); //push读出被回压的字符字节,放入数组b. out.print(new String(b)); } else if(s.equals(">")) //回压的条件 { push.unread('&'); push.read(b,0,1); //push读出被回压的字符字节,放入数组b. out.print(new String(b)); push.unread('G'); push.read(b,0,1); //push读出被回压的字符字节,放入数组b. out.print(new String(b)); push.unread('T'); push.read(b,0,1); //push读出被回压的字符字节,放入数组b. out.print(new String(b));
} else if(s.equals("\n")) { out.print(" } else {out.print(new String(b)); } } push.close(); } catch(IOException e){} out.println(""); out.println(""); } //doGet方法将显示JSP源文件运行的效果 public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { String name=request.getParameter("name"); //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");File f=new File("d:/Tomcat/Jakarta-tomcat-4.0/webapps/root",name); try{ FileReader in=new FileReader(f) ; BufferedReader bufferin=new BufferedReader(in); String str=null; while((str=bufferin.readLine())!=null) {out.print(" } bufferin.close(); in.close(); } catch(IOException e){} out.println(""); out.println("");
} }
在这节,我们通过一个servlet实现小说内容的续写来说明servlet在写文件中的技巧。 在下面的例子13中,通过一个JSP页面显示给用户一个小说文件的已有内容,小说文件存放在服务器的F:/2000下,文件名字是story.txt。JSP文件存放在Root服务目录下。用户可以通过Post方式将小说的新内容提交给一个servlet,该servelt存放在服务目录examples下的Web-inf/classes中。这个servlet将用户提交的内容写入小说文件的尾部。 例子13(效果如图7.17所示) 提交小说内容的JSP页面 story.jsp: <%@ page contentType="text/html;charset=GB2312" %> <%@ page import ="java.io.*" %>
小说已有内容:<% File f=new File("F:/2000","story.txt"); //列出小说的内容: try{ RandomAccessFile file= new RandomAccessFile(f,"r"); String temp=null; while((temp=file.readUTF())!=null) { byte d[]=temp.getBytes("ISO-8859-1"); temp=new String(d); out.print(" } file.close(); } catch(IOException e){} %>
请输入续写的新内容:
续写文件的servlet源文件: Write.java: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Write extends HttpServlet { //声明一个共享的文件和共享字符串: File f=null; String use="yes" ; public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { //获取提交的文件内容: String content=request.getParameter("content"); //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");f=new File("F:/2000","story.txt"); //把对文件的操作放入一个同步块中,并通知 //其它用户该文件正在被操作中: if(use.startsWith("yes")) { synchronized(f) { use="using"; try{ RandomAccessFile file=new RandomAccessFile(f,"rw"); file.seek(file.length()); //定位到文件的末尾。 file.writeUTF(content); file.close(); use="yes"; out.print(" } catch(IOException e){} } } //如果该小说正在被续写,就通知客户等待: else {out.print("file is writing,wait please"); } out.println(""); out.println(""); } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } } 7.7 用servlet访问数据库 有关数据库连接的一些知识可参见第5章。本节通过例子说明servlet在数据库方面的应用。我们仍然使用第5章的数据源sun ,该数据源为Server服务器上的pubs数据库,该库有一个表:students。 7.7.1 数据库记录查询 在下面的例子14中,客户通过condition.jsp页面输入查询条件,例如,查询某个姓名的成绩、查询成绩在某个分数段范围内的学生成绩等等。用户通过Post方式提交姓名给servlet;分数区间通过Get方式提交给servlet。该servlet根据不同的提交方式采取相应的查询方法。 例子14(效果如图7.18所示) 提交查询条件的JSP页面 condition.jsp: <%@ page contentType="text/html;charset=GB2312" %>
负责查询的servlet源文件: Inquire.java: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import java.sql.*; public class Inquire extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } //通过Post方法按名字查询记录: public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");//获取提交的姓名: String name=request.getParameter("name"); String number,xingming; Connection con=null; Statement sql=null; ResultSet rs=null; int math,english,physics; try{Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); } catch(ClassNotFoundException e){} try { con=DriverManager.getConnection("jdbc:odbc:sun","sa",""); sql=con.createStatement(); String condition="SELECT * FROM students WHERE 姓名 = "+"'"+name+"'"; rs=sql.executeQuery(condition); out.print("
con.close(); } catch(SQLException e) { } out.println(""); out.println(""); } //通过Get方法按成绩查询记录: public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");//获取提交的分数的最大值和最小值: String englishmax=request.getParameter("englishmax"); String englishmin=request.getParameter("englishmin"); String mathmax=request.getParameter("mathmax"); String mathmin=request.getParameter("mathmin"); String number,xingming; Connection con=null; Statement sql=null; ResultSet rs=null; int math,english,physics; try{Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); } catch(ClassNotFoundException e){} try { con=DriverManager.getConnection("jdbc:odbc:sun","sa",""); sql=con.createStatement(); String eCondition="英语成绩 <= "+englishmax+" AND "+"英语成绩 >= "+englishmin; String mCondition="数学成绩 <= "+mathmax+" AND "+"数学成绩 >= "+mathmin;
String condition="SELECT * FROM students WHERE "+mCondition+" and "+eCondition; rs=sql.executeQuery(condition); out.print("
con.close(); } catch(SQLException e) { } out.println(""); out.println(""); } } 7.7.2 使用共享连接 数据库操作中,建立连接是耗时最大的操作之一。如果客户访问的是同一数据库,那么,为每个客户都建立一个连接是不合理的。我们已经知道,servlet的成员变量是被所有用户共享的。这样,我们可以把Connection对象作为一个成员变量被所有的客户共享,也就是说第一个访问数据库的客户负责建立连接,以后所有的客户共享这个连接,每个客户都不要关闭这个共享的连接。下面的servlet使用共享连接查询数据库的所有记录。
例子15 使用共享连接的servlet源文件 ShareInquire.java: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; import java.sql.*; public class ShareInquire extends HttpServlet { Connection con=null; //共享连接。 public void init(ServletConfig config) throws ServletException {super.init(config); //加载JDBC-ODBC桥接器: try{Class.forName("sun.jdbc.odbc.JdbcOdbcDriver"); } catch(ClassNotFoundException e){} } //通过Post方法按名字查询记录: public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");Statement sql=null; ResultSet rs=null; if(con==null) { try { //第一个用户负责建立连接con。 con=DriverManager.getConnection("jdbc:odbc:sun","sa",""); sql=con.createStatement(); String condition="SELECT * FROM students"; rs=sql.executeQuery(condition); out.print("
} catch(SQLException e) { } } //其它客户通过同步块使用这个连接: else { synchronized(con) {try{ sql=con.createStatement(); String condition="SELECT * FROM students"; rs=sql.executeQuery(condition); out.print("
} catch(SQLException e) { } } } out.println(""); out.println(""); } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } } 7.8会话管理 7.8.1 获取用户的会话 我们已经知道,HTTP协议是一种无状态协议。一个客户向服务器发出请求(request)然后服务器返回响应(respons),连接就被关闭了。在服务器端不保留连接的有关信息,因此当下一次连接时,服务器已没有以前的连接信息了,无法判断这一次连接和以前的连接是否属于同一客户。因此,必须使用客户的会话,记录有关连接的信息。 一个servlet使用HttpServletRequest 对象request调用getSession方法获取用户的会话对象:
HttpSession session=request.getSession(true);
一个用户在不同的servlet中获取的session对象是完全相同的,不同的用户的session对象互不相同。有关会话对象常用方法可参见第4章。 在下面的例子16中,有两个servlet,Boy和Girl。客户访问Boy时,将一个字符串对象,存入自己的会话中,然后访问Girl,在Girl中再输出自己的session对象中的字符串对象。
例子16(效果如图7.19所示) Boy.java: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Boy extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");HttpSession session=request.getSession(true); //获取客户的会话对象 session.setAttribute("name","Zhoumin"); out.println(session.getId()); //获取会话的Id. out.println(""); out.println(""); } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } } Girl.java: import java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class Girl extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");HttpSession session=request.getSession(true); //获取客户的会话对象 session.setAttribute("name","Zhoumin"); out.println(session.getId()); //获取会话的Id. String s=(String)session.getAttribute("name"); //获取会话中存储的数据。 out.print(" out.println(""); out.println(""); } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } } 7.8.2 购物车 用户通过一个JSP页面:choice.jsp选择商品,提交给servlet:AddCar,该servlet负责将商品添加到用户的session对象中(相当于用户的一个购物车),并将session对象中的商品显示给用户。用户可以不断地从choice.jsp页面提交商品给AddCar。用户通过remove.jsp页面选择要从购物车中删除的商品提交给servlet:RemoveGoods,该servlet负责从用户的购物车(用户的session对象)删除商品。
例子17(效果如图7.20、7.21所示) 负责选择商品的JSP页面 choice.jsp: <%@ page contentType="text/html;charset=GB2312" %> <%@ page import="java.util.*" %> <%@ page import="Car1" %> 这里是第一百货商场,选择您要购买的商品添加到购物车:
负责添加商品的servlet AddCar.java: import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class AddCar extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");HttpSession session=request.getSession(true); //获取客户的会话对象 String item =request.getParameter("item"), //获取客户选择的商品名称。 mount=request.getParameter("mount"), //获取客户购买的数量。 unit =request.getParameter("unit"); //获取商品的计量单位。 //将客户的购买信息存入客户的session对象中。 String str="Name: "+item+" Mount:"+mount+" Unit:"+unit; session.setAttribute(item,str); //将购物车中的商品显示给客户: out.println(" goods in your car: "); Enumeration enum=session.getAttributeNames(); while(enum.hasMoreElements()) { String name=(String)enum.nextElement(); out.print(" } } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } }
选择删除商品的JSP页面 remove.jsp: <%@ page contentType="text/html;charset=GB2312" %> <%@ page import="java.util.*" %> <%@ page import="Car1" %> 选择要从购物车中删除的商品:
负责删除商品的servlet RemoveGoods.java import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class RemoveGoods extends HttpServlet {public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { //获得一个向客户发送数据的输出流: PrintWriter out=response.getWriter(); response.setContentType("text/html;charset=GB2312");//设置响应的MIME类型。 out.println(""); out.println(" ");HttpSession session=request.getSession(true); //获取客户的会话对象 String item =request.getParameter("item"); //获取要删除的商品名称。 session.removeAttribute(item); //删除商品。 //将购物车中的商品显示给客户: out.println(" Now goods in your car:");Enumeration enum=session.getAttributeNames(); while(enum.hasMoreElements()) { String name=(String)enum.nextElement(); out.print(" } } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } } 7.8.3 猜数字 在第3章、第6章讲述JSP内置对象以及javabeans时,曾分别举过猜数字的例子。在这里,我们再使用servlet来实现猜数字这个小游戏,这样,我们就用3种方式实现了这个小游戏:直接由JSP页面来实现、通过javabeans来实现、通过servlet来实现。 当客户访问servlet:GetNumber时,随机分配给客户一个1到100之间的数,然后将这个数字存在客户的session对象中。客户在表单里输入一个数,来猜测分配给自己的那个数字。客户输入一个数字后,提交给servlet:Result,该servlet负责判断这个数是否和客户的session对象中存放的那个数字相同,如果相同就连接到servlet:Success;如果不相同就连接到servlet: Large或Small。然后,客户在这些servlet中重新提交数字到Result。
servlet源文件 GetNumber.java: import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class GetNumber extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { response.setContentType("text/html"); ServletOutputStream out=response.getOutputStream(); out.print("A number between 1 and 100 to you,guess it out please! "); HttpSession session=request.getSession(true); session.setAttribute("count",new Integer(0)); int number=(int)(Math.random()*100)+1; //获取一个随机数。 session.setAttribute("save",new Integer(number)); out.print(" ");} public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } }
Result.java: import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class Result extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { response.setContentType("text/html"); ServletOutputStream out=response.getOutputStream(); HttpSession session=request.getSession(true); String str=request.getParameter("boy"); if(str==null) {str="0"; } int guessNumber=Integer.parseInt(str); Integer integer=(Integer)session.getAttribute("save"); int realnumber=integer.intValue(); if(guessNumber==realnumber) { int n=((Integer)session.getAttribute("count")).intValue(); n=n+1; session.setAttribute("count",new Integer(n)); response.sendRedirect("Success"); } else if(guessNumber>realnumber) { int n=((Integer)session.getAttribute("count")).intValue(); n=n+1; session.setAttribute("count",new Integer(n)); response.sendRedirect("Larger"); } else if(guessNumber { int n=((Integer)session.getAttribute("count")).intValue(); n=n+1; session.setAttribute("count",new Integer(n)); response.sendRedirect("Smaller"); } } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } } Larger.java: import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class Larger extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { response.setContentType("text/html"); ServletOutputStream out=response.getOutputStream(); out.print("Larger ,try again!"); //所猜的数比实际的数大,请再猜。 out.print(" out.print(""); out.print(""); out.print(""); } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } }
Smaller.java import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class Smaller extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { response.setContentType("text/html"); ServletOutputStream out=response.getOutputStream(); out.print("Smaller ,try again!"); //所猜的数比实际的数小,请再猜。 out.print(" } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } }
Success.java import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class Success extends HttpServlet { public void init(ServletConfig config) throws ServletException {super.init(config); } public void doPost(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { response.setContentType("text/html"); ServletOutputStream out=response.getOutputStream(); HttpSession session=request.getSession(true); int count=((Integer)session.getAttribute("count")).intValue(); int num=((Integer)session.getAttribute("save")).intValue(); long startTime=session.getCreationTime(); long endTime=session.getLastAccessedTime(); long spendTime=(endTime-startTime)/1000; out.println("Congratulatuon! You are right"); out.println("afer just"+count+"tries") ; out.println("you spend"+spendTime+"Seconds"); out.println("That Number is"+num); } public void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { doPost(request,response); } }
|