Servlet概述

 servlet的基本概念

一、Servlet的结构

  在具体掌握servlet之前,须对Java语言有所了解。在Servlet API中最重要的是Servlet接口(interface),所有的servlets都必须实现该接口,途径有很多:一是直接实现该接口,二是通过扩展类(class)来实现,如 HttpServlet。 这个Servlet接口提供了servlet与客户端联系的方法。Servlet编写者可以在他们开发 servlet程序时提供更多一些或所有的这样方法。

  当一个servlet接收来自客户端的调用请求, 它接收两个对象:一个是ServletRequest,另外一个是ServletResponse。这个ServletRequest类概括从客户端到服务器之间的联系,而 ServletResponse类概括从servlet返回客户端的联系。

  ServletRequest接口可以获取到这样一些信息,如由客户端传送的阐述名称,客户端正在使用的协议,产生请求并且接收请求的服务器远端主机名。它也提供获取数据流的ServletInputStream, 这些数据是客户端引用中使用HTTP POST 和 PUT 方法递交的。一个ServletRequest的子类可以让servlet获取更多的协议特性数据。例如:HttpServletRequest 包含获取 HTTP-specific头部信息的方法。

  ServletResponse接口给出相应客户端的servlet方法。它允许servlet设置内容长度和回应的mime类型,并且提供输出流ServletOutputStream,通过编写者可以发回相应的数据。ServletResponse子类可以给出更多protocol-specific内容的信息。 例如:HttpServletResponse 包含允许servlet 操作HTTP-specific头部信息的方法。

  上面有关类和接口的描述,构成了一个基本的Servlet框架。HTTP servlets有一些附加的可以提供session-tracking capabilities的方法。servlet编写者可以利用这些API,在有他人操作时维护servlet与客户端之间的状态。

二、Servlet的接口

  我们编写的Servlet ,一般从Javax包的HttpServlet类扩展而来,在HttpServlet中加入了一些附加的方法,这些方法可以被协助处理HTTP 基本请求的HttpServlet类中的方法service自动地调用。这些方法有:

  · doGet 用来处理HTTP的GET请求。

  这个GET操作仅仅允许客户从HTTP server上取得(GET)资源。重载此方法的用户自动允许支持方法HEAD。这个GET操作被认为是安全的,没有任何的负面影响,对用户来说是很可靠的。比如,大多数的正规查询都没有副作用。打算改变存储数据的请求必须用其他的HTTP方法。这些方法也必须是个安全的操作。方法doGet的缺省实现将返回一个HTTP的BAD_REQUEST错误。

  方法doGet的格式:
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
  throws ServletException,IOException;

  · doPost 用来处理HTTP的POST请求。

  这个POST操作包含了在必须通过此servlet执行的请求中的数据。由于它不能立即取得资源,故对于那些涉及到安全性的用户来说,通过POST请求操作会有一些副作用。

  方法doPost的缺省实现将返回一个HTTP的BAD_REQUEST错误。当编写servlet时,为了支持POST操作必须在子类HttpServlet中实现(implement)此方法。

  此方法的格式:

  protected void doPost(HttpServletRequest request, HttpServletResponse response)
  throws ServletException,IOException;

  · doPut用来处理HTTP的PUT请求。

  此PUT操作模拟通过FTP发送一个文件。对于那些涉及到安全性的用户来说,通过PUT请求操作也会有一些副作用。

  此方法的格式:

  protected void doPut(HttpServletRequest request,HttpServletResponse response)
  throws ServletException,IOException;

  · doDelete用来处理HTTP的DELETE请求。

  此操作允许客户端请求一个从server移出的URL。对于那些涉及到安全性的用户来说,通过DELETE请求操作会有一些副作用。

  方法doDelete的缺省实现将返回一个HTTP的BAD_REQUEST错误。当编写servlet时,为了支持DELETE操作,必须在子类HttpServlet中实现(implement)此方法。

  此方法的格式:

  protected void doDelete (HttpServletRequest request, HttpServletResponse response)
  throws ServletException,IOException;

  · doHead 用来处理HTTP的HEAD请求。

  缺省地,它会在无条件的GET方法执行时运行,但是不返回任何数据到客户端。只返回包含内容信息的长度的header。由于用到GET操作,此方法应该是很安全的(没有副作用)也是可重复使用的。此方法的缺省实现(implement)自动地处理了HTTPDE的HEAD操作并且不需要通过一个子类实现(implement)。

  此方法的格式:

  protected void doHead (HttpServletRequest request,HttpServletResponse response)
  throws ServletException,IOException;

  · doOptions用来处理HTTP的OPTIONS请求。

  此操作自动地决定支持什么HTTP方法。比如说,如果读者创建HttpServlet的子类并重载方法doGet,然后方法doOptions会返回下面的header:

  Allow:GET,HEAD,TRACE,OPTIONS

  一般不需要重载方法doOptions。

  此方法的格式:

  protected void doOptions (HttpServletRequest request, HttpServletResponse response)
throws ServletException,IOException;

  · doTrace用来处理HTTP的TRACE请求。

  此方法的缺省实现产生一个包含所有在trace请求中的header的信息的应答(response)。在开发servlet时,多数情况下需要重载此方法。

  此方法的格式:

  protected void doTrace (HttpServletRequest request, HttpServletResponse response)
  throws ServletException,IOException;

  在开发以HTTP为基础的servlet中,Servlet开发者关心方法doGet和方法doPost即可。

三、Servlet的生命周期

  如果读者写过Java的小应用程序(Applet),那Servlet对你来说就不会太难,也许更为简单。因为Servlet不用考虑图形界面的应用。与小应用程序一样,Servlet也有一个生命周期。Servlet的生命周期是当服务器装载运行servlets:接收来自客户端的多个请求并且返回数据给客户端,然后再删除移开servlets。下面详细描述如下:

  1.初始化时期
  当一个服务器装载servlet时,它运行servlet的 init() 方法。
public void init(ServletConfig config) throws ServletException
{
super.init(); //一些初始化的操作,如数据库的连接
}

  需要记住的是一定要在init()结束时调用super.init()。init()方法不能反复调用,一旦调用就是重装载servlet。直到服务器调用destroy方法卸载servlet后才能再调用。

  2.Servlet的执行时期
  在服务器装载初始化servlet后,servlet就能够处理客户端的请求,我们可以用 service 方法来实现。每个客户端请求有它自己service方法:这些方法接收客户端请求,并且发回相应的响应。Servlets能同时运行多个service。这是很重要的,这样,service方法可以按一个thread-safe 样式编写。如:service方法更新servlet对象中的一个字段field,这个字段是可以同时存取的。假如某个服务器不能同时并发运行 service方法,也可以用SingleThreadModel 接口。这个接口保证不会有两个以上的线程(threads)并发运行。在Servlet执行期间其最多的应用是处理客户端的请求并产生一个网页。其代码如下:

PrintWriter out = response.getWriter();
out.println("<html>");
out.println("<head><title>"# Servlet </title></head>");
out.println("<body>");
out.println("Hello World");
out.println("</body></html>");
out.close();


3.Servlet结束时期
  Servlets一直运行到他们被服务器卸载。在结束的时候需要收回在init()方法中使用的资源,在Servlet中是通过destory()方法来实现的。
public void destroy()
{
//回收在init()中启用的资源,如关闭数据库的连接等。
}

JSP与servlet之间是怎样的关系?

  JSP主要关注于HTML(或者XML)与Java代码的结合,以及加入其中的JSP标记。如果一个支持JSP的服务器遇到一个JSP页面,它首先查看该页面是否被编译成为一个servlet。由此可见,JSP被编译成servlet,即被转变为纯Java,然后被装载入服务器执行。当然,这一过程,根据不同的JSP引擎而略有不同。

  JSP和servlet在应用上有什么区别

  简单的说,SUN首先发展出SERVLET,其功能比较强劲,体系设计也很先进,只是,它输出HTML语句还是采用了老的CGI方式,是一句一句输出,所以,编写和修改HTML非常不方便。

  后来SUN推出了类似于ASP的嵌套型的JSP,把JSP TAG嵌套到HTML语句中,这样,就大大简化和方便了网页的设计和修改。新型的网络语言如ASP,PHP都是嵌套型的。

  从网络三层结构的角度看,一个网络项目最少分三层:data layer,business layer,,presentation layer。当然也可以更复杂。

  SERVLET用来写business layer是很强大的,但是对于写presentation layer就很不方便。JSP则主要是为了方便写presentation layer而设计的。当然也可以写business layer。写惯了ASP,PHP,CGI的朋友,经常会不自觉的把presentation layer和business layer混在一起。比如把数据库处理信息放到JSP中,其实,它应该放在business layer中。

  根据SUN自己的推荐,JSP中应该仅仅存放与presentation layer有关的部分,也就是说,只放输出HTML网页的部份。而所有的数据计算、数据分析、数据库联结处理,统统是属于business layer,应该放在JAVA BEANS中。通过JSP调用JAVA BEANS,实现两层的整合。

  实际上,微软前不久推出的DNA技术,简单说,就是ASP+COM/DCOM技术。与JSP+BEANS完全类似,所有的presentation layer由ASP完成,所有的business layer由COM/DCOM完成。通过调用,实现整合。

  为什么要采用这些组件技术呢?因为单纯的ASP/JSP语言是非常低效率执行的,如果出现大量用户点击,纯SCRIPT语言很快就到达了他的功能上限,而组件技术就能大幅度提高功能上限,加快执行速度。

  另外一方面,纯SCRIPT语言将presentation layer和business layer混在一起,造成修改不方便,并且代码不能重复利用。如果想修改一个地方,经常会牵涉到十几页CODE,采用组件技术就只改组件就可以了。

  综上所述,SERVLET是一个不完善的产品,写business layer很好,写presentation layer就很逊色许多了,并且两层混杂。所以,推出JSP+BAEN,用JSP写presentation layer,用BAEN写business layer。SUN自己的意思也是将来用JSP替代SERVLET。

  所以,学了JSP,不会用JAVA BEAN并进行整合,等于没学。

如何调用servlet?

  要调用Servlet或Web应用程序,请使用下列任一种方法:由URL调用、在<FORM>标记中调用、在<SERVLET>标记中调用、在ASP文件中调用。

  1.由URL调用 Servlet

  这里有两种用Servlet的URL从浏览器中调用该Servlet的方法:

  (1)指定 Servlet 名称:当用 WebSphere应用服务器管理器来将一个Servlet实例添加(注册)到服务器配置中时,必须指定"Servlet 名称"参数的值。例如,可以指定将hi作为HelloWorldServlet的Servlet名称。要调用该Servlet,需打开http://your.server.name/servlet/hi。也可以指定Servlet和类使用同一名称(HelloWorldServlet)。在这种情况下,将由http://your.server.name/servlet/ HelloWorldServlet 来调用Servlet的实例。

  (2)指定 Servlet 别名:用 WebSphere应用服务器 管理器来配置Servlet别名,该别名是用于调用Servlet的快捷URL。快捷URL中不包括Servlet名称。

  2.在<FORM>标记中指定Servlet

  可以在<FORM>标记中调用Servlet。HTM 格式使用户能在Web页面(即从浏览器)上输入数据,并向Servlet提交数据。例如:

 

<FORM METHOD="GET" ACTION="/servlet/myservlet">
<OL>
<INPUT TYPE="radio" NAME="broadcast" VALUE="am">AM<BR>
<INPUT TYPE="radio" NAME="broadcast" VALUE="fm">FM<BR>
</OL>
(用于放置文本输入区域的标记、按钮和其它的提示符。)
</FORM>

  ACTION特性表明了用于调用Servlet的URL。关于METHOD的特性,如果用户输入的信息是通过GET方法向Servlet提交的,则 Servlet 必须优先使用doGet()方法。反之,如果用户输入的信息是通过POST方法向Servlet提交的,则 Servlet 必须优先使用doPost()方法。使用GET方法时,用户提供的信息是查询字符串表示的URL编码。无需对URL进行编码,因为这是由表单完成的。然后URL编码的查询字符串被附加到Servlet URL中,则整个URL提交完成。URL编码的查询字符串将根据用户同可视部件之间的交互操作,将用户所选的值同可视部件的名称进行配对。例如,考虑前面的HTML代码段将用于显示按钮(标记为AM和FM),如果用户选择FM按钮,则查询字符串将包含name=value的配对操作为broadcast=fm。因为在这种情况下,Servlet将响应HTTP请求,因此Servlet应基于HttpServlet类。Servlet 应根据提交给它的查询字符串中的用户信息使用的 GET 或 POST 方法,而相应地使用 doGet() 或 doPost() 方法。

   3.在<SERVLET>标记中指定Servlet

  当使用<SERVLET>标记来调用Servlet时,如同使用<FORM>标记一样,无需创建一个完整的HTML页面。作为替代,Servlet的输出仅是HTML页面的一部分,且被动态嵌入到原始HTML页面中的其它静态文本中。所有这些都发生在服务器上,且发送给用户的仅是结果HTML页面。建议在Java服务器页面(JSP)文件中使用 <SERVLET> 标记。

  原始HTML页面中包含<SERVLET> 和</SERVLET> 标记。Servlet将在这两个标记中被调用,且Servlet的响应将覆盖这两个标记间的所有东西和标记本身。如果用户的浏览器可以看到HTML源文件,则用户将看不到<SERVLET>和</SERVLET>标记。要在 Domino Go Webserver 上使用该方法,请启用服务器上的服务器端包括功能。部分启用过程将会涉及到添加特殊文件类型SHTML。当Web服务器接收到一个扩展名为SHTML的Web页面请求时,它将搜索 <SERVLET> 和 </SERVLET> 标记。对于所有支持的Web服务器,WebSphere应用服务器将处理SERVLET标记间的所有信息。下列 HTML 代码段显示了如何使用该技术。

<SERVLET NAME="myservlet" CODE="myservlet.class" CODEBASE="url" initparm1= "value">
<PARAM NAME="parm1" VALUE="value">
</SERVLET>

  使用NAME和CODE属性带来了使用上的灵活性。可以只使用其中一个属性,也可以同时使用两个属性。NAME属性指定了Servlet的名称(使用WebSphere应用服务器管理器配置的),或不带.class扩展名的Servlet类名。CODE属性指定了Servlet类名。使用WebSphere应用服务器时,建议指定NAME和CODE,或当NAME指定了Servlet名称时,仅指定NAME。如果仅指定了CODE,则会创建一个NAME=CODE的Servlet实例。装入的Servlet将假设Servlet名称与NAME属性中指定的名称匹配。然后,其它SHTML文件可以成功地使用NAME属性来指定Servlet的名称,并调用已装入的Servlet。NAME的值可以直接在要调用Servlet的URL中使用。如果NAME和CODE都存在,且NAME指定了一个现有Servlet,则通常使用NAME中指定的Servlet。由于Servlet创建了部分HTML文件,所以当创建Servlet时,将可能会使用HttpServlet的一个子类,并优先使用doGet()方法(因为GET方法是提供信息给Servlet的缺省方法)。另一个选项是优先使用service()方法。另外,CODEBASE是可选的,它指定了装入Servlet的远程系统的URL。请使用WebSphere应用服务器管理器来从JAR文件配置远程Servlet装入系统。

  在上述的标记示例中,initparm1是初始化参数名,value是该参数的值。可以指定多个"名称-值"对的集合。利用ServletConfig对象(被传递到Servlet的init()方法中)的getInitParameterNames()和getInitParameter()方法来查找参数名和参数值的字符串数组。在示例中,parm1是参数名,并在初始化Servlet后被才被设置某个值。因为只能通过使用"请求"对象的方法来使用以<PARAM>标记设置的参数,所以服务器必须调用Servlet service()方法,以从用户处传递请求。要获得有关用户的请求信息,请使用getParameterNames()、getParameter()和getParameterValues()方法。

  初始化参数是持续的。假设一台客户机通过调用一个包含某些初始化参数的SHTML文件来调用Servlet。并假设第二台客户机通过调用第二个SHTML文件来调用同一个Servlet,且该SHTML中未指定任何初始化参数。那么第一次调用Servlet时所设置的初始化参数将一直可用,并且通过所有其它SHTML文件而调用的所有后继Servlet都不会更改该参数。直到Servlet调用了destroy()方法后,才能重新设置初始化参数。例如,如果另一个SHTML文件指定了另一个不同的初始化参数值,虽然已此时已装入了Servlet,但该值仍将被忽略。

   4.在ASP文件中调用Servlet
  如果在Microsoft Internet Information Server(IIS)上有遗留的ASP文件,并且无法将ASP文件移植成JSP文件时,可用ASP文件来调用Servlet。在WebSphere应用服务器中的ASP支持包括一个用于嵌入Servlet的ActiveX控制,下面介绍ActiveX控制AspToServlet的方法和属性。

  该方法说明如下:

  (1)String ExecServletToString(String servletName);执行ServletName,并将其输出返回到一个字符串中。

  (2)ExecServlet(String servletName);执行ServletName,并将其输出直接发送至 HTML 页面。

  (3)String VarValue(String varName);获得一预置变量值(其它格式)。

  (4)VarValue(String varName, String newVal);设置变量值。变量占据的总大小应小于0.5个千字节(Kbyte)。且仅对配置文件使用这些变量。

  其属性如下:

  = Boolean WriteHeaders;若该属性为真,则Servlet提供的标题被写入用户处。缺省值为假。
  = Boolean OnTest;若该属性为真,服务器会将消息记录到生成的HTML页面中。缺省值为假。
  下列ASP 脚本示例是以Microsoft Visual Basic Scripting(VBScript)书写的。

<%
' Small sample asp file to show the capabilities of the servlets and the ASP GateWay ...
%>
<H1> Starting the ASP->Java Servlet demo</H1>
<%
' Create a Servlet gateway object and initialize it ...
Set Javaasp = Server.CreateObject("AspToServlet.AspToServlet")
' Setting these properties is only for the sake of demo.
' These are the default values ...
Javaasp.OnTest = False
Javaasp.WriteHeaders = False
' Add several variables ...
Javaasp.VarValue("gal") = "lag"
Javaasp.VarValue("pico")= "ocip"
Javaasp.VarValue("tal") = "lat"
Javaasp.VarValue("paz") = "zap"
Javaasp.VarValue("variable name with spaces") = "variable value with spaces"
%>
<BR>
Lets check the variables
<%
Response.Write("variable gal = ")
Response.Write(Javaasp.VarValue("gal"))
%>
<BR>
<%
Response.Write("variable pico = " & Javaasp.VarValue("pico"))
%>

<BR>
<HR>
<%
galout = Javaasp.ExecServletToString("SnoopServlet")
If Javaasp.WriteHeaders = True Then
%>
Headers were written <%
Else
%>
Headers were not written <%
End If
Response.Write(galout)
%>
<H1> The End ...</H1>

如何设置servlet类的路径?

  因为各个服务器对访问servlet的策略不尽相同,所以在设置servlet类路径时应该视情况而定。
对于开发中的servlet,只需确认包含Javax.servlet 的JAR文档在您的类路径中,并运用如Javac的普通开发工具。
  对于 JSDK:JSDK_HOME/servlet.jar
  JSDK_HOME/server.jar
  对于 Tomcat:TOMCAT_HOME/lib/servlet.jar
  对于运行中的servlet,必须为servlet引擎设置类路径,这根据不同的引擎,有不同的配置,如哪些库和目录应包括,哪些不应包括。注:对于servlet的动态加载引擎如JRun, Apache Jserv, Tomcat,包含servlet类文件的目录不应在类路径中,而应在config文件中配置。否则,servlet可以运行,但不能被动态再加载。

  Servlet 2.2 规范认为以下应被容器自动包括,因此您不必把他们手工添加到类路径。

  · 所有的类应放在 webapp/WEB-INF/classes目录下

  · 所有JAR文件放在webapp/WEB-INF/lib 目录下

  · 对webapps的应用体现在文档系统中,对已打包进JAR文档的webapps的应用应放入容器的webapps目录。(例如,TOMCAT_HOME/webapps/myapp.jar)

  另外,由Gene McKenna(mckenna@meangene.com)撰写的"The Complete CLASSPATH Guide for Servlets"详细叙述了如何为JavaWebServer和Jrun设置类路径。

  如何实现servlet与applet的通信?

  这个例子将向读者展示服务器端程序(Servlet)和小应用程序(Applet)之间是如何完成通信活动的。它由三个文件组成,一个是sendApplet.Java文件,用于实现Applet,一个是receiveservlet.Java,用于实现servlet,还有一个是add-servlet.html,用于调用Applet。

  在sendApplet.Java文件中,最重要的要属init()函数和Send()函数,其中init()函数用来生成整个Applet的用户操作界面,包括消息文本框、发送按钮等等。而消息的发送过程则由Send()函数来完成。请仔细阅读下面的代码:

 

private void Send()
{
message = sendText.getText();
//清除用户的输入信息
sendText.setText("");
showStatus("Message send!");
//把输入的字符串转化为 x-www-form-urlencoded 格式
String queryString = "/servlet/ReceiveServlet?message=" + URLEncoder.encode ( message ) ;
p("Attempting to send:"+message);

//建立与Servlet的联接,并取得Servelt的输出信息
try {
connect = (new URL(chatURL,queryString)).openConnection();
showStatus("open connection!");
//下次连接不用Cache
connect.setDefaultUseCaches(false);
//这次连接也不用Cache
connect.setUseCaches(false);
//打开淂流用于读数据
connect.setDoInput(true);
//不能用于写数据
connect.setDoOutput(false);
//服务器与客户的真正连接
connect.connect();
p("Made connection to "+connect);
showStatus("Open Stream!");
DataInputStream in = new DataInputStream(connect.getInputStream());
showStatus("reading!");
message = in.readLine();
while (message! = null)
{
//在消息文本框显示Servlet生成的信息
messageText.setText(message);
message = in.readLine();
}
}catch(MalformedURLException e2)
{
System.err.println("MalformedURLException!");
e2.printStackTrace(System.err);
showStatus("MalformedURLException!");
}catch(IOException e1)
{
System.err.println("IOException!");
e2.printStackTrace(System.err);
showStatus("IOException");
}
}

  整个Applet的详细代码请见sendApplet.Java。

  当Applet与Servlet建立连接后,工作就可以交给Servlet了,由它来解析客户端的请求,获得参数message的值,然后将适当信息返回给客户端,并由Applet进行显示。完成该功能的是receiveservlet.Java中的service()函数:

public void service (HttpServletRequest req,HttpServletResponse res)
throws ServletException,IOException
{
res.setContentType("text/plain");
ServletOutputStream out = res.getOutputStream();
out.print("receive user message:");
out.print(req.getParameter("message"));
}

  该Servlet的详细源代码请见receiveservlet.Java。
  最后一个文件是add-servlet.html,它用来调用Applet:

<html>
<head>
<title>sendApplet</title>
</head>
<body>
<hr>
<applet code=sendApplet width=400 height=300 ></applet>
<hr>
</body>
</html>

如何应用应用Servlet进行图象处理?

  我们在处理数据时,有时希望能用图象直观的表述,在这里有一个巧方法,能方便快捷的实现一些简单的图形(不能称之图象),比如条形图,我们不必去用Java来生成并显示图象,(Java生成图象很慢),我们可以这样来作,先用作图工具作一个很小的你需要的图片,再根据你所处理的数据量来实时的加长它,就可以得到所要表述的图例。比如我们在数据库中得到了一组数据,我们从中找出最大的那一个,按比列设定其<img>标签的长度,其它的数据图形则可与它相比,得到<img>的长度,这样,一个简简单单的条形图就出来。但有时一些简单的图形已经不能解决我们实际遇到的情况,比如曲线图就不能用这种方法,这时我们需要生成Java图象,也许大家都用过applet这样的程序吧,若访问量不大,而实时性又很特殊时(比如股票系统),必须这样用它。但事实上,我们web程序大多有前后台之分,前台浏览,后台维护。这样我们可以在后台用servlet实时动态定时地生成图象文件,而前台只是查看静态图片,这比你用applet来动态产生图象的速度快了不知多少倍,因为applet来动态产生图象,有两个地方很费时,一是数据库查询时间,二是applet本身生成图象就很慢。下面以一个简单的例子来说明一下怎样生成并写入图象文件,本例注重的是怎样写入图象文件,相信写过applet的读者会生成更加漂亮的图象。

 

package test;
import Javax.servlet.*;
import Javax.servlet.http.*;
import Java.io.*;
import Java.util.*;
import Java.awt.image.BufferedImage;
import com.sun.image.codec.jpeg.*;
import Java.awt.image.*;
import Java.awt.*;

public class Servlet2 extends HttpServlet
{
public void init(ServletConfig config) throws ServletException {
super.init(config);
}

public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
String sFileName = "e:/temp/name.jpg";
try{
FileOutputStream fos = new
FileOutputStream(sFileName);
BufferedImage myImage = new BufferedImage(225, 225,BufferedImage. TYPE_INT_RGB);
Graphics g = myImage.getGraphics();
g.setColor(Color.white);
g.fillRect(0,0,225,225);
g.setColor(Color.black);
g.drawString("Finance Balance Summary", 40, 15);
g.drawString("Primary", 90, 30);
g.setColor(Color.darkGray);
g.fillRect(15,193,7,7);
g.setColor(Color.black);
g.drawString("% Operating", 25, 200);
g.setColor(Color.yellow);
g.fillRect(130,193,7,7);
g.setColor(Color.black);
g.drawString("% Term", 140, 200);
g.setColor(Color.lightGray);
g.fillRect(15,213,7,7);
g.setColor(Color.black);
g.drawString("% Mortgage", 25, 220);
g.setColor(Color.green);
g.fillRect(130,213,7,7);
g.setColor(Color.black);
g.drawString("% Lease", 140, 220);
JPEGImageEncoder jpg = JPEGCodec.createJPEGEncoder(fos);
jpg.encode(myImage);
}catch (Exception e)
{
String exceptionThrown = e.toString();
String sourceOfException = " Method";
System.out.println("Origional Exception Thrown: " +exceptionThrown + '/r' + '/n');
System.out.println("Origional SourceOfException: " + sourceOfException +'/r' + '/n');
} // CatchStatementEnd
}
}

  如何通过Servlet调用JavaBean输出结果集

  以此我们通过一个例子进行说明,该例演示了如何通过Servlet调用JavaBean输出结果集,并打印的方法,共由两个文件组成,一个是JavaBean,用于实现对数据库的访问,并获得结果集;另一个是Servlet,主要负责JavaBean的调用,并将结果集发送到客户端。

  在JavaBean中,我们将访问DB2样例数据库(sample)中的STAFF表,至于如何实现对数据库的访问,读者可以参考《JSP与JDBC》一章。此外,读者可以通过修改部分参数,来实现对其他数据库、表的访问,达到举一反三的效果。

  该JavaBean的核心是execute()函数:

public void execute()
{
try {
//装载JDBC驱动程序
Class.forName("COM.ibm.db2.jdbc.app.DB2Driver").newInstance();
//建立对数据库的连接
conn = DriverManager.getConnection("jdbc:db2:sample", "db2admin", "db2admin");
stmt = conn.createStatement();
String sql = "SELECT * FROM STAFF WHERE DEPT=20";
//执行查询语句,返回结果集
ResultSet rs = stmt.executeQuery(sql);
setResult(rs);
} catch (SQLException e) {
} catch (IllegalAccessException e2) {
} catch (ClassNotFoundException e3) {
} catch (InstantiationException e4) {}
}

  JavaBean的具体源代码请见Tbean.Java。

  知道数据是如何获取之后,下面我们来看一下Servlet是如何来调用上述JavaBean的。

  同样看service()方法即可(详细源代码请见Tservlet.Java):

public void service(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException
{
try {
//实例化JavaBean
Demo.TBean Javabean = new Demo.TBean();
Javabean.execute();
ResultSet rs1 = Javabean.getResult();
PrintWriter out = res.getWriter();
res.setContentType("text/html");
out.println("<table border=1>"=;
out.println("<H1>Hello World</H1>"=;
out.println("<td>ID</td><td>NAME</td><td>DEPT</td><td>JOB</td><td>YEARS</td><td>SALARY</td><td>COMM</td>"=;
while (rs1.next())
{
out.println("<tr>"=;
for (int i = 1; i <= 7; i++=
out.println("<td>" + rs1.getString(i) + "</td>"=;
out.println("</tr>"=;
}
out.println("</table>"=;
Javabean.Sqlclose();
} catch (SQLException e) {}
}
//运行:在VisualAge for Java 的IBM Websphere Test Environment的环境下:
//http://localhost:8080/servlet/Demo.TServlet

Hello Print ID NAME DEPT JOB YEARS SALARY COMM
10 Sanders 20 Mgr 7 18357.50 null
20 Pernal 20 Sales 8 18171.25 612.45
80 James 20 Clerk null 13504.60 128.20
190 Sneider 20 Clerk 8 14252.75 126.50

如何用Servlet来中断涉及的多线程

  现在我们已经知道,当服务器要卸载一个Servlet时,它会在所有的service都已经完成后再调用destroy()方法。如果程序的操作运行需要很长时间,destroy()被调用时就可能还有其他线程在运行。Servlet程序员必须保证所有的线程都已经完成。

  长时间运行响应客户端请求的那些Servlet应当保留当前有多少方法在运行的记录。它的long-running方法应当周期性地轮流询问以确保它们能够继续运行下去。如果Servlet被destroy()方法调用,那么这个long-running方法必须停止工作或清除。

  举例,变量serviceCounter用来统计有多少service方法在运行,变量shuttingDown显示这个Servlet是否被destroy。每个变量有它自己的获取方法:

public ShutdownExample extends HttpServlet
{
private int serviceCounter = 0;
private Boolean shuttingDown;

//serviceCounter
protected synchronized void enteringServiceMethod()
{
serviceCounter++;
}
protected synchronized void leavingServiceMethod()
{
serviceCounter--;
}
protected synchronized int numServices()
{
return serviceCounter;
}
//shuttingDown
protected setShuttingDown(Boolean flag)
{
shuttingDown = flag;
}
protected Boolean isShuttingDown()
{
return shuttingDown;
}
这个service方法每次在它进入时要增加,而在它返回退出时要减少:
protected void service(HttpServletRequest req , HttpServletResponse resp)
throws ServletException IOException
{
enteringServiceMethod();
try{
super.service(req , resp);
}
finally {leavingServiceMethod();}
}

  destroy方法应当检查serviceCounter,如果存在长时间方式运行的话,设置变量shuttingDown。这个变量将会让那个正在处理请求的线程知道该结束了。destroy方法应当等待这几个service方法完成,这样就是一个清楚的关闭过程了。

public void destroy()
{
//检查是否有线程在运行,如果有,告诉它们停止
if (numServices() > 0)
{
setShuttingDown(true);
}
//等待它们停止
while(numService() > 0)
{
try{
thisThread.sleep(interval);
}catch(InterruptedException e) {}
}
}
long-running方法如必要应当检查这个变量,并且解释它们的工作:
public void doPost(…)
{

for(i = 0; ((i < lotsOfStuffToDo) && !isShuttingDown()); i++)
{
try{
partOfLongRunningOperation(i);
}catch (InterruptedException e) {}
}
}

附录:深入理解servlet

  Servlets(Java小服务器程序)是Java中新增加的一个全新功能。一般来说,Servlets是由服务器端调用和执行的任何Java类,浏览器端运行的Java程序叫Applet,Web服务器端运行的Java程序叫做Servlet。自从有了Servlet后,Java的电子商务才真正的开始,之后的JSP又是在Servlet上有了更进一步的发展。了解Servlet的机制也就掌握到了到JSP的实现原理。
  在编写Servlet的时候不需要关心一个Servlet是如何被装载到服务器环境中,只需要调用Java Servlet API编程接口就行了。在使用Servlet API的时候,程序员完全可以不必了解内部运行方式,服务器头、Cookies、会话都可以通过Servlet来处理。但当我们需要一些特殊功能的时候,就需要了解它的一些实现机制了。
  先从Servlet的生命周期说起。一般情况下,可以归纳为几点:
  1.装载Servlets。这项操作一般是动态执行的。有些Server提供了相应的管理功能,可以在启动的时候就装载Servlet;
  2.Server创建一个Servlet实例;
  3.Server调用Servlet的init()方法;
  4.一个客户端的请求到达Server;
  5.server创建一个请求对象;
  6.Server创建一个响应对象;
  7.Server激活Servlet的Service()方法,并传递请求和响应对象;
  8.service()方法获得关于请求对象的的信息、处理请求、访问其他资源、获得需要的信息;
  9.service()方法使用响应对象的方法,将响应传回Server,最终到达客户端。Service()方法可能机或其他方法已处理请求,如doGet()或doPost()或程序员自己开发的方法;
  10.对于更多的客户端请求,Server创建新的请求和响应对象,仍然激活此Servlet的service()方法,将这两个对象作为参数传递给它。如此重复以上的循环,但无需再次调用init()方法。也就是说,Servlet()只初始化一次。
  11.当Server不再需要Servlet时(一般是当Server关闭的时候),Server调用Servlets的destroy()方法。
  从前面我们还可以更进一步得出以下几点:
  Servlet运行时是多线程的,在开发的时候由Servlet API实现,开发人员不需要做特别的处理。Servlet的init()、destroy()的使用就像是一个C++编写的结构、析构函数,要注意前后的一致。比如在init()打开了一个数据库,就要在destroy()中关闭。
  对一些底层的处理,如要控制消息响应的方式,我们就要重载server()方法,并重新编写。
  为便于解释Servlet的实现机制,围绕HttpServlet,用下图描述Javax.Http包中常用到的类之间的关系(除掉了JSP部分),限于篇幅,没有给出每个接口、类的属性和方法。通过图示,我们很容易开发一些较低层次的代码。

  从下图,非常明显的可以看出HttpServlet是从GenericServlet类继承下来的。而事实上,GenericServlet类主要是为了起到三个接口的联合(这个词用得可能不科学):Servlet,ServletConfig,Serializable,以获得系统的支持;除了init()外几乎没有实现方法操作。真正调用系统所支持的各项功能是在HttpServlet类中。

  就HttpServlet的service()方法,一般来说,当它接收到一个OPTIONS请求时,会调用doOptions()方法,当接收到一个TRACE请求时调用doTrace()。doOptions()缺省执行方式是自动决定什么样的HTTP被选择并且返回哪个信息。相关源代码如下:

public void service(ServletRequest req,ServletResponse res)
throws ServletException,IOException
{
HttpServletRequest request;
    HttpServletResponse response;
    try
    {
    request = (HttpServletRequest)req;
      response = (HttpServletResponse)res;
  } catch(ClassCastException _ex)
  {
      throw new ServletException("non-HTTP request or response");
}
service(request, response);
  }
  protected void service(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
  {
      String method = req.getMethod();
      if(method.equals("GET"))
      {
        long lastModified = getLastModified(req);
        if(lastModified == -1L)
        {
          doGet(req, resp);
        }
        else
        {
          long ifModifiedSince = req.getDateHeader("If-Modified-Since");
          if(ifModifiedSince < (lastModified / 1000L) * 1000L)
          {
            maybeSetLastModified(resp, lastModified); doGet(req, resp);
          }
          else
          {
            resp.setStatus(304);
          }
        }
      }
      else if(method.equals("HEAD"))
      {
        long lastModified = getLastModified(req);
        maybeSetLastModified(resp, lastModified);
        doHead(req, resp);
      }
      else
      if(method.equals("POST"))
        doPost(req, resp);
      else
      if(method.equals("PUT"))
        doPut(req, resp);
      else
      if(method.equals("DELETE"))
        doDelete(req, resp);
      else
      if(method.equals("OPTIONS"))
        doOptions(req, resp);
      else
      if(method.equals("TRACE")) {
        doTrace(req, resp);
      }
      else
      {
        String errMsg = lStrings.getString("http.method_not_implemented");
        Object errArgs[] = new Object[1];
        errArgs[0] = method;
        errMsg = MessageFormat.format(errMsg, errArgs);
        resp.sendError(501, errMsg);
      }
    }
  protected void doOptions(HttpServletRequest req, HttpServletResponse resp)
    throws ServletException, IOException
    {
      Method methods[] = getAllDeclaredMethods(getClass());
      boolean ALLOW_GET = false; boolean ALLOW_HEAD = false;
      boolean ALLOW_POST = false; boolean ALLOW_PUT = false;
      boolean ALLOW_DELETE = false; boolean ALLOW_TRACE = true;
      boolean ALLOW_OPTIONS = true;
      for(int i = 0; i < methods.length; i++)
      {
        Method m = methods[i];
        if(m.getName().equals("doGet"))
        {
          ALLOW_GET = true;
          ALLOW_HEAD = true;
        }
        if(m.getName().equals("doPost"))
        ALLOW_POST = true;
        if(m.getName().equals("doPut"))
        ALLOW_PUT = true;
        if(m.getName().equals("doDelete"))
        ALLOW_DELETE = true;
        }
        String allow = null;
        if(ALLOW_GET && allow == null)
          allow = "GET";
        if(ALLOW_HEAD)
          if(allow == null)
           allow = "HEAD";
          else allow = allow + ", HEAD";
        if(ALLOW_POST)
          if(allow == null)
          allow = "POST";
          else allow = allow + ", POST";
        if(ALLOW_PUT)
          if(allow == null)
          allow = "PUT";
          else allow = allow + ", PUT";
        if(ALLOW_DELETE)
          if(allow == null)
        allow = "DELETE";
        else allow = allow + ", DELETE";
      if(ALLOW_TRACE)
        if(allow == null)
        allow = "TRACE";
        else allow = allow + ", TRACE";
      if(ALLOW_OPTIONS)
        if(allow == null)
        allow = "OPTIONS";
        else allow = allow + ", OPTIONS";
      resp.setHeader("Allow", allow);
      }
    protected void doTrace(HttpServletRequest req, HttpServletResponse resp)
      throws ServletException, IOException
    {
      String CRLF = "/r/n";
     String responseString = "TRACE " + req.getRequestURI() + " " + req.getProtocol();
      for(Enumeration reqHeaderEnum = req.getHeaderNames();
          reqHeaderEnum.hasMoreElements();)
    {
          String headerName = (String)reqHeaderEnum.nextElement();
     responseString = responseString + CRLF + headerName + ": " + req.getHeader(headerName);
   }
responseString = responseString + CRLF; int responseLength = responseString.length();
 resp.setContentType("message/http");
resp.setContentLength(responseLength);
 ServletOutputStream out = resp.getOutputStream();
out.print(responseString);
out.close();
}

  如果我们改写了上面的代码,将可以写出一些针对性更强的特殊领域中的应用。限于篇幅,这里将不在举例。另外,在HttpServlet类(或子类)中,可以对一些系统底层支持的功能进行一些操作,比如Session、Cookie等。其中HttpSession接口在Sun公司提供的包中没有找到它的类,应该是由Server引擎来实现、管理它的相关功能。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值