jsp语言学习资料 366页

  • 1.40 MB
  • 2022-08-11 发布

jsp语言学习资料

  • 366页
  • 当前文档由用户上传发布,收益归属用户
  1. 1、本文档由用户上传,淘文库整理发布,可阅读全部内容。
  2. 2、本文档内容版权归属内容提供方,所产生的收益全部归内容提供方所有。如果您对本文有版权争议,请立即联系网站客服。
  3. 3、本文档由用户上传,本站不保证质量和数量令人满意,可能有诸多瑕疵,付费之前,请仔细阅读内容确认后进行付费下载。
  4. 网站客服QQ:403074932
测试你对技术的掌握度:JSP程序员成长之路在本文章中使用精通、熟练、熟悉、了解标志你对某技术的掌握程度。  精通:能够掌握此技术的85%技术要点以上,使用此技术时间超过两年,并使用此技术成功实施5个以上的项目。能使用此技术优化性能或代码,做到最大可能的重用。  熟练:能够掌握此技术的60%技术要点以上,使用此技术时间超过一年,并使用此技术成功实施3个以上的项目。能使用此技术实现软件需求并有经验的积累在实现之前能做优化设计尽可能的实现模块或代码的重用。  熟悉:能够掌握此技术的50%技术要点以上,使用此技术时间超过半年上,并使用此技术成功实施1个以上的项目。能使用此技术实现软件需求。  了解:可以在实际需要时参考技术文档或帮助文件满足你的需要,基本知道此项技术在你运用是所起的作用,能够调用或者使用其根据规定提供给你的调用方式。  二:基本要求  1:html掌握程度:熟练。原因:不会html你可能写JSP?  2:javascript/jscript:掌握程度:熟悉。原因:client端的数据校验、一些页面处理需要你使用脚本。  3:css掌握程度:熟悉。原因:实现页面风格的统一通常会使用css去实现。  4:java基础编程掌握程度:熟练。原因:不会java你能写JSP?开玩笑吧。还有你必须非常熟悉以下几个包java.lang;java.io;java.sql;java.util;java.text;javax.sevrlet;javax.servlet.http;javax.mail;等。  5:sql掌握程度:熟练。原因:如果你不使用数据库的话你也许不需要掌握sql。同时你必须对以下几种数据库中的一种以上的sql比较熟悉。Oracle,DB2,Mysql,Postgresql.  6:xml掌握程度:了解原因:AppServer的配置一般是使用XML来实现的。\n  7:ejb掌握程度:了解原因:很多项目中商业逻辑是由ejb来实现的,所以呢……  8:以下几种AppServer(engnier)你需要了解一个以上。  a:)Tomcat  b:)WebLogic  c:)WebSphere  d:)JRun  e:)Resin  原因:你的jsp跑在什么上面啊!  三:选择要求(因项目而定)  1:LDAP掌握程度:了解 原因:LADP越来越多的运用在权限控制上面。  2:Struts掌握程度:熟练 原因:如果符合MVC设计通常会使用Struts实现C。  3:Xsp掌握程度:根据需要而定很多时候是不使用的,但在不需要使用ejb但jsp+servlet+bean实现不了的时候Xsp是一个非常不错的选择。  4:Linux掌握程度:熟悉 原因:如果你的运用跑在Linux/Unix上你最少要知道rm,mv,cp,vi,targzip/gunzip是用来做什么的吧。  四:工具的使用  1:UltraEdit(EditPlus)+jakarta-ant+jakarta-log4j;  2:Jubilder4-6  3:VisualAgeForJava  4:VCafe  以上的工具你选择你自己熟悉的吧。不过强烈建议你用log4j做调试工具。  五:成长之路  1:html\n学习时间,如果你的智商在80以上,15天时间应该够用了。至少你能手写出一个页面来。  2:jacascript/jscript学习时间,这真的不好说,比较深奥的东西,够用的话一个礼拜可以学写皮毛。  3:css学习时间,三天的时间你应该知道如何使用css了,不要求你写,一般是美工来写css。  4:java学习时间,天才也的三个月吧。慢满学吧。如果要精通,那我不知道需要多少时间了。用来写jsp,四个月应该够了。  5:sql学习时间,只需要知道insert,delete,update,select,create/droptable的话一天你应该知道了。  6:xml学习时间,我不知道我还没有学会呢。呵呵。不过我知道DTD是用来做什么的。  7:ejb学习时间,基本的调用看3天你会调用了。不过是建立在你学会java的基础上的。  8:熟悉AppServer,Tomcat四天你可以掌握安装,配置。把jsp跑起来了。如果是WebLogic也够了,但要使用ejb那不关你的事情吧。SA做什么去了。  9:熟悉Linux那可得需要不少时间。慢慢看man吧。  10:Struts如果需要你再学习。  六:结束语  我是闲的无聊,所以花了半个小时写了写,如果你觉得简直是一堆Shit,自己知道就行了,不用告诉我,呵呵。  如果对你还有点帮助,别忘了夸我两句。如果需要联系我:bingo_ge@hotmail.com转自:动态网制作指南www.knowsky.com\n成为一个成功Jsp程序员的九步如何成为一个成功的Jsp程序员?一个普通的错误是把JSP当作简化的Java。它不是,(事实上,JSP是简化的servlets。)程序员通常试着没有学习要求的支持技巧而直接学习JSP。JSP是一个衔接技术,并且成功地连接你需要理解的另外的技术。如果你已经知道Java,HTML和Javascript,这意味着JSP将确实是简单的。  需要成为一个成功的JSP程序员可以参考这个时间表。请注意下列:  *忽略你已经熟悉的步骤。  *训练的时间只是代表学习好足够的基础时间,这样才能转移到下一步。  1、建立并且理解你的WebServer。  因为Apache是免费的并且在大多数平台上工作,为训练目的推荐Apache。  安装时间:2天。  2、保证你理解HTML/XHTML。  你将需要了解html基础,特别是HTML布局中的table的使用。XHTML不久将代替HTML,学习XHTML的基础是一个好主意。许多程序员通过HTMLIDE学习HTML(集成开发环境)。因为大多数HTMLIDE产生混乱的HTMl语法,所以花时间学习手工写作html是很有必要的。因为你将会使用JSP和HTML混合编程,精通HTML语法是重要的。所以,你必须能流利地写HTML。  训练时间:2~4个星期。  3、开始学习Java。  开始学习Java1.4理解Java基础是很重要的。不用担心学习Swing或Java的图形方面,因为在JSP中你不会使用这些特征。集中精力在Java工作的细节,学习Java的逻辑,也在JavaBean上花时间。学习Applet是好的,但是就象Swing,JSP的大多数应用将不使用小程序。  训练时间:3~6个星期。  4、学习JavaScript  学习怎么将JavaScript在HTML中验证输入的Form元素。也学习JavaScript怎么能在一HTML页以内修改Form的元素。最后要求你能从一  HTML页内的事件中触发JavaScriptFunction。  训练时间:一~2个星期。\n  5、学习并且理解你的WebServer的更好的细节。  熟悉WebServer的特征,这是很重要的。  训练时间:2天。  6、建立你的JSPServer  我推荐以Tomcat开始。它可以很好地运行JSP程序。当你不能在生产使用Tomcat时,学习尽可能多的知识以便于更好的运行程序。另外,许多JSP程序员使用Tomcat。因此当你遇到一个问题时,你将容易发现帮助。  安装时间:1~2天。  7、开始学习JSP。  基本的JSP学习通过的步骤1到步骤6可以完成,然后使用JSP对象和脚本写JSP程序来联系。学习JSP的另外一个方面可以学习怎么创建一个分布式的应用程序。  训练时间:4~6个星期。  8、学习更多的JSPserver。  没有关于更多的JSPServer当然也可以运行jsp程序。然而,许多JSPserver都由自己特殊的特征,可以让你更好的理解你的JSP工程。  学习更多的Jspserver如何处理jsp程序是有必要的。同样也可以优化你的JSP应用程序,并且使之运行得更快而不出任何问题。  训练时间:2~7天。  9、学习JDBC。  JSP大多数应用将使用数据库,JDBC被用于数据库连接。经常忽略的一个事实就是,每个JDBCDriver所支持的东西是相当不同的。了解并熟悉在jsp工程上被使用的JDBCdriver的细节是很重要的。(有时这部分的学习被包含在前面Java或JSP的学习中了。)  训练时间:1~2个星期。  到现在,你已经成为了熟练的JSP程序员。仍然有很多需要学习,你可以考虑扩展你的知识比如DHTML,XML,java证书,JSPTagLibraries或Servlets,看你想要造什么类型的网站而决定了。  这些训练是JSP的核心。你不必都学习上面所有的,取决于你在工程中分配到什么任务和你已经有什么知识。但是这是我成功地训练程序员的时间表。关键的单元是时间。平均的说,5个月时间确实能够训练一个人(从开始到完成)成为一个对jsp熟悉程序员。5个月时间似乎很长,但要成为一个资深的WEB程序员所学的东西远远不止这一些。\n  也许你认为这样学习一种语言花费的时间太长了,因为学ASP会更快、时间会更短。但是学习ASP不需要学习java的。\nStruts学习傻瓜式入门篇或许有人觉得struts不容易学,似乎里面的一些概念让未接触过的人迷惑,MVC1、MVC2、模式……我写这篇文章是想让从来没有接触过struts的人,能有个简单的入门指引,当然,系统地学习struts是必要的,里面有很多让人心醉的东东,那是后话了。  该案例包括首页,用户登陆、网站向导页面。就这么简单,没有深奥的struts概念,主要靠动手,然后用心体会。  WEBServer用tomcat4。到http://jakarta.apache.org下载struts1.1,把zip文件释放到c:\struts,拷贝C:\struts\webapps\struts-example.war到c:\tomcat4\webapps中,启动tomcat,war包被释放为struts-example文件夹,删除war包,把struts-example文件夹更名为test。  一、把WEB-INF\web.xml改成:actionorg.apache.struts.action.ActionServletconfig/WEB-INF/struts-config.xml1action*.coolindex.jsp  二、把test\WEB-INF\struts-config.xml改成:  三、增加一个FormBean,类路径为test.UserForm,以下是这个类的内容:packagetest;importorg.apache.struts.action.ActionForm;publicclassUserFormextendsActionForm{ privateStringname="lpw";//用户名 privateStringps="1111";//密码 publicUserForm(){} publicvoidsetName(Strings){name=s;} publicStringgetName(){returnname;} publicvoidsetPs(Strings){ps=s;} publicStringgetPs(){returnps;}}  四、增加一个Action的子类,类路径为test.RegistAction,以下是这个类的内容:packagetest;importjava.lang.reflect.InvocationTargetException;importjava.util.Locale;importjavax.servlet.ServletException;importjavax.servlet.http.HttpServletRequest;importjavax.servlet.http.HttpSession;importjavax.servlet.http.HttpServletResponse;importorg.apache.struts.action.Action;importorg.apache.struts.action.ActionError;importorg.apache.struts.action.ActionErrors;importorg.apache.struts.action.ActionForm;importorg.apache.struts.action.ActionForward;importorg.apache.struts.action.ActionMapping;importorg.apache.struts.util.MessageResources;import\ntest.UserForm;publicfinalclassRegistActionextendsAction{ publicActionForwardexecute(ActionMappingmapping,ActionFormform,HttpServletRequestrequest,HttpServletResponseresponse) throwsException {  Localelocale=getLocale(request);  MessageResourcesmessages=getResources(request);  HttpSessionsession=request.getSession();  UserFormuserform=(UserForm)form;  //此处可以调用其他类来执行数据库写入或其他逻辑判断  //如果UserForm传来的参数name的值为默认的lpw,将forward到failed,  //该名称将到struts-config.xml的中寻找映射的url地址  //(可以是绝对路径,也可以是相对路径),对于本例,是转到failed.cool,  //还记得吗?后缀为cool的请求全部到action-mappings中寻找  //对应的action处理,最终目录是wuwu.jsp*/  if("lpw".equals(userform.getName()))   return(mapping.findForward("failed"));  else   return(mapping.findForward("regist")); }}  五、以下所有新增或修改的页面相当于struts的View部分,把首页index.jsp改成:<%@pagecontentType="text/html;charset=GBK"language="java"%><%@pageimport="test.*"%>站点导航
用户:
密码:
  六、增加hello.jsp,用于站点导航:\n

sitemap

Thefollowingiscontentfillingbyreader  七、增加wuwu.jsp,当没有新用户登陆时,将转到这个页面:<%@pagecontentType="text/html;charset=GBK"language="java"%>现有用户:<%=beanlpw.getName()%>
密码:<%=beanlpw.getPs()%>
  没有得到新的用户!  八、增加regist.jsp,当有新用户登陆时,将转到这个页面:<%@pagecontentType="text/html;charset=GBK"language="java"%>新用户帐号:<%=beanlpw.getName()%>
密码:<%=beanlpw.getPs()%>  九、启动tomcat4,浏览器中键入http://localhost:8080/test/index.jsp,操作一下,就可以看到结果,并初步理解struts的M、V、C各部分的协同工作原理,当然这是作者的良好意愿,如果读者看得一头雾水,欢迎指出错误在哪里:)转自:动态网制作指南www.knowsky.com\n入门教程:JSP标准模板库(上)简介JSP标准模板库(JSTL)是SUN公司发布的一个针对JSP开发的新组件。JSTL允许你使用标签(tags)来进行JSP页面开发,而不是使用多数JSP程序员已经习惯了的scriptlet代码方式开发。JSTL几乎能够做到传统JSPscriptlet代码能做的任何事情。你可能会疑惑,为什么我们需要另一种这样的HTML生成语言呢?STL允许JSP程序员使用tags而不是JAVA代码来编程。为了展示为什么这个是更优越的,下面将给出示例。我们会检查一个非常简单的从1数到10的JSP页面。我们将通过两种方法来检查,一种是基于JSP的scriptlet,一种是JSTL。当这个计数器页面的例子是用JSPscriptlet来编写时,JSP页面如下所示:    Countto10inJSPscriptlet  <% for(inti=1;i<=10;i++){%><%=i%>
<%}%> 正如你在上例中看到的看到的那样,使用scriptlet代码产生的页面源代码会包含混合的HTML标签和JAVA语句。这种混合型的编程方式不是最佳的方式,其主要原因有以下几点。主要的原因是它的可读性。这个可读性主要依赖于人类和计算机。JSTL可以允许程序员查看一个只包含完整HTML和类似HTML的标签的页面。SPscriptlet代码的可读性不适合人类。这种混合的scriptlet和HTML代码对于计算机来说也很难读。尤其是针对那些HTML官方工具如Dreamweaver和Microsoft\nFrontPage,所表现出来的不直观性更突出。目前,大多数HTML官方工具会以不可编辑块(non-editableblocks)的形式来隔离JSPscriptlet代码。这种HTML官方工具通常是不能直接修改JSPscriptlet代码的。下面这段代码展示这个计数器范例如何使用JSTL方式来编写。正如你所看到的,这个代码列表有不变性,仅仅一个标签被使用。HTML和JSTL标签混合起来产生了这个程序。<%@tagliburi="http://java.sun.com/jstl/core"prefix="c"%>    Countto10Example(usingJSTL)               
    当你检查上面这个例子的代码时,你会看到,JSP页面只包含标签。上面的代码使用诸如和
这样的HTML标签。这种标签用法不限制于HTML标签。这段代码也可以使用诸如这样的JSTL标签。在本文中,将会介绍一些JSTL的基础。安装JSTL要使用JSTL,你必须安装有一个JSP1.2(或者更高版本)的容器。最普通的JSP容器是ApacheTomcat。你能够从http://jakarta.apache.org/tomcat/这里下载到。独立的Tomcat允许你使用常规的JSPscriptlet代码。要使用JSTL,你必须在Tomcat中安装JSTL。JSTL的主要URL是http://java.sun.com/products/jsp/jstl/。要使用JSTL,必须解压这个文件,然后将它安装到Tomcat的正确位置。要在Tomcat中安装JSTL,有下面三个步骤:拷贝JSTLJAR文件到Tomcat的lib目录中.\n如果你使用的是Windows,那么lib目录最可能的位置是C:\ProgramFiles\ApacheTomcat4.0\webapps\ROOT\WEB-INF\lib。你应该拷贝这些JAR包到你的TomcatJAR目录。拷贝JSTLTLD文件到Tomcat的web-inf目录中你检查JSTL发布文件时,你应该会注意到有8个文件是以TLD扩展名结尾的。所有这8个文件都应该复制到你的web-inf目录中。修改web.xml文件包含这些TLD文件。最后,你必须修改你的web.xml,添加8个标签库的表项(entry)。需要添加的表项如下所示:   http://java.sun.com/jstl/fmt   /WEB-INF/fmt.tld   http://java.sun.com/jstl/fmt-rt   /WEB-INF/fmt-rt.tld   http://java.sun.com/jstl/core   /WEB-INF/c.tld   http://java.sun.com/jstl/core-rt   /WEB-INF/c-rt.tld   http://java.sun.com/jstl/sql   /WEB-INF/sql.tld   \nhttp://java.sun.com/jstl/sql-rt   /WEB-INF/sql-rt.tld   http://java.sun.com/jstl/x   /WEB-INF/x.tld   http://java.sun.com/jstl/x-rt   /WEB-INF/x-rt.tld完成上面这三步后,你现在可以准备测试你的JSTL安装了。可以通过创建一个包含JSTL的JSP页面来验证。一个最简单的范例程序就是上面的那个计数器程序。你应该将它放置在你的Webroot目录中(C:\ProgramFiles\ApacheTomcat5.0\webapps\ROOT)。一旦Tomcat启动后,你应该能够通过浏览http://127.0.0.1:8080/count.jsp来查看这个页面。如果你没有正确安装JSTL,那么可能不会出现错误信息。如果JSTL不能解释你的标签含义,那么它在Web浏览器上会直接跳过。然后Web浏览器将解释这些未知的HTML标签。多数浏览器仅仅只是忽略这些未知的HTML标签。Tobecontinued......TranslatedbyCaiyi0903(Willpower),2004.2.19\n入门教程:JSP标准模板库(下)JSTL标签库JSTL通常被认为是一种单标签库。但JSTL实际上有4个标签库。这些标签库被概括为如下:核心标签库—包含一些Web应用的必要的标签。核心标签库的实例包括循环,表达式赋值和基本输入输出。.格式化/国际化标签库—包括被用来解析数据的标签库。这种标签会解析数据,比如日期,不同的场所区域等。数据库标签库—包含被用来访问SQL数据库的标签。这些标签通常仅仅只创建原形(prototype)程序。这个是因为大多数程序不会直接从JSP页面中来处理数据库访问。数据库访问应该被嵌入到能够被JSP页面所访问的EJB中。XML标签库—包含被用来访问XML元素的标签。因为XML广泛使用于Web开发,XML的处理是JSTL的一个重要的特征。 在本文中,我们将仅仅只对核心标签做一个简单介绍。我们将考察一个简单的范例,来说明如何处理用户在表单中输入的数据。在我们开始检查程序之前,我们必须先看看JSTL是如何处理表达式的,JSTL的表达式处理是通过使用EL表达式语言来完成的,仅仅只能在JSP2.0被使用。下一节,我们会考察EL表达式语言。EL表达式语言JSP2.0的一个主要的组件就是这个新的叫做EL的表达式语言。EL被广泛用在JSTL中。然而,重要的是记住,EL是JSP的功能而不是JSTL的。被用在JSP2.0中JSPscriptlet代码能够包含EL表达式。下面这行代码演示了JSPscriptlet代码中的EL:

 Yourtotal,includingshippingis${total+shipping}

正如你看到的,total和shipping相加后会产生HTML并且在浏览器中显示它们的值。这些表达式也可以在JSTL标签中使用。JSTL1.0的一个重要问题是,JSTL不能用在JSP1.2的版本上。因为JSP1.2不支持EL,所以有必要提供一些额外的JSTL标签来帮助EL的使用。例如,如果你想使用JSTL来显示上面的表达式,你可以使用下面的代码。\n

 Yourtotal,includingshippingis

值得注意的是,JSTL不需要JSP2.0来运行。通过提供一个能够显示EL表达式的标签,就可以达到这样的需求。JSTL范例我们将考察一个简单的使用JSTL的范例。在这个例子里,我们将考察Web应用中的一个常见过程。我们会看到如何提交(POST)一个表单,并处理POST的结果。程序代码如下:<%@tagliburi="http://java.sun.com/jstl/core"prefix="c"%>    IfwithBody          Youguessedit!     
     
     
          Youarewrong     
     
     
           Guesswhatcomputerlanguage                       Iamthinkingof?   \n      
    这个简单的Web页面显示一个表单,要求用户猜测程序所想的计算机语言。当然,计算机想的是“Java”。这个页面通过检查“是否已提交”来开始。这使得表单和处理表单的代码可以放在同一个页面中。是通过下面的JSTLif语句实现的。现在你看到了,标签使用EL表达式来决定是否请求的方式是POST的。如果数据被post到页面,那么用户输入的他们的猜想值就会存储在名为“guest”的参数中。这是因为“guest”是表单输入项的名字。我们现在必须检查是否这个参数等于“Java”。这个是通过下面的标签来完成:         Youguessedit!     正如你所看到的,如果表达式为true,标签的标签体就被执行。在本文中,我们开始考察了JSTL如何被安装,以及它是如何运转的,也提供了一些JSTL小范例帮助大家理解。JSTL的核心标签也包含循环,叠代器和变量处理。通过使用标签,你能在collections里进行iterate,访问用户的会话数据(sessiondata),执行其他的核心任务等。除了核心标签库,XML,数据库和格式化标签库也提供了更多高级的用处。(End)注:ELExpressionLanguage这里翻译成EL表达式语言,有的是翻译成EL表达语言,是一个意思。TranslatedbyCaiyi0903(Willpower),2004.2.19\nJSP与XML的结合综述:可扩展标注语言(eXtensibleMarkupLanguage,XML)正被迅速的运用于业界,它已作为与平台、语言和协议无关的格式描述和交换数据的广泛应用标准。XML和它的辅助规范可用于描述数据的文档表现,描述XML文档类型的限制,描述XML文档和资源之间的链接,描述XML文档的自动转换和格式化。  如何开发自定义标签库?  我使用JSP和ASP编程已经有一段颇长的时间了,在两种服务器端的编程方式中,我越来越觉得JSP的功能要强大得多。不提别的,其中JSP的标签库就是我选择JSP作为首选服务器端Web应用开发工具的原因。为什么?因为:维护和开发的速度。在一个单一的服务器页面中,你可以混合使用各种不同的脚本方法和对象。就?quot;混凝土"一样,这种混合可令服务器端的脚本变得强大,并且让服务器端的编程者设计出非常灵活和动态的Web页面。不过这种自由的混合也有其缺点,那就是维护起来非常麻烦,特别是当项目逐渐变大时。由于最终的产品是经由一个传统的Web设计者来维护的,因此会带来问题。更糟糕的是,随着代码的复杂性增加,开发的速度就会变慢,不利于开发中等和大型的Web应用,一旦开发完,站点还要找合格的编程者来维护这些颇为复杂的代码。幸好,JSP提供了一个很好解决的办法。标签库提供了一个简单的方法来建立一个可重用的代码块。一旦标签库设计好,它就可以在许多项目中再次使用。更方便的是,与COM和J2EE不同,你无需学习任何其它的技巧就可以建立一个标签库!只要你懂得写JSP,你就可以建立一个标签库。标签库还可以改善Web应用的维护。这个是得益于JSP页面自定义标签的简单XML接口。这样,Web设计者甚至可以做到无需知道任何JSP的知识,就可以建立JSP的Web应用。这个开放式的Web开发对于团队运作是非常有效的。JSP编程者可以建立自定义的标签和后台的代码模块,而Web设计者可以使用自定义的标签来建立Web应用,并且将精力集中在Web设计上。  1.标签库的定义  JSP标签库(也称自定义库)可看成是一套产生基于XML脚本的方法,它经由JavaBeans来支持。在概念上说,标签库是非常简单和可以重用的代码构造。  执行XML/XSL转换的标签范例和HTML页面<%@tagliburi="http://www.jspinsider.com/jspkit/JAXP"prefix="JAXP"%>c:/xml/example.xmlc:/xml/example.xsl\n  在这个例子中,通过使用简单的标签来访问后台更为强大的代码,一个XML被装载,并且通过一个XSL文件来产生一个结果,并发送给客户端,全部通过使用一个简单的标签调用就做到了。  自定义标签为在JSP项目中创建易于重用的代码打开了一扇大门。你所需要的只是标签库和它的文档说明。  2.标签的组件  虽然标签库非常易于使用,不过要建立一个内里的设计来支持标签库是颇复杂的,起码要比建立一个简单的JavaBean复杂。这个复杂是来自于标签库是由几部分构成的。不过,你只需要知道Java和JSP的知识就够了。一个简单的标签由下面的元素构成:  ⑴JavaBeans:为了得到Java与生具来的面向对象的好处,可重用的代码应该放到一个独立的代码容器中。这些JavaBeans并不是标签库的一部分。不过它是你的代码库用来执行相关任务的基本代码块。  ⑵标签处理:这是标签库的真正核心。一个标签处理器将引用它需要的任何资源(你的JavaBeans)和访问你的JSP页面的全部信息(pageContext对象)。JSP页面也会将所有已经被设置的标签属性和JSP页面上的标签体中的内容传送给标签处理器。在标签处理器处理完毕后,它将发回输出到你的JSP页面进行处理。  ⑶标签库的描述(tld文件):这是一个简单的XML文件,它记录着标签处理器的属性、信息和位置。JSP容器通过这个文件来得知从哪里及如何调用一个标签库。  ⑷网站的web.xml文件:这是你网站的初始化文件,在这个文件中,你定义了网站中用到的自定义标签,以及哪个tld文件用来描述每个自定义的标签。  ⑸分发文件(一个WAR或者JAR文件):如果你想重用自定义标签的话,你需要一个方法来将它由一个项目转移到另一个项目中。将标签库打包为一个JAR文件是一个简单而且有效的方式。  ⑹在你的JSP文件中作标签库声明:很简单,如果要用到该标签的话,只要在页面声明一下就可以,其后,你就可以在该JSP页面的任何地方使用它。  看来要做的工作很多,不过其实并不是很难。它的要点并不在于编码,而是在于如何将各部分正确地组织起来。不过,这样的分层是很重要的,它可令标签的使用灵活和更容易转移。更重要的是,这些层的存在可让处理建立标签的工程通过一个JSPIDE(JSP的集成开发环境)自动完成。期望将来的JSPIDE可自动完成创建一个自定义标签的大部分工作,这样你只需要写代码和标签处理就可以了。  注意:一个标签处理仅定义一个自定义标签;一个标签库是几个处理相同任务的标签处理器的集合。\n  3.建立自己的标签  以下将一步一步地教你如何建立自定义的标签,具体的例子是扩展JSP,令它拥有自己的HTML编码功能。这个功能将所有的<和>字符用HTML代码来代替。它可以很容易地扩展为做其它的编码处理。为了简化,这个例子只解释了建立自定义标签的基本要素。  ⑴创建一个JavaBean  你代码中任何可重新使用的部分都应该放到一个JavaBean中。这个很重要,因为你要经常在项目的其它地方用到这些代码。放在标签处理器中的任何代码在标签外都是不可以重新使用的,因此将可重用的代码部分独立开来是很重要的。在这个例子总,为HTML编码的逻辑是常用的,因此放到JavaBean中。  ⑵HTML编码JavaBean/*HTML_Format.Java*/publicclassHTML_FormatextendsObjectimplementsJava.io.Serializable{/**创建新的HTML_Format*/publicHTML_Format(){}/**将一个字符串中所有的所有<和>字符用响应的HTML编码代替*/publicStringHTML_Encode(Stringas_data){intli_len=as_data.length();/*stringbuffer的长度要比原来的字符串长*/StringBufferlsb_encode=newStringBuffer(li_len+(li_len/10));/*循环替换全部的<和>字符*/for(intli_count=0;li_count"))ls_next=">";lsb_encode.append(ls_next);}return(lsb_encode.toString());}}  ⑶创建一个标签处理器  标签处理器使用以下的代码:HTML编码标签处理器importJava.io.IOException;import\nJavax.servlet.jsp.*;importJavax.servlet.jsp.tagext.*;publicclassHTML_FormatTagextendsBodyTagSupport{/*1}在标签末将会调用这个函数*/publicintdoEndTag()throwsJspTagException{try{/*2}得到标签中的文本*/BodyContentl_tagbody=getBodyContent();Stringls_output="";/*3}如果标签体有文本,就处理它*/if(l_tagbody!=null){HTML_Formatl_format=newHTML_Format();/*3a}将标签体的内容转换为一个字符串*/Stringls_html_text=l_tagbody.getString();ls_output=l_format.HTML_Encode(ls_html_text);}/*4}将结果写回到数据流中*/pageContext.getOut().write(ls_output.trim());}catch(IOExceptione){thrownewJspTagException("TagError:"+e.toString());}/*让JSP继续处理以下页面的内容*/returnEVAL_PAGE;}}   这个处理很简单,它包括有:  o读入标签开始和结束间的文本  o调用html编码函数  o返回结果到JSP页面。  ⑷创建一个标签描述器  需要描述自定义标签以让系统知道如何处理。该描述文件的后缀为.tld,通常它的名字和标签处理器相同,并存放在"/WEB-INF/"目录。HTML编码标签描述器1.01.1HTML_FormatTagHTMLEncodingTagHTMLEncodeHTML_FormatTagEncodeHTML  ⑸更新WebXML文件  现在可告诉JSP容器使用标签库。为此要修改web.xml文件,具体说来是要在其中加入一个taglib的项目来注册该标签库。最重要的是,要为tag分配一个URI。URI是一个唯一的引用,只应用在该网站的这个特别的标签上。使用全长的URL或者包名是一个好的习惯,它可以确保唯一性,因为该标签可以在不同的网站使用。这个例子是简化了。修改web.xml文件HTMLEncode/WEB-INF/HTML_FormatTag.tld\n  ⑹使用新的标签  自定义的标签已经设置好,可以用在一个JSP页面上。要做到这一点,只需在该页面使用taglib指示命令声明一下该标签就可以了,该标签通过它唯一的URI被引用,并且会被分配一个名字空间前缀。前缀可以任意,只要它不与其它的名字空间冲突便可。  在一个JSP页面上使用HTML编码标签:<%@tagliburi="HTMLEncode"prefix="Examples"%>
范例代码的输出whichdisplaysas:  通过这个标签,我就将该页面的所有代码编码了。有趣的是所有的自定义标签都是在服务器上处理的。这意味着你将不会在输出的页面上看到自定义的标签。  建立一个标签不是很难吧。最困难的部分是要学习标签处理的所有细节。这是一个很强大的功能,我们只是提到了最基本的地方。由于这个处理需要几步,新的JSP编程者在创建标签时将会感到迷惑。  如何利用JSP开发DOM应用?  DOM是DocumentObjectModel的缩写,即文档对象模型。XML将数据组织为一颗树,所以DOM就是对这颗树的一个对象描叙。通俗的说,就是通过解析XML文档,为XML文档在逻辑上建立一个树模型,树的节点是一个个对象。我们通过存取这些对象就能够存取XML文档的内容。  下面我们来看一个简单的例子,看看在DOM中,我们是如何来操作一个XML文档的。这是一个XML文档,也是我们要操作的对象:Good-byeserialization,helloJava! \n  下面,我们需要把这个文档的内容解析到一个个的Java对象中去供程序使用,利用JAXP,我们只需几行代码就能做到这一点。首先,我们需要建立一个解析器工厂,以利用这个工厂来获得一个具体的解析器对象:  DocumentBuilderFactorydbf=DocumentBuilderFactory.newInstance();  我们在这里使用DocumentBuilderFacotry的目的是为了创建与具体解析器无关的程序,当DocumentBuilderFactory类的静态方法newInstance()被调用时,它根据一个系统变量来决定具体使用哪一个解析器。又因为所有的解析器都服从于JAXP所定义的接口,所以无论具体使用哪一个解析器,代码都是一样的。所以当在不同的解析器之间进行切换时,只需要更改系统变量的值,而不用更改任何代码。这就是工厂所带来的好处。  DocumentBuilderdb=dbf.newDocumentBuilder();  当获得一个工厂对象后,使用它的静态方法newDocumentBuilder()方法可以获得一个DocumentBuilder对象,这个对象代表了具体的DOM解析器。但具体是哪一种解析器,微软的或者IBM的,对于程序而言并不重要。  然后,我们就可以利用这个解析器来对XML文档进行解析了:  Documentdoc=db.parse("c:/xml/message.xml");  DocumentBuilder的parse()方法接受一个XML文档名作为输入参数,返回一个Document对象,这个Document对象就代表了一个XML文档的树模型。以后所有的对XML文档的操作,都与解析器无关,直接在这个Document对象上进行操作就可以了。而具体对Document操作的方法,就是由DOM所定义的了。  从得到的Document对象开始,我们就可以开始我们的DOM之旅了。使用Document对象的getElementsByTagName()方法,我们可以得到一个NodeList对象,一个Node对象代表了一个XML文档中的一个标签元素,而NodeList对象,观其名而知其意,所代表的是一个Node对象的列表:  NodeListnl=doc.getElementsByTagName("message");  我们通过这样一条语句所得到的是XML文档中所有标签对应的Node对象的  一个列表。然后,我们可以使用NodeList对象的item()方法来得到列表中的每一个Node对象:  Nodemy_node=nl.item(0);  当一个Node对象被建立之后,保存在XML文档中的数据就被提取出来并封装在这个Node中了。在这个例子中,要提取Message标签内的内容,我们通常会使用Node对象的getNodeValue()方法:Stringmessage=my_node.getFirstChild().getNodeValue();\n  请注意,这里还使用了一个getFirstChild()方法来获得message下面的第一个子Node对象。虽然在message标签下面除了文本外并没有其它子标签或者属性,但是我们坚持在这里使用getFirseChild()方法,这主要和W3C对DOM的定义有关。W3C把标签内的文本部分也定义成一个Node,所以先要得到代表文本的那个Node,我们才能够使用getNodeValue()来获取文本的内容。现在,既然我们已经能够从XML文件中提取出数据了,我们就可以把这些数据用在合适的地方,来构筑应用程序。  DOM实例  先说说这个例子到底要做的是什么吧,我们在一个名为link.xml文件中保存了一些URL地址,我们希望可以通过DOM把这些URL读出并显示出来,也可以反过来向这个XML文件中写入加入的URL地址。很简单,却很实用,也足够来例示DOM的绝大部分用法了。  第一个程序我们称为xmldisplay.Java,主要的功能就是读取这个XML文件中各个节点的内容,然后在格式化输出在System.out上,我们来看看这个程序:importJavax.xml.parsers.*;importorg.w3c.dom.*;  这是引入必要的类,因为在这里使用的是Sun所提供的XML解析器,因而需要引入Java.xml.parsers包,其中包含了有DOM解析器和SAX解析器的具体实现。org.w3c.dom包中定义了w3c所制定的DOM接口。DocumentBuilderFactoryfactory=DocumentBuilderFactory.newInstance();DocumentBuilderbuilder=factory.newDocumentBuilder();Documentdoc=builder.parse("links.xml");doc.normalize();    除了上面讲到的,还有一个小技巧,对Document对象调用normalize(),可以去掉XML文档中作为格式化内容的空白而映射在DOM树中的不必要的TextNode对象。否则你得到的DOM树可能并不如你所想象的那样。特别是在输出的时候,这个normalize()更为有用。  NodeListlinks=doc.getElementsByTagName("link");  刚才说过,XML文档中的空白符也会被作为对象映射在DOM树中。因而,直接调用Node方法的getChildNodes方法有时候会有些问题,有时不能够返回所期望的NodeList对象。解决的办法是使用Element的getElementByTagName(String),返回的NodeLise就是所期待的对象了。然后,可以用item()方法提取想要的元素。for(inti=0;iOgdenNashFleasAdam \n  当XMLReader读到标签时,就会调用ContentHandler.startElement()方法,并把标签名POEM作为参数传递过去。在你实现的startElement()方法中需要做相应的动作,以处理当出现时应该做的事情。各个事件随着解析的过程(也就是文档读入的过程)一个个顺序的被抛出,相应的方法也会被顺序的调用,最后,当解析完成,方法都被调用后,对文档的处理也就完成了。下面的这个表,列出了在解析上面的那个XML文件的时候,顺序被调用的方法:  遇到的项目方法回调{文档开始}startDocument()startElement(null,"POEM",null,{Attributes})"\n"characters("\n...",6,1)startElement(null,"AUTHOR",null,{Attributes})"OgdenNash"characters("\n...",15,10)endElement(null,"AUTHOR",null)"\n"characters("\n...",34,1)startElement(null,"TITLE",null,{Attributes})"Fleas"characters("<POEM>\n...",42,5)endElement(null,"TITLE",null)"\n"characters("\n...",55,1)startElement(null,"LINE",null,{Attributes})"Adam"characters("\n...",62,4)endElement(null,"LINE",null)"\n"characters("\n...",67,1)endElement(null,"POEM",null){文档结束}endDocument()   ContentHandler实际上是一个接口,当处理特定的XML文件的时候,就需要为其创建一个实现了ContentHandler的类来处理特定的事件,可以说,这个实际上就是SAX处理XML文件的核心。下面我们来看看定义在其中的一些方法:  voidcharacters(char[]ch,intstart,intlength):这个方法用来处理在XML文件中读到字符串,它的参数是一个字符数组,以及读到的这个字符串在这个数组中的起始位置和长度,我们可以很容易的用String类的一个构造方法来获得这个字符串的String类:StringcharEncontered=newString(ch,start,length)。  voidstartDocument():当遇到文档的开头的时候,调用这个方法,可以在其中做一些预处理的工作。  voidendDocument():和上面的方法相对应,当文档结束的时候,调用这个方法,可以在其中做一些善后的工作。  voidstartElement(StringnamespaceURI,StringlocalName,StringqName,Attributes\natts):当读到一个开始标签的时候,会触发这个方法。在SAX1.0版本中并不支持名域,而在新的2.0版本中提供了对名域的支持,这儿参数中的namespaceURI就是名域,localName是标签名,qName是标签的修饰前缀,当没有使用名域的时候,这两个参数都未null。而atts是这个标签所包含的属性列表。通过atts,可以得到所有的属性名和相应的值。要注意的是SAX中一个重要的特点就是它的流式处理,在遇到一个标签的时候,它并不会纪录下以前所碰到的标签,也就是说,在startElement()方法中,所有你所知道的信息,就是标签的名字和属性,至于标签的嵌套结构,上层标签的名字,是否有子元属等等其它与结构相关的信息,都是不得而知的,都需要你的程序来完成。这使得SAX在编程处理上没有DOM来得那么方便。  voidendElement(StringnamespaceURI,StringlocalName,StringqName):这个方法和上面的方法相对应,在遇到结束标签的时候,调用这个方法。  我们还是沿用讲DOM的时候使用的那个文档例子,但首先,我们先看一个简单一些的应用,我们希望能够统计一下XML文件中各个标签出现的次数。这个例子很简单,但是足以阐述SAX编程的基本思路了。一开始当然还是import语句了:importorg.xml.sax.helpers.DefaultHandler;importJavax.xml.parsers.*;importorg.xml.sax.*;importorg.xml.sax.helpers.*;importJava.util.*;importJava.io.*;   然后,我们创建一个继承于DefaultHandler的类,具体的程序逻辑在这儿可以暂且放在一边,要注意的是程序的结构:publicclassSAXCounterextendsDefaultHandler{privateHashtabletags;//这个Hashtable用来记录tag出现的次数//处理文档前的工作publicvoidstartDocument()throwsSAXException{tags=newHashtable();//初始化Hashtable}//对每一个开始元属进行处理publicvoidstartElement(StringnamespaceURI,StringlocalName,StringrawName,Attributesatts)throwsSAXException{Stringkey=localName;……\n  我们来看看这段程序作了些什么。在main()方法中,主要做的就是创建解析器,然后解析文档。实际上,在这儿创建SAXParser对象的时候,为了使程序代码于具体的解析器无关,使用了同DOM中一样的设计技巧:通过一个SAXParserFactory类来创建具体的SAXParser对象,这样,当需要使用不同的解析器的时候,要改变的,只是一个环境变量的值,而程序的代码可以保持不变。这就是FactoryMethod模式的思想。在这儿不再具体讲了,如果还有不明白的,可以参看上面DOM中的解释,原理是一样的。  不过在这儿还有一点点要注意的地方,就是SAXParser类和XMLReader类之间的关系。你可能有些迷糊了吧,实际上SAXParser是JAXP中对XMLReader的一个封装类,而XMLReader是定义在SAX2.0种的一个用来解析文档的接口。你可以同样的调用SAXParser或者XMLReader中的parser()方法来解析文档,效果是完全一样的。不过在SAXParser中的parser()方法接受更多的参数,可以对不同的XML文档数据源进行解析,因而使用起来要比XMLReader要方便一些。  这个例子仅仅涉及了SAX的一点皮毛,而下面的这个,可就要高级一些了。下面我们要实现的功能,在DOM的例子中已经有实现了,就是从XML文档中读出内容并格式化输出,虽然程序逻辑看起来还是很简单,但是SAX可不比DOM哦,看着吧。  前面说过,当遇到一个开始标签的时候,在startElement()方法中,我们并不能够得到这个标签在XML文档中所处的位置。这在处理XML文档的时候是个大麻烦,因为在XML中标签的语义,有一部分是由其所处的位置所决定的。而且在一些需要验证文档结构的程序中,这更是一个问题。当然,没有解决不了的问题了,我们可以使用一个栈来实现对文档结构的纪录。  栈的特点是先进先出,我们现在的想法是,在startElemnt()方法中用push将这个标签的名字添加到栈中,在endElement()方法中在把它pop出来。我们知道对一个结构良好的XML而言,其嵌套结构是完备的,每一个开始标签总会对应一个结束标签,而且不会出现标签嵌套之间的错位。因而,每一次startElement()方法的调用,必然会对应一个endElement()方法的调用,这样push和pop也是成对出现的,我们只需要分析栈的结构,就可以很容易的知道当前标签所处在文档结构中的位置了。publicclassSAXReaderextendsDefaultHandler{Java.util.Stacktags=newJava.util.Stack();……  在这儿虽然没有使用到栈的分析,但实际上栈的分析是一件很容易的事情,应为Java.util.Stack继承了Java.util.Vector类,而且Stack中的元素是按栈的结构由底至上排列的,因个,我们可以使用Vector类的size()方法来得到Stack的元素个数,还可以使用Vector的get(int)方法来得到具体的每一个元属。实际上,如果把Stack的元素从底向上逐一排列出来,我们就得到了从XML根节点到当前节点的一条唯一的路径,有了这条路径的信息,文档的结构就在清楚不过了。\n  到目前为止,我们已经掌握了对于XML编程的两大利器:DOM和SAX,也知道了该如何在一个Java程序中使用它们。DOM编程相对简单,但是速度比较慢,占用内存多,而SAX编程复杂一些,但是速度快,占用内存少。所以,我们应该根据不同的环境选择使用不同的方法。大部分的XML应用基本都可以用它们来解决。需要特别说明的是,DOM和SAX其实都是语言无关的,并非Java所独有,也就是说,只要有相应的语言实现,DOM和SAX可以应用在任何面向对象的语言中。\n使用lomboz调试JSP前言   凡是有JSP编写经验的人都有JSP调试困难的体会。由于自动化调试工具的缺乏使得在调试JSP时不得不在文件中大量的使用打印语句。这种做法对于定位JSP范围内的错误是一个简单有效的手段,但如何快速有效的定位在JSP中调用的对象的内部错误就显得有点力不从心了。因为这样将不得不频繁地编译、发布、启停服务器,给调试带来极大的不便。   传说中所见即所得的JSP调试工具,就笔者目前掌握的情况来看,并不存在。如果读者有这方面信息的希望告知,呵呵。但是JSP的调试工具却的确存在,而且它是免费的!It’slomboz。下载地址:http://www.objectlearn.com/products/download.jsp   本文从一个简单的例子出发来描述如何使用lomboz来调试JSP。本例的开发环境如下:-JDK版本:1.4.1;-JAVAIDE:Eclipse2.1;-lomboz:适用于Eclipse2.1的插件;-JSP/Servlet容器:Tomcat4.1.18。lomboz简介   严格的说lomboz是Eclipse的一个J2EE的插件,它将很多Java应用服务器、J2EE组件和Web应用开发集成到Eclipse中,可以帮助Java开发者使用Eclipse建立、测试、部署J2EE应用。对于本文的重点,JSP的调试,它也有相关的支持:-支持JSP编写,包括:语法着色和语法检查;-支持JSP的调试。对于JSP的调试,lomboz的原理是对要调试的JSP页面所产生的java代码来进行调试。整个调试过程与java应用程序的调试过程一样,这样不论是JSP本身的调试还是被JSP调用的对象的调试本无缝地联结到了一块。\n在开始本文的例子之前,我们先来看看lomboz的安装,以及为能正常的使用其JSP调试功能而进行的配置。安装和配置lomboz的安装过程非常简单,Eclipse的插件安装过程大多都非常的简单,主要步骤如下:1.从lomboz的下载地址(http://www.objectlearn.com/products/download.jsp)下载支持Eclipse2.1(或Eclipse的其他版本)的插件。2.如果Eclipse已运行,那么请先关闭它。3.把下载的文件解压到Eclipse安装目录下的plugins目录下。4.重新启动Eclipse,打开“AboutEclipsePlatformPlug-ins”(Help->AboutEclipsePlatform->Plug-inDetails),如果lomboz安装成功将会出现有关它的信息。如下图:注:如果想卸掉lomboz,你只需要把plugins目录下对应lomboz的目录删除即可。5.设置LombozJ2EEView,打开Window->CustomizePerspective,如图设置:\n接下来就是如何配置lomboz使之能正常的工作。与安装过程相反,配置过程可不是那么简单的,J。Lomboz的配置步骤如下:1.配置JDK,选择“Windows->Preferences”,选择Lomboz,进行JDK的配置。如图例:\n2.定义服务器,如果你的服务器已经包含在Lomboz默认的服务器列表中,这一步可以跳过。Lomboz默认服务器列表可以通过在上一步的界面中展开Lomboz,然后选择其中的“ServerDefinitions”获得。以下以添加Tomcat4.1.18为例说明如何增加一个服务器。相关步骤如下:a)进入Lomboz安装目录的servers子目录,复制一个.server文件(如tomcat410.server),并将其改名为tomcat418.server。b)打开这个文件分别改动这个文件的以下位置:-,将其中的name的值改为你想要的一个名字(如ApacheTomcatv4.1.8),这个名字用来显示在“ServerDefinitions”界面的服务器列表部分;-,其中default的值改为你机器上Tomcat所在的目录,如D:/ProgramFiles/Java/tomcat-4.1.18;\n-,其中default的值改为任意名字(如TOMCAT418),这个名字用于代表Lomboz工程的缺省的ClassPath的变量名;-,其中default的值改为你机器上Tomcat所在的目录,如D:/ProgramFiles/Java/tomcat-4.1.18;c)添加jasper-runtime.jar。在默认情况下TomcatX.server文件中只包含2个jar文件:servlet.jar和bootstrap.jar,但缺少jasper-runtime.jar。而这个jar文件在JSP的调试过程中起到了非常重要的作用,因此请加上它。给一个server添加jar有两种做法:方法1:在刚才编辑的.server文件中的部分添加一行:${classPathVariableName}/common/lib/jasper-runtime.jar;方法2:使用“ServerDefinitions”界面,在此不再赘述。配置好的服务器会在“ServerDefinitions”窗体中显示,如下图:\n注:对于Tomcat4.x的服务器需要打上补丁才能使Lomboz正常工作,补丁的下载地址:http://www.sysdeo.com/至此,Lomboz的安装和配置已经全部完毕,那么让我们来创建一个“HelloWorld!”的例子来体会一下Lomboz调试JSP的过程吧。调试JSP创建工程要想使用Lomboz调试JSP,首先必须创建一个Lomboz工程。步骤如下:1.打开File->New->LombozJ2EEProject,创建一个名为debugJsp的项目;\n2.选择“Next”进入Lomboz工程属性选择页面,添加Web模块和所使用的服务器。由于我们的主要任务是调试JSP,因此EJB模块可以为空。如下图:3.选择Finish,Lomboz工程即创建完毕。此时,Lomboz生成的文件结构如下图:\n接下来,我们就可以创建自己所需要的JSP文件了。在本例中我直接使用默认的index.jsp,将它的内容改为:Welcome
<%="HelloWorld!"%>
当目前为止,我们调试JSP的准备工作大致都完成了,但是对使用Tomcat4.x的人员还需要多余的步骤(这些步骤主要也是由tomcat4.x本身引起的):1.在Tomcat的config目录下的server.xml文件中添加以下行(具体位置请参见server.xml相关的文献):其中,path表示Web应用的URL名字;docBase表示web应用的物理位置;workDir表示jsp所对应的java文件产生的位置,必须创建org\apache\jsp这样的目录结构和名称。2.根据org\apache\jsp在对应的j2src目录下创建相对应的子目录结构,同时把j2src设置为工程的source目录。语法检查现在,让我们开始享受调试JSP的过程吧。在调试之前,JSP应该是没有语法错误的。Lomboz提供了JSP语法检查的功能,具体操作是:1.选择要进行语法检查的JSP文件,单击鼠标右键;2.选择Lombozj2ee…->CheckJspSyntax\n调试解决完语法错误之后,接下来就是解决逻辑错误的时候了。对于这种错误就只能通过调试来解决了。使用Lomboz调试JSP的过程与普通JAVA应用程序的调试非常类似,因为它的原理就是先利用服务器生成JSP对应的JAVA文件,然后对他们进行调试。调试JSP的步骤如下:1.打开LombozJ2EEView,选择服务器,单击鼠标右键选择“debugserver”。如图:如果.server文件定义正确,Tomcat将会启动。如果没有象预想的那样启动Tomcat,那么请从这两个方面排错:Tomcat环境变量和Lomboz的server文件。2.打开浏览器,如IE,在地址栏中输入:http://127.0.0.1:8080/debugJsp/index.jsp。如果JSP文件语法没有错误,将会在工程的j2src\org\apache\jsp目录下生成这个JSP文件对应的JAVA文件。重新刷新工程,即可看到这些文件。如图:这一步主要是触发服务器根据jsp产生可供调试的java文件,不必太在意结果是否正确。因为一旦java文件生成,那么这些错误都可通过调试排除。\n3.打开这个java文件,在其中设置断点,然后在IE的地址栏再次输入这个URL,此时Eclipse的调试功能即被激活。至此就可有针对性地对JSP进行调试了,这时的情形如图: 至于JSP所调用对象的调试,在完成上述步骤后,然后在对象对应的java文件中设置断点即可。结束语虽然目前JSP的调试尚未方便到如同普通的java文件调试般方便,但是Lomboz这类工具的出现至少结束了手动调试JSP的阶段。从此java的web应用开发者不必因为一些错误而时不时地停下程序来手工添加打印语句来观察变量的值,相反他们完全可以如调试普通JAVA应用一样在程序运行的同时来观察这些值的变化。这大大地方便了基于WEB应用的开发,减少了以前为调试所浪费的时间。\nJSP实践要点在Internet众多网站中,基于Web数据库的动态网站应用相当广泛。基于Web网络数据库的动态网站由一个Web浏览器作为客户端界面,一个数据库服务器用做信息存储和一个连接两者的Web应用服务器组成。原有开发动态网站的CGI技术,随着Web应用程序的客户端技术不断地发展,逐渐被JavaApplet、ActiveX控件、DHTML和JavaScript所取代。这些技术极大地改善了用户界面,但当它们尝试做一些深入的工作时,开始遇到客户端浏览器不兼容、服务器负担过重、访问速度下降以及安全性等问题。JSP技术就是解决这些问题的一把金钥匙,本文主要讨论在使用JSP技术构建动态网站的过程中的一些技术问题。JSP技术JSP是基于JavaServlet以及整个Java体系的Web开发技术,利用这一技术可以建立先进、安全、快速和跨平台的动态网站。在传统的网页HTML文件中加入Java程序片段和JSP标记,就构成了JSP网页。Web服务器接收到访问JSP网页的请求时,首先执行其中的程序片段,然后将执行结果以HTML格式返回给客户。程序片段可以操作数据库、重新定向网页以及发送E-mail等等,这就是建立动态网站所需要的功能。所有程序操作都在服务器端执行,网络上传送给客户端的仅是得到的结果,对客户浏览器的要求很低。如图1所示,在用户连接JSP网站时,用户请求网页,JSP页面独自响应请求,将用户对网页的请求转化为对数据的请求,通过JavaBean处理请求并将返回的数据封装成HTML页面返回给用户。JSP有许多优势:1.程序写一次,到处可以运行。JSP在设计时,充分考虑到应用平台的无关性。依赖于Java的可移植性,JSP得到目前许多流行操作平台的支持,可在Apache、NetScape、IIS等服务器上执行。2.执行速度快。JSP页面只需编译一次转化为Java字节代码,其后一直驻留于服务器内存中,加快了对JSP页面的响应速度。若不考虑JSP页面第一次编译所花的时间,则JSP的响应速度要比ASP快得多。3.Java的优势。JSP技术是用Java语言作为脚本语言的。跨平台、成熟、健壮、易扩充的Java技术使得开发人员的工作在其他方面也变得容易和简单。在Windows系统被怀疑可能会崩溃时,Java能有效地防止系统崩溃。Java语言通过提供防止内存泄漏的方法,在内存管理方面大显身手。加之,JSP为应用程序提供了更为健壮的意外事件处理机制,充分发挥了Java的优势。\nJSP技术难点1.连接数据库数据库连接对动态网站来说是最为重要的部分,在与后端数据库连接时可以采用ODBC或JDBC技术。虽然ODBC作为传统的连接数据库的手段是一种选择,但是ODBC有以下致命缺陷,从而使它无法胜任JSP的请求:(1)ODBC是C语言实现的API,从Java程序调用本地的C程序会带来一系列类似安全性、完整性、健壮性方面的问题。(2)其次,完全精确地实现从C代码ODBC到JavaAPI翻译的ODBC不会令人满意,因为在Java中没有指针,而ODBC中大量地使用了指针,包括极易出错的空指针“void*”。(3)考虑到平台移植性,在开发JSP程序中使用ODBC会带来负面影响,使得代码不适合于移植。为了使程序在具有安全性、完整性、健壮性的同时,可以方便地移植,采用JDBC连接数据库更合适一些。JDBC是一种可用于执行SQL语句的JavaAPI,它由一些Java语言写的类、界面组成,使开发人员可以用纯Java语言编写完整的数据库应用程序。通过使用JDBC,可以很方便地将SQL语句传送到几乎任何一种数据库。也就是说,可以不必写一个程序访问Sybase,写另一个程序访问Oracle,再写一个程序访问Microsoft的SQLServer。用JDBC写的程序能够自动地将SQL语句传送给相应的数据库管理系统。在本地数据库程序使用Microsoft的Access等数据库时,可以使用Sun公司开发的JDBC-ODBC桥,借用此技术JSP程序就可以访问带有ODBC驱动程序的数据库。这样既保留JDBC的优点,又可以使用Microsoft提供的ODBC数据源与Access连接。不管对方是何种数据库,只要有ODBC接口就可以直接使用JDBC-ODBC桥与数据库连接,而无需因为后端数据库的改变而改动相应的程序代码,实现了应用层与数据库层的完美分离。如果需要变后端数据库为MySQL,只需在ODBC数据源中安装MySQL的驱动程序之后,就可以直接使用MySQL数据库了。2.内建的组件\n在实现网站的时候,由于客观需要,为了方便区分本地局域网用户与远端连上来的用户,并提供相应的权限,可以采用内建的组件Request来捕获每一个连到服务器上的用户的IP地址,通过比较之后给出相应的权限。这样做到本局域网内用户可以使用网站内所有公开的和不对外公开的资源。还可以将现有的方法加以改进,将各种IP地址输入到数据库中并且赋予不同的IP地址不同的权限,以完整地控制用户使用网站资源。会话状态维持是Web应用开发者必须面对的问题。为了了解用户是否还在线,使用内建的Session组件,通过给每个登录用户一个Session变量,可以在用户非正常离开网站之后,关闭该用户使用的资源,达到节省内存,提高服务器性能的目的。在JSP中还提供了Cookie类,其构造器有两个参数,分别代表Cookie的名称和值。Cookie类中提供了各种方法设置Cookie的属性,如通过setMaxAge方法可以设置Cookie的生存时间。若生存时间为负值,代表浏览器关闭Cookie,即消失;生存时间为0,代表删除Cookie;生存时间为正数,代表Cookie存在多少秒。可以用Cookie临时保存用户的账号和口令,JSP可随时读取,验证用户的合法性。可以将用户的浏览状态保存在Cookie中,下次用户再访问网页时,由JSP向浏览器显示个性化页面。3.转换Unicode编码在许多JSP页面的调试过程中都碰到过由于汉字编码与Unicode编码转换引起的问题,如在浏览器中看到的JSP页面中的汉字都是乱码、JSP页面无法正常显示汉字、JSP不能接收表单提交的汉字、JSP数据库读写无法获得正确的内容等等,这是因为现在大部分具有国际化特征的软件核心字符处理都是以Unicode为基础的,在软件运行时根据当时“Locale/Lang/Codepage”设置确定相应的本地字符编码设置,并依此处理本地字符,所以应该在处理过程中实现Unicode和本地字符集的相互转换,甚至以Unicode为中介的两个不同本地字符集的相互转换。这种方式在网络环境下被进一步延伸,任何网络两端的字符信息也需要根据字符集的设置转换成可接受的内容。由于IE默认字符集为GB2312,然而Windows默认为GBK,Java则默认为Unicode,所以如果不通过一定的转换,直接在GB2312字符集上显示从GBK或Unicode得到的页面将是一片乱码。Java语言采用Unicode处理字符,但从另一个角度来说,在Java程序中也可以采用非Unicode,重要的是保证程序入口和出口的汉字信息不失真。如完全采用ISO-8859-1来处理汉字也能达到正确的结果,经过转换之后并将网页字符集强制设为GB2312字符集显示,就能够正常显示汉字了。\n指南:想成为一个JSP网站程序员吗?任何Web开发人员需要必备的技巧主要有以下这些技术。 开发Web应用程序的技术已经变得更成熟、更复杂了。现在,构建一个Web应用程序不仅仅需要简单的HTML技术了。数据库访问、脚本语言和管理都是一个Web程序员需要具备的技术。让我们来看看要成为一个市场上受欢迎的Web开发人员都需要些什么技能吧。自从CERN(欧洲粒子物理研究所),日内瓦附近的高能物理研究中心,在1991年发布了Web以来,Web技术已经从静态的内容和CommonGatewayInterface(CGI)发展成servlet技术和JavaServerPages了。然而,在这个竞争更激烈的社会中,一个Web程序员需要更多的知识。例如,如果在面试中,你提到你熟悉XML并在JNDI方面有些经验(这两种技术初看似乎同Web编程没有很紧密的关系),那么你就会给你未来的老板留下更深的印象。设想你已经了解了Java编程语言和面向对象的编程,下面还有两组技术是一个Web开发人员日常工作中所需要的。第一组包括每个Web程序员必须具备的技术。第二组包含要想成为一个高级程序员所应该掌握的技术。基本技能如果想称自己是个Web开发人员,下面就是必须具备的技术。HTML(HyperTextMarkupLanguage)HTML几乎是显示在浏览器上所有内容的语言。难怪HTML就好像是一个Web程序员的生存本能一样。如果你仍需要在你的HTML中查找或,那么你真的需要提高你的HTML技术了。HTML的当前版本是4.01,你可以从http://www.w3.org/TR/1999/REC-html401-19991224/了解更多关于它的内容。Servlets和JSPJavaservlet技术是开发JavaWeb应用程序的主要技术。它是由SunMicrosystems在1996年开发的,当前的版本是2.3,但人们正在为版本2.4做准备。JSP是servlet技术的扩展,现在的版本是1.2(2.0版将很快定下来)。有人认为JSP是servlets的替代,但实际并不是这样的。Servlets和JSP是一起用于复杂的Web应用程序的。用Java进行Web编程的一个好的开端就是学习servlet技术。即使你打算在你的Web应用程序中只运用JSP页面,你仍需要学习servlet技术。在更复杂的Web应用程序中,JSP页面只用于显示,而JavaBeans和自定义标签库用来嵌入商业逻辑。即:你也必须精通JavaBeans和自定义标签库。\nJavaScriptJavaScript是运行于所有的主要的浏览器中的脚本语言。你用JavaScript来进行客户端的编程。客户端编程中最重要的工作就是确认用户输入。运用客户端输入验证的好处是减少服务器的工作量并提高响应时间。另外,JavaScript可以用于重新定向(redirection)、cookie处理、控制applets、创建导航树、打开一个浏览器的一个新的实例、等等。SQL(StruturedQueryLanguage)和JDBC(JavaDatabaseConnectivity)如今,大多数Web应用程序都包括访问关系数据库中的数据。作为一个Web程序员,你需要知道如何存储、得到并操作数据库中的数据。有时侯,你也需要设计数据库,构建数据库中的表和其它结构。SQL就是用来操作数据库中数据的语言。你通常需要编写SQL语句(常常是动态的),把它们传递到数据库服务器,并得到返回的数据(如果有的话)。运用Java语言,你需要用JDBC来帮助Web应用程序和数据库服务器进行通讯。JDBC有两部分:JDBCCoreAPI(ApplicationProgrammingInterface)和JDBCOptionalPackageAPI。第一组用来执行基本的数据操作,如创建一个连接或读取、更新并删除一个表中的记录。第二组提供更高级的数据库连接功能,如连接池、事务和RowSet。JDBC的当前版本是3.0,API包含在J2SEv.1.4中。WebContainer管理和应用程序部署你的servlets和JSP页面在一个叫做servlet/JSPcontainer或Webcontainer的引擎中运行。你至少需要知道如何为测试以及生产运行部署你的Web资源。例如,如果你运用Tomcat,你需要了解的一件事就是如何映射配置文件(server.xml)中的应用程序,使Tomcat知道如何调用你的JSP页面。另外,你需要知道在哪里保存你的库以及如何创建应用程序部署描述符。XML(eXtensibleMarkupLanguage)XML是计算机领域中一个成功的后起之秀。由WorldWideWebConsortium在1996年开发,XML现在已经是用于数据交换和可扩展数据结构的一个广泛的、公认的标准了。XML在JavaWeb开发中扮演着一个重要的角色。例如,每个应用程序的部署描述符都是XML格式的。而且,如果你在开发Webservies,你就会用到SOAP(SimpleObjectAccessProtocol),它主要是基于HTTP和XML的。另外,在Web应用程序中,XML也可能用于存储分等级的数据。Model2结构这种技术在该类别中是最先进的。建议用这种结构来构建相当复杂的JavaWeb应用程序。Model2结构是基于Model-View-Controller设计范例的。\n高级技术下面这些技术可以将你同初学者区别开来。JSTL(JSPStandardTagLibraries)、JakartaTaglibs项目和其它库为了加速应用程序的开发,你应该经常重用代码。简单地说,代码重用就是,如果有人已经编写了用来执行某些功能的代码,你最好就去用那些代码,而不要自己编写了。因此,JSP可以让你运用自定义标签。你可以运用几个库,最受欢迎的是Apache的JakartaTaglibs项目中的库。从http://jakarta.apache.org/taglibs/index.html可以下载这个包,你在开始创建新类前,可以运用在这个包中找到的任何现成的东西。JSTL最近已经成为了一个标准。其它标签库可以免费或以商业方式得到。Apache的Struts项目Struts是一个Apache赞助的公共资源项目,它为构建Model2JavaWeb应用程序提供了一个构架。Struts为MVC结构提供它自己的Controller组件,将EJB、JDBC和JNDI用于Model,将JSP和其它技术用于View。你可以从它的网站找到更多关于这个项目的更详细的信息:http://jakarta.apache.org/struts/index.html。XHTML(ExtensibleHyperTextMarkupLanguage)XHTML是努力将HTML和XML结合起来的一种技术。你可以把XHTML当作下一代的HTML。其当前的版本是1.0(第二版是于2002年8月1日发布的),XHTML还没有像HTML那么流行,但它在将来会发挥更重要的作用。根据Web设计专家MollyHolzschlag的观点,推动各个公司转向XHTML的主要原因是美国的关于公开访问(accessibility)的法律。更多关于XHTML的信息,参阅Holzschlag访谈。DHTML(动态HTML)DHTML可以允许人们在你的网站上进行更多的交互。例如,运用DHTML,当用户移动鼠标到一个链接上时,你就可以很容易地创建并显示子菜单。运用DHTML的最大的挑战是创建跨浏览器的页面。的确,在理论上,页面设计应该是由美工处理的,其中动态的HTML是通过运用一个工具而产生的。然而,一个Web程序员通常要负责集成所有的部分,如果在页面中生成的代码被破坏了,你就需要了解DHTML来修理它。Applet\n编程Applets曾经在提供交互性方面很重要,尤其在DHTML出现前。现在,applets的作用被削减了,更多的程序员已经不用applets了。Microsoft决定在它的新浏览器中不为applets提供缺省的支持极大地削减了applets在Web应用程序中的作用。然而,applets并没有消亡。对于某些任务,如显示新闻标题,applets仍然是不可替代的,而且applets不会产生另人头痛的跨浏览器兼容方面的问题。HTTP协议JavaWeb程序员通常运用比HTTP更高的协议,如运用servlet和JSPAPIs。这些APIs隐藏了HTTP协议的复杂性。因此,你仍可以构建重要的应用程序而不需要知道多少关于HTTP协议的知识。只有当你需要处理原始数据,比如将文件作为附件上载或传送时,你才需要更多关于协议的知识。EJB(EnterpriseJavaBeans)EJB是J2EE的一部分,当可扩展性和强大性是你的Web应用程序的主要需求时,EJB就很重要。在当前规范(EJB2.0)中有三种类型的EJBs:会话(session)EJBs、实体(entity)EJBs和消息驱动的(messagedriven)EJBs。新的规范,2.1版,正在设计中。JNDI(JavaNamingandDirectoryInterface)当你在开发企业beans时,JNDI很重要,因为对一个EJB的访问是通过JNDI的命名服务完成的。运用一个命名服务来查找与一个特定名字相关的一个对象。在EJBcontext中,一个命名服务找到一个企业bean,给定这个bean的名字。因此,了解JNDI在开发一个EJB应用程序中是至关重要的。另外,JDBC可以用JNDI来访问一个关系数据库。其它工具了解在哪里可以找到特定的支持工具通常有助于的你的事业的发展。例如,如果你碰巧被分配去做关于基准的任务,那么你如果知道你可以从Apache的JakartaProject下载Jmeter,你就会很高兴。另外,如果你需要以PDF格式发送输出结果,建议你从http://www.lowagie.com/iText/运用可以免费下载的Java-PDF库。Internet技术范围很广而且发展很快。这就是说,作为一个Web程序员,你应该时时留心业界出现了什么新技术,发生了什么大事。在这个方面,没有什么比Internet本身更伟大的资源了。关于作者: JavafortheWebwithServlet,JSP,andEJBBudiKurniawan是位IT顾问,他专门从事Internet和面向对象的编程,并教授Java和Microsoft技术。他是销量很好的JavafortheWebwithServlets,JSP,andEJB:aDeveloper’sGuidetoScalableSolutions(New\nRiders)一书的作者,他还开发了最受欢迎的JavaUploadBean,你可以从BrainySoftware.com得到它,许多重要的公司都得到许可并将它用于项目中了。Budi的联系方式是budi@brainysoftware.com。\nJSP的运行内幕经常有朋友问起,JSP和Servlet之间有什么区别,两者之间又有什么联系?其实Servlet技术的出现时间很早,是当时为了Java的服务器端应用而开发的。大家都知道Applet是应用小程序,Servlet就是服务器端小程序了。但在Microsoft公司的ASP技术出现后,使用Servlet进行响应输出时一行行的输出语句就显得非常笨拙,对于复杂布局或者显示页面更是如此。JSP就是为了满足这种需求在Servlet技术之上开发的。可见,JSP和Servlet之间有着内在的血缘关系,在学习JSP时,如果能够抓住这种联系,就能更深刻地理解JSP的运行机理,达到事半功倍的效果。本文将通过对一个JSP运行过程的剖析,深入JSP运行的内幕,并从全新的视角阐述一些JSP中的技术要点。HelloWorld.jsp我们以Tomcat4.1.17服务器为例,来看看最简单的HelloWorld.jsp是怎么运行的。代码清单1:HelloWorld.jspHelloWorld.jsp<% Stringmessage="HelloWorld!";%><%=message%>   这个文件非常简单,仅仅定义了一个String的变量,并且输出。把这个文件放到Tomcat的webapps\ROOT\目录下,启动Tomcat,在浏览器中访问http://localhost:8080/HelloWorld.jsp,浏览器中的输出为“HelloWorld!”  让我们来看看Tomcat都做了什么。转到Tomcat的\work\Standalone\localhost\_目录下,可以找到如下的HelloWorld_jsp.java,这个文件就是Tomcat解析HelloWorld.jsp时生成的源文件:  代码清单2:HelloWorld_jsp.javapackageorg.apache.jsp;\nimportjavax.servlet.*;importjavax.servlet.http.*;importjavax.servlet.jsp.*;importorg.apache.jasper.runtime.*;publicclassHelloWorld_jspextendsHttpJspBase{ ......publicvoid_jspService(HttpServletRequestrequest,HttpServletResponseresponse)throwsjava.io.IOException,ServletException { JspFactory_jspxFactory=null; javax.servlet.jsp.PageContextpageContext=null; HttpSessionsession=null; ServletContextapplication=null; ServletConfigconfig=null; JspWriterout=null; Objectpage=this; JspWriter_jspx_out=null; try{  _jspxFactory=JspFactory.getDefaultFactory();  response.setContentType("text/html;charset=ISO-8859-1");  pageContext=_jspxFactory.getPageContext(this,request,response,null,true,8192,true);  application=pageContext.getServletContext();  config=pageContext.getServletConfig();  session=pageContext.getSession();  out=pageContext.getOut();  _jspx_out=out;  Stringmessage="HelloWorld!";  out.print(message); }catch(Throwablet){  out=_jspx_out;  if(out!=null&&out.getBufferSize()!=0)   out.clearBuffer();  if(pageContext!=null)pageContext.handlePageException(t); }finally{ if(_jspxFactory!=null)\n_jspxFactory.releasePageContext(pageContext); } }}   从上面可以看出,HelloWorld.jsp在运行时首先解析成一个Java类HelloWorld_jsp.java,该类继承于org.apache.jasper.runtime.HttpJspBase基类,HttpJspBase实现了HttpServlet接口。可见,JSP在运行前首先将编译为一个Servlet,这就是理解JSP技术的关键。  我们还知道JSP页面中内置了几个对象,如pageContext、application、config、page、session、out等,你可能会奇怪,为什么在JSP中的代码片断中可以直接使用这些内置对象。观察_jspService()方法,实际上这几个内置对象就是在这里定义的。在对JSP文件中的代码片断进行解析之前,先对这几个内置对象进行初始化。  首先,调用JspFactory的getDefaultFactory()方法获取容器实现(本文中指Tomcat4.1.17)的一个JspFactory对象的引用。JspFactory是javax.servlet.jsp包中定义的一个抽象类,其中定义了两个静态方法set/getDefaultFactory()。set方法由JSP容器(Tomcat)实例化该页面Servlet(即HelloWorld_jsp类)的时候置入,所以可以直接调用JspFactory.getDefaultFactory()方法得到这个JSP工厂的实现类。Tomcat是调用org.apache.jasper.runtime.JspFactoryImpl类。  然后,调用这个JspFactoryImpl的getPageContext()方法,填充一个PageContext返回,并赋给内置变量pageConext。其它内置对象都经由该pageContext得到。具体过程见上面的代码,这里不再赘述。该页面Servlet的环境设置完毕,开始对页面进行解析。HelloWorld.jsp页面仅仅定义了一个String变量,然后直接输出。解析后的代码如下:  代码清单3:JSP页面解析后的代码片断Stringmessage="HelloWorld!";out.print(message);   定制标签的解析过程\n  在一个中大型的Web应用中,通常使用JSP定制标签来封装页面显示逻辑。剖析容器对定制标签的解析过程,对我们深入理解定制标签的运行机理非常有帮助。下面我们以Struts1.1b中附带的struts-example应用的主页运行为例加以说明。  包含定制标签的index.jsp  Struts1.1b的下载地址是http://jakarta.apache.org/struts/index.html。将下载的包解压,在webapps目录下可以找到struts-example.war。将该War包拷贝到Tomcat的webapps目录下,Tomcat会自动安装此应用包。在浏览器中通过http://localhost:8080/struts-example访问struts-example应用,将显示应用的首页(见图1)。   图一应用的首页  代码清单4:index.jsp<%@pagecontentType="text/html;charset=UTF-8"language="java"%><%@tagliburi="/WEB-INF/struts-bean.tld"prefix="bean"%><%@tagliburi="/WEB-INF/struts-html.tld"prefix="html"%><%@tagliburi="/WEB-INF/struts-logic.tld"prefix="logic"%><bean:messagekey="index.title"/>……   我们仅以index.jsp中的标签的解析为例进行分析,看容器是怎样把这个自定义标签解析成HTML输出的。上面代码省略了页面的其它显示部分。首先,查看上面浏览器中页面的源文件:MailReaderDemonstrationApplication(Struts1.0)……   可见,容器已经把替换成一个字串,显示为页面的标题。  解析过程  那么,JSP容器是怎样完成解析的呢?查看在工作目录jakarta-tomcat-4.1.17\work\Standalone\localhost\struts-example下解析后的index_jsp.java文件:  代码清单5:index_jsp.javapackageorg.apache.jsp;importjavax.servlet.*;importjavax.servlet.http.*;importjavax.servlet.jsp.*;importorg.apache.jasper.runtime.*;publicclassindex_jspextendsHttpJspBase{ //为所有的定制标签定义处理器池类的引用 privateorg.apache.jasper.runtime.TagHandlerPool; _jspx_tagPool_bean_message_key; …… //页面类构造方法 publicindex_jsp(){ _jspx_tagPool_bean_message_key= neworg.apache.jasper.runtime.TagHandlerPool();  …… } publicvoid_jspService(HttpServletRequestrequest,   HttpServletResponseresponse)\n   throwsjava.io.IOException,ServletException{ …… _jspxFactory=JspFactory.getDefaultFactory(); response.setContentType("text/html;charset=UTF-8"); pageContext=_jspxFactory.getPageContext(this,   request,response,null,true,8192,true); application=pageContext.getServletContext(); config=pageContext.getServletConfig(); session=pageContext.getSession(); out=pageContext.getOut(); _jspx_out=out; …… if(_jspx_meth_html_html_0(pageContext)) return; …… } //页面在处理退出时释放所有定制标签的属性 publicvoid_jspDestroy(){ _jspx_tagPool_bean_message_key.release(); …… }}   生成的index_jsp.java继承于org.apache.jasper.runtime.HttpJspBase。研究这个文件为我们了解定制标签的运行机理提供了途径。  从上面可以看出,Tomcat在解析一个JSP页面时,首先为每一个定制标签定义并实例化了一个TagHandlerPool对象。页面的处理方法覆盖父类的_jspService()方法,_jspService方法首先初始化环境,为内置对象赋值。由于index.jsp页面整体由一个标签包裹,Tomcat对每一个标签都产生一个私有方法加以实现。标签的处理方法是_jspx_meth_html_html_0()。这个方法的命名规范大家也可以从这里看出,就是“_jspx_meth+标签的前缀+标签名+该标签在JSP页面同类标签中出现的序号”。其它标签都被包含在该标签中,所以其它标签在_jspx_meth_html_html_0()方法中进行解析。具体的代码实现请参见赛迪网http://linux.ccidnet.com期刊浏览2003年第6期。\n  在_jspx_meth_html_html_0()方法中,首先从_jspx_tagPool_html_html_locale池中得到一个org.apache.struts.taglib.html.HtmlTag的实例,然后设置这个tag实例的页面上下文及上级标签,由于html:html标签是页面的最顶层标签,所以它的parent是null。然后对该标签的内容进行解析。HTML代码直接输出,下面主要看看标签之间包含的标签的解析。对bean:message标签的解析类似于html:html,Tomcat也将其放入一个单独的方法_jspx_meth_bean_message_0()中进行。  bean:message标签的解析  代码清单7:_jspx_meth_bean_message_0()方法片断//对message定制标签的处理方法privateboolean_jspx_meth_bean_message_0(javax.servlet.jsp.tagext.Tag_jspx_th_html_html_0,javax.servlet.jsp.PageContextpageContext)throwsThrowable{ JspWriterout=pageContext.getOut(); /*---- bean:message----*/ org.apache.struts.taglib.bean.MessageTag _jspx_th_bean_message_0= (org.apache.struts.taglib.bean.MessageTag) _jspx_tagPool_bean_message_key.get( org.apache.struts.taglib.bean.MessageTag.class); _jspx_th_bean_message_0.setPageContext(pageContext); _jspx_th_bean_message_0.setParent(_jspx_th_html_html_0); _jspx_th_bean_message_0.setKey("index.title"); int_jspx_eval_bean_message_0=_jspx_th_bean_message_0.doStartTag(); if(_jspx_th_bean_message_0.doEndTag()==javax.servlet.jsp.tagext.Tag.SKIP_PAGE) returntrue; _jspx_tagPool_bean_message_key.reuse(_jspx_th_bean_message_0); returnfalse;} \n  同样,对html:bean也需要从池中得到一个标签类的实例,然后设置环境。这里不再赘述。我们只专注对MessageTag定制标签类特殊的处理部分。定制标签类的开发不在本文讨论范围之内。在index.jsp中定义了一个bean:message标签,并设置了一个属性:。Tomcat在解析时,调用MessageTag对象的key属性设置方法setKey(),将该属性置入。然后调用MessageTag的doStartTag()和doEndTag()方法,完成解析。如果doEndTag()方法的返回值为javax.servlet.jsp.tagext.Tag.SKIP_PAGE,表明已经完成解析,返回true,Tomcat将立即停止剩余页面代码的执行,并返回。否则把该MessageTag的实例放回池中。  标签类对象实例的池化  为了提高运行效率,Tomcat对所有的定制标签类进行了池化,池化工作由org.apache.jasper.runtime.TagHandlerPool类完成。TagHandlerPool类主要有两个方法,代码如下:  代码清单8:TagHandlerPool.javapublicclassTagHandlerPool{ privatestaticfinalintMAX_POOL_SIZE=5; privateTag[]handlers; publicsynchronizedTagget(ClasshandlerClass)throwsJspException{……} publicsynchronizedvoidreuse(Taghandler){……}}   TagHandlerPool简单地实现了对标签类的池化,其中MAX_POOL_SIZE是池的初始大小,handlers是一个Tag的数组,存储标签类的实例。get(ClasshandlerClass)得到一个指定标签类的实例,如果池中没有可用实例,则新实例化一个。reuse(Taghandler)把handler对象放回池中。  至此,我们对JSP在容器中的运行过程已经了然于胸了。虽然每种JSP容器的解析结果会有差异,但其中的原理都雷同。对于编写JSP应用,我们并不需要干涉容器中的运行过程,但如果你对整个底层的运行机制比较熟悉,就能对JSP/Servlet技术有更深的认识。\n初学Java所需要注意的几点Java总有它的千般好处使你选择它,但这些随便翻翻书或在网上逛一圈就能找到答案。在本文中,笔者把自己学习Java的一些切身体会和过程写出来,供初学者做个参考。我在学习Java的过程中主要围绕以下几个方面来学习:1.时刻提醒自己Java是一种OOP语言工具,而不仅仅是编码,只有这样才能总体把握和运用Java。2.在学习的过程中,最好能够了解Java的底层机制,而不是仅仅停留在表层,不是抄书上的例子运行出结果就可以。要注意,即便对一个简单的例子也要有耐心去琢磨、调试、改动。3.在学习的过程中一定要动手做、写代码,而不是抱一本书看看就行。很多东西和体会必须自己动手才能真正属于自己,最好能参与一些实际的项目。4.在学到一定阶段后,你开始希望用学过的东西做些什么。这时的你应该开始学习一些更多、更复杂的知识,比如J2EE平台的构建、EJB的开发等。对于这一部分,我建议最好找一本较薄的书先了解一个大概,心里有个总体的认识,对更多的技术术语做个初步掌握。我认为这个阶段看看《J2EE技术实践》很不错,它可以让你了解J2EE包含的各种技术和框架,同时提供很多实际的例子来加深对J2EE的整体了解。学习Java的兴趣和决心起了很关键的作用。在有了上述基础后,我便开始一步一步地学习Java。Java环境的搭建要运行Java程序,必须安装JDK。JDK是整个Java的核心,其中包括了Java编译器、JVM、大量的Java工具以及Java基础API。可以从http://Java.sun.com下载JDK,有1.4版本和1.31版本。我的学习环境中首先,采用的是1.31版本。解压安装。然后,进行环境设置。1.对于Windows平台要进行以下设置:\nsetPATH=YOUR_INSTALL_DIR\bin;C:\Windows;C:\Windows\Commandsetclasspath=.;YOUR_INSTALL_DIR\lib\tools.jar2.对于Linux平台要编辑/etc/profile文件:JAVA_HOME=your_install_dir/JDK/j2sdkCLASSPATH=$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/td.jar:$JAVA_HOME/jr-e/lib/rt.jar:.PATH=$PATH:$JAVA_HOME/binexportPATHPS1USERLOGNAMEMAILHOSTNAMEHISTSIZEHISTFILESIZEINPUTRCJAVA_HOMECLASSPATHRESIN_HOME最后,在终端上输入Java看能不能找到这个命令,如果能找到安装就成功了。下面介绍一下JDK的几个重要的命令:◆Java执行工具,是启动JVM(虚拟机)并执行class(BYTECODE)文件的命令;◆javac编译器,由.java文件生成.class文件;◆jarJava压缩打包工具;◆Javadoc文档生成器。最后就是JDKDocumentation,这是JDK的联机帮助文档,是最有用和最重要的学习参考文档,应该多看。开始写自己的代码\n现在环境有了,应该写个简单的代码进行测试了。还是从经典的“helloword”开始。1.先用编辑器写一代码(我用的是Linux的vi):[stone@coremsgwork]$viHello.JavapublicclassHello{publicstaticvoidmain(String[]argc){System.out.println("HelloWord!");}}2.编译:[stone@coremsgwork]$JavacHello.Java3.执行:[stone@coremsgwork]$JavaHelloHelloWord!成功了!这就是我的第一个Java程序。从那时起我知道已开始走进Java的世界,接下来就靠自己的努力了。在这个过程中,笔者认为有几点需要注意。\n学习一门新的语言,参考书是离不开的。我的建议是开始最好找一本篇幅较短的入门书来学习那些最简单、最基本的东西,包括学习Java语法等。同时,对一个最简单的程序也应该多去调试,多想想如果改动一下会出现什么结果?为什么必须那样写?多去想想这些问题然后去操作,会让你有更多的收获。这样反复地思考是很有用的。此外,在这一阶段还应该多看JDK的联机帮助,尽量多地掌握JDK提供的Java基本类库API。在有一定基础、能够写一些简单的程序后,可以开始看《ThinkinginJava》这本书。它比较完整地介绍了Java的语法、面向对象的特性、核心类库等。通过这一层次的学习能够加深对Java的理解和底层原理的运用,同时又可以完整地了解Java的整个体系。在这一阶段,应该重点学习Java的面向对象编程语言的特性,比如继承、构造器、抽象类、接口、方法的多态、重载、覆盖、Java的异常处理机制等,要对上述概念有非常清楚的了解。这样做的目的,是要让自己把这些技术应用到实践中进行合理的程序设计(比如,你会考虑一个类是用抽象还是接口来设计等)。这就要求必须在大量的实践中去应用和学习。这也是当初很多朋友给我的建议。学习更多如果要用Java来完成各种功能更强大的任务,那么就要学习语言以外的更多的东西。1.JavaWeb编程对于JavaWeb编程来说,应该而且必须熟悉和掌握HTTP协议,可以参考Stevens的《TCP/IP详解》第三卷。JavaServlet技术提供了生成动态Web页面内容的能力,这在你的Java项目中是最基本的功能之一,所以必须学习。通过这一阶段的学习应该掌握Servlet/JSP的Web编程。2.J2EE的学习J2EE包含的技术太多了。如果你想坐在桌子旁边抱着一大堆书来学习的话,效果不大的。我建议在开始这一阶段的学习的时候,可以按以下步骤来做,总的思想是“总体把握,各个击破”。◆了解J2EE中的技术术语的含义。我的感觉是J2EE标准中涉及到的各种技术很多,如果一开始就一个一个去学习的话是不现实的,也是没效果的。我的建议是,先对其中的技术有个大概的了解,比如EJB、JavaIDL、JTA等。可能你不知道怎么去写一个EJB,但是要知道什么是EJB、它能做什么,当有了这样的概念后,再去有目的地学习它就会快很多。我还要再重复一句——必须要在实践中动手去做才行。\n◆了解J2EE中的设计模式,这样能帮助你对J2EE做个整体把握。MVC开发模式被证明是有效的处理方法之一。它可以分离数据访问和数据表现。你可以开发一个有伸缩性的、便于扩展的控制器,来维护整个流程。通过这一层次的学习,当你面对一个项目的时候,应该首先把握它的总体架构的设计,以及决定采用J2EE标准中的哪些技术。◆了解一些J2EE平台的典型案列,加深对这一技术的概念和理解。平时可以多留意这方面,熟悉一些典型案例,分析它为什么要采用那个时间?那样做能达到什么样的目的?然后联系到自己身边的项目是否可以作为参考。◆学习J2EE下的各种技术。在有了前几阶段的学习后,可以自己搭建一个J2EE平台开始具体学习每一种技术。你可以参与公司相关项目进行学习,也可以自己搭建一个平台进行学习。这时候应该找点相关的书来一步一步学习,没有捷径可走。如果你不满足于这些,那么还应该更深入地学习UML、设计模式等方面的东西。\n利用JSP2.0开发Web应用程序JSP(JavaServerPages)技术是对Servlet的进一步抽象,它由JCP(JavaCommunityProcess)开发,是用于生成动态内容的开放式的、可免费获取的规范,也是J2EE(Java2EnterpriseEdition)规范的重要组成部分。许多商业应用服务器如BEAWebLogic、IBMWebSphere、LiveJrun和Orion都支持JSP技术。从机票预订系统、银行系统到购物系统,Web上到处都在应用JSP技术。新发布的2.0版是对JSP1.2的升级,增加了一些有趣的新特性。JSP2的目标是使动态网页的设计更加容易,而且无需学习Java编程语言。本文内容包括:·提供具有丰富示例代码的教程来使你熟悉JSP2.0·介绍JSP2.0的新特性·提供利用JSP2.0开发应用程序的一些体会·给出了一些示例代码,对这些代码稍加修改即可用于你自己的应用程序。如果你还不熟悉JSP,那么直接从JSP2.0入手是一个不错的主意。但是如果希望学习JSP1.2,也许你需要从JSP教程开始学习。JSP2.0\nJSP2.0是对JSP1.2的升级,增加了几个有趣的新特性,可以使Web应用程序设计人员和开发人员的工作更容易一些。JSP2.0的目标是比以前更易于使用,更重要的是无须学习Java语言本身就可以使用它。它增加了一种称为SimpleTag的新扩展机制来简化标签API(TagAPI)。除了其他一些改进外,JSP2.0引入的最主要的新特性包括:1.一种简单的表达式语言(EL),能够用来容易地从JSP页面访问数据,这种表达式语言简化了基于JSP的不含脚本的应用程序的编写,不需要使用JavaScriptlet或者Java表达式;2.用于直接使用JSP技术来定义可重用的自定义行为的新语法,该语法使用.tag和.tagx文件,这类文件可由开发人员或者网页作者编写;3.3XML语法得到了实质性的改进,增加了新的标准文件扩展名(.tagx用于标签文件,.jspx用于JSP文件)。本文主要讨论表达式语言、简化的标签API和标签文件。相信目前的JSP开发人员将会发现这些重要的特性不但有趣,而且非常有用。为何要从1.2跨越到2.0?\n在Java规格请求(JSR152)中版本号最初定为1.3。但是正如你将在后面看到的那样,由于这些新特性对JSP应用程序的开发模型产生了如此深刻的影响,专家组感到有必要把主版本号升级到2.0,这样才能充分反映这种影响。此外,新的版本号也有助于把开发人员的注意力吸引到这些有趣的新特性上来。令人欣慰的是,所有合法的JSP1.2页面同时也是合法的JSP2.0页面。JSP2.0起步在着手学习JSP2.0之前,你需要一个支持JSP2.0和JavaServlet2.4规范的JSP环境。幸运的是,JakartaTomcat5.0(alpha测试版)支持新的JSP2.0和Servlet2.4规范,可以从http://jakarta.apache.org/builds/jakarta-tomcat/release/v5.0.4-alpha/下载并安装Tomcat5.0。JSP表达式语言向JSP网页传递信息是通过JSPscoped属性和请求参数完成的。专门为网页作者量身定做的一种表达式语言(EL)把JSPscoped属性提升为从业务逻辑向JSP页面传递信息的标准方式。但是要注意,尽管这种表达式语言是JSP的一个重要特性,它并不是一种通用的程序语言。它仅仅是一种数据访问语言,可以方便地访问和处理应用程序数据,而无需使用scriptlet或者请求时(request-time)表达式的值。\n在JSP2.0之前,网页作者只能使用表达式<%=aName%>访问系统的值,比如下面的例子:">或者使用自定义JavaBeans组件的值:<%=aCustomer.getAddress().getCountry()%>表达式语言允许网页作者使用简单的语法访问对象。比如要访问一个简单变量,可以像下面这样写:而访问嵌套的JavaBeans属性,则可以像下面这样写:${aCustomer.address.country}你可能会问,这不是JavaScript的语法吗?完全正确!如果你使用过JavaScript,就会感到非常轻松,因为表达式语言正是借用了访问结构化数据的JavaScript语法。注意:\n表达式语言最初是作为JSP标准标签库(JSTL)1.0的一部分而开发的,JSTL是一种标准标签库,它提供对通用的、结构化的任务的支持,如迭代和条件、XML文档处理、国际化和利用SQL访问数据库等。JSTL规范是由JSR52专家组开发的。关于JSTL的介绍,请参见FasterDevelopmentwithJSTL(使用JSTL加速开发).访问应用程序数据可以使用点号运算符(.)访问作为对象属性的应用程序数据,也可使用方括号运算符[‘name’]访问命名的数组元素。表达式${data}表示名为data的scoped变量。可以使用点号(.)或方括号([])运算符从集合中检索属性值:·点号运算符用于访问命名的属性,比如表达式${customer.name}表示scoped变量customer的name属性;·方括号运算符可用于检索命名的属性,比如在${customer[“name”]}中。也可以通过${customers[0]}的形式来访问集合customers中的第一项。表达式语言统一了对点号和方括号运算符的处理,因此${customer.name}与${customer[“name”]}是等价的。正如你看到的那样,表达式必须使用${和}包围起来。\nEL的标识符求值方式是使用PageContext.findAttribute(String),把标识符作为一个属性来查找它的值。如果没有找到该属性,则返回null。运算符表达式语言支持算术运算符、关系运算符和逻辑运算符,以完成大多数的数据处理操作。此外,它还提供了一个用于测试一个对象是否为空的特殊运算符。运算符如表1所示。你可以使用empty运算符判断某个集合或字符串是否为空。比方说,只有当要求的命名参数param没有给出时,表达式${emptyparam.name}才返回真。Empty运算符可以与“!”运算符一起使用,比如${!emptyparam.name}当要求的命名参数param存在时返回真。表1:表达式语言运算符运算符说明+加-减*乘/或div除%或mod模(求余)==或=等于!=或!=不等于<或lt小于>或gt大于<=或le小于等于>=或ge大于等于&&或and逻辑与||oror逻辑或!或not逻辑非empty检查是否为空值a?b:c条件运算符\n隐含对象除了运算符外,表达式语言还定义了一些隐含对象以支持网页作者访问需要的应用程序数据。表达式语言定义的隐含对象如表2所示,后面将给出使用这些隐含对象的一个例子。表2:表达式语言中的隐含对象隐含对象内容applicationScope应用程序范围内的scoped变量组成的集合cookie所有cookie组成的集合headerHTTP请求头部,字符串headerValuesHTTP请求头部,字符串集合initParam全部应用程序参数名组成的集合pageContext当前页面的javax.servlet.jsp.PageContext对象pageScope页面范围内所有对象的集合param所有请求参数字符串组成的集合paramValues所有作为字符串集合的请求参数requestScope所有请求范围的对象的集合sessionScope所有会话范围的对象的集合表达式语言的例子如你所言,网页作者无须学习Java也能够使用这种表达式语言。示例代码1显示了一些表达式的例子和隐含对象的使用。代码示例1:ex1.jsp要运行这个例子,请参照以下步骤,这里我们假定Tomcat5.0安装在c:\tomcat5.0下。1.切换目录到c:\Tomcat5.0\webapps\jsp-examples2.创建新目录,名字可以是任意的,比如jsp2-tutorial3.切换到jsp2-tutorial\n1.把ex1.jsp复制并保存到该目录下2.启动Tomcat5服务器:指向“开始”->“程序”->“ApacheTomcat5.0”,单击“ApacheTomcat5.0”来启动Tomcat3.在浏览器中输入http://localhost:8080/jsp-examples/jsp2-tutorial/ex1.jsp你将看到类似图1所示的画面。使用表达式语言就这么简单!图1:JSP表达式语言和隐含对象注意:在本文中,所有的JSP文件都保存在c:\Tomcat5.0\webapps\jsp-examples\jsp2-tutorial目录下。\n填充表单例子隐含对象$paran[var]可用于读取填充表单的数据。代码示例2给出了一个简单的表单,该表单提示用户输入一个名字。代码示例2:form.jspFormContent

Fill-out-form

Name=

TheNameis:${param.name}在本例中,当用户输入名字并单击“提交”按钮时,输入的名字就会显示同一页面中的“TheNameis:”字样后面,如图2所示。同样,运行这个例子只需要把form.jsp复制到c:\Tomcat5.0\webapps\jsp-examples\jsp2-tutorial下并用浏览器打开它。\n图2:表单处理定义和使用函数表达式语言允许你定义可在表达式中调用的函数。函数必须作为public类中的publicstatic方法编写。函数一经定义,它的签名(signature)就映射到标签库描述符(TLD)中。为了说明函数的使用,我们举一个简单的例子,把两个数相加。首先要编写求两数之和的Java方法代码,如代码示例3所示,这里定义了一个静态方法,它接收两个字符串参数,把它们解析成整数并返回它们的和。代码示例3:Compute.javapackagejsp2.examples.el;importjava.util.*;publicclassCompute{publicstaticintadd(Stringx,Stringy){inta=0;intb=0;try{a=Integer.parseInt(x);b=Integer.parseInt(y);}catch(Exceptione){}returna+b;\n}}用javac把这段代码编译成功后,下一步就要把函数的签名映射到标签库。代码示例4说明了如何把add函数映射到包含函数实现和函数签名的类。后面将说明把这段代码添加到哪儿。代码示例4:函数描述符addxandyaddjsp2.examples.el.Computeintadd(java.lang.String,java.lang.String)现在我们就可以编写一个JSP页面来使用这个函数。代码示例5是包含两个字段的一个表单,用户输入两个数字并按下“求和”按钮,就会调用上面的函数并把两个数相加,结果在同一个页面中显示出来。代码示例5:math.jsp<%@taglibprefix="my"uri="http://jakarta.apache.org/tomcat/jsp2-example-taglib%>Functions\n

AddNumbers

X=
Y=

Thesumis:${my:add(param["x"],param["y"])}要运行这个例子:1.复制Compute.java并保存到C:\Tomcat5.0\webapps\jsp-examples\WEB-INF\classes\jsp2\examples\el下;2.使用javac编译Compute.java;3.编辑文件C:\Tomcat5.0\webapps\jsp-examples\WEB-INF\jsp2\jsp2-example-taglib.tld,把代码示例4中的代码段添加到文件中最后一个标签之后,标签之前;4.复制math.jsp并保存到c:\Tomcat5.0\webapps\jsp-examples\jsp2-tutorial下;5.用浏览器打开该文件。如果一切正常,应该会看到类似如图3所示的窗口。\n图3:使用函数标签处理器JSP1.2中传统的标签处理API由于允许标签体中包含scriptlet而变得复杂,但是现在利用表达式语言可以编写不含scriptlet的JSP网页。最终,JSP2.0引入了一种新的标签扩展机制,称为“简单标签扩展”,这种机制有两种使用方式:1.Java开发人员可以定义实现接口javax.servlet.jsp.tagext.SimpleTag的类;2.不懂Java的网页编写人员则可以使用标签文件。首先来看第一种方式,代码示例6给出了一个简单的标签处理器,它的作用仅仅是打印“Thisismyfirsttag!”。代码示例6:HelloTag.javapackagejsp2.examples.simpletag;importjavax.servlet.jsp.JspException;importjavax.servlet.jsp.tagext.SimpleTagSupport;importjava.io.IOException;/***SimpleTaghandlerthatprints"Thisismyfirsttag!"*/\npublicclassHelloTagextendsSimpleTagSupport{publicvoiddoTag()throwsJspException,IOException{getJspContext().getOut().write("Thisismyfirsttag!");}}编译成功后,下一步就要在TLD中定义一个标签描述符,下面是标签描述符的例子。代码示例7:标签描述符Printsthisismyfirsttaghellojsp2.examples.simpletag.HelloTagempty最后再编写一个使用上述标签的JSP页面文件,见代码示例8。代码示例8:helloworld.jsp<%@taglibprefix="mytag"uri="/WEB-INF/jsp2/jsp2-example-taglib.tld"%>SimpleTagHandler

SimpleTagHandler

Myfirsttagprints:要运行这个例子:\n1.复制HelloTg.java并保存到C:\Tomcat5.0\webapps\jsp-examples\WEB-INF\classes\jsp2\examples\simpletag下;2.使用javac编译HelloTag.java;3.把代码示例7中的标签描述符添加到文件C:\Tomcat5.0\webapps\jsp-examples\WEB-INF\jsp2\jsp2-example-taglib.tld中的之前;4.复制helloworld.jsp并保存到c:\Tomcat5.0\webapps\jsp-examples\jsp2-tutorial目录中;5.用浏览器打开helloworld.jsp。如果一切正常,应该会看到类似如图4所示的画面。图4:简单标签处理器标签文件使用简单标签扩展机制的另一种方法是通过标签文件。标签文件是一种资源文件,网页作者可以利用它抽取一段JSP代码,通过定制功能来实现代码的复用。换句话说,标签文件允许JSP网页作者使用JSP语法创建可复用的标签库。标签文件的扩展名必须是“.tag”。\n为说明使用标签文件是多么容易,考虑一下代码示例9。没错,这就是一个标签文件!代码示例9:greetings.tagHellothere.Howareyoudoing?一旦定义了标签文件,就可以在JSP网页的编写中使用这种定制的功能。比如代码示例10中的JSP网页。代码示例10:chat.jsp<%@taglibprefix="tags"tagdir="/WEB-INF/tags"%>JSP2.0Examples-HelloWorldUsingaTagFile

TagFileExample

Theoutputofmyfirsttagfileis:要运行这个例子:1.复制标签文件greetings.tags并保存在c:\Tomcat5.0\webapps\jsp-examples\WEB-INF\tags目录下;2.复制JSP网页文件char.jsp并保存在c:\Tomcat5.0\webapps\jsp-examples\jsp2-tutorial目录下;3.用浏览器打开chat.jsp文件。\n如果一切正常,应该会看到类似如图5所示的窗口。图5:简单的标签文件注意:重要的是要注意到这里没有为问候标签编写TLD,而是创建了一个标签文件并放在特殊的目录中,然后用taglib指令导入并直接使用它。另一个标签文件的例子标签文件可以作为模板使用。考虑代码示例11中的标签文件display.tag,这个例子是根据Tomcat5.0中的面板的例子改写的。指令attribute类似于TLD中的元素,允许声明自定义的动作属性。代码示例11:display.tag<%@attributename="color"%><%@attributename="bgcolor"%><%@attributename="title"%>\n${title}代码示例12给出了使用上述标签的一个简单的JSP页面。代码示例12:newsportal.jsp<%@taglibprefix="tags"tagdir="/WEB-INF/tags"%>AnotherTagFileExample

NewsPortal:AnotherTagFileExample

LastFrenchConcordeArrivesinNY
AnotherTravelHeadline
YetAnotherTravelHeadline
Javaforin-flightentertainment
\nAnotherTechnologyHeadline
AnotherTechnologyHeadline
AmericanFootball
NBA
Soccer
要运行这个例子:1.复制文件display.tag并保存在c:\Tomcat5.0\webapps\jsp-examples\WEB-INF\tag下;2.复制文件newsportal.jsp并保存到c:\Tomcat5.0\webapps\jsp-examples\jsp2-tutorial下;3.用浏览器打开newsportal文件。结果应该会得到与图6类似的画面。\n图6:把标签文件用作模板结论JSP2.0使得快速开发和维护动态网页比以前更加容易,尽管“Java”一词出现在JSP中,但使用JSP2.0,网页作者无须学习Java程序语言本身,就能开发出全新的动态网页。本文中的例子说明了使用JSP2.0的新特性开发动态网页是多么容易。更多信息·FastTrackJSP1.2·JavaServerPagesTechnology·JavaServerPagesSpecification(JSR152)·TheTomcat5Servlet/JSPContainer·JSPDevelopersForum·JavaServerPagesStandardTagLibrary(JSTL)\nJSP学习心得作者:徐春金下面是本人在学习JSP时的一些心得:一、JSP工作原理在一个JSP文件第一次被请求时,JSP引擎把该JSP文件转换成为一个servlet。而这个引擎本身也是一个servlet,在JSWDK或WEBLOGIC中,它就是JspServlet。JSP引擎先把该JSP文件转换成一个Java源文件,在转换时如果发现jsp文件有任何语法错误,转换过程将中断,并向服务端和客户端输出出错信息;如果转换成功,JSP引擎用javac把该Java源文件编译成相应的class文件。然后创建一个该SERVLET的实例,该SERVLET的jspInit()方法被执行,jspInit()方法在servlet的生命周期中只被执行一次。然后jspService()方法被调用来处理客户端的请求。对每一个请求,JSP引擎创建一个新的线程来处理该请求。如果有多个客户端同时请求该JSP文件,则JSP引擎会创建多个线程。每个客户端请求对应一个线程。以多线程方式执行可大大降低对系统的资源需求,提高系统的并发量及响应时间.但应该注意多线程的编程限制,由于该servlet始终驻于内存,所以响应是非常快的。\n如果.jsp文件被修改了,服务器将根据设置决定是否对该文件重新编译,如果需要重新编译,则将编译结果取代内存中的servlet,并继续上述处理过程。虽然JSP效率很高,但在第一次调用时由于需要转换和编译而有一些轻微的延迟。此外,如果在任何时候如果由于系统资源不足的原因,JSP引擎将以某种不确定的方式将servlet从内存中移去。当这种情况发生时jspDestroy()方法首先被调用,然后servlet实例便被标记加入"垃圾收集"处理。jspInit()及jspDestory()格式如下:可在jspInit()中进行一些初始化工作,如建立与数据库的连接,或建立网络连接,从配置文件中取一些参数等,在jspDestory()中释放相应的资源。<%!publicvoidjspInit(){      System.out.println("jspinit");} %> <%!publicvoidjspDestory(){      System.out.println("jspDestory");}\n%>二、服务端的输出缓冲区缺省情况下:服务端要输出到客户端的内容,不直接写到客户端,而是先写到一个输出缓冲区中.只有在下面三中情况下,才会把该缓冲区的内容输出到客户端上:1.该JSP网页已完成信息的输出2.输出缓冲区已满3.JSP中调用了out.flush()或response.flushbuffer()输出缓冲区的大小可以用:或response.setBufferSize()设置,如下:1.设置输出缓冲区的大小为1KB。或response.setBufferSize(1);2.设置输出缓冲区的大小为0,即不缓冲。或response.setBufferSize(0);用response.getBufferSize()或out.getBufferSize()可取的输出缓冲区的大小,单位为字节.用response.isCommitted()可检查看服务端是否已将数据输出到客户端.如果返回值是TRUE则已将数据输出到客户端,是FALSE则还没有.三、服务端输出重定向\n有以下3种方法可以做到输出重定向:1.RESPONSE.SETREDERECT("URL")该方法通过修改HTTP协议的HEADER部分,对浏览器下达重定向指令的,使浏览器显示重定向网页的内容.response.sendRedirect("http://localhost:7001/index.html");2.下面的方法也能改变HTTPHEADER属性,它的原理和1是一样的.<%response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);StringnewLocn="/index.html";response.setHeader("Location",newLocn);%>3.采用该方法是利用服务器端先将数据输出到缓冲区的机制,在把缓冲区的内容发送到客户端之前,原来的不发送,改为发送该页面的内容,如果在之前有很多输出,前面的输出已使缓冲区满,将自动输出到客户端,那么该语句将不起作用,这一点应该特别注意.如下面的例子中(1)会输出index.html的内容,2不会输出index.html的内容,而是输出out.println("@@@@@@@@@@@@@@@@@");中的内容,并且在服务端会抛出:java.lang.IllegalStateException:\nResponsealreadycommitted异常,但客户端没有任何错误输出。(1)<%@pagebuffer="1kb"%> <%longi=0; for(i=0;i<10;i++){      out.println("@@@@@@@@@@@@@@@@@");}%>   (2)<%@pagebuffer="1kb"%> <%longi=0;\n for(i=0;i<600;i++){                     out.println("@@@@@@@@@@@@@@@@@");}%> 说明:1.方法(1),(2)可以使用变量表示重定向的地址;方法(3)不能使用变量表示重定向的地址。Stringadd="./index.html";无法重定向到index.html中去Stringadd=http://localhost:7001/index.htmlresponse.sendRedirect(add);可以重定向到http://localhost:7001/index.html中去。2.采用方法(1),(2)request中的变量(通过request.setAttribute()保存到request中的值)不能在新的页面中采用,采用方法(3)能.综上,我们应该采用(1),(2)重定向比较好.\n四、JSP中正确应用类:应该把类当成JAVABEAN来用,不要在<%%>中直接使用.如下的代码(1)经过JSP引擎转化后会变为代码(2):从中可看出如果把一个类在JSP当成JAVABEAN使用,JSP会根据它的作用范围把它保存到相应的内部对象中.如作用范围为request,则把它保存到request对象中.并且只在第一次调用(对象的值为null)它时进行实例化.而如果在<%%>中直接创建该类的一个对象,则每次调用JSP时,都要重新创建该对象,会影响性能.代码(1) <%test.print("thisisusejavabean"); testdemotd=newtestdemo();td.print("thisisusenew");%> \n代码(2)demo.com.testdemotest=(demo.com.testdemo)request.getAttribute("test");if(test==null){      try      {               test=(demo.com.testdemo)java.beans.Beans.instantiate(getClass().getClassLoader(),"demo.com.testdemo");      }      catch(Exception_beanException)      {             thrownewweblogic.utils.NestedRuntimeException("cannotinstantiate'demo.com.testdemo'",_beanException);      }      request.setAttribute("test",test);      out.print("\r\n");}out.print("\r\n\r\n\r\n");test.print("thisisusejavabean"); testdemotd=newtestdemo();td.print("thisisusenew"); 五、JSP的调试\nJSP的调试比较麻烦,特别是当bean是在一个session中存在时,更加困难。得从好几个页面开始往里面走才行。通常是用out.println()或System.out.print()来打一大堆的信息来查问题。如果是用jbuilder做开发,它能直接调试JSP.不过更重要的是知道错误产生的原因及解决方法。下面对一些JSP编程常见错误进行分析。(1).java.lang.NullPointerException异常一般是对一个为NULL值的变量进行操作引起的.如下面的操作就会抛出java.lang.NullPointerExceptionStringa=null;a.substring(0,1); 为避免这种异常最好在对变量操作之前检查看它是否为NULL值.如:<%Stringss=Session.getAttribute("NAME")ifisnull(ss){}else{}%>\n(2).JSP是用JAVA写的,所以它是大小写敏感的,用过其他编程语言的人最容易犯这个错误。另外在浏览器的地址栏中输入的访问JSP的地址也是区分大小写的.如http://localhost:7001/demo/t.jsp与http://localhost:7001/Demo/t.jsp是不一样的(3).在jsp中判断字符串要使用compareTo方法,不要用==,因为在java中String变量不是一个简单的变量而是一个类实例,不同的方法会得到不同的结果,如下所示:  1.  Stringstr1="ABCD";  Stringstr2="ABCD";(或Stringstr2="AB"+"CD";)  if(str1==str2)  out.print("yes");  else  out.print("no");  结果是"yes"。 2.  Stringstr1,str2,str3;  str1="ABCD";\n  str2="AB";  str3=str2+"CD";  if(str1==str3)  out.print("yes");  else  out.print("no");  结果是"no"。1.Stringstr1=newString("ABCD");  Stringstr2=newString("ABCD");  if(str1==str2)  out.print("yes");  else  out.print("no");  结果是"no"。2.Stringstr1=newString("ABCD");  Stringstr2=newString("ABCD");  if(str1.compareTo(str2)==0)  out.print("yes");  else\n  out.print("no");  结果是"yes"。(4)防止JSP或SERVLET中的输出被浏览器保存在缓冲区中:浏览器在默认情况下会把浏览过的网页保存在缓冲区中,在调试时,一般不希望这样.把下面的脚本加入程序中,就可防止JSP或SERVLET中的输出被浏览器保存在缓冲区中<%response.setHeader("Cache-Control","no-store");//HTTP1.1response.setHeader("Pragma","no-cache");//HTTP1.0response.setDateHeader("Expires",0);//preventscachingattheproxyserver%>在IE中也可通过设置实现:把/工具/INTERNET选项/常规/设置/的检察所存页面的较新版本,设为每次访问该页时都检查.六、COOKIEHTTPCOOKIE实质是服务端与在客户端之间传送的普通HTTP头,可保存也可不保存在客户的硬盘上.如果保存,每一个文件大小不超过4K的文本文件.多个COOKIE可保存到同一个文件中.如果从编程角度来看,在JSP中COOKIE就是JAVA提供的一个类.常用的方法如下所表示,因为客户端可能不接受COOKIE,所以建议不用它,改用SESSION等其他方式。\npublicclasscookie{publicStringgetDomain()//返回该COOKIE的有效域publicintgetMaxAge()//返回该COOKIE的有效期,单位为秒publicStringgetName()//返回该COOKIE的名称publicStringgetPath()//返回该COOKIE的有效路径publicbooleangetSecure()//返回该COOKIE的安全设置publicStringgetValue()//返回该COOKIE的值publicvoidsetDomain(java.lang.Stringpattern)//设置该COOKIE的有效域publicvoidsetMaxAge(intexpiry)//设置该COOKIE的有效期,单位为秒publicvoidsetPath(java.lang.Stringuri)//设置该COOKIE的有效路径publicvoidsetSecure(booleanflag)//设置该COOKIE的安全设置publicvoidsetValue(java.lang.StringnewValue)//设置该COOKIE的值}一个COOKIE包含以下五部分:1.NAME/VALUE对,设置该COOKIE的名字及它保存的值2.\nCOOKIE通常和服务器相关,如果将域设为JAVA.SUN.COM,那么该COOKIE就和这个域相关,只对该网址起作用,当浏览该网址时,浏览器将把该COOKIE的内容发送给服务端,COOKIE是作为HTTPHEADER的一部分被发送的,如果没有设置域,那么COOKIE就只和创建该COOKIE的服务器相关.1.路径用于指定服务器上可以使用该COOKIE的文件所在的路径,它只对该网址下的该路径下的应用起作用."/"表示服务器上所有目录都可以使用该COOKIE.2.COOKIE都有一个有效期,有效期默认值为-1,这表示没有保存该COOKIE,当该浏览器退出时,该COOKIE立即失效.3.安全选项true/false,如果设置为true,那么在服务端与在客户端之间传送该COOKIE的内容时,采用HTTPS协议.如何检查一个客户端是否支持COOKIE的方法:用下面的方法写一个COOKIE到客户端,并确认成功try{Cookiec=newCookie("mycookie","COOKIETEST");response.addCookie(c);}catch(Exceptione){      System.out.println(e);}\n然后在一个新的JSP文件中:用下面的方法取客户端的COOKIE到cookies中,如果cookies.length==0,说明该客户端的浏览器不支持COOKIEtry{Cookie[]cookies=request.getCookies();if(cookies.length==0){      System.out.println("notsupportcookie");}}catch(Exceptione){      System.out.println(e);}七、JSP和SERVLET的区别:SUN首先发展出SERVLET,其功能比较强劲,体系设计也很先进,只是,它输出HTML语句还是采用了老的CGI方式,是一句一句输出,所以,编写和修改HTML非常不方便。\n后来SUN推出了类似于ASP的JSP,把JAVA代码嵌套到HTML语句中,这样,就大大简化和方便了网页的设计和修改。ASP,PHP,JSP都是嵌套型的SCRIPT语言。一个分布式系统应分为三层:表示层,业务逻辑层,数据存取层,在J2EE体系结构中,SERVLET用来写业务逻辑层是很强大的,但是对于写表示层就很不方便。JSP则主要是为了方便写表示层而设计的。ENTITYBEAN实现数据存取层,SESSIONBEAN实现业务逻辑层。如果是简单的应用系统,可采用JSP+BEANS的结构进行设计,JSP中应该仅仅存放与表示层有关的东西,也就是说,只放输出HTML网页的部份。而所有的数据计算,数据分析,数据库联结处理,统统是属于业务逻辑层,应该放在JAVABEANS中。通过JSP调用JAVABEANS,实现两层的整合。实际上,微软的DNA技术,简单说,就是ASP+COM/DCOM技术。与JSP+BEANS完全类似,所有的表示层由ASP完成,所有的业务逻辑由COM/DCOM完成。为什么要采用这些组件技术呢?因为单纯的ASP/JSP语言是非常低效率执行的,如果出现大量用户点击,纯SCRIPT语言很快就到达了他的功能上限,而组件技术就能大幅度提高功能上限,加快执行速度。另外一方面,纯SCRIPT语言将表示层和业务逻辑层混在一起,造成修改不方便,并且代码不能重复利用,采用组件技术就只改组件就可以了。对于复杂的应用,应该采用ENTITYBEAN实现数据存取层,SESSIONBEAN实现业务逻辑层,用JSP来调用SESSIONBEAN,由SESSIONBEAN调用ENTITY\nBEAN。即采用JSP+EJB来构建一个复杂的分布式系统。它比JSP+BEAN具有更高的吞吐量,可靠性,安全性。综上所述,对简单应用,可采用JSP+BAEN,对复杂的应用系统,应采用JSP+EJB,SERVLET变的无足轻重。用JSP完全可替代它。\nJSP迅速入门JavaServerPages(JSP)是一种以Java为主的跨平台web开发语言。JSP与微软的ActiveServerPages兼容,但它是使用类似HTML的卷标以及Java程序代码段而不是VBScript。当你所使用的网站服务器没有提供本地ASP支持,也就是Apache或Netscape服务器时,你可以考虑使用JSP。虽然你可以取得这些服务器的ASP附加配备模块,但价格相当昂贵,而目前Sun并没有对你所需要的JSP组件收取费用(虽然Sun未来有可能会收费)。对于Solaris与Linux以及Windows,这些组件也都很容易取得。 请不要将JSP与伺服端的JavaScript混为一谈。网站服务器会自动将以JSP写成的Java程序代码段转换成Javaservlets。而许多先前必须以Perl手写程序或服务器特定的API(如ASP)控制的功能也都可透过JSP来自动化处理。现在就让我们开始动手帮助你建立一个可执行JSP范例网站。安装好你的机器来使用JSP你将会需要Java2软件开发工具(JSDK),它原来的名称是Java发展工具(JDK)以及JavaServer网站发展工具(JSWDK),Tomcat,或是其它支持JSP的网络服务器。Sun免费提供JSDK与JSWDK来供Windows,Solaris,以及Linux平台使用。 \n如果你想要在你目前的网络服务器上使用JSP,但服务器本身并不支持JSP与Javaservlets,你可以试试看Allaire的Jrun,它的作用就像是针对Netscape企业版与FastTrack服务器、微软的网际网络信息服务器(IIS)与个人网络服务器(PWS)、Apache、以及其它服务器的网络服务器附加设备。你也可以使用Apache网络服务器的Java版本,最新的JSWDK里有提供。下载与安装你需要的组件目前发布的1.2.2-001,JSDK可下载的版本是以可安装的压缩档形式。下载的档案大约是20MB,可提供完整的Java发展环境,让你能建立利用标准API为核心的Java解决之道。然而,你的网络服务器需要应用到JSP的唯一一件事是Java编译器。要让网络服务器知道编译器的位置,将环境变量JAVA.HOME设到JSDK的安装目录。如果你是在Windows上安装并且接受预设目录,将这行程序代码setJAVA.HOME=C:.2.2加到你的autoexec.bat档案并且重新开机。在安装好JSDK之后,下载并且安装JSWDK或beta版的Tomcat,以Java为主的Apache网络服务器。安装在哪里并不重要,重要的是你可以找到它。一般而言,它会放在上层目录,这种方式可以让你取代JSWDK或JSDK的网络服务器,不需要移动其它的网络服务器。在你安装好这个档案之后,你就可以准备发展JSP了。在你正确的安装JSWDK之后,执行startserver指令文件来激活网络服务器,预设通讯端口为8080。要看你在激活服务器之后是均C有正确的安装工具,你可以加载范例JSP档案中的任何一个(http://localhost:8080/examples/jsp/)。如果你能够成功的执行一个范例档案,你可以知道你已经正确的设定好软件了。如果你在激活服务器的控制台窗口看到错误讯息,那么你需要解决这个问题。最常发生的问题是没有设定(或者不正确设定)环境变量JAVA.HOME。要检视目前的环境设定,在DOS模式下键入set。开始\n解释JSP语法之前,先建立一个显示目前日期与时间的快速网页并且将它储存成sample.jsp:FirstPage

Todayis:<%=newjava.util.Date()%>

.将这个档案与你所有的HTML与JSP网页放在你JSWDK安装目录下的网页目录里.你可以在http://localhost:8080/sample.jsp下载此页.当你第一次参观这个网页时,网站服务器会将JSP翻译成Javaservlet程序代码,那你就会看到目前的日期与时间.现在你已经下载,安装,并且架构好发展环境,你已经准备好要了解JSP语法与建立你自己的JSP为主的解决之道.\nJSP语法的基本原理安装之后,接下来我们要讨论JSP的语法.如果要偷懒,你可以下载语法卡而如果你不熟悉Java的程序设计,你可能会想要参考Sun的使用手册;然而,网站建立者不应该做太多的Java发展.除了几个函式呼叫之外,出现在你JSP网页上的Java程序代码应该将它减到最少;记住这点之后,现在让我们先来看看JSP的编译器指引与指令组件,之后我们将解释JavaBeans与内部对象.JSP编译器指引与指令组件有五种型态.JSP1.0之后,大部分的JSP是包含在以<%作为开始%>作为结束的单一卷标里.新的JSP1.1规格已经发表了,它同时也与XML兼容.JSP的编译器指引与指令组件 编译器指示<%@编译器指示%> 声明<%!声明%>\n 表达式<%=表达式%> 程序代码段/小型指令<%程序代码片段%> 注释<%--注释--%> 编译器指示JSP的编译器指示是针对JSP引擎。它们并不会直接产生任何看得见的输出;相反的,它们是在告诉引擎如何处理其它的JSP网页。它们永远包含在<%@?%>卷标里。两个主要的指引是page与include。我们不会讨论taglib编译器指引但它可以在JSP1.1里用来建立自订卷标。你几乎可以在你所有的JSP网页最上面找到page编译器指示。虽然这不是必须的,但它可以让你指定到哪里可以找到支持的Java类别这类的事:\n<%@pageimport="java.util.Date"%>, 当发生Java问题的事件时应该将讯息传送到哪里:<%@pageerrorPage="errorPage.jsp"%>, 以及你是?需要为使用者管理通话期的信息,可能存取多个网页(稍后在JavaBeans里会有更多通话期的讨论):<%@pagesession="true"%>。  include编译器指示让你将你的内容分成几个可管理的组件,就像那些有表头或脚注的网页。所包含的网页可以是固定格式的HTML网页或者是JSP内容的网页:<%@includefile="filename.jsp"%>。宣告JSP声明让你定义网页层的变量,来储存信息或定义支持的函式,让JSP网页的其余部分能够使用。如果你发现自己有太多的程序代码,你最好将它们放在不同的Java类别里。你可以在<%!?%>卷标里找到声明。记住要在变量声明的后面加上分号,就跟任何有效的Java叙述的形式一样:<%!inti=0;%>。表达式\nJSP里有表达式,评估表达式的结果可以转换成字符串并且直接使用在输出网页上。JSP运算是属于<%=?%>卷标里,并不包含分号,加引号字符串的无用部分。<%=i%><%="Hello"%>。程序代码段/小型指令文件JSP程序代码片段或小型指令文件是包含在<%?%>卷标里。当网络服务器接受这段请求时,这段Java程序代码会执行。小型指令文件可以是原始的HTML或XML,其内部的程序代码片段可以让你建立有条件的执行程序代码,或者只是一些使用另一块程序代码的东西。举例来说,下列的程序代码结合了表达式与小型指令文件,在H1,H2,H3,以及H4卷标里显示字符串"Hello"。小型指令文件不限于一行的原始程序代码:<%for(inti=1;i<=4;i++){%>>Hello><%}%>。注释\n最后一个主要JSP组件是嵌入式注释。虽然你可以在你的档案里包含HTML注释,如果使用者检视网页的原始码,他们也会看到这些注释。如果你不要让使用者看到你的批注,你可以将它放在<%--?--%>卷标里:<%--针对伺服端的注释--%>。JSP与JavaBean虽然你可以在小型指令文件里放入一大块的程序代码,但是大多数的Java程序代码是属于可以重复使用的组件,称为JavaBean。JavaBean就跟ActiveX控件一样:它们提供已知的功能,并且是为了可随时重复使用的目的而设计的。JavaBean的价值在于它可以经由一组特性来使用,而这些特性则提供对JavaBean设定的存取。以人来作范例,此人就是JavaBean,而他的姓名,社会福利安全号码,以及住址可以是特性。对于JSP网站,基本上你是将'JavaBean'动态的连接到你的网站。假设JavaBean是在?html> JSP入门JavaServerPages(JSP)是一种以Java为主的跨平台web开发语言。\n JSP与微软的ActiveServerPages兼容,但它是使用类似HTML的卷标以及Java程序代码段而不是VBScript。当你所使用的网站服务器没有提供本地ASP支持,也就是Apache或Netscape服务器时,你可以考虑使用JSP。虽然你可以取得这些服务器的ASP附加配备模块,但价格相当昂贵,而目前Sun并没有对你所需要的JSP组件收取费用(虽然Sun未来有可能会收费)。对于Solaris与Linux以及Windows,这些组件也都很容易取得。 请不要将JSP与伺服端的JavaScript混为一谈。网站服务器会自动将以JSP写成的Java程序代码段转换成Javaservlets。而许多先前必须以Perl手写程序或服务器特定的API(如ASP)控制的功能也都可透过JSP来自动化处理。现在就让我们开始动手帮助你建立一个可执行JSP范例网站。安装好你的机器来使用JSP你将会需要Java2软件开发工具(JSDK),它原来的名称是Java发展工具(JDK)以及JavaServer网站发展工具(JSWDK),Tomcat,或是其它支持JSP的网络服务器。Sun免费提供JSDK与JSWDK来供Windows,Solaris,以及Linux平台使用。 \n如果你想要在你目前的网络服务器上使用JSP,但服务器本身并不支持JSP与Javaservlets,你可以试试看Allaire的Jrun,它的作用就像是针对Netscape企业版与FastTrack服务器、微软的网际网络信息服务器(IIS)与个人网络服务器(PWS)、Apache、以及其它服务器的网络服务器附加设备。你也可以使用Apache网络服务器的Java版本,最新的JSWDK里有提供。下载与安装你需要的组件目前发布的1.2.2-001,JSDK可下载的版本是以可安装的压缩档形式。下载的档案大约是20MB,可提供完整的Java发展环境,让你能建立利用标准API为核心的Java解决之道。然而,你的网络服务器需要应用到JSP的唯一一件事是Java编译器。要让网络服务器知道编译器的位置,将环境变量JAVA.HOME设到JSDK的安装目录。如果你是在Windows上安装并且接受预设目录,将这行程序代码setJAVA.HOME=C:.2.2加到你的autoexec.bat档案并且重新开机。在安装好JSDK之后,下载并且安装JSWDK或beta版的Tomcat,以Java为主的Apache网络服务器。安装在哪里并不重要,重要的是你可以找到它。一般而言,它会放在上层目录,这种方式可以让你取代JSWDK或JSDK的网络服务器,不需要移动其它的网络服务器。在你安装好这个档案之后,你就可以准备发展JSP了。在你正确的安装JSWDK之后,执行startserver指令文件来激活网络服务器,预设通讯端口为8080。要看你在激活服务器之后是均C有正确的安装工具,你可以加载范例JSP档案中的任何一个(http://localhost:8080/examples/jsp/)。如果你能够成功的执行一个范例档案,你可以知道你已经正确的设定好软件了。如果你在激活服务器的控制台窗口看到错误讯息,那么你需要解决这个问题。最常发生的问题是没有设定(或者不正确设定)环境变量JAVA.HOME。要检视目前的环境设定,在DOS模式下键入set。开始\n解释JSP语法之前,先建立一个显示目前日期与时间的快速网页并且将它储存成sample.jsp:FirstPage

Todayis:<%=newjava.util.Date()%>

.将这个档案与你所有的HTML与JSP网页放在你JSWDK安装目录下的网页目录里.你可以在http://localhost:8080/sample.jsp下载此页.当你第一次参观这个网页时,网站服务器会将JSP翻译成Javaservlet程序代码,那你就会看到目前的日期与时间.\n现在你已经下载,安装,并且架构好发展环境,你已经准备好要了解JSP语法与建立你自己的JSP为主的解决之道.JSP语法的基本原理安装之后,接下来我们要讨论JSP的语法.如果要偷懒,你可以下载语法卡而如果你不熟悉Java的程序设计,你可能会想要参考Sun的使用手册;然而,网站建立者不应该做太多的Java发展.除了几个函式呼叫之外,出现在你JSP网页上的Java程序代码应该将它减到最少;记住这点之后,现在让我们先来看看JSP的编译器指引与指令组件,之后我们将解释JavaBeans与内部对象.JSP编译器指引与指令组件有五种型态.JSP1.0之后,大部分的JSP是包含在以<%作为开始%>作为结束的单一卷标里.新的JSP1.1规格已经发表了,它同时也与XML兼容.JSP的编译器指引与指令组件 编译器指示<%@编译器指示%> 声明<%!声明%>\n 表达式<%=表达式%> 程序代码段/小型指令<%程序代码片段%> 注释<%--注释--%> 编译器指示JSP的编译器指示是针对JSP引擎。它们并不会直接产生任何看得见的输出;相反的,它们是在告诉引擎如何处理其它的JSP网页。它们永远包含在<%@?%>卷标里。两个主要的指引是page与include。我们不会讨论taglib编译器指引但它可以在JSP1.1里用来建立自订卷标。\n你几乎可以在你所有的JSP网页最上面找到page编译器指示。虽然这不是必须的,但它可以让你指定到哪里可以找到支持的Java类别这类的事:<%@pageimport="java.util.Date"%>, 当发生Java问题的事件时应该将讯息传送到哪里:<%@pageerrorPage="errorPage.jsp"%>, 以及你是?需要为使用者管理通话期的信息,可能存取多个网页(稍后在JavaBeans里会有更多通话期的讨论):<%@pagesession="true"%>。  include编译器指示让你将你的内容分成几个可管理的组件,就像那些有表头或脚注的网页。所包含的网页可以是固定格式的HTML网页或者是JSP内容的网页:<%@includefile="filename.jsp"%>。宣告JSP声明让你定义网页层的变量,来储存信息或定义支持的函式,让JSP网页的其余部分能够使用。如果你发现自己有太多的程序代码,你最好将它们放在不同的Java类别里。你可以在<%!?%>卷标里找到声明。记住要在变量声明的后面加上分号,就跟任何有效的Java叙述的形式一样:<%!inti=0;%>。\n表达式JSP里有表达式,评估表达式的结果可以转换成字符串并且直接使用在输出网页上。JSP运算是属于<%=?%>卷标里,并不包含分号,加引号字符串的无用部分。<%=i%><%="Hello"%>。程序代码段/小型指令文件JSP程序代码片段或小型指令文件是包含在<%?%>卷标里。当网络服务器接受这段请求时,这段Java程序代码会执行。小型指令文件可以是原始的HTML或XML,其内部的程序代码片段可以让你建立有条件的执行程序代码,或者只是一些使用另一块程序代码的东西。举例来说,下列的程序代码结合了表达式与小型指令文件,在H1,H2,H3,以及H4卷标里显示字符串"Hello"。小型指令文件不限于一行的原始程序代码:<%for(inti=1;i<=4;i++){%>>Hello><%}%>。注释\n最后一个主要JSP组件是嵌入式注释。虽然你可以在你的档案里包含HTML注释,如果使用者检视网页的原始码,他们也会看到这些注释。如果你不要让使用者看到你的批注,你可以将它放在<%--?--%>卷标里:<%--针对伺服端的注释--%>。JSP与JavaBean虽然你可以在小型指令文件里放入一大块的程序代码,但是大多数的Java程序代码是属于可以重复使用的组件,称为JavaBean。JavaBean就跟ActiveX控件一样:它们提供已知的功能,并且是为了可随时重复使用的目的而设计的。JavaBean的价值在于它可以经由一组特性来使用,而这些特性则提供对JavaBean设定的存取。以人来作范例,此人就是JavaBean,而他的姓名,社会福利安全号码,以及住址可以是特性。对于JSP网站,基本上你是将'JavaBean'动态的连接到你的网站。假设JavaBean是在建立网站之前建好的,你要做的第一件事是告诉JSP网页它所需要使用JavaBean.这工作可以用卷标来完成:.卷标需要你以id\n属性来辨识豆子.在这里,你提供一个名称让JSP网页来辨识豆子,除了id属性之外,你也必须告诉网页要到哪里去找这个豆子,或者是它的Java类别名称。类别属性提供如何在各式方法之中找到它,最后一个需要的组件是scope属性.有了范围属性的帮助,你可以告诉豆子,要它为单一网页(预设)[scope="page"];为一个被请求的网页[scope="request"];为通话期[scope="session"];或为整个应用程序[scope="application"]来维护它自己的信息.对于通话期范围,你可以很容易的维护JSP网页里的项目,例如购物车。一但你宣告了JavaBean之后,你就可以存取它的特性来订定它。要取得一特性的值,使用卷标。有了卷标,你可以指定要使用的豆子名称(从useBean的id字段),以及你要取得值的特性。接着,真正的值就会放在输出里:.要更改JavaBean的特性,你需要使用卷标.对这个卷标,你也需要辨认豆子以及要修正的特性,除此之外,你还需要提供新值.如果命名正确,这些可以直接经由规定的格式取得:; 要从一参数取得,你必须直接命名此特性以及参数:; 或是直接以名称与值来设定:or/>.\n有关JavaBean的最后一点:要让网络服务器可以找到JavaBean,你需要将它们的类别档案放在特别位置。对JSWDK而言,最简单的地方是在安装目录里的类别目录,例如\jswdk-1.0.1\classes.JSP的内部对象最后一个与JSP语法有关的组件叫做内部对象.在JSP小型指令文件内,你可以存取这些内部对象来与执行JSP网页的servlet环境相互作用。许多对内部对象的存取应该要简化。然而,这些是范例,它们的存取都是可接受的,要完整的利用内部对象设定则需要对最新的JavaServletAPI有所了解。下表列出你可以使用的内部对象。 内部对象说明 request客户端请求,此请求会包含来自GET/POST请求的参数\n response网页传回客户端的响应 pageContext网页的属性是在这里管理 session与请求有关的会话 applicationservlet正在执行的内容 out用来传送响应的输出流 configservlet的架构对象 page\nJSP网页本身 exception针对错误网页,未捕捉的例外 那么,这些是做什么的,而你应该如何使用它们呢?基本上,在你的小型指令文件里,你可以使用它们来存取执行JSP程序代码的servlet。为了避免谈论到太多ServletAPI的细节,让我们来检视一些你可以利用它们来做的事: 不必使用表达式,你可以直接存取内部out对象来打印一些东西到response:<%out.println("Hello");%>. 不必直接传送参数到JavaBean,你可以藉由请求对象来取得参数的值:<%Stringname=request.getParameter("name");out.println(name);%>。当你以JSP写了许多的应用程序之后,如果你建立了JavaBeans或者发现你自己将太多的Java原始码放入你的JSP档案,你需要建立支持的Java类别,这样可以鼓励重复使用并且降低JSP网页转换时所需要的时间。当你需要建立Java类别时,你必须:\n将JDSWK的安装目录\bin目录加到你的PATH。在你的autoexec.bat档案的PATH行的最后,加入C:.2.2\bin;。 以下面的指令将JAR档案复制到\jre\lib\ext目录:copyc:\jswdk-1.0.1\lib\servlet.jarc:\jdk1.2.2\jre\lib\ext.\n几则JSP入门知识总结从去年9月份,我就开始着手学JSP,以前也只有一点程序的意识,一路上摸索过来,经过了很多磨难,终于有一天,我就像一个旱鸭子学会游泳一样,心里无比高兴,熬了几天夜,终于写成了这个纯JSP的文章发布程序。相信下面的几则小知识对向我这样水平的菜鸟有一定的帮助! ==============================================================================1.传递表单参数:Stringname=newString(request.getParameter("name"));2.数据库连接:~~MYSQL//设置数据库的URLStringurl="jdbc:mysql://localhost:3306/jspsky";try//加载驱动程序Class.forname("org.gjt.mm.mysql.Driver").newInstance();//建立连接java.sql.Connectionconnection=java.sql.DriverManager.getConnection(url);java.sql.Statementstatement=connection.createStatement();//SQL语句StringsqlStringi="insertintocommu(name,tel,mobile,oicq,email)values(‘"+name+"’,‘"+tel+"’,‘"+mobile+"’,‘"+oicq+"’,‘"+email+"’)";//运行SQL语句,并建立结果集java.sql.ResultSetrsi=statement.executeQuery(sqlStringi);//在屏幕上输出库中的内容while(rss.next()){Stringa_name=rss.getString(1);out.println(a_name);{}//关闭连接connection.close();}//捕捉异常\ncatch(java.sql.SQLExceptione)out.println(e.getMessage());{}catch(ClassNotFoundExceptione)out.println(e.getMessage());{}~~DB2//定义数据库的URLStringurl="jdbc:db2:portal";try//加载驱动程序Class.forName("COM.ibm.db2.jdbc.app.DB2Driver");//建立连接,java.sql.Connectionconnection=java.sql.DriverManager.getConnection(url,"user","password");java.sql.Statementstatement=connection.createStatement();//SQL语句StringsqlString="select*fromclient";//执行SQL语句java.sql.ResultSetrs=statement.executeQuery(sqlString);//在屏幕上显示所连表中的内容while(rs.next()){Stringname=rs.getString(1);out.println(name);{}//关闭连接connection.close();}//捕捉异常catch(java.sql.SQLExceptione)out.println(e.getMessage());\n{}catch(ClassNotFoundExceptione)out.println(e.getMessage());{}3.文件操作~~将一个字符串写到一个指定的文件中,如果该文件不存在,则新建一个文件,并完成写入;如果存在,则用此字符串覆盖原文件的所有内容importjava.io.*;Stringstr="printme雪峰!";//定义好打印的目标文件名//取得当前主机存放WEB页面的绝对路径Stringhostdir=System.getProperty("user.dir");//取得当前主机所采用的路径分隔符StringfileBar=System.getProperty("file.separator");//书写完整的目标文件存放路径StringnameOfFile=hostdir+fileBar+"test.html";try//实例化一个文件输出流对象FileOutputStreamafile=newFileOutputStream(nameOfFile);//将文件输出流,创建一个打印输出流对象PrintWriterpw=newPrintWriter(afile);pw.println(str);//cleanuppw.close();{}catch(IOExceptione)out.println(e.getMessage());{}~~列出指定目录下的文件列表importjava.io.*;Stringcdur=System.getProperty("user.dir");\nStringfileBar=System.getProperty("file.separator");Stringmydir=cdur+fileBar+"doc"+fileBar+"jspsky";Filemy=newFile(mydir);Stringd[]=my.list();inti;intl=d.length;for(i=0;iout.print(d[i]);{}4.计数器Integercount=null;synchronized(application)count=(Integer)application.getAttribute("d");if(count==null)count=newInteger("0");count=newInteger(count.intValue()+1);application.setAttribute("d",count);{}out.println(count);//首先定义一个整形对象,并初始化为:NULL,//取回APPLICATION对像的属性D的值,并强制转化为整形对象,赋给COUNT//判断COUNT是否为空,为空时,将O赋给COUNT对象,//否则,通过COUNT。INTVALUE()方法,实现COUNT对象加1,并赋值给COUNT//最后,将COUNT对象保存在APPLICATION对象的D变量中。+++++++++++++++++++下一步学习重点文件的删除文件内容的修改图片的上传邮件列表JAVABEANSEJB\nXMLJAVASCRIPT对数据库的操作及维护了解商业项目开发流程实例练习留言板聊天室发送邮件新闻系统截取网页内容购物车多做练习,在实践中不断熟悉JAVA包的使用!\nJSP的9种基本内置组件来源:网友提供如有版权问题请与我们联系基本组件JSP共有以下9种基本内置组件(可与ASP的6种内部组件相对应):request用户端请求,此请求会包含来自GET/POST请求的参数response网页传回用户端的回应pageContext网页的属性是在这里管理session与请求有关的会话期applicationservlet正在执行的内容out用来传送回应的输出configservlet的构架部件pageJSP网页本身exception针对错误网页,未捕捉的例外你可以使用它们来存取执行JSP代码的servlet。为了避免谈论到太多ServletAPI的细节,让我们来检视一些你可以利用它们来做的事:不必使用运算式,你可以直接存取内部out物件来列印一些东西到response:<%out.println("Hello");%>不必直接传送参数到JavaBean,你可以按照请求部件来取得参数的值:<%Stringname=request.getParameter("name");out.println(name);%>。等等。以下着重介绍session对象。 会话状态维持是Web应用开发者必须面对的问题。有多种方法可以用来解决这个问题,如使用Cookies、隐藏的表单输入域,或直接将状态信息附加到URL中。JavaServlet提供了一个在多个请求之间持续有效的会话对象,该对象允许用户存储和提取会话状态信息。JSP也同样支持Servlet中的这个概念。 在Sun的JSP指南中可以看到许多有关隐含对象的说明(隐含的含义是,这些对象可以直接引用,不需要显式地声明,也不需要专门的代码创建其实例)。例如request对象,它是HttpServletRequest的一个子类。该对象包含了所有有关当前浏览器请求的信息,包括Cookies,HTML表单变量等等。session对象也是这样一个隐含对象。这个对象在第一个JSP页面被装载时自动创建,并被关联到request对象上。与ASP中的会话对象相似,JSP中的session对象对于那些希望通过多个页面完成一个事务的应用是非常有用的。  为说明session对象的具体应用,接下来我们用三个页面模拟一个多页面的Web应用。第一个页面(q1.html)仅包含一个要求输入用户名字的HTML表单,代码如下: \n   请输入您的姓名:       第二个页面是一个JSP页面(q2.jsp),它通过request对象提取q1.html表单中的thename值,将它存储为name变量,然后将这个name值保存到session对象中。session对象是一个名字/值对的集合,在这里,名字/值对中的名字为“thename”,值即为name变量的值。由于session对象在会话期间是一直有效的,因此这里保存的变量对后继的页面也有效。q2.jsp的另外一个任务是询问第二个问题。下面是它的代码:   <%@pagelanguage="java"%> <%!Stringname="";%> <% name=request.getParameter("thename"); session.putValue("thename",name); %> 您的姓名是:<%=name%> 

  您喜欢吃什么?  

      第三个页面也是一个JSP页面(q3.jsp),主要任务是显示问答结果。它从session对象提取thename的值并显示它,以此证明虽然该值在第一个页面输入,但通过session对象得以保留。q3.jsp的另外一个任务是提取在第二个页面中的用户输入并显示它:  \n <%@pagelanguage="java"%> <%!Stringfood="";%> <% food=request.getParameter("food"); Stringname=(String)session.getValue("thename"); %> 您的姓名是:<%=name%> 

 您喜欢吃:<%=food%>  \nJSP由浅入深(1)——熟悉JSP服务器熟悉JSP服务器本“JSP由浅入深”系列教程是面向中级和高级用户的,它需要HTML和Java的基础。你应该会将HTML网页连接到一起,并且会利用Java来进行编程。如果你还没有这个基础,建议你还是先打好基础为好。这个系列教程将通过编制简单的例子到复杂的例子来教会你JSP。本系列教程是采用循序渐进的方法来进行阐述的,即由浅入深。为了使你能够获得最大的进步,建议你再学习的过程中将所有的例子自己进行调试。开始的例子可能会很简单,所以开始的时候你要特别耐心,不要认为太简单而跳过。如果你仔细地调试例子,那么你就会很快地熟悉JSP的本质方法。好吧,开始我们的第一个教程:熟悉JSP服务器。如果你没有一个JSP网络服务器,那么你在开始教程之前还是先下载它吧。以下的几个服务器可以免费下载或者进行开发:Blazix(1.5Megabytes,JSP,ServletsandEJBs)来自www.blazix.com/blazix.htmlServletExec(3.8Megabytes,JSPandServlets)来自www.unifyeware.com/servletExec/JRun(11Megabytes,JSP,ServletsandEJBs)来自www.jrun.com/WebLogic(44Megabytes,JSP,ServletsandEJBs)来自www.beasys.com/WebSphere(105Megabytes,JSP,ServletsandEJBs)来自www-4.ibm.com/sofeware/webservers/如果你还没有服务器,那我建议你下载Blazix,因为它包含了标签库(可以用于以后关于标签库的教程)。Blazix同样也是很小的并且它可以很容易地下载,而且可以运行于所有的操作系统,包括处于主流的Windows98。还有一个优点,就是它的安装的速度更块。\n为了真正学习JSP,最重要的是,你要在一个真实的服务器来调试教程的例子。最好的方法是通过自己的实践来学习JSP的技巧。如果你现在还没有服务器,那就先下载一个安装它就行了。装上网络服务器以后,你应该学会以下关于网络服务器的的一些知识:应该在哪里放置文件?怎样访问来自浏览器的文件(是以http:开头的,而不是file:开头)?你首先要创建以下的简单文件,比如:Hello,world怎样放置文件并在浏览器(以http://)中浏览它,这个步骤对应不同的网络服务器是不同的,所以你需要参看网络服务器的文档并找出答案。\nJSP由浅入深(2)——第一个JSP第一个JSP实际上,JSP只是简单地将Java放到HTML网页中去而已。你可以将现有的HTML网页将它们的扩展名由“.html”改为“.jsp”,这是一个创建第一个JSP最好的方法。我们可以将上一个练习中的文件将它的扩展名由“.html”改为“.jsp”。然后在浏览器中装载新的文件,即以“.jsp”为扩展名的文件。此时,你可以看到的输出,但是它需要更长的时间。这个现象也只发生在你第一次装载它的时候。如果你重新装载它,速度就正常了。到底出现的什么事情了?原来是你的JSP被转变成Java文件,并且被编译以及装载。而编译只进行一次,所以第一次装载的时候时间会长一点,原来是花在了编译上了。而在第二次装载的时候它不再需要编译,所以器装载速度就回复正常了。这里要提醒的是,当将HTML格式的文件转变成JSP格式的文件,都需要重新编译。当然,只是编写HTML网页再将其转变成.jsp扩展名的文件是没有用的,在后面的教程中你将学到什么才能使JSP变得有用。\nJSP由浅入深(3)——通过表达式增加动态内容在我们前面的章节中,任何的HTML文件都可以转变成JSP文件,做法是通过改变它的扩展名为.jsp。当然,我们要知道是什么使得JSP有用呢?答案是嵌入Java的能力。将下列文本放置在一个以.jsp为扩展名的文件中,比如说这个文件为myjsp.jsp,然后将这个文件放置到你的JSP目录下并且在浏览器上看它。以下是具体的代码:Hello!Thetimeisnow<%=newjava.util.Date()%>这里要注意,每次你在浏览器中重载网页的时候,它就出现当前时间。字符系列<%=and%>的作用是圈起Java表达式,这个表达式将在运行的时候被计算。正因为这样,使用JSP产生动态HTML网页来响应用户的动作才变为可能。好吧,教程结束之后你最好作个练习:为不同的系统属性编写一个JSP用来输出由System.getProperty返回的数值,比如java.version、java.home、os.name、user.home以及user.dir等等。\nJSP由浅入深(4)——Scriptlets上面的教程我们已经学会了如何在JSP网页中通过在<%=and%>字符系列之间嵌入Java表达式。但是通过将Java表达式放置到HTML进行大量的编程工作是很困难的。JSP另外允许你编写Java代码块嵌入到JSP中。具体做法是:将Java代码段放置于<%and%>字符之间。其实跟表达式是一样的,只不过在开始的地方没有等号而已。代码块就是我们熟知的“scriptlet”。如果单单是scriptlet,对于HTML起不了任何作用。而每次scriptlet包含了被执行的Java代码的时候,JSP就被调用。下面的代码是对上一节JSP教程进行修改后的代码,我们增加了一个scriptlet:<%//Thisisascriptlet.Noticethatthe"date"//variablewedeclarehereisavailableinthe//embeddedexpressionlateron.System.out.println("Evaluatingdatenow");java.util.Datedate=newjava.util.Date();%>Hello!Thetimeisnow<%=date%>\n如果你运行上面的例子,你将会注意到“System.out.println”输出在服务器日志中。这是一个方便的方法来作一些简单的调试。有些服务器在IDE中有调试JSP的功能,你可以参见服务器文档来查看是否有这种功能。\nJSP由浅入深(5)——Scriptlets和HTML的混合在前面的教程中我们已经在一个scriptlet中使用了“out”变量来产生HTML输出。对于更复杂的HTML,如果我们还是使用“out”变量那就会失去JSP编程的许多优势。其实我们可以很简单地就实现Scriptlets和HTML的混合。假如你要在HTML产生一个表格。产生表格是一个普通的操作,在实践中你可能想从一个SQL表格或者从文件的行产生一个表格。为了是所举的例子尽量的简单,我们产生一个表格,使这个表格包含从1到N的数字。虽然以下的例子不是很有用,但是你从中可以学到一些技巧的。以下就是JSP的程序片段:<%for(inti=0;iNumber<%=i+1%><%}%>在编制代码之前,你应该首先定义一个整型(int)变量“n”,利用这个变量我们要输出“n”行的表格。从上面的代码中我们可以注意到%>和\n<%字符出现在“for”循环中,原来这是为了使你退到HTML然后又回到scriptlet,其实这正是Scriptlets和HTML的混合的技巧。上面的代码其实很简单:可以退出scriptlet的时候,就编写HTML;然后又回到scriptlet中去。任何的循环控制表达式,比如“while”或者“for”循环以及“if”语句都可以控制HTML。如果HTML处在一个循环中它就会在每一次的循环中执行一次。\nJSP由浅入深(6)——JSP声明现在你所编写的JSP要变成一个类的定义。所有你编写的scriptlets要放置到这个类的一个方法中。同样,你可以增加变量和方法声明到这个类中。当然你也可以从scriptlets和表达式中使用这些变量和方法。为了增加一个声明,你必须使用<%!and%>来圈起你的声明,比如:<%@pageimport="java.util.*"%><%!DatetheDate=newDate();DategetDate(){System.out.println("IngetDate()method");returntheDate;}%>Hello!Thetimeisnow<%=getDate()%>上面的例子已经声明了变量和方法:一个Date变量theDate以及一个方法getDate。这两个从现在开始在scriptlets和表达式中都是有效的了,因为它们已经定义了。但是不幸的是,上面的这个例子并不能正常工作:不管你怎么重载网页,日期都是相同的。其中的原因是这些声明,它们只在网页被装载的时候才被计算一次。这一点就象我们在VisualC++中创建一个类并定义变量的初始化值。本节教程的练习为:修改上面的例子,增加另外一个函数computeDate来重新初始化theDate。并且增加一个scriptlet来每次调用computeDate。\nJSP由浅入深(7)——JSPDirectives在前面的教程中,我们已经使用了java.util.Date。可以有人就会问:为什么不只使用importjava.util.*呢?其实,在JSPs中也可以使用import语句,但是它的语法跟普通的Java是有些差别的。下面给出一个例子:<%@pageimport="java.util.*"%><%System.out.println("Evaluatingdatenow");Datedate=newDate();%>Hello!Thetimeisnow<%=date%>上面的代码中第一行我们调用了一个“directive”。这个JSP“directive”是以<%@字符为开头的。这是一个"pagedirective"。这个"pagedirective"可以包含所有的引入的项目。假如你为了引入多于一个的项目,你就可以利用逗号(,)来分隔项目,比如:<%@pageimport="java.util.*,java.text.*"%>在"pagedirective"中可以有多个JSPdirectives。除了"pagedirective"以外,其它有用的directives有include和taglib。在后面的教程中我们再对taglib进行详细的讨论。这里只讨论includedirective。\nJSP由浅入深(8)——JSPTagsJSP另外一个重要的语法是Tags(标记)。JSPTags不是使用<%,而只是使用<字符。JSPTag有点象HTMLTag。JSPtags可以有一个“starttag”、一个“tagbody”以及一个“endtag”。开始和结束的标志都可以使用标志的名字,都被圈在<和>字符里面。结束的标志是在字符<后面加一个反斜杆(/)字符。这个标志名字有一个嵌入的冒号(:)字符在里面,其中冒号前面的部分描述了标志的类型。举个例子吧:body如果标志不需要一个主体,那么开始和结束标志可以合成在一起,比如:这里我们利用一个/>替代了>字符,这样我们就可以快速地结束标志而不需要一个实体。这条语法跟XML的是一样的。标志可以分成两种类型:一种是从外部标志库中转载的,另外一种是预先定义的标志。预先定义的标志是以jsp:字符开始的。举个例子吧,jsp:include是一个预先定义的标志,它用于包括其它的网页。在前面的教程我们已经学习了“includedirective”。其实它跟jsp:include是很相似的。它们都不是在原始文件中装载被包含的文件的文本,而是在运行的时候调用被包括的标志。下面是一个jsp:include使用的例子:Goingtoincludehello.jsp...
现在将上面代码中的“jsp:include”改为“jsp:forward”看看它们有什么区别。其实这两个预先定义的标志是很有用的。\n再给出本节的练习吧,编写一个JSP,使之根据一个布尔型(boolean)变量来对一个包含的文件。这里还是提示一下吧,要用到HTML和scriptlets的混合编程来处理JSP标志。\nJSP由浅入深(9)——JSPSessions在经典的网站上,访问者可以浏览几个网页并执行一些交互的行为。如果你在编写这样的网站,利用每一个用户的一些数据是非常有用的。为了这个目的,我们可以使用JSP中的"sessions"。Session是一个跟用户相关的对象。当用户访问网站的时候,一些数据就被存放于session中,并在需要的时候从中取出数据。Session为不同的用户保存了不同了数据。以下的网页将用户的名字放置于session中,并可以在其它地方来显示它。首先我们要制作一个表单,然后将它命名为GetName.htmlWhat'syourname?

这个表单的目标是“SaveName.jsp”,它在session保存了用户的名字。<%Stringname=request.getParameter("username");session.setAttribute("theName",name);%>Continue\nSaveName.jsp在session保存了用户了名字,并连接到另外一个网页NextPage.jsp。NextPage.jsp显示了怎样取出被保存的名字:Hello,<%=session.getAttribute("theName")%>如果你打开两种不同的浏览器,或者从两台不同的机器上运行两个浏览器,你可以在一个浏览器中放置一个名字,而在另外一个浏览器中放置另外的名字,但是两个名字都将被跟踪。Session保持跟踪直到超时,这时它就会假设用户没有访问网站了,所以就取消了session。最后再布置一下本教程的联系:利用session,在上面的例子中增加一个用户的“age”属性。\nJSP由浅入深(10)——BeansandForms处理表单(Forms)是在网站交互的一个很普通的方法。JSP使表单处理更加简单。在JSP中处理表单的标准方法是定义一个“bean”。这个“bean”不是一个完全的Javabean。你只需在定义一个类使它有个区域,这个区域跟表单中的每一个区域相对应。这个类区域必须有“setters”来匹配表单区域的名字。举个例子,让我们修改一下前面教程的GetName.html并且收集Email地址和用户的年龄。具体代码修改如下:What'syourname?
What'syoure-mailaddress?
What'syourage?

为了收集数据,我们要定义一个Java类,使它有“username”、“email”、“age”的区域,并且我们要提供“setter”方法“setUsername”、“setEmail”和“setAge”。这个“setter”方法只是一个以“set”开始其后是区域名的一种方法。区域名字的第一个字母要用大写的。所以如果区域为“email”,它的“setter”方法就为“setEmail”。同样的,“Getter”方法也类似定义,它只不过是用“get”来代替“set”而已。并且要使得setters和getters必须为公共的(public)。例如:publicclassUserData{Stringusername;Stringemail;intage;\npublicvoidsetUsername(Stringvalue){username=value;}publicvoidsetEmail(Stringvalue){email=value;}publicvoidsetAge(intvalue){age=value;}publicStringgetUsername(){returnusername;}publicStringgetEmail(){returnemail;}publicintgetAge(){returnage;}}\nJSP由浅入深(11)——标记库JSP1.1提出了一种扩展的JSP标志的方法,这种方法命名为“标志库”。这些库允许另外的一些类似于jsp:include或者jsp:forward的标志,但是它们不是以jsp:为前缀的并且还附件一些特性。  为了介绍标志库,在本教程中我们使用Blazix标志库作为例子。这个标志库只能用在Blazix服务器中,这个服务器你可以免费下载。每一个标志库都有它自己的标志库特殊的文档。为了使用标志库,你可以使用"taglib"directive来指出标志库“description”处在什么地方。对于Blazix标志库而言,directive如下:<%@taglibprefix="blx"uri="/blx.tld"%>上面者句中“uri”就是指出了标志库描述所在的地方。标志库的前缀是不同的。这个指令的意思是说我们可以使用blx:来使用库中的标志。Blazix标志库提供了一个blx:getProperty标志。这个标志可以允许用户编辑表单数据。现在,在GetName.jsp文件中,我们增加一个jsp:useBean并且将表单放置在blx:getProperty中:<%@taglibprefix="blx"uri="/blx.tld"%>What'syourname?
What'syoure-mailaddress?
What'syourage?

\n从上面的代码中我们可以发现blx:getProperty不是以/>结尾的,而是用来结尾。这条语句将所有的表单输入区域输入到blx:getProperty中,这样它们就可以被标志库正确地修改。接着建立一个从GetName.jsp到NextPage.jsp的连接,你将发现bean的数据自动显示在输入区域中。所以现在用户可以编辑数据了。\nJSP由浅入深(12)——表单编辑比如来自Blazix服务器的标志库在你的系统环境中不能使用,那么不使用标志库来实现相似的功能呢?本教程就是为这个问题而提出的。这个问题是可以解决的,只是代码要长一点。主要地,你必须亲自编辑HTML标志,然后将它设置缺省值。从下面的例子的例子中我们可以学到怎样修改GetName.jsp以提供类似于blx:getProperty的特性,但是我们只能手工地编辑HTML标志:What'syourname?">
What'syoure-mailaddress?">
What'syourage?>

如上面的代码所示,它只简单地在INPUT标志上增加一个“VALUE”区域,并且用一个表达是初始化这个区域。为了处理在输入过程中的异常错误,我们可以在bean中使用“String”区域并且将它转换为目标数据类型就可实现。所以这样也可以处理异常错误。\n作为课后练习,我们可以将前面教程中的例子改成不用Blazix标志库。至此,本“JSP由浅入深”系列教程到此全部结束,希望对你有帮助。\nJDBCTM指南:入门本简介是从《JDBCTMDatabaseAccessfromJavaTM:ATutorialandAnnotatedReference》这本书中摘引来的。JavaSoft目前正在准备这本书。这是一本教程,同时也是JDBC的重要参考手册,它将作为Java系列的组成部份在1997年春季由Addison-Wesley出版公司出版。1.1什么是JDBCTM?JDBCTM是一种用于执行SQL语句的JavaTMAPI(有意思的是,JDBC本身是个商标名而不是一个缩写字;然而,JDBC常被认为是代表“Java数据库连接(JavaDatabaseConnectivity)”)。它由一组用Java编程语言编写的类和接口组成。JDBC为工具/数据库开发人员提供了一个标准的API,使他们能够用纯JavaAPI来编写数据库应用程序。有了JDBC,向各种关系数据库发送SQL语句就是一件很容易的事。换言之,有了JDBCAPI,就不必为访问Sybase数据库专门写一个程序,为访问Oracle数据库又专门写一个程序,为访问Informix数据库又写另一个程序,等等。您只需用JDBCAPI写一个程序就够了,它可向相应数据库发送SQL语句。而且,使用Java编程语言编写的应用程序,就无须去忧虑要为不同的平台编写不同的应用程序。将Java和JDBC结合起来将使程序员只须写一遍程序就可让它在任何平台上运行。Java具有坚固、安全、易于使用、易于理解和可从网络上自动下载等特性,是编写数据库应用程序的杰出语言。所需要的只是Java应用程序与各种不同数据库之间进行对话的方法。而JDBC正是作为此种用途的机制。JDBC扩展了Java的功能。例如,用Java和JDBCAPI可以发布含有applet的网页,而该applet使用的信息可能来自远程数据库。企业也可以用JDBC通过Intranet将所有职员连到一个或多个内部数据库中(即使这些职员所用的计算机有Windows、Macintosh和UNIX等各种不同的操作系统)。随着越来越多的程序员开始使用Java编程语言,对从Java中便捷地访问数据库的要求也在日益增加。MIS管理员们都喜欢Java和JDBC的结合,因为它使信息传播变得容易和经济。企业可继续使用它们安装好的数据库,并能便捷地存取信息,即使这些信息是储存在不同数据库管理系统上。新程序的开发期很短。安装和版本控制将大为简化。程序员可只编写一遍应用程序或只更新一次,然后将它放到服务器上,随后任何人就都可得到最新版本的应用程序。对于商务上的销售信息服务,Java和JDBC可为外部客户提供获取信息更新的更好方法。\n1.1.1JDBC的用途是什么?简单地说,JDBC可做三件事:与数据库建立连接,发送SQL语句,处理结果。下列代码段给出了以上三步的基本示例:Connectioncon=DriverManager.getConnection("jdbc:odbc:wombat","login","password");Statementstmt=con.createStatement();ResultSetrs=stmt.executeQuery("SELECTa,b,cFROMTable1");while(rs.next()){intx=rs.getInt("a");Strings=rs.getString("b");floatf=rs.getFloat("c");}1.1.2JDBC是一种低级API,是高级API的基础JDBC是个“低级”接口,也就是说,它用于直接调用SQL命令。在这方面它的功能极佳,并比其它的数据库连接API易于使用,但它同时也被设计为一种基础接口,在它之上可以建立高级接口和工具。高级接口是“对用户友好的”接口,它使用的是一种更易理解和更为方便的API,这种API在幕后被转换为诸如JDBC这样的低级接口。在编写本文时,正在开发两种基于JDBC的高级API:一种用于Java的嵌入式SQL。至少已经有一个提供者计划编写它。DBMS实现SQL:一种专门设计来与数据库联合使用的语言。JDBC要求SQL语句必须作为String传给Java方法。相反,嵌入式SQL预处理器允许程序员将SQL语句直接与Java混在一起使用。例如,可在SQL语句中使用Java变量,用以接受或提供SQL值。然后,嵌入式SQL预处理器将通过JDBC调用把这种Java/SQL的混合物转换为Java。关系数据库表到Java类的直接映射。JavaSoft和其它提供者都声称要实现该API。在这种“对象/关系”映射中,表中的每行对应于类的一个实例,而每列的值对应于该实例的一个属性。于是,程序员可直接对Java对象进行操作;存取数据所需的SQL调用将在“掩盖下”自动生成。此外还可提供更复杂的映射,例如将多个表中的行结合进一个Java类中。随着人们对JDBC的兴趣日益增涨,越来越多的开发人员一直在使用基于JDBC\n的工具,以使程序的编写更加容易。程序员也一直在编写力图使最终用户对数据库的访问变得更为简单的应用程序。例如,应用程序可提供一个选择数据库任务的菜单。任务被选定后,应用程序将给出提示及空白供填写执行选定任务所需的信息。所需信息输入后,应用程序将自动调用所需的SQL命令。在这样一种程序的协助下,即使用户根本不懂SQL的语法,也可以执行数据库任务。1.1.3JDBC与ODBC和其它API的比较目前,Microsoft的ODBC(开放式数据库连接)API可能是使用最广的、用于访问关系数据库的编程接口。它能在几乎所有平台上连接几乎所有的数据库。为什么Java不使用ODBC?对这个问题的回答是:Java可以使用ODBC,但最好是在JDBC的帮助下以JDBC-ODBC桥的形式使用,这一点我们稍后再说。现在的问题已变成:“为什么需要JDBC”?回答如下:ODBC不适合直接在Java中使用,因为它使用C语言接口。从Java调用本地C代码在安全性、实现、坚固性和程序的自动移植性方面都有许多缺点。从ODBCCAPI到JavaAPI的字面翻译是不可取的。例如,Java没有指针,而ODBC却对指针用得很广泛(包括很容易出错的指针"void*")。您可以将JDBC想象成被转换为面向对象接口的ODBC,而面向对象的接口对Java程序员来说较易于接收。ODBC很难学。它把简单和高级功能混在一起,而且即使对于简单的查询,其选项也极为复杂。相反,JDBC尽量保证简单功能的简便性,而同时在必要时允许使用高级功能。启用“纯Java”机制需要象JDBC这样的JavaAPI。如果使用ODBC,就必须手动地将ODBC驱动程序管理器和驱动程序安装在每台客户机上。如果完全用Java编写JDBC驱动程序则JDBC代码在所有Java平台上(从网络计算机到大型机)都可以自动安装、移植并保证安全性。总之,JDBCAPI对于基本的SQL抽象和概念是一种自然的Java接口。它建立在ODBC上而不是从零开始。因此,熟悉ODBC的程序员将发现JDBC很容易使用。JDBC保留了ODBC的基本设计特征;事实上,两种接口都基于X/OpenSQLCLI(调用级接口)。它们之间最大的区别在于:JDBC以Java风格与优点为基础并进行优化,因此更加易于使用。最近,Microsoft又引进了ODBC之外的新API:RDO、ADO和OLEDB。这些设计在许多方面与JDBC是相同的,即它们都是面向对象的数据库接口且基于可在ODBC上实现的类。但在这些接口中,我们未看见有特别的功能使我们要转而选择它们来替代ODBC,尤其是在ODBC\n驱动程序已建立起较为完善的市场的情况下。它们最多也就是在ODBC上加了一种装饰而已。这并不是说JDBC不需要从其最初的版本再发展了;然而,我们觉得大部份的新功能应归入诸如前一节中所述的对象/关系映射和嵌入式SQL这样的高级API。1.1.4两层模型和三层模型JDBCAPI既支持数据库访问的两层模型,同时也支持三层模型。在两层模型中,Javaapplet或应用程序将直接与数据库进行对话。这将需要一个JDBC驱动程序来与所访问的特定数据库管理系统进行通讯。用户的SQL语句被送往数据库中,而其结果将被送回给用户。数据库可以位于另一台计算机上,用户通过网络连接到上面。这就叫做客户机/服务器配置,其中用户的计算机为客户机,提供数据库的计算机为服务器。网络可以是Intranet(它可将公司职员连接起来),也可以是Internet。在三层模型中,命令先是被发送到服务的“中间层”,然后由它将SQL语句发送给数据库。数据库对SQL语句进行处理并将结果送回到中间层,中间层再将结果送回给用户。MIS主管们都发现三层模型很吸引人,因为可用中间层来控制对公司数据的访问和可作的的更新的种类。中间层的另一个好处是,用户可以利用易于使用的高级API,而中间层将把它转换为相应的低级调用。最后,许多情况下三层结构可提供一些性能上的好处。到目前为止,中间层通常都用C或C++这类语言来编写,这些语言执行速度较快。然而,随着最优化编译器(它把Java字节代码转换为高效的特定于机器的代码)的引入,用Java来实现中间层将变得越来越实际。这将是一个很大的进步,它使人们可以充分利用Java的诸多优点(如坚固、多线程和安全等特征)。JDBC对于从Java的中间层来访问数据库非常重要。1.1.5SQL的一致性结构化查询语言(SQL)是访问关系数据库的标准语言。困难之处在于:虽然大多数的DBMS(数据库管理系统)对其基本功能都使用了标准形式的SQL,但它们却不符合最近为更高级的功能定义的标准SQL语法或语义。例如,并非所有的数据库都支持储存程序或外部连接,那些支持这一功能的数据库又相互不一致。人们希望SQL中真正标准的那部份能够进行扩展以包括越来越多的功能。但同时JDBCAPI又必须支持现有的SQL。\nJDBCAPI解决这个问题的一种方法是允许将任何查询字符串一直传到所涉及的DBMS驱动程序上。这意味着应用程序可以使用任意多的SQL功能,但它必须冒这样的风险:有可能在某些DBMS上出错。事实上,应用程序查询甚至不一定要是SQL,或者说它可以是个为特定的DBMS设计的SQL的专用派生物(例如,文档或图象查询)。JDBC处理SQL一致性问题的第二种方法是提供ODBC风格的转义子句。这将在4.1.5节“语句对象中的SQL转义语法”中讨论。转义语法为几个常见的SQL分歧提供了一种标准的JDBC语法。例如,对日期文字和已储存过程的调用都有转义语法。对于复杂的应用程序,JDBC用第三种方法来处理SQL的一致性问题。它利用DatabaseMetaData接口来提供关于DBMS的描述性信息,从而使应用程序能适应每个DBMS的要求和功能。由于JDBCAPI将用作开发高级数据库访问工具和API的基础API,因此它还必须注意其所有上层建筑的一致性。“符合JDBC标准TM"代表用户可依赖的JDBC功能的标准级别。要使用这一说明,驱动程序至少必须支持ANSISQL-2EntryLevel(ANSISQL-2代表美国国家标准局1992年所采用的标准。EntryLevel代表SQL功能的特定清单)。驱动程序开发人员可用JDBCAPI所带的测试工具包来确定他们的驱动程序是否符合这些标准。“符合JDBC标准TM”表示提供者的JDBC实现已经通过了JavaSoft提供的一致性测试。这些一致性测试将检查JDBCAPI中定义的所有类和方法是否都存在,并尽可能地检查程序是否具有SQLEntryLevel功能。当然,这些测试并不完全,而且JavaSoft目前也无意对各提供者的实现进行标级。但这种一致性定义的确可对JDBC实现提供一定的可信度。随着越来越多的数据库提供者、连接提供者、Internet提供者和应用程序编程员对JDBCAPI的接受,JDBC也正迅速成为Java数据库访问的标准。1.2JDBC产品在编写本文时,有几个基于JDBC的产品已开发完毕或正在开发中。当然,本节中的信息将很快成为过时信息。因此,有关最新的信息,请查阅JDBC的网站,可通过从以下URL开始浏览找到:http://java.sun.com/products/jdbc1.2.1JavaSoft框架JavaSoft提供三种JDBC产品组件,它们是Java开发工具包(JDK)的组成部份:\nJDBC驱动程序管理器,JDBC驱动程序测试工具包,和JDBC-ODBC桥。JDBC驱动程序管理器是JDBC体系结构的支柱。它实际上很小,也很简单;其主要作用是把Java应用程序连接到正确的JDBC驱动程序上,然后即退出。JDBC驱动程序测试工具包为使JDBC驱动程序运行您的程序提供一定的可信度。只有通过JDBC驱动程序测试包的驱动程序才被认为是符合JDBC标准TM的。JDBC-ODBC桥使ODBC驱动程序可被用作JDBC驱动程序。它的实现为JDBC的快速发展提供了一条途径,其长远目标提供一种访问某些不常见的DBMS(如果对这些不常见的DBMS未实现JDBC)的方法。1.2.2JDBC驱动程序的类型我们目前所知晓的JDBC驱动程序可分为以下四个种类:JDBC-ODBC桥加ODBC驱动程序:JavaSoft桥产品利用ODBC驱动程序提供JDBC访问。注意,必须将ODBC二进制代码(许多情况下还包括数据库客户机代码)加载到使用该驱动程序的每个客户机上。因此,这种类型的驱动程序最适合于企业网(这种网络上客户机的安装不是主要问题),或者是用Java编写的三层结构的应用程序服务器代码。本地API-部份用Java来编写的驱动程序:这种类型的驱动程序把客户机API上的JDBC调用转换为Oracle、Sybase、Informix、DB2或其它DBMS的调用。注意,象桥驱动程序一样,这种类型的驱动程序要求将某些二进制代码加载到每台客户机上。JDBC网络纯Java驱动程序:这种驱动程序将JDBC转换为与DBMS无关的网络协议,之后这种协议又被某个服务器转换为一种DBMS协议。这种网络服务器中间件能够将它的纯Java客户机连接到多种不同的数据库上。所用的具体协议取决于提供者。通常,这是最为灵活的JDBC驱动程序。有可能所有这种解决方案的提供者都提供适合于Intranet用的产品。为了使这些产品也支持Internet访问,它们必须处理Web所提出的安全性、通过防火墙的访问等方面的额外要求。几家提供者正将JDBC\n驱动程序加到他们现有的数据库中间件产品中。本地协议纯Java驱动程序:这种类型的驱动程序将JDBC调用直接转换为DBMS所使用的网络协议。这将允许从客户机机器上直接调用DBMS服务器,是Intranet访问的一个很实用的解决方法。由于许多这样的协议都是专用的,因此数据库提供者自己将是主要来源,有几家提供者已在着手做这件事了。最后,我们预计第3、4类驱动程序将成为从JDBC访问数据库的首选方法。第1、2类驱动程序在直接的纯Java驱动程序还没有上市前将会作为过渡方案来使用。对第1、2类驱动程序可能会有一些变种(下表中未列出),这些变种要求有连接器,但通常这些是更加不可取的解决方案。第3、4类驱动程序提供了Java的所有优点,包括自动安装(例如,通过使用JDBC驱动程序的appletapplet来下载该驱动程序)。下表显示了这4种类型的驱动程序及其属性:驱动程序种类纯JAVA?网络协议1-JDBC-OCBC桥非直接2-基于本地API的非直接3-JDBC网络的是要求连接器4-基于本地协议的是直接1.2.3JDBC驱动程序的获取在编写本文时,已有几十个属于种类的驱动程序,即可与Javasoft桥联合使用的1:ODBC驱动程序的驱动程序。有大约十多个属于种类2的驱动程序是以DBMS的本地API为基础编写的。只有几个属于种类3的驱动程序。目前至少有2个属于种类4的驱动程序,但到1997年底,我们预计主要的DBMS都会有种类4的驱动程序。要获取关于驱动程序的最新信息,请查阅JDBC的网站,其网址为:http://java.sun.com/products/jdbc。提供第3种驱动程序的首批提供者是SCO、OpenHorizon、Visigenic和WebLogic。JavaSoft和数据库连接的领先提供者Intersolv合作研制了JDBC-ODBC桥和JDBC驱动程序测试工具包。1.2.4其它产品各种JDBC应用程序的开发工具正在开发中。请注意查阅JavaSoft网页以得到更新信息。\nJDBCTM指南:入门2-连接内容:2-连接本概述是从《JDBCTMDatabaseAccessfromJavaTM:ATutorialandAnnotatedReference》这本书中摘引来的。JavaSoft目前正在准备这本书。这本书是一本教程,同时也是JDBC的重要参考手册,它将作为Java系列的组成部份在1997年春季由Addison-Wesley出版公司出版。2.1概述Connection对象代表与数据库的连接。连接过程包括所执行的SQL语句和在该连接上所返回的结果。一个应用程序可与单个数据库有一个或多个连接,或者可与许多数据库有连接。2.1.1打开连接与数据库建立连接的标准方法是调用DriverManager.getConnection方法。该方法接受含有某个URL的字符串。DriverManager类(即所谓的JDBC管理层)将尝试找到可与那个URL所代表的数据库进行连接的驱动程序。DriverManager类存有已注册的Driver类的清单。当调用方法getConnection时,它将检查清单中的每个驱动程序,直到找到可与URL中指定的数据库进行连接的驱动程序为止。Driver的方法connect使用这个URL来建立实际的连接。用户可绕过JDBC管理层直接调用Driver方法。这在以下特殊情况下将很有用:当两个驱动器可同时连接到数据库中,而用户需要明确地选用其中特定的驱动器。但一般情况下,让DriverManager类处理打开连接这种事将更为简单。下述代码显示如何打开一个与位于URL"jdbc:odbc:wombat"的数据库的连接。所用的用户标识符为"oboy",口令为"12Java":Stringurl="jdbc:odbc:wombat";Connectioncon=DriverManager.getConnection(url,"oboy","12Java");2.1.2一般用法的URL由于URL常引起混淆,我们将先对一般URL作简单说明,然后再讨论JDBCURL。URL(统一资源定位符)提供在Internet上定位资源所需的信息。可将它想象为一个地址。URL的第一部份指定了访问信息所用的协议,后面总是跟着冒号。常用的协议有"ftp"(代表“文件传输协议”)和"http"(代表“超文本传输协议”)。如果协议是"file",表示资源是在某个本地文件系统上而非在Internet上(下例用于表示我们所描述的部分;它并非URL的组成部分)。ftp://javasoft.com/docs/JDK-1_apidocs.ziphttp://java.sun.com/products/jdk/CurrentReleasefile:/home/haroldw/docs/books/tutorial/summary.htmlURL的其余部份(冒号后面的)给出了数据资源所处位置的有关信息。如果协议是file,则URL的其余部份是文件的路径。对于ftp和http协议,URL的其余部份标识了主机并可选地给出某个更详尽的地址路径。例如,以下是JavaSoft主页的URL。该URL只标识了主机:http://java.sun.com从该主页开始浏览,就可以进到许多其它的网页中,其中之一就是JDBC主页。JDBC主页的URL更为具体,它看起来类似:http://java.sun.com/products/jdbc2.1.3JDBCURLJDBCURL\n提供了一种标识数据库的方法,可以使相应的驱动程序能识别该数据库并与之建立连接。实际上,驱动程序编程员将决定用什么JDBCURL来标识特定的驱动程序。用户不必关心如何来形成JDBCURL;他们只须使用与所用的驱动程序一起提供的URL即可。JDBC的作用是提供某些约定,驱动程序编程员在构造他们的JDBCURL时应该遵循这些约定。由于JDBCURL要与各种不同的驱动程序一起使用,因此这些约定应非常灵活。首先,它们应允许不同的驱动程序使用不同的方案来命名数据库。例如,odbc子协议允许(但并不是要求)URL含有属性值。第二,JDBCURL应允许驱动程序编程员将一切所需的信息编入其中。这样就可以让要与给定数据库对话的applet打开数据库连接,而无须要求用户去做任何系统管理工作。第三,JDBCURL应允许某种程度的间接性。也就是说,JDBCURL可指向逻辑主机或数据库名,而这种逻辑主机或数据库名将由网络命名系统动态地转换为实际的名称。这可以使系统管理员不必将特定主机声明为JDBC名称的一部份。网络命名服务(例如DNS、NIS和DCE)有多种,而对于使用哪种命名服务并无限制。JDBCURL的标准语法如下所示。它由三部分组成,各部分间用冒号分隔:jdbc:<子协议>:<子名称>JDBCURL的三个部分可分解如下:jdbc─协议。JDBCURL中的协议总是jdbc。<子协议>─驱动程序名或数据库连接机制(这种机制可由一个或多个驱动程序支持)的名称。子协议名的典型示例是"odbc",该名称是为用于指定ODBC风格的数据资源名称的URL专门保留的。例如,为了通过JDBC-ODBC桥来访问某个数据库,可以用如下所示的URL:jdbc:odbc:fred本例中,子协议为"odbc",子名称"fred"是本地ODBC数据资源。如果要用网络命名服务(这样JDBCURL中的数据库名称不必是实际名称),则命名服务可以作为子协议。例如,可用如下所示的URL:jdbc:dcenaming:accounts-payable本例中,该URL指定了本地DCE命名服务应该将数据库名称"accounts-payable"解析为更为具体的可用于连接真实数据库的名称。<子名称>─一种标识数据库的方法。子名称可以依不同的子协议而变化。它还可以有子名称的子名称(含有驱动程序编程员所选的任何内部语法)。使用子名称的目的是为定位数据库提供足够的信息。前例中,因为ODBC将提供其余部份的信息,因此用"fred"就已足够。然而,位于远程服务器上的数据库需要更多的信息。例如,如果数据库是通过Internet来访问的,则在JDBCURL中应将网络地址作为子名称的一部份包括进去,且必须遵循如下所示的标准URL命名约定://主机名:端口/子协议假设"dbnet"是个用于将某个主机连接到Internet上的协议,则JDBCURL类似:jdbc:dbnet://wombat:356/fred2.1.4"odbc"子协议子协议odbc是一种特殊情况。它是为用于指定ODBC风格的数据资源名称的URL而保留的,并具有下列特性:允许在子名称(数据资源名称)后面指定任意多个属性值。odbc子协议的完整语法为:jdbc:odbc:<数据资源名称>[;<属性名>=<属性值>]*因此,以下都是合法的jdbc:odbc名称:jdbc:odbc:qeor7jdbc:odbc:wombatjdbc:odbc:wombat;CacheSize=20;ExtensionCase=LOWER\njdbc:odbc:qeora;UID=kgh;PWD=fooey2.1.5注册子协议驱动程序编程员可保留某个名称以将之用作JDBCURL的子协议名。当DriverManager类将此名称加到已注册的驱动程序清单中时,为之保留该名称的驱动程序应能识别该名称并与它所标识的数据库建立连接。例如,odbc是为JDBC-ODBC桥而保留的。示例之二,假设有个Miracle公司,它可能会将"miracle"注册为连接到其MiracleDBMS上的JDBC驱动程序的子协议,从而使其他人都无法使用这个名称。JavaSoft目前作为非正式代理负责注册JDBC子协议名称。要注册某个子协议名称,请发送电子邮件到下述地址:jdbc@wombat.eng.sun.com2.1.6发送SQL语句连接一旦建立,就可用来向它所涉及的数据库传送SQL语句。JDBC对可被发送的SQL语句类型不加任何限制。这就提供了很大的灵活性,即允许使用特定的数据库语句或甚至于非SQL语句。然而,它要求用户自己负责确保所涉及的数据库可以处理所发送的SQL语句,否则将自食其果。例如,如果某个应用程序试图向不支持储存程序的DBMS发送储存程序调用,就会失败并将抛出异常。JDBC要求驱动程序应至少能提供ANSISQL-2EntryLevel功能才可算是符合JDBC标准TM的。这意味着用户至少可信赖这一标准级别的功能。JDBC提供了三个类,用于向数据库发送SQL语句。Connection接口中的三个方法可用于创建这些类的实例。下面列出这些类及其创建方法:Statement─由方法createStatement所创建。Statement对象用于发送简单的SQL语句。PreparedStatement─由方法prepareStatement所创建。PreparedStatement对象用于发送带有一个或多个输入参数(IN参数)的SQL语句。PreparedStatement拥有一组方法,用于设置IN参数的值。执行语句时,这些IN参数将被送到数据库中。PreparedStatement的实例扩展了Statement,因此它们都包括了Statement的方法。PreparedStatement对象有可能比Statement对象的效率更高,因为它已被预编译过并存放在那以供将来使用。CallableStatement─由方法prepareCall所创建。CallableStatement对象用于执行SQL储存程序─一组可通过名称来调用(就象函数的调用那样)的SQL语句。CallableStatement对象从PreparedStatement中继承了用于处理IN参数的方法,而且还增加了用于处理OUT参数和INOUT参数的方法。以下所列提供的方法可以快速决定应用哪个Connection方法来创建不同类型的SQL语句:createStatement方法用于:简单的SQL语句(不带参数)prepareStatement方法用于:带一个或多个IN参数的SQL语句经常被执行的简单SQL语句prepareCall方法用于:调用已储存过程2.1.7事务事务由一个或多个这样的语句组成:这些语句已被执行、完成并被提交或还原。当调用方法commit或rollback时,当前事务即告就结束,另一个事务随即开始。缺省情况下,新连接将处于自动提交模式。也就是说,当执行完语句后,将自动对那个语句调用commit方法。这种情况下,由于每个语句都是被单独提交的,因此一个事务只由一个语句组成。如果禁用自动提交模式,事务将要等到commit或llback方法被显式调用时\n才结束,因此它将包括上一次调用commit或rollback方法以来所有执行过的语句。对于第二种情况,事务中的所有语句将作为组来提交或还原。方法commit使SQL语句对数据库所做的任何更改成为永久性的,它还将释放事务持有的全部锁。而方法rollback将弃去那些更改。有时用户在另一个更改生效前不想让此更改生效。这可通过禁用自动提交并将两个更新组合在一个事务中来达到。如果两个更新都是成功,则调用commit方法,从而使两个更新结果成为永久性的;如果其中之一或两个更新都失败了,则调用rollback方法,以将值恢复为进行更新之前的值。大多数JDBC驱动程序都支持事务。事实上,符合JDBC的驱动程序必须支持事务。DatabaseMetaData给出的信息描述DBMS所提供的事务支持水平。2.1.8事务隔离级别如果DBMS支持事务处理,它必须有某种途径来管理两个事务同时对一个数据库进行操作时可能发生的冲突。用户可指定事务隔离级别,以指明DBMS应该花多大精力来解决潜在冲突。例如,当事务更改了某个值而第二个事务却在该更改被提交或还原前读取该值时该怎么办假设第一个事务被还原后,第二个事务所读取的更改值将是无效的,那么是否可允许这种冲突?JDBC用户可用以下代码来指示DBMS允许在值被提交前读取该值(“dirty读取”),其中con是当前连接:con.setTransactionIsolation(TRANSACTION_READ_UNCOMMITTED);事务隔离级别越高,为避免冲突所花的精力也就越多。Connection接口定义了五级,其中最低级别指定了根本就不支持事务,而最高级别则指定当事务在对某个数据库进行操作时,任何其它事务不得对那个事务正在读取的数据进行任何更改。通常,隔离级别越高,应用程序执行的速度也就越慢(由于用于锁定的资源耗费增加了,而用户间的并发操作减少了)。在决定采用什么隔离级别时,开发人员必须在性能需求和数据一致性需求之间进行权衡。当然,实际所能支持的级别取决于所涉及的DBMS的功能。当创建Connection对象时,其事务隔离级别取决于驱动程序,但通常是所涉及的数据库的缺省值。用户可通过调用setIsolationLevel方法来更改事务隔离级别。新的级别将在该连接过程的剩余时间内生效。要想只改变一个事务的事务隔离级别,必须在该事务开始前进行设置,并在该事务结束后进行复位。我们不提倡在事务的中途对事务隔离级别进行更改,因为这将立即触发commit方法的调用,使在此之前所作的任何更改变成永久性的。\nJDBCTM指南:入门3–DriverManager内容:3-DriverManager3.1概述DriverManager类是JDBC的管理层,作用于用户和驱动程序之间。它跟踪可用的驱动程序,并在数据库和相应驱动程序之间建立连接。另外,DriverManager类也处理诸如驱动程序登录时间限制及登录和跟踪消息的显示等事务。对于简单的应用程序,一般程序员需要在此类中直接使用的唯一方法是DriverManager.getConnection。正如名称所示,该方法将建立与数据库的连接。JDBC允许用户调用DriverManager的方法getDriver、getDrivers和registerDriver及Driver的方法connect。但多数情况下,让DriverManager类管理建立连接的细节为上策。3.1.1跟踪可用驱动程序DriverManager类包含一列Driver类,它们已通过调用方法DriverManager.registerDriver对自己进行了注册。所有Driver类都必须包含有一个静态部分。它创建该类的实例,然后在加载该实例时DriverManager类进行注册。这样,用户正常情况下将不会直接调用DriverManager.registerDriver;而是在加载驱动程序时由驱动程序自动调用。加载Driver类,然后自动在DriverManager中注册的方式有两种:通过调用方法Class.forName。这将显式地加载驱动程序类。由于这与外部设置无关,因此推荐使用这种加载驱动程序的方法。以下代码加载类acme.db.Driver:Class.forName("acme.db.Driver");如果将acme.db.Driver编写为加载时创建实例,并调用以该实例为参数的DriverManager.registerDriver(本该如此),则它在DriverManager的驱动程序列表中,并可用于创建连接。通过将驱动程序添加到java.lang.System的属性jdbc.drivers中这是一个由DriverManager类加载的驱动程序类名的列表,由冒号分隔:初始化DriverManager类时,它搜索系统属性jdbc.drivers,如果用户已输入了一个或多个驱动程序,则DriverManager类将试图加载它们。以下代码说明程序员如何在~/.hotjava/properties中输入三个驱动程序类(启动时,HotJava将把它加载到系统属性列表中):\njdbc.drivers=foo.bah.Driver:wombat.sql.Driver:bad.test.ourDriver;对DriverManager方法的第一次调用将自动加载这些驱动程序类。注意:加载驱动程序的第二种方法需要持久的预设环境。如果对这一点不能保证,则调用方法Class.forName显式地加载每个驱动程序就显得更为安全。这也是引入特定驱动程序的方法,因为一旦DriverManager类被初始化,它将不再检查jdbc.drivers属性列表。在以上两种情况中,新加载的Driver类都要通过调用DriverManager.registerDriver类进行自我注册。如上所述,加载类时将自动执行这一过程。由于安全方面的原因,JDBC管理层将跟踪哪个类加载器提供哪个驱动程序。这样,当DriverManager类打开连接时,它仅使用本地文件系统或与发出连接请求的代码相同的类加载器提供的驱动程序。3.1.2建立连接加载Driver类并在DriverManager类中注册后,它们即可用来与数据库建立连接。当调用DriverManager.getConnection方法发出连接请求时,DriverManager将检查每个驱动程序,查看它是否可以建立连接。有时可能有多个JDBC驱动程序可以与给定的URL连接。例如,与给定远程数据库连接时,可以使用JDBC-ODBC桥驱动程序、JDBC到通用网络协议驱动程序或数据库厂商提供的驱动程序。在这种情况下测试驱动程序的顺序至关重要,因为DriverManager将使用它所找到的第一个可以成功连接到给定URL的驱动程序。首先DriverManager试图按注册的顺序使用每个驱动程序(jdbc.drivers中列出的驱动程序总是先注册)。它将跳过代码不可信任的驱动程序,除非加载它们的源与试图打开连接的代码的源相同。它通过轮流在每个驱动程序上调用方法Driver.connect,并向它们传递用户开始传递给方法DriverManager.getConnection的URL来对驱动程序进行测试,然后连接第一个认出该URL的驱动程序。这种方法初看起来效率不高,但由于不可能同时加载数十个驱动程序,因此每次连接实际只需几个过程调用和字符串比较。以下代码是通常情况下用驱动程序(例如JDBC-ODBC桥驱动程序)建立连接所需所有步骤的示例:\nClass.forName("sun.jdbc.odbc.JdbcOdbcDriver");//加载驱动程序Stringurl="jdbc:odbc:fred";DriverManager.getConnection(url,"userID","passwd");\nJDBCTM指南:入门4–Statement4-Statement本概述是从《JDBCTMDatabaseAccessfromJavaTM:ATutorialandAnnotatedReference》这本书中摘引来的。JavaSoft目前正在准备这本书。这是一本教程,同时也是JDBC的重要参考手册,它将作为Java系列的组成部份在1997年春季由Addison-Wesley出版公司出版。4.1概述Statement对象用于将SQL语句发送到数据库中。实际上有三种Statement对象,它们都作为在给定连接上执行SQL语句的包容器:Statement、PreparedStatement(它从Statement继承而来)和CallableStatement(它从PreparedStatement继承而来)。它们都专用于发送特定类型的SQL语句:Statement对象用于执行不带参数的简单SQL语句;PreparedStatement对象用于执行带或不带IN参数的预编译SQL语句;CallableStatement对象用于执行对数据库已存储过程的调用。Statement接口提供了执行语句和获取结果的基本方法。PreparedStatement接口添加了处理IN参数的方法;而CallableStatement添加了处理OUT参数的方法。4.1.1创建Statement对象建立了到特定数据库的连接之后,就可用该连接发送SQL语句。Statement对象用Connection的方法createStatement创建,如下列代码段中所示:Connectioncon=DriverManager.getConnection(url,"sunny","");Statementstmt=con.createStatement();为了执行Statement对象,被发送到数据库的SQL语句将被作为参数提供给Statement的方法:ResultSetrs=stmt.executeQuery("SELECTa,b,cFROMTable2");4.1.2使用Statement对象执行语句Statement接口提供了三种执行SQL语句的方法:executeQuery、executeUpdate和execute。使用哪一个方法由SQL语句所产生的内容决定。方法executeQuery用于产生单个结果集的语句,例如SELECT语句。方法executeUpdate用于执行INSERT、UPDATE或DELETE语句以及SQLDDL(数据定义语言)语句,例如CREATETABLE和DROPTABLE。INSERT、UPDATE或DELETE\n语句的效果是修改表中零行或多行中的一列或多列。executeUpdate的返回值是一个整数,指示受影响的行数(即更新计数)。对于CREATETABLE或DROPTABLE等不操作行的语句,executeUpdate的返回值总为零。方法execute用于执行返回多个结果集、多个更新计数或二者组合的语句。因为多数程序员不会需要该高级功能,所以本概述后面将在单独一节中对其进行介绍。执行语句的所有方法都将关闭所调用的Statement对象的当前打开结果集(如果存在)。这意味着在重新执行Statement对象之前,需要完成对当前ResultSet对象的处理。应注意,继承了Statement接口中所有方法的PreparedStatement接口都有自己的executeQuery、executeUpdate和execute方法。Statement对象本身不包含SQL语句,因而必须给Statement.execute方法提供SQL语句作为参数。PreparedStatement对象并不将SQL语句作为参数提供给这些方法,因为它们已经包含预编译SQL语句。CallableStatement对象继承这些方法的PreparedStatement形式。对于这些方法的PreparedStatement或CallableStatement版本,使用查询参数将抛出SQLException。4.1.3语句完成当连接处于自动提交模式时,其中所执行的语句在完成时将自动提交或还原。语句在已执行且所有结果返回时,即认为已完成。对于返回一个结果集的executeQuery方法,在检索完ResultSet对象的所有行时该语句完成。对于方法executeUpdate,当它执行时语句即完成。但在少数调用方法execute的情况中,在检索所有结果集或它生成的更新计数之后语句才完成。有些DBMS将已存储过程中的每条语句视为独立的语句;而另外一些则将整个过程视为一个复合语句。在启用自动提交时,这种差别就变得非常重要,因为它影响什么时候调用commit方法。在前一种情况中,每条语句单独提交;在后一种情况中,所有语句同时提交。4.1.4关闭Statement对象Statement对象将由Java垃圾收集程序自动关闭。而作为一种好的编程风格,应在不需要Statement对象时显式地关闭它们。这将立即释放DBMS资源,有助于避免潜在的内存问题。4.1.5Statement对象中的SQL转义语法Statement可包含使用SQL转义语法的SQL语句。转义语法告诉驱动程序其中的代码应该以不同方式处理。驱动程序将扫描任何转义语法,并将它转换成特定数据库可理解的代码。这使得转义语法与DBMS无关,并允许程序员使用在没有转义语法时不可用的功能。\n转义子句由花括号和关键字界定:{keyword...parameters...}该关键字指示转义子句的类型,如下所示。escape表示LIKE转义字符字符“%”和“_”类似于SQLLIKE子句中的通配符(“%”匹配零个或多个字符,而“_”则匹配一个字符)。为了正确解释它们,应在其前面加上反斜杠(“\”),它是字符串中的特殊转义字符。在查询末尾包括如下语法即可指定用作转义字符的字符:{escape'escape-character'}例如,下列查询使用反斜杠字符作为转义字符,查找以下划线开头的标识符名:stmt.executeQuery("SELECTnameFROMIdentifiersWHEREIdLIKE`\_%'{escape`\'};fn表示标量函数几乎所有DBMS都具有标量值的数值、字符串、时间、日期、系统和转换函数。要使用这些函数,可使用如下转义语法:关键字fn后跟所需的函数名及其参数。例如,下列代码调用函数concat将两个参数连接在一起:{fnconcat("Hot","Java")};可用下列语法获得当前数据库用户名:{fnuser()};标量函数可能由语法稍有不同的DBMS支持,而它们可能不被所有驱动程序支持。各种DatabaseMetaData方法将列出所支持的函数。例如,方法getNumericFunctions返回用逗号分隔的数值函数列表,而方法getStringFunctions将返回字符串函数,等等。\n驱动程序将转义函数调用映射为相应的语法,或直接实现该函数。d、t和ts表示日期和时间文字DBMS用于日期、时间和时间标记文字的语法各不相同。JDBC使用转义子句支持这些文字的语法的ISO标准格式。驱动程序必须将转义子句转换成DBMS表示。例如,可用下列语法在JDBCSQL语句中指定日期:{d`yyyy-mm-dd'}在该语法中,yyyy为年代,mm为月份,而dd则为日期。驱动程序将用等价的特定于DBMS的表示替换这个转义子句。例如,如果'28-FEB-99'符合基本数据库的格式,则驱动程序将用它替换{d1999-02-28}。对于TIME和TIMESTAMP也有类似的转义子句:{t`hh:mm:ss'}{ts`yyyy-mm-ddhh:mm:ss.f...'}TIMESTAMP中的小数点后的秒(.f...)部分可忽略。call或?=call表示已存储过程如果数据库支持已存储过程,则可从JDBC中调用它们,语法为:{callprocedure_name[(?,?,...)]}或(其中过程返回结果参数):{?=callprocedure_name[(?,?,...)]}方括号指示其中的内容是可选的。它们不是语法的必要部分。\n输入参数可以为文字或参数。有关详细信息,参见JDBC指南中第7节,“CallableStatement”。可通过调用方法DatabaseMetaData.supportsStoredProcedures检查数据库是否支持已存储过程。oj表示外部连接外部连接的语法为{ojouter-join}其中outer-join形式为tableLEFTOUTERJOIN{table/outer-join}ONsearch-condition外部连接属于高级功能。有关它们的解释可参见SQL语法。JDBC提供了三种DatabaseMetaData方法用于确定驱动程序支持哪些外部连接类型:supportsOuterJoins、supportsFullOuterJoins和supportsLimitedOuterJoins。方法Statement.setEscapeProcessing可打开或关闭转义处理;缺省状态为打开。当性能极为重要时,程序员可能想关闭它以减少处理时间。但通常它将出于打开状态。应注意:setEscapeProcessing不适用于PreparedStatement对象,因为在调用该语句前它就可能已被发送到数据库。有关预编译的信息,参见PreparedStatement。4.1.6使用方法executeexecute方法应该仅在语句能返回多个ResultSet对象、多个更新计数或ResultSet对象与更新计数的组合时使用。当执行某个已存储过程或动态执行未知SQL字符串(即应用程序程序员在编译时未知)时,有可能出现多个结果的情况,尽管这种情况很少见。例如,用户可能执行一个已存储过程(使用CallableStatement对象-参见第135页的CallableStatement),并且该已存储过程可执行更新,然后执行选择,再进行更新,再进行选择,等等。通常使用已存储过程的人应知道它所返回的内容。\n因为方法execute处理非常规情况,所以获取其结果需要一些特殊处理并不足为怪。例如,假定已知某个过程返回两个结果集,则在使用方法execute执行该过程后,必须调用方法getResultSet获得第一个结果集,然后调用适当的getXXX方法获取其中的值。要获得第二个结果集,需要先调用getMoreResults方法,然后再调用getResultSet方法。如果已知某个过程返回两个更新计数,则首先调用方法getUpdateCount,然后调用getMoreResults,并再次调用getUpdateCount。对于不知道返回内容,则情况更为复杂。如果结果是ResultSet对象,则方法execute返回true;如果结果是Javaint,则返回false。如果返回int,则意味着结果是更新计数或执行的语句是DDL命令。在调用方法execute之后要做的第一件事情是调用getResultSet或getUpdateCount。调用方法getResultSet可以获得两个或多个ResultSet对象中第一个对象;或调用方法getUpdateCount可以获得两个或多个更新计数中第一个更新计数的内容。当SQL语句的结果不是结果集时,则方法getResultSet将返回null。这可能意味着结果是一个更新计数或没有其它结果。在这种情况下,判断null真正含义的唯一方法是调用方法getUpdateCount,它将返回一个整数。这个整数为调用语句所影响的行数;如果为-1则表示结果是结果集或没有结果。如果方法getResultSet已返回null(表示结果不是ResultSet对象),则返回值-1表示没有其它结果。也就是说,当下列条件为真时表示没有结果(或没有其它结果):((stmt.getResultSet()==null)&&(stmt.getUpdateCount()==-1))如果已经调用方法getResultSet并处理了它返回的ResultSet对象,则有必要调用方法getMoreResults以确定是否有其它结果集或更新计数。如果getMoreResults返回true,则需要再次调用getResultSet来检索下一个结果集。如上所述,如果getResultSet返回null,则需要调用getUpdateCount来检查null是表示结果为更新计数还是表示没有其它结果。当getMoreResults返回false时,它表示该SQL语句返回一个更新计数或没有其它结果。因此需要调用方法getUpdateCount来检查它是哪一种情况。在这种情况下,当下列条件为真时表示没有其它结果:((stmt.getMoreResults()==false)&&(stmt.getUpdateCount()==-1))下面的代码演示了一种方法用来确认已访问调用方法execute所产生的全部结果集和更新计数:stmt.execute(queryStringWithUnknownResults);while(true){introwCount=stmt.getUpdateCount();if(rowCount>0){//它是更新计数\nSystem.out.println("Rowschanged="+count);stmt.getMoreResults();continue;}if(rowCount==0){//DDL命令或0个更新System.out.println("NorowschangedorstatementwasDDLcommand");stmt.getMoreResults();continue;}//执行到这里,证明有一个结果集//或没有其它结果ResultSetrs=stmt.getResultSet;if(rs!=null){...//使用元数据获得关于结果集列的信息while(rs.next()){...//处理结果stmt.getMoreResults();continue;}break;//没有其它结果\nJDBCTM指南:入门5–ResultSet5-ResultSet本概述是从《JDBCTMDatabaseAccessfromJavaTM:ATutorialandAnnotatedReference》这本书中摘引来的。JavaSoft目前正在准备这本书。这是一本教程,同时也是JDBC的重要参考手册,它将作为Java系列的组成部份在1997年春季由Addison-Wesley出版公司出版。5.1概述ResultSet包含符合SQL语句中条件的所有行,并且它通过一套get方法(这些get方法可以访问当前行中的不同列)提供了对这些行中数据的访问。ResultSet.next方法用于移动到ResultSet中的下一行,使下一行成为当前行。结果集一般是一个表,其中有查询所返回的列标题及相应的值。例如,如果查询为SELECTa,b,cFROMTable1,则结果集将具有如下形式:abc-------------------------12345CupertinoCA83472RedmondWA83492BostonMA下面的代码段是执行SQL语句的示例。该SQL语句将返回行集合,其中列1为int,列2为String,而列3则为字节数组:java.sql.Statementstmt=conn.createStatement();ResultSetr=stmt.executeQuery("SELECTa,b,cFROMTable1");while(r.next()){//打印当前行的值。inti=r.getInt("a");Strings=r.getString("b");floatf=r.getFloat("c");System.out.println("ROW="+i+""+s+""+f);}5.1.1行和光标ResultSet维护指向其当前数据行的光标。每调用一次next方法,光标向下移动一行。最初它位于第一行之前,因此第一次调用next将把光标置于第一行上,使它成为当前行。随着每次调用next\n导致光标向下移动一行,按照从上至下的次序获取ResultSet行。在ResultSet对象或其父辈Statement对象关闭之前,光标一直保持有效。在SQL中,结果表的光标是有名字的。如果数据库允许定位更新或定位删除,则需要将光标的名字作为参数提供给更新或删除命令。可通过调用方法getCursorName获得光标名。注意:不是所有的DBMS都支持定位更新和删除。可使用DatabaseMetaData.supportsPositionedDelete和supportsPositionedUpdate方法来检查特定连接是否支持这些操作。当支持这些操作时,DBMS/驱动程序必须确保适当锁定选定行,以使定位更新不会导致更新异常或其它并发问题。5.1.2列方法getXXX提供了获取当前行中某列值的途径。在每一行内,可按任何次序获取列值。但为了保证可移植性,应该从左至右获取列值,并且一次性地读取列值。列名或列号可用于标识要从中获取数据的列。例如,如果ResultSet对象rs的第二列名为“title”,并将值存储为字符串,则下列任一代码将获取存储在该列中的值:Strings=rs.getString("title");Strings=rs.getString(2);注意列是从左至右编号的,并且从列1开始。同时,用作getXXX方法的输入的列名不区分大小写。提供使用列名这个选项的目的是为了让在查询中指定列名的用户可使用相同的名字作为getXXX方法的参数。另一方面,如果select语句未指定列名(例如在“select*fromtable1”中或列是导出的时),则应该使用列号。这些情况下,用户将无法确切知道列名。有些情况下,SQL查询返回的结果集中可能有多个列具有相同的名字。如果列名用作getXXX方法的参数,则getXXX将返回第一个匹配列名的值。因而,如果多个列具有相同的名字,则需要使用列索引来确保检索了正确的列值。这时,使用列号效率要稍微高一些。关于ResultSet中列的信息,可通过调用方法ResultSet.getMetaData得到。返回的ResultSetMetaData对象将给出其ResultSet对象各列的编号、类型和属性。如果列名已知,但不知其索引,则可用方法findColumn得到其列号。\n5.1.3数据类型和转换对于getXXX方法,JDBC驱动程序试图将基本数据转换成指定Java类型,然后返回适合的Java值。例如,如果getXXX方法为getString,而基本数据库中数据类型为VARCHAR,则JDBC驱动程序将把VARCHAR转换成JavaString。getString的返回值将为JavaString对象。下表显示了允许用getXXX获取的JDBC类型及推荐用它获取的JDBC类型(通用SQL类型)。小写的x表示允许getXXX方法获取该数据类型;大写的X表示对该数据类型推荐使用getXXX方法。例如,除了getBytes和getBinaryStream之外的任何getXXX方法都可用来获取LONGVARCHAR值,但是推荐根据返回的数据类型使用getAsciiStream或getUnicodeStream方法。方法getObject将任何数据类型返回为JavaObject。当基本数据类型是特定于数据库的抽象类型或当通用应用程序需要接受任何数据类型时,它是非常有用的。可使用ResultSet.getXXX方法获取常见的JDBC数据类型。“x”表示该getXXX方法可合法地用于获取给定JDBC类型。“X”表示推荐使用该getXXX方法来获取给定JDBC类型。 TINYINTSMALLINTINTEGERBIG\nNTREALFLOATDOUBLEDECIMALNUMERICBITCHARVARCHARLONGVAR\nCHARBINARYVARBINARYLONGVARBINARYDATETIMETIMESTAMPgetByteXxxxxxxxxxxxx      \ngetShortxXxxxxxxxxxxx      getIntxxXxxxxxxxxxx      getLongxxxXxxxxxxxxx      getFloatxxxxXxxxxxxxx      getDoublexxxxxXXxxxxxx      getBigDecimalxxxxxxxXXxxxx      getBooleanxxxxxxxxxXxxx      getStringxxxxxxxxxxXXxxxxxxxgetBytes             XXx   getDate          xxx   X xgetTime          xxx    XxgetTimestamp          xxx   x XgetAsciiStream          xxXxxx   getUnicodeStream          xxXxxx   getBinaryStream             xxX   getObjectxxxxxxxxxxxxxxxxxxx5.1.4对非常大的行值使用流ResultSet可以获取任意大的LONGVARBINARY或LONGVARCHAR数据。方法getBytes和getString将数据返回为大的块(最大为Statement.getMaxFieldSize的返回值)。但是,以较小的固定块获取非常大的数据可能会更方便,而这可通过让ResultSet类返回java.io.Input流来完成。从该流中可分块读取数据。注意:必须立即访问这些流,因为在下一次对ResultSet调用getXXX时它们将自动关闭(这是由于基本实现对大块数据访问有限制)。JDBCAPI具有三个获取流的方法,分别具有不同的返回值:getBinaryStream返回只提供数据库原字节而不进行任何转换的流。getAsciiStream返回提供单字节ASCII字符的流。getUnicodeStream返回提供双字节Unicode字符的流。注意:它不同于Java流,后者返回无类型字节并可(例如)通用于ASCII和Unicode字符。下列代码演示了getAsciiStream的用法:\njava.sql.Statementstmt=con.createStatement();ResultSetr=stmt.executeQuery("SELECTxFROMTable2");//现在以4K块大小获取列1结果:bytebuff=newbyte[4096];while(r.next()){Java.io.InputStreamfin=r.getAsciiStream(1);for(;;){intsize=fin.read(buff);if(size==-1){//到达流末尾break;}//将新填充的缓冲区发送到ASCII输出流:output.write(buff,0,size);}}5.1.5NULL结果值要确定给定结果值是否是JDBCNULL,必须先读取该列,然后使用ResultSet.wasNull方法检查该次读取是否返回JDBCNULL。当使用ResultSet.getXXX方法读取JDBCNULL时,方法wasNull将返回下列值之一:Javanull值:对于返回Java对象的getXXX方法(例如getString、getBigDecimal、getBytes、getDate、getTime、getTimestamp、getAsciiStream、getUnicodeStream、getBinaryStream、getObject等)。零值:对于getByte、getShort、getInt、getLong、getFloat和getDouble。false值:对于getBoolean。5.1.6可选结果集或多结果集通常使用executeQuery(它返回单个ResultSet)或executeUpdate(它可用于任何数据库修改语句,并返回更新行数)可执行SQL语句。但有些情况下,应用程序在执行语句之前不知道该语句是否返回结果集。此外,有些已存储过程可能返回几个不同的结果集和/或更新计数。为了适应这些情况,JDBC提供了一种机制,允许应用程序执行语句,然后处理由结果集和更新计数组成的任意集合。这种机制的原理是首先调用一个完全通用的execute方法,然后调用另外三个方法,getResultSet、getUpdateCount和\ngetMoreResults。这些方法允许应用程序一次一个地研究语句结果,并确定给定结果是ResultSet还是更新计数。用户不必关闭ResultSet;当产生它的Statement关闭、重新执行或用于从多结果序列中获取下一个结果时,该ResultSet将被Statement自动关闭。\nJDBCTM指南:入门6-PreparedStatement6-PreparedStatement本概述是从《JDBCTMDatabaseAccessfromJavaTM:ATutorialandAnnotatedReference》这本书中摘引来的。JavaSoft目前正在准备这本书。这是一本教程,同时也是JDBC的重要参考手册,它将作为Java系列的组成部份在1997年春季由Addison-Wesley出版公司出版。6.1概述该PreparedStatement接口继承Statement,并与之在两方面有所不同:PreparedStatement实例包含已编译的SQL语句。这就是使语句“准备好”。包含于PreparedStatement对象中的SQL语句可具有一个或多个IN参数。IN参数的值在SQL语句创建时未被指定。相反的,该语句为每个IN参数保留一个问号(“?”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的setXXX方法来提供。由于PreparedStatement对象已预编译过,所以其执行速度要快于Statement对象。因此,多次执行的SQL语句经常创建为PreparedStatement对象,以提高效率。作为Statement的子类,PreparedStatement继承了Statement的所有功能。另外它还添加了一整套方法,用于设置发送给数据库以取代IN参数占位符的值。同时,三种方法execute、executeQuery和executeUpdate已被更改以使之不再需要参数。这些方法的Statement形式(接受SQL语句参数的形式)不应该用于PreparedStatement对象。6.1.1创建PreparedStatement对象以下的代码段(其中con是Connection对象)创建包含带两个IN参数占位符的SQL语句的PreparedStatement对象:PreparedStatementpstmt=con.prepareStatement("UPDATEtable4SETm=?WHEREx=?");pstmt对象包含语句"UPDATEtable4SETm=?WHEREx=?",它已发送给DBMS,并为执行作好了准备。6.1.2传递IN参数在执行PreparedStatement对象之前,必须设置每个?参数的值。这可通过调用setXXX方法来完成,其中XXX是与该参数相应的类型。例如,如果参数具有\nJava类型long,则使用的方法就是setLong。setXXX方法的第一个参数是要设置的参数的序数位置,第二个参数是设置给该参数的值。例如,以下代码将第一个参数设为123456789,第二个参数设为100000000:pstmt.setLong(1,123456789);pstmt.setLong(2,100000000);一旦设置了给定语句的参数值,就可用它多次执行该语句,直到调用clearParameters方法清除它为止。在连接的缺省模式下(启用自动提交),当语句完成时将自动提交或还原该语句。如果基本数据库和驱动程序在语句提交之后仍保持这些语句的打开状态,则同一个PreparedStatement可执行多次。如果这一点不成立,那么试图通过使用PreparedStatement对象代替Statement对象来提高性能是没有意义的。利用pstmt(前面创建的PreparedStatement对象),以下代码例示了如何设置两个参数占位符的值并执行pstmt10次。如上所述,为做到这一点,数据库不能关闭pstmt。在该示例中,第一个参数被设置为"Hi"并保持为常数。在for循环中,每次都将第二个参数设置为不同的值:从0开始,到9结束。pstmt.setString(1,"Hi");for(inti=0;i<10;i++){pstmt.setInt(2,i);introwCount=pstmt.executeUpdate();}6.1.3IN参数中数据类型的一致性setXXX方法中的XXX是Java类型。它是一种隐含的JDBC类型(一般SQL类型),因为驱动程序将把Java类型映射为相应的JDBC类型(遵循该JDBCGuide中§8.6.2“映射Java和JDBC类型”表中所指定的映射),并将该JDBC类型发送给数据库。例如,以下代码段将PreparedStatement对象pstmt的第二个参数设置为44,Java类型为short:pstmt.setShort(2,44);驱动程序将44作为JDBCSMALLINT发送给数据库,它是Javashort类型的标准映射。程序员的责任是确保将每个IN参数的Java类型映射为与数据库所需的JDBC数据类型兼容的JDBC类型。不妨考虑数据库需要JDBCSMALLINT的情况。如果使用方法setByte,则驱动程序将JDBCTINYINT发送给数据库。这是可行的,因为许多数据库可从一种相关的类型转换为另一种类型,并且通常TINYINT可用于SMALLINT\n适用的任何地方。然而,对于要适用于尽可能多的数据库的应用程序,最好使用与数据库所需的确切的JDBC类型相应的Java类型。如果所需的JDBC类型是SMALLINT,则使用setShort代替setByte将使应用程序的可移植性更好。6.1.4使用setObject程序员可使用setObject方法显式地将输入参数转换为特定的JDBC类型。该方法可以接受第三个参数,用来指定目标JDBC类型。将JavaObject发送给数据库之前,驱动程序将把它转换为指定的JDBC类型。如果没有指定JDBC类型,驱动程序就会将JavaObject映射到其缺省的JDBC类型(参见第8.6.4节中的表格),然后将它发送到数据库。这与常规的setXXX方法类似;在这两种情况下,驱动程序在将值发送到数据库之前,会将该值的Java类型映射为适当的JDBC类型。二者的差别在于setXXX方法使用从Java类型到JDBC类型的标准映射(参见第8.6.2节中的表格),而setObject方法使用从JavaObject类型到JDBC类型的映射(参见第8.6.4节中的表格)。方法setObject允许接受所有Java对象的能力使应用程序更为通用,并可在运行时接受参数的输入。这种情况下,应用程序在编译时并不清楚输入类型。通过使用setObject,应用程序可接受所有Java对象类型作为输入,并将其转换为数据库所需的JDBC类型。第8.6.5节中的表格显示了setObject可执行的所有可能转换。6.1.5将JDBCNULL作为IN参数发送setNull方法允许程序员将JDBCNULL值作为IN参数发送给数据库。但要注意,仍然必须指定参数的JDBC类型。当把Javanull值传递给setXXX方法时(如果它接受Java对象作为参数),也将同样把JDBCNULL发送到数据库。但仅当指定JDBC类型时,方法setObject才能接受null值。6.1.6发送大的IN参数setBytes和setString方法能够发送无限量的数据。但是,有时程序员更喜欢用较小的块传递大型的数据。这可通过将IN参数设置为Java输入流来完成。当语句执行时,JDBC驱动程序将重复调用该输入流,读取其内容并将它们当作实际参数数据传输。JDBC提供了三种将IN参数设置为输入流的方法:setBinaryStream用于含有未说明字节的流,setAsciiStream用于含有ASCII字符的流,而setUnicodeStream用于含有Unicode字符的流。因为必须指定流的总长度,所以这些方法所采用的参数比其它的setXXX方法要多一个。这很有必要,因为一些数据库在发送数据之前需要知道其总的传送大小。\n以下代码例示了使用流作为IN参数来发送文件内容:java.io.Filefile=newjava.io.File("/tmp/data");intfileLength=file.length();java.io.InputStreamfin=newjava.io.FileInputStream(file);java.sql.PreparedStatementpstmt=con.prepareStatement("UPDATETable5SETstuff=?WHEREindex=4");pstmt.setBinaryStream(1,fin,fileLength);pstmt.executeUpdate();当语句执行时,将反复调用输入流fin以传递其数据。\nJDBCTM指南:入门7-CallableStatement7-CallableStatement本概述是从《JDBCTMDatabaseAccessfromJavaTM:ATutorialandAnnotatedReference》这本书中摘引来的。JavaSoft目前正在准备这本书。这本书是一本教程,同时也是JDBC的重要参考手册,它将作为Java系列的组成部份在1997年春季由Addison-Wesley出版公司出版。7.1概述CallableStatement对象为所有的DBMS提供了一种以标准形式调用已储存过程的方法。已储存过程储存在数据库中。对已储存过程的调用是CallableStatement对象所含的内容。这种调用是用一种换码语法来写的,有两种形式:一种形式带结果参数,另一种形式不带结果参数(有关换码语法的信息,参见第4节“语句”)。结果参数是一种输出(OUT)参数,是已储存过程的返回值。两种形式都可带有数量可变的输入(IN参数)、输出(OUT参数)或输入和输出(INOUT参数)的参数。问号将用作参数的占位符。在JDBC中调用已储存过程的语法如下所示。注意,方括号表示其间的内容是可选项;方括号本身并不是语法的组成部份。{call过程名[(?,?,...)]}返回结果参数的过程的语法为:{?=call过程名[(?,?,...)]}不带参数的已储存过程的语法类似:{call过程名}通常,创建CallableStatement对象的人应当知道所用的DBMS是支持已储存过程的,并且知道这些过程都是些什么。然而,如果需要检查,多种DatabaseMetaData方法都可以提供这样的信息。例如,如果DBMS支持已储存过程的调用,则supportsStoredProcedures方法将返回true,而getProcedures方法将返回对已储存过程的描述。CallableStatement继承Statement的方法(它们用于处理一般的SQL语句),还继承了PreparedStatement的方法(它们用于处理IN参数)。CallableStatement中定义的所有方法都用于处理OUT参数或INOUT参数的输出部分:注册OUT参数的JDBC类型(一般SQL类型)、从这些参数中检索结果,或者检查所返回的值是否为JDBCNULL。\n7.1.1创建CallableStatement对象CallableStatement对象是用Connection方法prepareCall创建的。下例创建CallableStatement的实例,其中含有对已储存过程getTestData调用。该过程有两个变量,但不含结果参数:CallableStatementcstmt=con.prepareCall("{callgetTestData(?,?)}");其中?占位符为IN、OUT还是INOUT参数,取决于已储存过程getTestData。7.1.2IN和OUT参数将IN参数传给CallableStatement对象是通过setXXX方法完成的。该方法继承自PreparedStatement。所传入参数的类型决定了所用的setXXX方法(例如,用setFloat来传入float值等)。如果已储存过程返回OUT参数,则在执行CallableStatement对象以前必须先注册每个OUT参数的JDBC类型(这是必需的,因为某些DBMS要求JDBC类型)。注册JDBC类型是用registerOutParameter方法来完成的。语句执行完后,CallableStatement的getXXX方法将取回参数值。正确的getXXX方法是为各参数所注册的JDBC类型所对应的Java类型(从JDBC类型到Java类型的标准映射见8.6.1节中的表)。换言之,registerOutParameter使用的是JDBC类型(因此它与数据库返回的JDBC类型匹配),而getXXX将之转换为Java类型。作为示例,下述代码先注册OUT参数,执行由cstmt所调用的已储存过程,然后检索在OUT参数中返回的值。方法getByte从第一个OUT参数中取出一个Java字节,而getBigDecimal从第二个OUT参数中取出一个BigDecimal对象(小数点后面带三位数):CallableStatementcstmt=con.prepareCall("{callgetTestData(?,?)}");cstmt.registerOutParameter(1,java.sql.Types.TINYINT);cstmt.registerOutParameter(2,java.sql.Types.DECIMAL,3);cstmt.executeQuery();bytex=cstmt.getByte(1);java.math.BigDecimaln=cstmt.getBigDecimal(2,3);CallableStatement与ResultSet不同,它不提供用增量方式检索大OUT值的特殊机制。7.1.3INOUT参数\n既支持输入又接受输出的参数(INOUT参数)除了调用registerOutParameter方法外,还要求调用适当的setXXX方法(该方法是从PreparedStatement继承来的)。setXXX方法将参数值设置为输入参数,而registerOutParameter方法将它的JDBC类型注册为输出参数。setXXX方法提供一个Java值,而驱动程序先把这个值转换为JDBC值,然后将它送到数据库中。这种IN值的JDBC类型和提供给registerOutParameter方法的JDBC类型应该相同。然后,要检索输出值,就要用对应的getXXX方法。例如,Java类型为byte的参数应该使用方法setByte来赋输入值。应该给registerOutParameter提供类型为TINYINT的JDBC类型,同时应使用getByte来检索输出值(第8节“JDBC和Java类型之间的映射”将给出详细信息和类型映射表)。下例假设有一个已储存过程reviseTotal,其唯一参数是INOUT参数。方法setByte把此参数设为25,驱动程序将把它作为JDBCTINYINT类型送到数据库中。接着,registerOutParameter将该参数注册为JDBCTINYINT。执行完该已储存过程后,将返回一个新的JDBCTINYINT值。方法getByte将把这个新值作为Javabyte类型检索。CallableStatementcstmt=con.prepareCall("{callreviseTotal(?)}");cstmt.setByte(1,25);cstmt.registerOutParameter(1,java.sql.Types.TINYINT);cstmt.executeUpdate();bytex=cstmt.getByte(1);7.1.4先检索结果,再检索OUT参数由于某些DBMS的限制,为了实现最大的可移植性,建议先检索由执行CallableStatement对象所产生的结果,然后再用CallableStatement.getXXX方法来检索OUT参数。如果CallableStatement对象返回多个ResultSet对象(通过调用execute方法),在检索OUT参数前应先检索所有的结果。这种情况下,为确保对所有的结果都进行了访问,必须对Statement方法getResultSet、getUpdateCount和getMoreResults进行调用,直到不再有结果为止。检索完所有的结果后,就可用CallableStatement.getXXX方法来检索OUT参数中的值。7.1.5检索作为OUT参数的NULL值返回到OUT参数中的值可能会是JDBCNULL。当出现这种情形时,将对JDBCNULL值进行转换以使getXXX方法所返回的值为null、0或false,这取决于getXXX方法类型。对于ResultSet对象,要知道0或false是否源于JDBCNULL的唯一方法,是用方法wasNull进行检测。如果getXXX\n方法读取的最后一个值是JDBCNULL,则该方法返回true,否则返回flase。第5节“ResultSet”将给出详细信息。\nJSP开发入门(一)--安装好你的机器来使用JSP你将会需要Java2软件开发工具(JSDK),它原来的名称是Java发展工具(JDK)以及JavaServer网站发展工具(JSWDK),Tomcat,或是其它支持JSP的网络服务器。Sun免费提供JSDK与JSWDK来供Windows,Solaris,以及Linux平台使用。 如果你想要在你目前的网络服务器上使用JSP,但服务器本身并不支持JSP与Javaservlets,你可以试试看Allaire的Jrun,它的作用就像是针对Netscape企业版与FastTrack服务器、微软的网际网络信息服务器(IIS)与个人网络服务器(PWS)、Apache、以及其它服务器的网络服务器附加设备。你也可以使用Apache网络服务器的Java版本,最新的JSWDK里有提供。  下载与安装你需要的组件  目前发布的1.2.2-001,JSDK可下载的版本是以可安装的压缩形式。下载的文件大约是20MB,可提供完整的Java发展环境,让你能建立利用标准API为核心的Java解决之道。然而,你的网络服务器需要应用到JSP的唯一一件事是Java编译器。要让网络服务器知道编译器的位置,将环境变量JAVA.HOME设到JSDK的安装目录。如果你是在Windows上安装并且接受预设目录,将这行程序代码setJAVA.HOME=C:\1.2.2加到你的autoexec.bat档案并且重新开机。在安装好JSDK之后,下载并且安装JSWDK或beta版的Tomcat,以Java为主的Apache网络服务器。安装在哪里并不重要,重要的是你可以找到它。一般而言,它会放在上层目录,这种方式可以让你取代JSWDK或JSDK的网络服务器,不需要移动其它的网络服务器。在你安装好这个档案之后,你就可以准备发展JSP了。  在你正确的安装JSWDK之后,执行startserver指令文件来激活网络服务器,预设通讯端口为8080。要看你在激活服务器之后是均C有正确的安装工具,你可以加载范例JSP档案中的任何一个(http://localhost:8080/examples/jsp/)。如果你能够成功的执行一个范例档案,你可以知道你已经正确的设定好软件了。如果你在激活服务器的控制台窗口看到错误讯息,那么你需要解决这个问题。最常发生的问题是没有设定(或者不正确设定)环境变量JAVA.HOME。要检视目前的环境设定,在DOS模式下键入set。  开始  解释JSP语法之前,先建立一个显示目前日期与时间的快速网页并且将它储存成sample.jsp:\n      FirstPage      

Todayis:  <%=newjava.util.Date()%>  

    .将这个档案与你所有的HTML与JSP网页放在你JSWDK安装目录下的网页目录里.你可以在http://localhost:8080/sample.jsp下载此页.当你第一次参观这个网页时,网站服务器会将JSP翻译成Javaservlet程序代码,那你就会看到目前的日期与时间.  现在你已经下载,安装,并且架构好发展环境,你已经准备好要了解JSP语法与建立你自己的JSP为主的解决之道.(二)----JSP语法的基本原理安装之后,接下来我们要讨论JSP的语法.如果要偷懒,你可以下载语法卡而如果你不熟悉Java的程序设计,你可能会想要参考Sun的使用手册;然而,网站建立者不应该做太多的Java发展。除了几个函式呼叫之外,出现在你JSP网页上的Java程序代码应该将它减到最少;  记住这点之后,现在让我们先来看看JSP的编译器指引与指令组件,之后我们将解释JavaBeans与内部对象.JSP编译器指引与指令组件有五种型态.JSP1.0之后,大部分的JSP是包含在以<%作为开始%>作为结束的单一卷标里.新的JSP1.1规格已经发表了,它同时也与XML兼容.JSP的编译器指引与指令组件  编译器指示       <%@编译器指示%>  声明          <%!声明%>\n  表达式        <%=表达式%>  程序代码段/小型指令   <%程序代码片段%>  注释          <%--注释--%>  编译器指示  JSP的编译器指示是针对JSP引擎。它们并不会直接产生任何看得见的输出;相反的,它们是在告诉引擎如何处理其它的JSP网页。它们永远包含在<%@?%>卷标里。两个主要的指引是page与include。我们不会讨论taglib编译器指引但它可以在JSP1.1里用来建立自订卷标。  你几乎可以在你所有的JSP网页最上面找到page编译器指示。虽然这不是必须的,但它可以让你指定到哪里可以找到支持的Java类别这类的事:  <%@pageimport="java.util.Date"%>,   当发生Java问题的事件时应该将讯息传送到哪里:  <%@pageerrorPage="errorPage.jsp"%>,   以及你是?需要为使用者管理通话期的信息,可能存取多个网页(稍后在JavaBeans里会有更多通话期的讨论):  <%@pagesession="true"%>。  include编译器指示让你将你的内容分成几个可管理的组件,就像那些有表头或脚注的网页。所包含的网页可以是固定格式的HTML网页或者是JSP内容的网页:  <%@includefile="filename.jsp"%>。  宣告  JSP声明让你定义网页层的变量,来储存信息或定义支持的函式,让JSP网页的其余部分能够使用。如果你发现自己有太多的程序代码,你最好将它们放在不同的Java类别里。你可以在<%!?%>卷标里找到声明。记住要在变量声明的后面加上分号,就跟任何有效的Java叙述的形式一样:<%!inti=0;%>。\n  表达式  JSP里有表达式,评估表达式的结果可以转换成字符串并且直接使用在输出网页上。JSP运算是属于<%=?%>卷标里,并不包含分号,加引号字符串的无用部分。  <%=i%>  <%="Hello"%>。  程序代码段/小型指令文件  JSP程序代码片段或小型指令文件是包含在<%?%>卷标里。当网络服务器接受这段请求时,这段Java程序代码会执行。小型指令文件可以是原始的HTML或XML,其内部的程序代码片段可以让你建立有条件的执行程序代码,或者只是一些使用另一块程序代码的东西。举例来说,下列的程序代码结合了表达式与小型指令文件,在H1,H2,H3,以及H4卷标里显示字符串"Hello"。小型指令文件不限于一行的原始程序代码:  <%for(inti=1;i<=4;i++){%>  >Hello>  <%}%>。  注释  最后一个主要JSP组件是嵌入式注释。虽然你可以在你的档案里包含HTML注释,如果使用者检视网页的原始码,他们也会看到这些注释。如果你不要让使用者看到你的批注,你可以将它放在<%--?--%>卷标里:  <%--针对服务器端的注释--%>。(三)--JSP与JavaBean虽然你可以在小型指令文件里放入一大块的程序代码,但是大多数的Java程序代码是属于可以重复使用的组件,称为JavaBean。JavaBean就跟ActiveX控件一样:它们提供已知的功能,并且是为了可随时重复使用的目的而设计的。\n  JavaBean的价值在于它可以经由一组特性来使用,而这些特性则提供对JavaBean设定的存取。以人来作范例,此人就是JavaBean,而他的姓名,社会福利安全号码,以及住址可以是特性。对于JSP网站,基本上你是将'JavaBean'动态的连接到你的网站。  假设JavaBean是在建立网站之前建好的,你要做的第一件事是告诉JSP网页它所需要使用JavaBean.这工作可以用卷标来完成:.  卷标需要你以id属性来辨识豆子.在这里,你提供一个名称让JSP网页来辨识豆子,除了id属性之外,你也必须告诉网页要到哪里去找这个豆子,或者是它的Java类别名称。类别属性提供如何在各式方法之中找到它,最后一个需要的组件是scope属性.有了范围属性的帮助,你可以告诉豆子,要它为单一网页(预设)[scope="page"];为一个被请求的网页[scope="request"];为通话期[scope="session"];或为整个应用程序[scope="application"]来维护它自己的信息.对于通话期范围,你可以很容易的维护JSP网页里的项目,例如购物车。  一但你宣告了JavaBean之后,你就可以存取它的特性来订定它。要取得一特性的值,使用卷标。有了卷标,你可以指定要使用的豆子名称(从useBean的id字段),以及你要取得值的特性。接着,真正的值就会放在输出里:.  要更改JavaBean的特性,你需要使用卷标.对这个卷标,你也需要辨认豆子以及要修正的特性,除此之外,你还需要提供新值.如果命名正确,这些可以直接经由规定的格式取得:;   要从一参数取得,你必须直接命名此特性以及参数:;   或是直接以名称与值来设定:or/>.  有关JavaBean的最后一点:要让网络服务器可以找到JavaBean,你需要将它们的类别档案放在特别位置。对JSWDK而言,最简单的地方是在安装目录里的类别目录,例如\jswdk-1.0.1\classes.(四)--JSP的内部对象最后一个与JSP语法有关的组件叫做内部对象.在JSP小型指令文件内,你可以存取这些内部对象来与执行JSP网页的servlet环境相互作用。许多对内部对象的存取应该要简化。然而,这些是范例,它们的存取都是可接受的,要完整的利用内部对象设定则需要对最新的JavaServletAPI有所了解。\n  下表列出你可以使用的内部对象。  内部对象说明  request  客户端请求,此请求会包含来自GET/POST请求的参数  response  网页传回客户端的响应  pageContext网页的属性是在这里管理  session  与请求有关的会话  applicationservlet正在执行的内容  out    用来传送响应的输出流  config   servlet的架构对象  page    JSP网页本身  exception  针对错误网页,未捕捉的例外  那么,这些是做什么的,而你应该如何使用它们呢?基本上,在你的小型指令文件里,你可以使用它们来存取执行JSP程序代码的servlet。为了避免谈论到太多ServletAPI的细节,让我们来检视一些你可以利用它们来做的事:   不必使用表达式,你可以直接存取内部out对象来打印一些东西到response:  <%out.println("Hello");%>.   不必直接传送参数到JavaBean,你可以藉由请求对象来取得参数的值:  <%Stringname=request.getParameter("name");out.println(name);%>。  当你以JSP写了许多的应用程序之后,如果你建立了JavaBeans或者发现你自己将太多的Java原始码放入你的JSP档案,你需要建立支持的Java类别,这样可以鼓励重复使用并且降低JSP网页转换时所需要的时间。当你需要建立Java类别时,你必须:  将JDSWK的安装目录\bin目录加到你的PATH。在你的autoexec.bat档案的PATH行的最后,加入C:\1.2.2\bin;。\n  以下面的指令将JAR档案复制到\jre\lib\ext目录:  copyc:\jswdk-1.0.1\lib\servlet.jarc:\jdk1.2.2\jre\lib\ext.(五)--JSP其他相关资源JSP其他相关资源:ServletsandJavaServerPages(JSP)1.0:ATutorialhttp://www.apl.jhu.edu/~hall/java/Servlet-Tutorial/JavaServerPagesTM:ADeveloper'sPerspectivehttp://developer.java.sun.com/developer/technicalArticles/Programming/jsp/JAVASERVERPAGESTMNEWS&ARTICLEShttp://java.sun.com/products/jsp/news.htmlJAVASERVERPAGESTMTECHNICALRESOURCEShttp://java.sun.com/products/jsp/technical.htmlSERVLETSTAVERNEhttp://www.interpasnet.com/JSS/servlets.comhttp://www.servlets.com/\n如何成为一个优秀的jsp程序员在网上看到很多问题是关于如何学习jsp的,正好网上看到一篇关于学习jsp的文章,就摘了一部分翻译过来,希望能对大家学习jsp有点指导。一个普通的错误是把JSP当作简化的Java。它不是,(事实上,JSP是简化的servlets。)程序员通常试着没有学习要求的支持技巧而直接学习JSP。JSP是一个衔接技术,并且成功地连接你需要理解的另外的技术。如果你已经知道Java,HTML和Javascript,这意味着JSP将确实是简单的。需要成为一个成功的JSP程序员可以参考这个时间表。请注意下列:*忽略你已经熟悉的步骤。*训练的时间只是代表学习好足够的基础时间,这样才能转移到下一步。1、建立并且理解你的WebServer。因为Apache是免费的并且在大多数平台上工作,为训练目的推荐Apache。安装时间:2天。2、保证你理解HTML/XHTML。你将需要了解html基础,特别是HTML布局中的table的使用。XHTML不久将代替HTML,学习XHTML的基础是一个好主意。许多程序员通过HTMLIDE学习HTML(集成开发环境)。因为大多数HTMLIDE产生混乱的HTMl语法,所以花时间学习手工写作html是很有必要的。因为你将会使用JSP和HTML混合编程,精通HTML语法是重要的。所以,你必须能流利地写HTML。训练时间:2~4个星期。3、开始学习Java。开始学习Java1.3理解Java基础是很重要的。不用担心学习Swing或Java的图形方面,因为在JSP中你不会使用这些特征。集中精力在Java工作的细节,学习Java的逻辑,也在JavaBean上花时间。学习Applet是好的,但是就象Swing,JSP的大多数应用将不使用小程序。训练时间:3~6个星期。3、学习JavaScript\n学习怎么将JavaScript在HTML中验证输入的Form元素。也学习JavaScript怎么能在一HTML页以内修改Form的元素。最后要求你能从一HTML页内的事件中触发JavaScriptFunction。训练时间:一~2个星期。4、学习并且理解你的WebServer的更好的细节。熟悉WebServer的特征,这是很重要的。训练时间:2天。5、建立你的JSPServer我推荐以Tomcat开始。它可以很好地运行JSP程序。当你不能在生产使用Tomcat时,学习尽可能多的知识以便于更好的运行程序。另外,许多JSP程序员使用Tomcat。因此当你遇到一个问题时,你将容易发现帮助。安装时间:一~2天。6、开始学习JSP。基本的JSP学习通过的步骤1到步骤6可以完成,然后使用JSP对象和脚本写JSP程序来联系。学习JSP的另外一个方面可以学习怎么创建一个分布式的应用程序。训练时间:4~6个星期。7、学习更多的JSPserver。没有关于更多的JSPServer当然也可以运行jsp程序。然而,许多JSPserver都由自己特殊的特征,可以让你更好的理解你的JSP工程。学习更多的Jspserver如何处理jsp程序是有必要的。同样也可以优化你的JSP应用程序,并且使之运行得更快而不出任何问题。训练时间:2~7天。8、学习JDBC。JSP大多数应用将使用数据库,JDBC被用于数据库连接。经常忽略的一个事实就是,每个JDBCDriver所支持的东西是相当不同的。了解并熟悉在jsp工程上被使用的JDBCdriver的细节是很重要的。(有时这部分的学习被包含在前面Java或JSP的学习中了。)训练时间:1~2个星期。到现在,你已经成为了熟练的JSP程序员。仍然有很多需要学习,你可以考虑扩展你的知识比如DHTML,XML,java证书,JSPTag\nLibraries或Servlets,看你想要造什么类型的网站而决定了。这些训练是JSP的核心。你不必都学习上面所有的,取决于你在工程中分配到什么任务和你已经有什么知识。但是这是我成功地训练程序员的时间表。关键的单元是时间。平均的说,5个月时间确实能够训练一个人(从开始到完成)成为一个对jsp熟悉程序员。5个月时间似乎很长,但要成为一个资深的WEB程序员所学的东西远远不止这一些。也许你认为这样学习一种语言花费的时间太长了,因为学ASP会更快、时间会更短。但是学习ASP不需要学习java的。下面是部分比较好的jsp学习书籍,可以直接在网上找到:ServletsandJavaServerPages(JSP)1.0:ATutorial(MartyHall.1999)http://www.apl.jhu.edu/~hall/java/Servlet-Tutorial/JSP:TheShortCourse(RayCarnes8.26.2000)http://www.jspinsider.com/tutorials/jsp/Ray/JSPB_Intro.htmlJavaServerPagesFundamentals(GovindSeshadri9.13.2000)http://developer.java.sun.com/developer/onlineTraining/JSPIntro/TheJavaTutorial(Sun)http://java.sun.com/docs/books/tutorial/JSPTagExtensions(Wrox2000)http://www.jspinsider.com/tutorials/tagextensions/wrox/4656_Content.htmlJSPProductPage(Sun)http://java.sun.com/products/jsp/\nJDBC入门(一)-开始你需要做的第一事情是你要正确的安装。这包含下列几个步骤:在你的计算机上安装Java和JDBC  Java数据库连接(JDBC)是一个标准SQL(StructuredQueryLanguage,结构化查询语言)数据库访问接口,可以为多种关系数据库提供统一访问。JDBC(JavaDataBaseConnection,Java数据库连接)也提供一种基准,据此可以构建更高级的工具和接口。目前的JDK(JavaDevelopmentKit,Java开发工具包)软件捆绑包括JDBC和JDBC-ODBC(OpenDataBaseConnection,开放式数据库连接)桥。这些包也可独立得到,以跟JDK1.0一起使用。应该注意的是,本文的示例使用了JDBC2.0接口,需要JDK2.0来运行,不能在JDK1.1下运行。  你可以从http://java.sun.com/products/JDK/CurrentRelease找到最新版。安装驱动程序  你的驱动程序应该有安装方法。为特定的DBMSs写的JDBC驱动程序安装时只要拷贝到你的计算机上就可以了。并不需要特殊的配置。  如果你下载的是Solaris或WindowsJDK1.1版本,桥作为包sun.jdbc.odbc与JDK一起自动安装。有关安装和配置ODBC的信息,请咨询ODBC驱动程序厂商。桥无须特殊配置。有关客户机安装和配置信息,请咨询数据库厂商。如果需要,安装数据库系统  如果你不能确认是否安装了数据库系统,你需要按照供应商的要求安装数据库。大多数用户都已经安装了数据库,可继续使用他们安装好的数据库。配置数据库  我们假设数据库COFFEEBREAK已经存在。(创建一个数据库并不困难,但需要一定的权限并通常是由数据库管理员来做)你还需要在此数据库里创建本教程作为例子使用的表。我们有意限制表的大小跟及数目,以便于管理。  假设我们的数据库是在一个咖啡馆里使用,咖啡豆按磅卖,而咖啡则以杯为单位。为了简单起见,还假定经营者只需要2张表,分别存放不同种类的咖啡及咖啡供应商的有关信息。  首先我们演示怎么打开一个DBMS连接,及JDBC是怎么发送SQL语句到你的DBMS。通过这些代码,我们将表明使用JDBC传递SQL语句到你的DBMS并处理返回的结果是非常简单的。\n  所有的代码在主要的几个DBMS产品做了测试。然而,如果你使用JDBC-ODBC桥来连接旧版本ODBC驱动程序时,可能会遇到一些兼容性问题(二)-建立联接你需要做的第一事情是你与想要使用的DBMS建立一个连接。这包含2个步骤:装载驱动程序并建立连接。装载驱动程序  装载驱动程序只需要非常简单的一行代码。例如,你想要使用JDBC-ODBC桥驱动程序,可以用下列代码装载它:    Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");  你的驱动程序文档将告诉你应该使用的类名。例如,如果类名是jdbc.DriverXYZ,你将用代码以下的代码装载驱动程序:    Class.forName("jdbc.DriverXYZ");  你不需要创建一个驱动程序类的实例并且用DriverManager登记它,因为调用Class.forName将自动将加载驱动程序类。如果你曾自己创建实例,你将创建一个不必要的副本,但它不会带来什么坏处。  加载Driver类后,它们即可用来与数据库建立连接。建立连接  第二步就是用适当的驱动程序类与DBMS建立一个连接。下列代码是一般的做法:    Connectioncon=DriverManager.getConnection(url,"myLogin","myPassword");  这个步骤也非常简单,最难的是怎么提供url。如果你正在使用JDBC-ODBC桥,JDBCURL将以jdbc:odbc开始:余下URL通常是你的数据源名字或数据库系统。因此,假设你正在使用ODBC存取一个叫"Fred"的ODBC数据源,你的JDBCURL是jdbc:odbc:Fred。把"myLogin"及"myPassword"替换为你登陆DBMS的用户名及口令。如果你登陆数据库系统的用户名为"Fernanda"口令为"J8",只需下面的2行代码就可以建立一个连接:Stringurl="jdbc:odbc:Fred";Connectioncon=DriverManager.getConnection(url,"Fernanda","J8");  如果你使用的是第三方开发了的JDBC驱动程序,文档将告诉你该使用什么subprotocol,就是在JDBCURL中放在jdbc后面的部分。例如,如果驱动程序开发者注册了acme作为subprotocol,JDBCURL的第一和第二部分将是jdbc:acme。驱动程序文档也会告诉你余下JDBCURL的格式。JDBCURL\n最后一部分提供了定位数据库的信息。  如果你装载的驱动程序识别了提供给DriverManager.getConnection的JDBCURL,那个驱动程序将根据JDBCURL建立一个到指定DBMS的连接。正如名称所示,DriverManager类在幕后为你管理建立连接的所有细节。除非你是正在写驱动程序,你可能无需使用此类的其它任何方法,一般程序员需要在此类中直接使用的唯一方法是DriverManager.getConnection。  DriverManager.getConnection方法返回一个打开的连接,你可以使用此连接创建JDBCstatements并发送SQL语句到数据库。在前面的例子里,con对象是一个打开的连接,并且我们要在以后的例子里使用它。(三)-设置表创建表  首先,我们在我们的示例数据库创建其中一张表COFFEES,包含在咖啡店所卖咖啡的必要的信息,包括咖啡名字,他们的价格,本星期卖了多少磅及迄今为止卖的数目。关于COFFEES表我们以后会详细描述,如下:COF_NAMESUP_IDPRICESALESTOTALColombian1017.9900French_Roast498.9900Espresso1509.9900Colombian_Decaf1018.9900French_Roast_Decaf499.9900  存储咖啡名的列是COF_NAME,它的SQL数据类型是VARCHAR,最大的长度为32个字符。因为我们所卖的每种类型咖啡都使用不同的名字,名字可用于作为唯一识别咖啡的标识,因此可用于作主键。第二个列叫SUP_ID,用于保存咖啡供应商标识;其SQL数据类型为INTEGER。第3列叫PRICE,因为它需要保存带小数的十进制数,因此它的SQL类型为FLOAT。(注意,通常钱的SQL类型为DECIMAL或NUMERIC,但在不同DBMSs间存在差异,为了避免于老版本的JDBC的不兼容性在本教程我们采用更标准的FLOAT类型)SALES列的SQL类型为INTEGER,其值为本星期所卖咖啡的磅数。最后一列,TOTAL的SQL类型为INTEGER,保存了迄今为止所卖咖啡的总磅数。  数据库里的第二个表SUPPLIERS,保存了每个供应商的信息:SUP_IDSUP_NAMESTREETCITYSTATEZIP101Acme,Inc.99MarketStreetGroundsvilleCA9519949SuperiorCoffee1PartyPlaceMendocinoCA95460150TheHighGround100CoffeeLaneMeadowsCA93966  COFFEES跟SUPPLIERS都包含列SUP_ID,它意味着可以用SELECT\n语句从这两张表中取得有关信息。列SUP_ID是SUPPLIERS表的主键,用于唯一识别每个咖啡供应商。在COFFEES表中,SUP_ID列被称外键。注意每个SUP_ID值在SUPPLIERS表里只出现一次;这对主键是必须的。在COFFEES表里,它作为外键,显然它可以有重复的SUP_ID值,因为同一供应商可以提供很多种的咖啡。在本节的最后,你将看见如何在SELECT语句中使用主键及外键的一个例子。  下面的SQL语句用于创建COFFEES表。列由列名跟空格跟SQL类型组成。列(包括列名及其SQL类型)跟下一个之间用逗号分隔。VARCHAR类型创建定义了最大长度,因此它需要有一个参数来表示最大长度。参数必须在类型后面的括号内。SQL语句如下,列COF_NAME的长度被限定为不得超过32个字符:CREATETABLECOFFEES(COF_NAMEVARCHAR(32),SUP_IDINTEGER,PRICEFLOAT,SALESINTEGER,TOTALINTEGER)  这些代码不带DBMS语句结束符,因为每个DBMS都可能不同。例如,Oracle使用一个分号(;)作为语句的结束,而Sybase使用go。你所使用的驱动程序会自动提供合适的语句结束符,因此你无须把它包括在你的JDBC代码中。  另外,我们应该指出的的是SQL语句的格式。在CREATETABLE语句中,关键字采用大写字符,并且每个项目都另起一行。SQL并没有此要求;仅仅是为了更容易阅读。SQL标准是不区分关键词的大小写的,因此,如下例中的SELECT语句可以有多种写法。因此下面两个不同写法的语句对SQL来说是一样的。SELECTFirst_Name,Last_NameFROMEmployeesWHERELast_NameLIKE"Washington"selectFirst_Name,Last_NamefromEmployeeswhereLast_Namelike"Washington"  然而,引号里的内容是区分大小写的:在名字"Washington"里"W"必须被大写,并且余下的字符必须是小写的。  对于标识,不同的DBMS有不同的要求,例如,某些DBMSs要求那些列名及表名必须跟创建时的一样,有些则没有此要求。为安全起见,我们全部使用大写标识如COFFEES、SUPPLIERS,因为我们是那样定义他们的。\n  到止我们写了创建COFFEES表的SQL语句。现在我们在它外面加上引号(使它成为字符串),并且字符串赋值给变量createTableCoffees,在以后的JDBC代码中我们可以使用此变量。正如看到的,DBMS并不在意分行,但对Java语言来,String对象分行是通不过编译的。因而,我们可以用加号(+)把每一行的串连接。StringcreateTableCoffees="CREATETABLECOFFEES"+"(COF_NAMEVARCHAR(32),SUP_IDINTEGER,PRICEFLOAT,"+"SALESINTEGER,TOTALINTEGER)";  我们在CREATETABLE语句中使用的数据类型是通用的SQL类型(也称JDBC类型)它们在类java.sql.Types中定义。DBMSs通常使用这些标准的类型,因此,当你要尝试一些JDBC应用程序时,你可以直接使用CreateCoffees.java应用程序,它使用了CREATETABLE语句。如果你的DBMS使用了它的自己的本地的类型名字,我们为你供应其它的应用程序,我们将在后面详细解释。  在运用任何应用程序前,当然,我们将让你了解JDBC的基础。创建JDBCStatements对象  Statement对象用于把SQL语句发送到DBMS。你只须简单地创建一个Statement对象并且然后执行它,使用适当的方法执行你发送的SQL语句。对SELECT语句来说,可以使用executeQuery。要创建或修改表的语句,使用的方法是executeUpdate。  需要一个活跃的连接的来创建Statement对象的实例。在下面的例子中,我们使用我们的Connection对象con创建Statement对象stmt:Statementstmt=con.createStatement();  到此stmt已经存在了,但它还没有把SQL语句传递到DBMS。我们需要提供SQL语句作为参数提供给我们使用的Statement的方法。例如,在下面的代码段里,我们使用上面例子中的SQL语句作为executeUpdate的参数:stmt.executeUpdate("CREATETABLECOFFEES"+"(COF_NAMEVARCHAR(32),SUP_IDINTEGER,PRICEFLOAT,"+"SALESINTEGER,TOTALINTEGER)");  因为我们已经把SQL语句赋给了createTableCoffees变量,我们可以如下方式书写代码:stmt.executeUpdate(createTableCoffees);\n执行语句  我们使用executeUpdate方法是因为在createTableCoffees中的SQL语句是DDL(数据定义语言)语句。创建表,改变表,删除表都是DDL语句的例子,要用executeUpdate方法来执行。你也可以从它的名字里看出,方法executeUpdate也被用于执行更新表SQL语句。实际上,相对于创建表来说,executeUpdate用于更新表的时间更多,因为表只需要创建一次,但经常被更新。  被使用最多的执行SQL语句的方法是executeQuery。这个方法被用来执行SELECT语句,它几乎是使用最多的SQL语句。马上你将看到如何使用这个方法。在表中输入数据  我们已经显示了如何通过指定列名、数据类型来创建表COFFEES,但是这仅仅建立表的结构。表还没有任何数据。我们将次输入一行数据到表中,提供每列的信息,注意插入的数据显示顺序跟表创建时候是一样的,既缺省顺序。  下列代码插入一个行数据,COF_NAME的值为Colombian,SUP_ID为101,PRICE为7.99,SALES0,TOTAL0。就象创建COFFEES表一样,我们创建一Statement对象,并执行executeUpdate方法。  因为SQL语句一行显示不下,因此我们把它分为两行,并用加号(+)相连。特别要注意的是,在COFFEES和VALUES之间要有空格。这个空格必须在引号之内并且要在COFFEES跟VALUES之间;没有这个空格,SQL语句将被错误地被读作为"INSERTINTOCOFFEESVALUES...",并且DBMS将寻找表COFFEESVALUES。还要注意的是在coffeename上我们使用了单引号。Statementstmt=con.createStatement();stmt.executeUpdate("INSERTINTOCOFFEES"+"VALUES('Colombian',101,7.99,0,0)");  下面的代码把第二行插入到表COFFEES中。我们可以在使用Statement对象而无须为每次执行创建一个新的。stmt.executeUpdate("INSERTINTOCOFFEES"+"VALUES('French_Roast',49,8.99,0,0)");  剩下行的数据如下:stmt.executeUpdate("INSERTINTOCOFFEES"+"VALUES('Espresso',150,9.99,0,0)");stmt.executeUpdate("INSERTINTOCOFFEES"+"VALUES('Colombian_Decaf',101,8.99,0,0)");stmt.executeUpdate("INSERTINTOCOFFEES"+\n"VALUES('French_Roast_Decaf',49,9.99,0,0)");从表中取得数据  既然表COFFEES中已经有数据了,我们就可以写一个SELECT语句来取得这些值。下面的SQL语句中星号(*)表示选择所有的列。因为没有用WHERE子句来限制所选的行,因此下面的SQL语句选择的是整个表。SELECT*FROMCOFFEES  结果是整个表的数据,如下:COF_NAMESUP_IDPRICESALESTOTAL------------------------------------Colombian1017.9900French_Roast498.9900Espresso1509.9900Colombian_Decaf1018.9900French_Roast_Decaf499.9900  如果你直接在数据库系统里输入SQL查询语句,你将在你的终端上看到如上的结果。当我们通过一个Java应用程序存取一个数据库时,正如我们马上要做的一样,我们需要检索结果以便我们能使用他们。你将在下一节看到如何实现。这是SELECT语句的另一个例子,这将得到咖啡及其各自每磅单价的列表。SELECTCOF_NAME,PRICEFROMCOFFEES查询的结果集将具有如下形式:COF_NAMEPRICE-----------------------Colombian7.99French_Roast8.99Espresso9.99Colombian_Decaf8.99French_Roast_Decaf9.99上面SELECT语句取得了所有咖啡的名字及价格。而下面的SELECT语句限制那些每磅价格低于$9.00的咖啡才被选择。SELECTCOF_NAME,PRICEFROMCOFFEESWHEREPRICE<9.00结果集将具有如下形式:\nCOF_NAMEPRICE--------------------Colombian7.99French_Roast8.99ColombianDecaf8.99\njsp基础学习资料一、JSP技术概述  在Sun正式发布JSP(JavaServerPages)之后,这种新的Web应用开发技术很快引起了人们的关注。JSP为创建高度动态的Web应用提供了一个独特的开发环境。按照Sun的说法,JSP能够适应市场上包括ApacheWebServer、IIS4.0在内的85%的服务器产品。即使您对ASP“一往情深”,我们认为,关注JSP的发展仍旧很有必要。  ㈠JSP与ASP的简单比较  JSP与Microsoft的ASP技术非常相似。两者都提供在HTML代码中混合某种程序代码、由语言引擎解释执行程序代码的能力。在ASP或JSP环境下,HTML代码主要负责描述信息的显示样式,而程序代码则用来描述处理逻辑。普通的HTML页面只依赖于Web服务器,而ASP和JSP页面需要附加的语言引擎分析和执行程序代码。程序代码的执行结果被重新嵌入到HTML代码中,然后一起发送给浏览器。ASP和JSP都是面向Web服务器的技术,客户端浏览器不需要任何附加的软件支持。  ASP的编程语言是VBScript之类的脚本语言,JSP使用的是Java,这是两者最明显的区别。此外,ASP与JSP还有一个更为本质的区别:两种语言引擎用完全不同的方式处理页面中嵌入的程序代码。在ASP下,VBScript代码被ASP引擎解释执行;在JSP下,代码被编译成Servlet并由Java虚拟机执行,这种编译操作仅在对JSP页面的第一次请求时发生。  ㈡运行环境  Sun公司的JSP主页在http://www.javasoft.com/products/jsp/index.html,从这里还可以下载JSP规范,这些规范定义了供应商在创建JSP引擎时所必须遵从的一些规则。  执行JSP代码需要在服务器上安装JSP引擎。此处我们使用的是Sun的JavaServerWebDevelopmentKit(JSWDK)。为便于学习,这个软件包提供了大量可供修改的示例。安装JSWDK之后,只需执行startserver命令即可启动服务器。在默认配置下服务器在端口8080监听,使用http://localhost:8080即可打开缺省页面。  在运行JSP示例页面之前,请注意一下安装JSWDK的目录,特别是“work”子目录下的内容。执行示例页面时,可以在这里看到JSP页面如何被转换成Java源文件,然后又被编译成class文件(即Servlet)。JSWDK软件包中的示例页面分为两类,它们或者是JSP文件,或者是包含一个表单的HTML文件,这些表单均由JSP代码处理。与ASP一样,JSP中的Java代码均在服务器端执行。因此,在浏览器中使用“查看源文件”菜单是无法看到JSP\n源代码的,只能看到结果HTML代码。所有示例的源代码均通过一个单独的“examples”页面提供。  ㈢JSP页面示例  下面我们分析一个简单的JSP页面。您可以在JSWDK的examples目录下创建另外一个目录存放此文件,文件名字可以任意,但扩展名必须为.jsp。从下面的代码清单中可以看到,JSP页面除了比普通HTML页面多一些Java代码外,两者具有基本相同的结构。Java代码是通过<%和%>符号加入到HTML代码中间的,它的主要功能是生成并显示一个从0到9的字符串。在这个字符串的前面和后面都是一些通过HTML代码输出的文本。  JSP页面  <%@pagelanguage="java"%> <%!Stringstr="0";%> <%for(inti=1;i<10;i++){ str=str+i; }%> JSP输出之前。 

 <%=str%> 

 JSP输出之后。    这个JSP页面可以分成几个部分来分析。首先是JSP指令。它描述的是页面的基本信息,如所使用的语言、是否维持会话状态、是否使用缓冲等。JSP指令由<%@开始,%>结束。在本例中,指令“<%@pagelanguage="java"%>”只简单地定义了本例使用的是Java语言(当前,在JSP规范中Java是唯一被支持的语言)。接下来的是JSP声明。JSP声明可以看成是定义类这一层次的变量和方法的地方。JSP声明由<%!开始,%>结束。如本例中的“<%!Stringstr="0";%>”定义了一个字符串变量。在每一项声明的后面都必须有一个分号,就象在普通Java类中声明成员变量一样。位于<%和%>之间的代码块是描述JSP页面处理逻辑的Java代码,如本例中的for循环所示。最后,位于<%=和%>之间的代码称为JSP表达式,如本例中的“<%=str%>”所示。JSP表达式提供了一种将JSP生成的数值嵌入HTML页面的简单方法。\n二、会话状态管理作者:仙人掌工作室  会话状态维持是Web应用开发者必须面对的问题。有多种方法可以用来解决这个问题,如使用Cookies、隐藏的表单输入域,或直接将状态信息附加到URL中。JavaServlet提供了一个在多个请求之间持续有效的会话对象,该对象允许用户存储和提取会话状态信息。JSP也同样支持Servlet中的这个概念。  在Sun的JSP指南中可以看到许多有关隐含对象的说明(隐含的含义是,这些对象可以直接引用,不需要显式地声明,也不需要专门的代码创建其实例)。例如request对象,它是HttpServletRequest的一个子类。该对象包含了所有有关当前浏览器请求的信息,包括Cookies,HTML表单变量等等。session对象也是这样一个隐含对象。这个对象在第一个JSP页面被装载时自动创建,并被关联到request对象上。与ASP中的会话对象相似,JSP中的session对象对于那些希望通过多个页面完成一个事务的应用是非常有用的。  为说明session对象的具体应用,接下来我们用三个页面模拟一个多页面的Web应用。第一个页面(q1.html)仅包含一个要求输入用户名字的HTML表单,代码如下:    请输入您的姓名:       第二个页面是一个JSP页面(q2.jsp),它通过request对象提取q1.html表单中的thename值,将它存储为name变量,然后将这个name值保存到session对象中。session对象是一个名字/值对的集合,在这里,名字/值对中的名字为“thename”,值即为name变量的值。由于session对象在会话期间是一直有效的,因此这里保存的变量对后继的页面也有效。q2.jsp的另外一个任务是询问第二个问题。下面是它的代码:   <%@pagelanguage="java"%> <%!Stringname="";%>\n <% name=request.getParameter("thename"); session.putValue("thename",name); %> 您的姓名是:<%=name%> 

  您喜欢吃什么?  

      第三个页面也是一个JSP页面(q3.jsp),主要任务是显示问答结果。它从session对象提取thename的值并显示它,以此证明虽然该值在第一个页面输入,但通过session对象得以保留。q3.jsp的另外一个任务是提取在第二个页面中的用户输入并显示它:   <%@pagelanguage="java"%> <%!Stringfood="";%> <% food=request.getParameter("food"); Stringname=(String)session.getValue("thename"); %> 您的姓名是:<%=name%> 

 您喜欢吃:<%=food%>  三、引用JavaBean组件作者:仙人掌工作室编译  JavaBean是一种基于Java的软件组件。JSP对于在Web应用中集成JavaBean组件提供了完善的支持。这种支持不仅能缩短开发时间(可以直接利用经测试和可信任的已有组件,避免了重复开发),也为JSP应用带来了更多的可伸缩性。JavaBean组件可以用来执行复杂的计算任务,或负责与数据库的交互以及数据提取等。如果我们有三个JavaBean,它们分别具有显示新闻、股票价格、天气情况的功能,则创建包含所有这三种功能的Web\n页面只需要实例化这三个Bean,使用HTML表格将它们依次定位就可以了。  为说明在JSP环境下JavaBean的应用,我们创建了一个名为TaxRate的Bean。它有两个属性,即Product(产品)和Rate(税率)。两个set方法分别用来设置这两个属性,两个get方法则用于提取这两个属性。在实际应用中,这种Bean一般应当从数据库提取税率值,此处我们简化了这个过程,允许任意设定税率。下面是这个Bean的代码清单: packagetax; publicclassTaxRate{ StringProduct; doubleRate; publicTaxRate(){ this.Product="A001"; this.Rate=5; } publicvoidsetProduct(StringProductName){ this.Product=ProductName; } publicStringgetProduct(){ return(this.Product); } publicvoidsetRate(doublerateValue){ this.Rate=rateValue; } publicdoublegetRate(){ return(this.Rate); } }  在JSP页面中应用上述Bean要用到标记。依赖于具体使用的JSP引擎的不同,在何处配置以及如何配置Bean的方法也可能略有不同。本文将这个Bean的.class文件放在c:jswdk-1.0examplesWEB-INFjspbeanstax目录下,这里的tax是一个专门存放该Bean的目录。下面是一个应用上述Bean的示例页面:   <%@pagelanguage="java"%>  <%taxbean.setProduct("A002"); taxbean.setRate(17); %> 使用方法1:

\n 产品:<%=taxbean.getProduct()%>
 税率:<%=taxbean.getRate()%> 

 <%taxbean.setProduct("A003"); taxbean.setRate(3); %> 使用方法2:

 产品: 
 税率:    在标记内定义了几个属性,其中id是整个JSP页面内该Bean的标识,scope属性定义了该Bean的生存时间,class属性说明了该Bean的类文件(从包名开始)。  这个JSP页面不仅使用了Bean的set和get方法设置和提取属性值,还用到了提取Bean属性值的第二种方法,即使用标记。中的name属性即为中定义的Bean的id,它的property属性指定的是目标属性的名字。  事实证明,JavaServlet是一种开发Web应用的理想构架。JSP以Servlet技术为基础,又在许多方面作了改进。JSP页面看起来象普通HTML页面,但它允许嵌入执行代码,在这一点上,它和ASP技术非常相似。利用跨平台运行的JavaBean组件,JSP为分离处理逻辑与显示样式提供了卓越的解决方案。JSP必将成为ASP技术的有力竞争者。 \nJSP简明教程:令人兴奋的脚本编程如果你是直接使用Javaservlets,那你将不得不在Java类中处理HTTP输入和HTML输出,你需要丰富的Java编程经验来构建复杂的应用程序。JSP的加入,使你可以把HTML的表达逻辑从植入servlets中的复杂的商务逻辑区分开来。这意味着可以由有经验的脚本编写者来编写表达层代码,而高级的Java开发者能够集中精力去解决servlets和bean中更为复杂的问题。  不管你有没有Java编程知识,都能够使用JSP。JSP包含了一些服务器端的标签,使得不用写一行Java代码就能显示动态数据。你可以直接访问bean来完成操作,然后使用JSP标签把结果显示为动态内容。你还可以用servlets生成bean,servlets操作的运算结果存于其中,然后再使用JSP标签显示结果,同样不需要在JSP页中写Java代码。  有三种方式可以用来在你的网页中加入Java代码:  1、使用declarations(声明),可以定义全局变量或是在页内任何地方都可以访问的Java方法。声明被包含在标记<%!...%>中。  2、使用scriptlets(脚本片断),你能书写页内处理所需的任何逻辑,它们包含在<%...%>标记内。  3、Expressions(表达式),包含于<%=...%>中。它提供一种简单的方法来显示Java表达式的结果。被附加上的表达式将被计算并在页面上显示出来,就好像你已经在代码中明确写出了运算结果的数值一样。  在你自己编写的代码中,可以使用一些隐含变量(implicitvariables)――JSP提供的预定义的Java对象。另外,通过使用JSP的指令(directives),还可以包含非Java代码模块,比如来自其他文件的HTML文本。  下面我们来仔细看一看这些脚本元素,在编写你自己的JSP脚本时将会经常用到它们。Directives(指令)  JSP定义了三个页内指令用于设置JSP参数或扩充代码。它们是page,include和taglib,必须写在JSP页的第一行。语法如下:  <%@directiveattribute="value"...%>  page指令允许你为网页设定一些基本参数,包括设置所用脚本语言的参数(默认为Java)、你的脚本片断中引入的Java类、设置输出缓冲区等等。完整的page指令参数表见《JSPSpecificationVersion1.0》(《JSP规范1.0》)的2.8.1章。\n  使用include指令,可以包含其他文件的内容,比如存于单独文件中的HTML报头和页脚。  taglib指令用于扩充标准的JSP标签集,这超出了本文的讨论范围。然而,了解JSP定义了一种扩充其标签集的方法还是很有好处的,当你是一个软件商,想扩充JSP的原始功能而又不想破坏其兼容性时,这一点尤为重要。Declarations(声明)  使用declarations,你可以在JSP页中定义方法或变量,它们可被同一页中的其他代码访问。在大多数情况下,你可能会在自己的bean中定义方法。然而,有时候在网页内定义方法可能更方便一些,尤其是当代码只用于单一页面时。不论定义方法还是变量,声明都包含在<%!%>标记内。  注意,声明并不在JSP页内产生任何输出。它们仅仅用于定义,而不生成输出结果。要生成输出结果,你应该用JSP表达式或脚本片断。Expressions(表达式)  Expressions是一种非常简单的JSP标签,它用来把在<%=%>中定义的JSP表达式的值转换成字串并将这个值以动态文本的形式送出。Expression的确是一条生成文本的捷径,有了它,你不必在每次要显示一段动态文本的时候都去调用print()方法。典型的应用就是,你可以用expressions显示简单的变量值或bean中的方法的返回值。  例如,下面的代码将会生成getName()方法的返回值:  

Welcome,<%=mybean.getName()%>

  事实上,在生成动态输出之前,JSP必须把方法的返回值转变为Java中的String对象。JSP规范中详细描述了在JSP表达式中,什么样的Java类型和Java类会被转变成字串。Scriptlets(脚本片断)  到现在为止你已经学会了使用指令来引入任何Java类或Java包,你能定义页面级的方法或变量并在页中使用它们,你还可以使用提供普通web处理功能的隐含变量。还能在JSP页内做些什么就取决于你了,因为你可以在scriptlets(脚本片断)里编写任何你想要的Java代码,如下所示:  <%...code...%>  通过在page指令中使用IMPORT参数,你可以从脚本片断内调用所有JavaAPI。因为你写的所有JSP代码实际上都被编译构成Javaservlet,它本身就是一个Java类,所以你所用的语言本身就是Java,而不是任何一种修改或整理过的版本。这就像在SSJS中你可以编写任何代码一样。而与SSJS不同,在JSP中你有权使用整套丰富的JavaAPI,因此几乎没有任何局限性。\nImplicitVariables(隐含变量)  前面提到过,JSP定义了一些隐含变量(即Java对象)供你在表达式和脚本片断中使用。《JSPSpecificationVersion1.0》的表2-2列出了JSP1.0中可用的隐含变量。这里列出一些常用的对象:  out对象,类型为javax.servlet.jsp.JspWriter,提供对方法(例如print()方法)的访问,用来在脚本片断内生成输出结果。  request对象直接与Java中的javax.servlet.http.HttpServletRequest类对应,具有该类的对象的一切属性和方法。举个例子,要获取一个从HTML表单或URL查询字串传入的值,可以调用request.getParameter()方法,根据名字获取参量。  response对象与Java中的javax.servlet.http.HttpServletResponse类对应,提供对你的网页产生的HTML响应的参数的访问权。因此,要在JSP页返回的HTML响应报头中加入一个值,你就可以调用theresponse.setHeader()方法来实现。另一个简单的例子  在下面的例子中,我们来看一看一个表单和它的JSP表单句柄之间的交互过程。使用前面讨论过的脚本元素,我实现了一个简单的web站点回馈表单(见图2)和一个JSP表单句柄用来验证输入,然后有条件地地生成基于回馈的输出。  图2.一个web站点回馈表单  图中按钮:submitquery--提交;reset――重填  表单句柄将会检验名称和意见栏以确定它们已被填写,如果其中任何一个或两个是空白的,表单句柄会生成一条错误信息;否则它将继续查看用户意见是否与预先设定的字串匹配。如果匹配,它就输出一条专门的信息;否则输出“thankyou”。  例2        FeedbackResults  \n  <%!  //姓名和意见栏不能为空白  //检查它们的值并返回结果  booleanvalidateInput(Stringname,Stringcomment){  booleanresult=true;  //如果姓名或意见未填写,返回false以表明输入无效  if(name.length()==0)    result=false;  if(comment.length()==0)    result=false;  returnresult;  }//结束输入验证validateInput  //根据表单上的意见栏输出结果  StringgetStringCheese(Stringcomment){  Stringcheese="Ilikecheese.";  Stringresult;  if(comment.compareTo(cheese)==0)    result="Welikecheesetoo!";  else    result="Wehopesomedayyou'lllearntolikecheese.";  returnresult;\n  }//结束getStringCheese  %>    <%  //获取通过表单提交的数据  Stringname=request.getParameter("name");  Stringage=request.getParameter("age");  Stringcomment=request.getParameter("comment");  booleanisValid;  isValid=validateInput(name,comment);  //根据用户是否未填写姓名或意见栏决定输出内容  if(isValid){  %>  

Thankyouforyourfeedback!

  

  <%    //输出意见栏的查询结果    out.println(getStringCheese(comment));  }//结束if程序段  else{    out.println("Youdidn'tgiveusyournameoracomment.");  %>\n  

  Pleasetryagain  <%  }//结束else程序段  %>      这个例子假定用户输入的意见是“Ilikecheese."(我喜欢奶酪)在代码中可以看到,这一响应是为填写这条意见的用户定制的。表单句柄将会返回如图3所示的页面:  图3.表单句柄输出  图中文字:谢谢你的反馈!我们也喜欢奶酪。  这个例子非常简单易懂。即便你只是一个JavaScript程序员,你也应该可以理解它。我还要指出这个例子中体现的在JSP规范中并不很明显的一些特性。首先,请注意我在声明部分(<%'...%>中的部分)定义了一些方法,与在Java类中定义方法一模一样。这是因为JSP引擎把这些方法转变为底层的Javaservlets,在浏览器向网页发出请求时由服务器来执行它们。因此,任何变量和方法的定义都必须遵守标准的Java语法。  还应注意到,在我的脚本片断的代码中,我把一个if...else语句分开了,它跨越了两个不同的脚本片断段。这完全是合法的!不仅合法,而且把脚本片断代码和静态HTML交叉起来是有条件生成HTML的好办法,就像我在本例中所做到的一样。  最后,你可以看到我通过调用request.getParameter()方法取得表单元素的值并把它赋给一个临时变量。这是处理从表单或查询字串输入的值的标准方法。转自:动态网制作指\nJSP简明教程:对比与总结JSP、SSJS与NAS  JSP不会取代或威胁到SSJS作为一种web开发平台的地位,它们拥有各自的市场。尽管JSP比SSJS更强大,更灵活,但是要编写脚本代码你就必须学习Java――至少在某个厂商推出像JavaScript这样的更加简单的语言之前是这样。同时,SSJS仍然提供简单强大的JavaScript语言,它比Java易学而且在某些方面更灵活,因为它可以被松散地键入。从NAS被人们注意时起,就可以肯定JSP不可能取代NAS提供的强大功能。JSP是一种先进的表达层技术,它可以很好地与NAS体系结构共存。NAS现在使用一种独有的标识语言用于构建表单层模板。这种标识语言有些“顽固",它不允许在页面内加入脚本,这一点显然不如JSP灵活。在NAS4.0中,网景公司将引入JSP作为一种可选择的标识语言与同样要在NAS4.0中引入的底层NASservlet模型一起工作。事实上,JSP的出现对于NAS开发者来说是个好消息。JSP与XML  JSP与XML有着有趣的联系。一方面,你可以把JSP和XML混合使用,就像混合JSP与HTML一样,这样一来就那用JSP来动态生成XML页。换句话说,你可以用JSP编写原始XML文档。JSP在几种特殊类中提供了这一功能,但这并不是它特有的。理论上,你可以使用任何脚本语言,包括CGI、SSJS和ASP,来动态生成XML页,与生成HTML的方法一样。在JSP规范中明确声明了JSP支持动态生成XML文档,并且证实这种生成动态内容的方法不违反任何XML规则。  另一方面,JSP页本身可被解释为XML文档。JSP规范中定义了XML适用的标签和XML不适用标签可供选择。比如,脚本片断(写在<%...%>中)同样可以写在XML适用标签和之间。显然,当你手工书写JSP代码时,前一种方法比XML格式更容易。然而,在IDE(集成开发环境)或其他JSP开发工具中,使用XML书写格式能够更容易地生成有效的JSP页。关于JSP和XML关系的详细资料,请参阅JSP规范。结束语  JSP一定会理所当然地受到各种各样的web开发人员的关注,因为它不仅通过标签的使用对初级的程序员通过支持,还适用于高级的脚本编写者和Java开发者。实际上,除网景以外的其他厂商也已经提供了这样或那样的JSP开发工具。尽管Sun还没有正式发布JSP1.0规范(写本文时发布的JSP1.0还只是一个草案),IBM、BEAWebLogic和LiveSystems已经在他们的应用服务器中实现了JSP。由于JSP是一个开放的标准,它允许用其他语言(如JavaScript)代替Java,所以它将会在行业中赢得更为广泛的支持。前面提到过,网景公司已经宣布NAS4.0将支持JSP和Javaservlets。\n  JSP与生俱来的灵活性、对组件为中心程序设计的支持、以及它的跨平台性使它成为Netscape跨平台服务体系的完美补充。由于NAS本身就是一个以组件为中心的支持Java的服务系统,JSP看来将会成为与未来版本的NAS相匹配的最理想的表单层技术。如果你已经或即将成为NAS程序员,我强烈建议你下载Sun发行的JSPreferenceimplementation并从现在开始学习它。这个工具不是作为生产软件产品使用的,但你可以用它构建和运行一些简单的应用程序,借此来熟悉JSP。其中还包括了一些示例程序,它们将和本文一起引导你走向JSP开发之路。\n初学jsp心得测试环境为jdk1.2.2jswdk-1.0winnt4.0中文版。1。java是大小写敏感的,用过其他编程语言的人最容易犯这个错误,尤其是刚上手的时候。我刚开始调试jsp的时50%以上的编译错误是都是因为这个。2。java的调用过程都是要加括号的,一开始比较容易忽视,如title=request.getParameter("title").trim();3。jsp中对应asp中的request.form()和request.querystring()的解决方法。jsp中取得参数没有form和queryString之分,都是通过request.getParameter("XXXX")来取得。虽然jsp也有request.getQueryString()方法,但测试结果是test.jsp?id=1&page=20得到id=1&page=20。  如果url和form有相同的参数名称呢?下面是一段测试代码:name都是id,结果是url的参数优先得到,jsp的这种处理方式和asp相比我觉的各有所长。4。头疼的汉字处理问题。在其他的文章里曾说到在中文NT环境下如下语句输出会得到乱码,<%="你好"%>及out.print("你好");等。解决方法是只要对字符串变量进行编码就可以得到正确结果,如下代码可以得到正确的输出:<%Stringtitle="你好";byte[]tmpbyte=title.getBytes("ISO8859_1");title=newString(tmpbyte);out.print(title);%>或者<%=title%>关于sql语句汉字问题,例句为select*fromtestwheretitle='谁是傻瓜'在jdbc-odbc驱动下连db2,不管是原句还是对sql语句进行编码后都死活通不过。换了ibm的jdbc直接驱动后,对sql语句编码后程序可以通过。这个问题的产生大概是中文NT的原因,在其他环境下可能就没汉字处理问题了,据说ibm的websphere对中文支持的很好,这也给jsp的开发带来一定的通用性问题。据说对字符串编码是一种通用的解决方法,不过没有这么多环境来测试。5。在asp中经常使用到字符串判断语句如ifstate="真是傻瓜"then.....  在java中String变量不是一个简单的变量而是一个类实例,不同的方法会得到不同的结果a.Stringstr1="我是傻瓜";\nStringstr2="我是傻瓜";(orStringstr2="我是"+"傻瓜";)if(str1==str2)out.print("yes");else  out.print("no");结果是"yes"。大概是编译优化,str1,str2指向同一个类实例;b.Stringstr1,str2,str3;str1="我是傻瓜";str2="我是";str3=str2+"傻瓜";if(str1==str3)out.print("yes");elseout.print("no");结果是"no"。Stringstr1=newString("我是傻瓜");Stringstr2=newString("我是傻瓜");if(str1==str2)out.print("yes");elseout.print("no");结果是"no"。Stringstr1=newString("我是傻瓜");Stringstr2=newString("我是傻瓜");if(str1.compareTo(str2)==0)out.print("yes");elseout.print("no");结果是"yes"。所以在jsp中判断字符串要使用compareTo方法,用惯传统语言还真一下子适应不过来,熟悉java的朋友应该没这个问题。6。如何判断数据库为空?  result=stmt.executeQuery(sql);  if(result.next())     ......  result执行后游标出于一个未明的状态,不能进行状态判断,也不能取值,一定要next()一下才可以用。\n   7。在jsp中实现分页。page是关键字,不能当变量。conn.jsp<%  StringsDBDriver="COM.ibm.db2.jdbc.app.DB2Driver";  StringsConnStr="jdbc:db2:faq";  Connectionconn=null;  Statementstmt=null;  ResultSetrs=null;      try{        Class.forName(sDBDriver);     }    catch(java.lang.ClassNotFoundExceptione)   {    out.print("faq():"+e.getMessage());  }     try{    conn=DriverManager.getConnection(sConnStr,"wsdemo","wsdemo1");         stmt=conn.createStatement();  }catch(SQLExceptione){    out.print(e.toString());  }%>query.jsp<%@pagelanguage="java"import="java.sql.*"%><%@pagecontentType="text/html;charset=gb2312"%><%@includefile="conn.jsp"%><%.......intpages=0;intpagesize=10;ResultSetresult=null;ResultSetrcount=null;pages=newInteger(request.getParameter("pages")).intValue();if(pages>0){Stringsql="state='我不傻'";\nintcount=0;try{rcount=stmt.executeQuery("SELECTcount(id)asidfromuserwhere"+sql);catch(SQLExceptionex){out.print("aq.executeQuery:"+ex.getMessage());}if(rcount.next())count=rcount.getInt("id");rcount.close();if(count>0){  sql="select*fromuserwhere"+sql;try{result=stmt.executeQuery(sql);  }  catch(SQLExceptionex){out.print("aq.executeQuery:"+ex.getMessage());}inti;Stringname;//result.first();//result.absolute((pages-1)*pagesize);//此方法jdbc2.0支持。编译通过,但执行不过,不知是不是跟驱动有关,只好用下面的笨办法。for(i=1;i<=(pages-1)*pagesize;i++)result.next();for(i=1;i<=pagesize;i++){if(result.next()){name=result.getString("name");out.print(name);}result.close();intn=(int)(count/pagesize);if(n*pagesize1){for(i=1;i<=n;i++)  out.print(""+i+" ");}}}%>\nJSP开发入门JavaServerPages(JSP)是一种以Java为主的跨平台web开发语言。    JSP与微软的ActiveServerPages兼容,但它是使用类似HTML的卷标以及Java程序代码段而不是VBScript。当你所使用的网站服务器没有提供本地ASP支持,也就是Apache或Netscape服务器时,你可以考虑使用JSP。虽然你可以取得这些服务器的ASP附加配备模块,但价格相当昂贵,而目前Sun并没有对你所需要的JSP组件收取费用(虽然Sun未来有可能会收费)。对于Solaris与Linux以及Windows,这些组件也都很容易取得。     请不要将JSP与服务器端的JavaScript混为一谈。网站服务器会自动将以JSP写成的Java程序代码段转换成Javaservlets。而许多先前必须以Perl手写程序或服务器特定的API(如ASP)控制的功能也都可透过JSP来自动化处理。    现在就让我们开始动手帮助你建立一个可执行JSP范例网站。  一、安装好你的机器来使用JSP  二、JSP语法的基本原理  三、JSP与JavaBean  四、JJSP的内部对象  五、JSP其他相关资源  JSP开发入门2  安装好你的机器来使用JSP    你将会需要Java2软件开发工具(JSDK),它原来的名称是Java发展工具(JDK)以及JavaServer网站发展工具(JSWDK),Tomcat,或是其它支持JSP的网络服务器。Sun免费提供JSDK与JSWDK来供Windows,Solaris,以及Linux平台使用。   如果你想要在你目前的网络服务器上使用JSP,但服务器本身并不支持JSP与Javaservlets,你可以试试看Allaire的Jrun,它的作用就像是针对Netscape企业版与FastTrack服务器、微软的网际网络信息服务器(IIS)与个人网络服务器(PWS)、Apache、以及其它服务器的网络服务器附加设备。你也可以使用Apache网络服务器的Java版本,最新的JSWDK里有提供。    下载与安装你需要的组件  \n  目前发布的1.2.2-001,JSDK可下载的版本是以可安装的压缩形式。下载的文件大约是20MB,可提供完整的Java发展环境,让你能建立利用标准API为核心的Java解决之道。然而,你的网络服务器需要应用到JSP的唯一一件事是Java编译器。要让网络服务器知道编译器的位置,将环境变量JAVA.HOME设到JSDK的安装目录。如果你是在Windows上安装并且接受预设目录,将这行程序代码setJAVA.HOME=C:1.2.2加到你的autoexec.bat档案并且重新开机。  在安装好JSDK之后,下载并且安装JSWDK或beta版的Tomcat,以Java为主的Apache网络服务器。安装在哪里并不重要,重要的是你可以找到它。一般而言,它会放在上层目录,这种方式可以让你取代JSWDK或JSDK的网络服务器,不需要移动其它的网络服务器。在你安装好这个档案之后,你就可以准备发展JSP了。    在你正确的安装JSWDK之后,执行startserver指令文件来激活网络服务器,预设通讯端口为8080。要看你在激活服务器之后是均C有正确的安装工具,你可以加载范例JSP档案中的任何一个(http://localhost:8080/examples/jsp/)。如果你能够成功的执行一个范例档案,你可以知道你已经正确的设定好软件了。如果你在激活服务器的控制台窗口看到错误讯息,那么你需要解决这个问题。最常发生的问题是没有设定(或者不正确设定)环境变量JAVA.HOME。要检视目前的环境设定,在DOS模式下键入set。    开始    解释JSP语法之前,先建立一个显示目前日期与时间的快速网页并且将它储存成sample.jsp:            FirstPage            

Todayis:    <%=newjava.util.Date()%>    

        .  \n将这个档案与你所有的HTML与JSP网页放在你JSWDK安装目录下的网页目录里.你可以在http://localhost:8080/sample.jsp下载此页.当你第一次参观这个网页时,网站服务器会将JSP翻译成Javaservlet程序代码,那你就会看到目前的日期与时间.    现在你已经下载,安装,并且架构好发展环境,你已经准备好要了解JSP语法与建立你自己的JSP为主的解决之道.  JSP开发入门3  JSP语法的基本原理安装之后,接下来我们要讨论JSP的语法.如果要偷懒,你可以下载语法卡而如果你不熟悉Java的程序设计,你可能会想要参考Sun的使用手册;然而,网站建立者不应该做太多的Java发展。除了几个函式呼叫之外,出现在你JSP网页上的Java程序代码应该将它减到最少;    记住这点之后,现在让我们先来看看JSP的编译器指引与指令组件,之后我们将解释JavaBeans与内部对象.JSP编译器指引与指令组件有五种型态.JSP1.0之后,大部分的JSP是包含在以<%作为开始%>作为结束的单一卷标里.新的JSP1.1规格已经发表了,它同时也与XML兼容.  JSP的编译器指引与指令组件    编译器指示       <%@编译器指示%>    声明          <%!声明%>    表达式        <%=表达式%>    程序代码段/小型指令   <%程序代码片段%>    注释          <%--注释--%>    编译器指示    JSP的编译器指示是针对JSP引擎。它们并不会直接产生任何看得见的输出;相反的,它们是在告诉引擎如何处理其它的JSP网页。它们永远包含在<%@?%>卷标里。两个主要的指引是page与include。我们不会讨论taglib编译器指引但它可以在JSP1.1里用来建立自订卷标。    你几乎可以在你所有的JSP网页最上面找到page编译器指示。虽然这不是必须的,但它可以让你指定到哪里可以找到支持的Java类别这类的事:  \n  <%@pageimport="java.util.Date"%>,     当发生Java问题的事件时应该将讯息传送到哪里:    <%@pageerrorPage="errorPage.jsp"%>,     以及你是?需要为使用者管理通话期的信息,可能存取多个网页(稍后在JavaBeans里会有更多通话期的讨论):    <%@pagesession="true"%>。    include编译器指示让你将你的内容分成几个可管理的组件,就像那些有表头或脚注的网页。所包含的网页可以是固定格式的HTML网页或者是JSP内容的网页:    <%@includefile="filename.jsp"%>。    宣告    JSP声明让你定义网页层的变量,来储存信息或定义支持的函式,让JSP网页的其余部分能够使用。如果你发现自己有太多的程序代码,你最好将它们放在不同的Java类别里。你可以在<%!?%>卷标里找到声明。记住要在变量声明的后面加上分号,就跟任何有效的Java叙述的形式一样:<%!inti=0;%>。    表达式    JSP里有表达式,评估表达式的结果可以转换成字符串并且直接使用在输出网页上。JSP运算是属于<%=?%>卷标里,并不包含分号,加引号字符串的无用部分。    <%=i%>    <%="Hello"%>。    程序代码段/小型指令文件    JSP程序代码片段或小型指令文件是包含在<%?%>\n卷标里。当网络服务器接受这段请求时,这段Java程序代码会执行。小型指令文件可以是原始的HTML或XML,其内部的程序代码片段可以让你建立有条件的执行程序代码,或者只是一些使用另一块程序代码的东西。举例来说,下列的程序代码结合了表达式与小型指令文件,在H1,H2,H3,以及H4卷标里显示字符串"Hello"。小型指令文件不限于一行的原始程序代码:    <%for(inti=1;i<=4;i++){%>    >Hello>    <%}%>。    注释    最后一个主要JSP组件是嵌入式注释。虽然你可以在你的档案里包含HTML注释,如果使用者检视网页的原始码,他们也会看到这些注释。如果你不要让使用者看到你的批注,你可以将它放在<%--?--%>卷标里:    <%--针对服务器端的注释--%>。  JSP开发入门4   JSP与JavaBean    虽然你可以在小型指令文件里放入一大块的程序代码,但是大多数的Java程序代码是属于可以重复使用的组件,称为JavaBean。JavaBean就跟ActiveX控件一样:它们提供已知的功能,并且是为了可随时重复使用的目的而设计的。    JavaBean的价值在于它可以经由一组特性来使用,而这些特性则提供对JavaBean设定的存取。以人来作范例,此人就是JavaBean,而他的姓名,社会福利安全号码,以及住址可以是特性。对于JSP网站,基本上你是将'JavaBean'动态的连接到你的网站。    假设JavaBean是在建立网站之前建好的,你要做的第一件事是告诉JSP网页它所需要使用JavaBean.这工作可以用卷标来完成:.    卷标需要你以id属性来辨识豆子.在这里,你提供一个名称让JSP网页来辨识豆子,除了id属性之外,你也必须告诉网页要到哪里去找这个豆子,或者是它的Java类别名称。类别属性提供如何在各式方法之中找到它,最后一个需要的组件是scope\n属性.有了范围属性的帮助,你可以告诉豆子,要它为单一网页(预设)[scope="page"];为一个被请求的网页[scope="request"];为通话期[scope="session"];或为整个应用程序[scope="application"]来维护它自己的信息.对于通话期范围,你可以很容易的维护JSP网页里的项目,例如购物车。    一但你宣告了JavaBean之后,你就可以存取它的特性来订定它。要取得一特性的值,使用卷标。有了卷标,你可以指定要使用的豆子名称(从useBean的id字段),以及你要取得值的特性。接着,真正的值就会放在输出里:.    要更改JavaBean的特性,你需要使用卷标.对这个卷标,你也需要辨认豆子以及要修正的特性,除此之外,你还需要提供新值.如果命名正确,这些可以直接经由规定的格式取得:;     要从一参数取得,你必须直接命名此特性以及参数:;     或是直接以名称与值来设定:or/>.    有关JavaBean的最后一点:要让网络服务器可以找到JavaBean,你需要将它们的类别档案放在特别位置。对JSWDK而言,最简单的地方是在安装目录里的类别目录,例如jswdk-1.0.1classes.  JSP开发入门5   JSP的内部对象  最后一个与JSP语法有关的组件叫做内部对象.在JSP小型指令文件内,你可以存取这些内部对象来与执行JSP网页的servlet环境相互作用。许多对内部对象的存取应该要简化。然而,这些是范例,它们的存取都是可接受的,要完整的利用内部对象设定则需要对最新的JavaServletAPI有所了解。    下表列出你可以使用的内部对象。    内部对象说明    request  客户端请求,此请求会包含来自GET/POST请求的参数    response  网页传回客户端的响应    pageContext网页的属性是在这里管理    session  与请求有关的会话  \n  applicationservlet正在执行的内容    out    用来传送响应的输出流    config   servlet的架构对象    page    JSP网页本身    exception  针对错误网页,未捕捉的例外    那么,这些是做什么的,而你应该如何使用它们呢?基本上,在你的小型指令文件里,你可以使用它们来存取执行JSP程序代码的servlet。为了避免谈论到太多ServletAPI的细节,让我们来检视一些你可以利用它们来做的事:     不必使用表达式,你可以直接存取内部out对象来打印一些东西到response:    <%out.println("Hello");%>.     不必直接传送参数到JavaBean,你可以藉由请求对象来取得参数的值:    <%Stringname=request.getParameter("name");out.println(name);%>。    当你以JSP写了许多的应用程序之后,如果你建立了JavaBeans或者发现你自己将太多的Java原始码放入你的JSP档案,你需要建立支持的Java类别,这样可以鼓励重复使用并且降低JSP网页转换时所需要的时间。当你需要建立Java类别时,你必须:    将JDSWK的安装目录in目录加到你的PATH。在你的autoexec.bat档案的PATH行的最后,加入C:1.2.2in;。    以下面的指令将JAR档案复制到jrelibext目录:    copyc:jswdk-1.0.1libservlet.jarc:jdk1.2.2jrelibext.  \nJSP开发导引JavaServerPages(JSP)是一种以Java为主的跨平台web开发语言。  JSP与微软的ActiveServerPages兼容,但它是使用类似HTML的卷标以及Java程序代码段而不是VBScript。当你所使用的网站服务器没有提供本地ASP支持,也就是Apache或Netscape服务器时,你可以考虑使用JSP。虽然你可以取得这些服务器的ASP附加配备模块,但价格相当昂贵,而目前Sun并没有对你所需要的JSP组件收取费用(虽然Sun未来有可能会收费)。对于Solaris与Linux以及Windows,这些组件也都很容易取得。   请不要将JSP与伺服端的JavaScript混为一谈。网站服务器会自动将以JSP写成的Java程序代码段转换成Javaservlets。而许多先前必须以Perl手写程序或服务器特定的API(如ASP)控制的功能也都可透过JSP来自动化处理。现在就让我们开始动手帮助你建立一个可执行JSP范例网站。安装好你的机器来使用JSP   你将会需要Java2软件开发工具(JSDK),它原来的名称是Java发展工具(JDK)以及JavaServer网站发展工具(JSWDK),Tomcat,或是其它支持JSP的网络服务器。Sun免费提供JSDK与JSWDK来供Windows,Solaris,以及Linux平台使用。    如果你想要在你目前的网络服务器上使用JSP,但服务器本身并不支持JSP与Javaservlets,你可以试试看Allaire的Jrun,它的作用就像是针对Netscape企业版与FastTrack服务器、微软的网际网络信息服务器(IIS)与个人网络服务器(PWS)、Apache、以及其它服务器的网络服务器附加设备。你也可以使用Apache网络服务器的Java版本,最新的JSWDK里有提供。下载与安装你需要的组件  目前发布的1.2.2-001,JSDK可下载的版本是以可安装的压缩档形式。下载的档案大约是20MB,可提供完整的Java发展环境,让你能建立利用标准API为核心的Java解决之道。然而,你的网络服务器需要应用到JSP的唯一一件事是Java编译器。要让网络服务器知道编译器的位置,将环境变量JAVA.HOME设到JSDK的安装目录。如果你是在Windows上安装并且接受预设目录,将这行程序代码setJAVA.HOME=C:1.2.2加到你的autoexec.bat档案并且重新开机。  在安装好JSDK之后,下载并且安装JSWDK或beta版的Tomcat,以Java为主的Apache网络服务器。安装在哪里并不重要,重要的是你可以找到它。一般而言,它会放在上层目录,这种方式可以让你取代JSWDK或JSDK的网络服务器,不需要移动其它的网络服务器。在你安装好这个档案之后,你就可以准备发展JSP了。\n   在你正确的安装JSWDK之后,执行startserver指令文件来激活网络服务器,预设通讯端口为8080。要看你在激活服务器之后是均C有正确的安装工具,你可以加载范例JSP档案中的任何一个(http://localhost:8080/examples/jsp/)。如果你能够成功的执行一个范例档案,你可以知道你已经正确的设定好软件了。如果你在激活服务器的控制台窗口看到错误讯息,那么你需要解决这个问题。最常发生的问题是没有设定(或者不正确设定)环境变量JAVA.HOME。要检视目前的环境设定,在DOS模式下键入set。开始解释JSP语法之前,先建立一个显示目前日期与时间的快速网页并且将它储存成sample.jsp:FirstPage

Todayis:<%=newjava.util.Date()%>

.  将这个档案与你所有的HTML与JSP网页放在你JSWDK安装目录下的网页目录里.你可以在http://localhost:8080/sample.jsp下载此页.当你第一次参观这个网页时,网站服务器会将JSP翻译成Javaservlet程序代码,那你就会看到目前的日期与时间.  现在你已经下载,安装,并且架构好发展环境,你已经准备好要了解JSP语法与建立你自己的JSP为主的解决之道.JSP语法的基本原理  安装之后,接下来我们要讨论JSP的语法.如果要偷懒,你可以下载语法卡而如果你不熟悉Java的程序设计,你可能会想要参考Sun的使用手册;然而,网站建立者不应该做太多的Java发展.除了几个函式呼叫之外,出现在你JSP网页上的Java程序代码应该将它减到最少;  记住这点之后,现在让我们先来看看JSP的编译器指引与指令组件,之后我们将解释JavaBeans与内部对象.JSP编译器指引与指令组件有五种型态.JSP1.0之后,大部分的JSP是包含在以<%作为开始%>作为结束的单一卷标里.新的JSP\n1.1规格已经发表了,它同时也与XML兼容.JSP的编译器指引与指令组件 编译器指示<%@编译器指示%>   声明<%!声明%>   表达式<%=表达式%>   程序代码段/小型指令<%程序代码片段%>   注释<%--注释--%>   编译器指示   JSP的编译器指示是针对JSP引擎。它们并不会直接产生任何看得见的输出;相反的,它们是在告诉引擎如何处理其它的JSP网页。它们永远包含在<%@?%>卷标里。两个主要的指引是page与include。我们不会讨论taglib编译器指引但它可以在JSP1.1里用来建立自订卷标。  你几乎可以在你所有的JSP网页最上面找到page编译器指示。虽然这不是必须的,但它可以让你指定到哪里可以找到支持的Java类别这类的事:<%@pageimport="java.util.Date"%>, 当发生Java问题的事件时应该将讯息传送到哪里:<%@pageerrorPage="errorPage.jsp"%>, 以及你是?需要为使用者管理通话期的信息,可能存取多个网页(稍后在JavaBeans里会有更多通话期的讨论):<%@pagesession="true"%>。  include编译器指示让你将你的内容分成几个可管理的组件,就像那些有表头或脚注的网页。所包含的网页可以是固定格式的HTML网页或者是JSP内容的网页:<%@includefile="filename.jsp"%>。\n宣告  JSP声明让你定义网页层的变量,来储存信息或定义支持的函式,让JSP网页的其余部分能够使用。如果你发现自己有太多的程序代码,你最好将它们放在不同的Java类别里。你可以在<%!?%>卷标里找到声明。记住要在变量声明的后面加上分号,就跟任何有效的Java叙述的形式一样:<%!inti=0;%>。表达式  JSP里有表达式,评估表达式的结果可以转换成字符串并且直接使用在输出网页上。JSP运算是属于<%=?%>卷标里,并不包含分号,加引号字符串的无用部分。<%=i%><%="Hello"%>。程序代码段/小型指令文件  JSP程序代码片段或小型指令文件是包含在<%?%>卷标里。当网络服务器接受这段请求时,这段Java程序代码会执行。小型指令文件可以是原始的HTML或XML,其内部的程序代码片段可以让你建立有条件的执行程序代码,或者只是一些使用另一块程序代码的东西。举例来说,下列的程序代码结合了表达式与小型指令文件,在H1,H2,H3,以及H4卷标里显示字符串"Hello"。小型指令文件不限于一行的原始程序代码:<%for(inti=1;i<=4;i++){%>>Hello><%}%>。注释  最后一个主要JSP组件是嵌入式注释。虽然你可以在你的档案里包含HTML注释,如果使用者检视网页的原始码,他们也会看到这些注释。如果你不要让使用者看到你的批注,你可以将它放在<%--?--%>卷标里:<%--针对伺服端的注释--%>。JSP与JavaBean   虽然你可以在小型指令文件里放入一大块的程序代码,但是大多数的Java程序代码是属于可以重复使用的组件,称为JavaBean。JavaBean就跟ActiveX控件一样:它们提供已知的功能,并且是为了可随时重复使用的目的而设计的。\n   JavaBean的价值在于它可以经由一组特性来使用,而这些特性则提供对JavaBean设定的存取。以人来作范例,此人就是JavaBean,而他的姓名,社会福利安全号码,以及住址可以是特性。对于JSP网站,基本上你是将'JavaBean'动态的连接到你的网站。   假设JavaBean是在建立网站之前建好的,你要做的第一件事是告诉JSP网页它所需要使用JavaBean.这工作可以用卷标来完成:.   卷标需要你以id属性来辨识豆子.在这里,你提供一个名称让JSP网页来辨识豆子,除了id属性之外,你也必须告诉网页要到哪里去找这个豆子,或者是它的Java类别名称。类别属性提供如何在各式方法之中找到它,最后一个需要的组件是scope属性.有了范围属性的帮助,你可以告诉豆子,要它为单一网页(预设)[scope="page"];为一个被请求的网页[scope="request"];为通话期[scope="session"];或为整个应用程序[scope="application"]来维护它自己的信息.对于通话期范围,你可以很容易的维护JSP网页里的项目,例如购物车。   一但你宣告了JavaBean之后,你就可以存取它的特性来订定它。要取得一特性的值,使用卷标。有了卷标,你可以指定要使用的豆子名称(从useBean的id字段),以及你要取得值的特性。接着,真正的值就会放在输出里:.   要更改JavaBean的特性,你需要使用卷标.对这个卷标,你也需要辨认豆子以及要修正的特性,除此之外,你还需要提供新值.如果命名正确,这些可以直接经由规定的格式取得:;    要从一参数取得,你必须直接命名此特性以及参数:;    或是直接以名称与值来设定:or/>.有关JavaBean的最后一点:要让网络服务器可以找到JavaBean,你需要将它们的类别档案放在特别位置。对JSWDK而言,最简单的地方是在安装目录里的类别目录,例如jswdk-1.0.1classes.JSP的内部对象\n   最后一个与JSP语法有关的组件叫做内部对象.在JSP小型指令文件内,你可以存取这些内部对象来与执行JSP网页的servlet环境相互作用。许多对内部对象的存取应该要简化。然而,这些是范例,它们的存取都是可接受的,要完整的利用内部对象设定则需要对最新的JavaServletAPI有所了解。   下表列出你可以使用的内部对象。 内部对象说明 request客户端请求,此请求会包含来自GET/POST请求的参数   response网页传回客户端的响应   pageContext网页的属性是在这里管理   session与请求有关的会话   applicationservlet正在执行的内容   out用来传送响应的输出流   configservlet的架构对象   pageJSP网页本身   exception针对错误网页,未捕捉的例外        那么,这些是做什么的,而你应该如何使用它们呢?基本上,在你的小型指令文件里,你可以使用它们来存取执行JSP程序代码的servlet。为了避免谈论到太多ServletAPI的细节,让我们来检视一些你可以利用它们来做的事: \n不必使用表达式,你可以直接存取内部out对象来打印一些东西到response:<%out.println("Hello");%>. 不必直接传送参数到JavaBean,你可以藉由请求对象来取得参数的值:<%Stringname=request.getParameter("name");out.println(name);%>。当你以JSP写了许多的应用程序之后,如果你建立了JavaBeans或者发现你自己将太多的Java原始码放入你的JSP档案,你需要建立支持的Java类别,这样可以鼓励重复使用并且降低JSP网页转换时所需要的时间。当你需要建立Java类别时,你必须:将JDSWK的安装目录bin目录加到你的PATH。在你的autoexec.bat档案的PATH行的最后,加入C:1.2.2bin;。 以下面的指令将JAR档案复制到jrelibext目录:copyc:jswdk-1.0.1libservlet.jarc:jdk1.2.2jrelibext.JSP其他相关资源:ServletsandJavaServerPages(JSP)1.0:ATutorialJavaServerPagesTM:ADeveloper'sPerspectiveJAVASERVERPAGESTMNEWS&ARTICLESJAVASERVERPAGESTMTECHNICALRESOURCESSERVLETSTAVERNEservlets.com\nJSP技术简介一、JSP技术概述    在Sun正式发布JSP(JavaServerPages)之后,这种新的Web应用开发技术很快引起了人们的关注。JSP为创建高度动态的Web应用提供了一个独特的开发环境。按照Sun的说法,JSP能够适应市场上包括ApacheWebServer、IIS4.0在内的85%的服务器产品。即使您对ASP"一往情深",我们认为,关注JSP的发展仍旧很有必要。    ㈠JSP与ASP的简单比较    JSP与Microsoft的ASP技术非常相似。两者都提供在HTML代码中混合某种程序代码、由语言引擎解释执行程序代码的能力。在ASP或JSP环境下,HTML代码主要负责描述信息的显示样式,而程序代码则用来描述处理逻辑。普通的HTML页面只依赖于Web服务器,而ASP和JSP页面需要附加的语言引擎分析和执行程序代码。程序代码的执行结果被重新嵌入到HTML代码中,然后一起发送给浏览器。ASP和JSP都是面向Web服务器的技术,客户端浏览器不需要任何附加的软件支持。    ASP的编程语言是VBScript之类的脚本语言,JSP使用的是Java,这是两者最明显的区别。此外,ASP与JSP还有一个更为本质的区别:两种语言引擎用完全不同的方式处理页面中嵌入的程序代码。在ASP下,VBScript代码被ASP引擎解释执行;在JSP下,代码被编译成Servlet并由Java虚拟机执行,这种编译操作仅在对JSP页面的第一次请求时发生。    ㈡运行环境    Sun公司的JSP主页在http://www.javasoft.com/products/jsp/index.html,从这里还可以下载JSP规范,这些规范定义了供应商在创建JSP引擎时所必须遵从的一些规则。    执行JSP代码需要在服务器上安装JSP引擎。此处我们使用的是Sun的JavaServerWebDevelopmentKit(JSWDK)。为便于学习,这个软件包提供了大量可供修改的示例。安装JSWDK之后,只需执行startserver命令即可启动服务器。在默认配置下服务器在端口8080监听,使用http://localhost:8080即可打开缺省页面。    在运行JSP示例页面之前,请注意一下安装JSWDK的目录,特别是"work"子目录下的内容。执行示例页面时,可以在这里看到JSP页面如何被转换成Java源文件,然后又被编译成class文件(即Servlet)。JSWDK软件包中的示例页面分为两类,它们或者是JSP文件,或者是包含一个表单的HTML文件,这些表单均由JSP代码处理。与ASP一样,JSP中的Java代码均在服务器端执行。因此,在浏览器中使用"查看源文件"菜单是无法看到JSP\n源代码的,只能看到结果HTML代码。所有示例的源代码均通过一个单独的"examples"页面提供。    ㈢JSP页面示例    下面我们分析一个简单的JSP页面。您可以在JSWDK的examples目录下创建另外一个目录存放此文件,文件名字可以任意,但扩展名必须为.jsp。从下面的代码清单中可以看到,JSP页面除了比普通HTML页面多一些Java代码外,两者具有基本相同的结构。Java代码是通过<%和%>符号加入到HTML代码中间的,它的主要功能是生成并显示一个从0到9的字符串。在这个字符串的前面和后面都是一些通过HTML代码输出的文本。      JSP页面      <%@pagelanguage="java"%>   <%!Stringstr="0";%>   <%for(inti=1;i<10;i++){   str=str+i;   }%>   JSP输出之前。   

   <%=str%>   

   JSP输出之后。          这个JSP页面可以分成几个部分来分析。  首先是JSP指令。它描述的是页面的基本信息,如所使用的语言、是否维持会话状态、是否使用缓冲等。JSP指令由<%@开始,%>结束。在本例中,指令"<%@pagelanguage="java"%>"只简单地定义了本例使用的是Java语言(当前,在JSP规范中Java是唯一被支持的语言)。  接下来的是JSP声明。JSP声明可以看成是定义类这一层次的变量和方法的地方。JSP声明由<%!开始,%>结束。如本例中的"<%!Stringstr="0";%>"定义了一个字符串变量。在每一项声明的后面都必须有一个分号,就象在普通Java类中声明成员变量一样。  位于<%和%>之间的代码块是描述JSP页面处理逻辑的Java代码,如本例中的for循环所示。  最后,位于<%=和%>之间的代码称为JSP表达式,如本例中的"<%=str%>"所示。JSP表达式提供了一种将JSP生成的数值嵌入HTML页面的简单方法。  \n  会话状态维持是Web应用开发者必须面对的问题。有多种方法可以用来解决这个问题,如使用Cookies、隐藏的表单输入域,或直接将状态信息附加到URL中。JavaServlet提供了一个在多个请求之间持续有效的会话对象,该对象允许用户存储和提取会话状态信息。JSP也同样支持Servlet中的这个概念。    在Sun的JSP指南中可以看到许多有关隐含对象的说明(隐含的含义是,这些对象可以直接引用,不需要显式地声明,也不需要专门的代码创建其实例)。例如request对象,它是HttpServletRequest的一个子类。该对象包含了所有有关当前浏览器请求的信息,包括Cookies,HTML表单变量等等。session对象也是这样一个隐含对象。这个对象在第一个JSP页面被装载时自动创建,并被关联到request对象上。与ASP中的会话对象相似,JSP中的session对象对于那些希望通过多个页面完成一个事务的应用是非常有用的。    为说明session对象的具体应用,接下来我们用三个页面模拟一个多页面的Web应用。第一个页面(q1.html)仅包含一个要求输入用户名字的HTML表单,代码如下:            请输入您的姓名:                   第二个页面是一个JSP页面(q2.jsp),它通过request对象提取q1.html表单中的thename值,将它存储为name变量,然后将这个name值保存到session对象中。session对象是一个名字/值对的集合,在这里,名字/值对中的名字为"thename",值即为name变量的值。由于session对象在会话期间是一直有效的,因此这里保存的变量对后继的页面也有效。q2.jsp的另外一个任务是询问第二个问题。下面是它的代码:         <%@pagelanguage="java"%>   <%!Stringname="";%>   <%   name=request.getParameter("thename");   session.putValue("thename",name);   %>  \n 您的姓名是:<%=name%>   

      您喜欢吃什么?      

                第三个页面也是一个JSP页面(q3.jsp),主要任务是显示问答结果。它从session对象提取thename的值并显示它,以此证明虽然该值在第一个页面输入,但通过session对象得以保留。q3.jsp的另外一个任务是提取在第二个页面中的用户输入并显示它:         <%@pagelanguage="java"%>   <%!Stringfood="";%>   <%   food=request.getParameter("food");   Stringname=(String)session.getValue("thename");   %>   您的姓名是:<%=name%>   

   您喜欢吃:<%=food%>          JavaBean是一种基于Java的软件组件。JSP对于在Web应用中集成JavaBean组件提供了完善的支持。这种支持不仅能缩短开发时间(可以直接利用经测试和可信任的已有组件,避免了重复开发),也为JSP应用带来了更多的可伸缩性。JavaBean组件可以用来执行复杂的计算任务,或负责与数据库的交互以及数据提取等。如果我们有三个JavaBean,它们分别具有显示新闻、股票价格、天气情况的功能,则创建包含所有这三种功能的Web页面只需要实例化这三个Bean,使用HTML表格将它们依次定位就可以了。    为说明在JSP环境下JavaBean的应用,我们创建了一个名为TaxRate的Bean。它有两个属性,即Product(产品)和Rate(税率)。两个set方法分别用来设置这两个属性,两个get方法则用于提取这两个属性。在实际应用中,这种Bean\n一般应当从数据库提取税率值,此处我们简化了这个过程,允许任意设定税率。下面是这个Bean的代码清单:   packagetax;   publicclassTaxRate{   StringProduct;   doubleRate;   publicTaxRate(){   this.Product="A001";   this.Rate=5;   }   publicvoidsetProduct(StringProductName){   this.Product=ProductName;   }   publicStringgetProduct(){   return(this.Product);   }   publicvoidsetRate(doublerateValue){   this.Rate=rateValue;   }   publicdoublegetRate(){   return(this.Rate);   }   }    在JSP页面中应用上述Bean要用到标记。依赖于具体使用的JSP引擎的不同,在何处配置以及如何配置Bean的方法也可能略有不同。本文将这个Bean的.class文件放在c:jswdk-1.0examplesWEB-INFjspeansax目录下,这里的tax是一个专门存放该Bean的目录。下面是一个应用上述Bean的示例页面:         <%@pagelanguage="java"%>      <%taxbean.setProduct("A002");   taxbean.setRate(17);   %>   使用方法1:

   产品:<%=taxbean.getProduct()%>
   税率:<%=taxbean.getRate()%>   

   <%taxbean.setProduct("A003");   taxbean.setRate(3);   %>  \n 使用方法2:

   产品:   
   税率:          在标记内定义了几个属性,其中id是整个JSP页面内该Bean的标识,scope属性定义了该Bean的生存时间,class属性说明了该Bean的类文件(从包名开始)。    这个JSP页面不仅使用了Bean的set和get方法设置和提取属性值,还用到了提取Bean属性值的第二种方法,即使用标记。中的name属性即为中定义的Bean的id,它的property属性指定的是目标属性的名字。    事实证明,JavaServlet是一种开发Web应用的理想构架。JSP以Servlet技术为基础,又在许多方面作了改进。JSP页面看起来象普通HTML页面,但它允许嵌入执行代码,在这一点上,它和ASP技术非常相似。利用跨平台运行的JavaBean组件,JSP为分离处理逻辑与显示样式提供了卓越的解决方案。JSP必将成为ASP技术的有力竞争者。 \nJSP教程(一)JSP概述  JSP(IAVASERVERPAGES)是由Sun公司在java语言上开发出来的一种动态网页制作技术,其可使您可以将网页中的动态部分和静态的HTML相分离。您可以使用平常得心应手的工具并按照平常的方式来书写HTML语句。然后,将动态部分用特殊的标记嵌入即可,这些标记常常以“<%”开始并以“%>”结束。例如,这儿有一个JSP页面:jsp教程<%out.println(“helloworld”);%>它将输出“helloworld”。  通常,您要将文件以“.jsp”为扩展名,并将它放置到任何您可以放置普通WEB页面的路径下。尽管JSP文件看起来更象是HTML文件而不是Servlet文件,但,事实上,它恰恰将转换为Servlet文件,其中的静态HTML仅仅用来输出Servlet服务方法返回的信息。如果JSPpages已经被转换为Servlet且Servlet被编译进而被装载(在第一次被Request时),当您再次Request此JSP页面时,将察觉不到一瞬的延迟。也请留意这个现象,一些WebServers允许您为它定义别名,从而,好象一个URL是指向一个HTML,但事实上它指向的是一个Servlet或JSPpages.  构造一个JSPpage,除了可内嵌的规则的HTML,还有三类主要的JSP元素:Scriptingelements,Directives,和Actions.使用Scriptingelements您可以定义最终转换为Servlet的部分,Directives使您可以控制这个Servlet的整体结构,而Actions使您可以指定可重用的已有组件,另外,还可控制JSP引擎的运行。为了简化Scriptingelements,您可以在某一段上利用一些预定义的变量,如request。本教程式是以JSP最新的1.1版本,进行讲解的。其语法概括如下表,其详细使用在随后的课程中详细讲解.\nJSP元素语法解释JSPExpression<%=表达式%>Expression用于计算并用于输出。表达式,可使用的预定义的变量有request,response,out,session,application,config,andpageContext(在Sriptlets中也可使用)。JSPScriptlet<%代码%>插入用于服务的代码。代码JSPDeclaration属于Servlet部分的代码但并不是服务方法。代码JSPpageDirective<%@pageatt=”val”%>指向Servlet引擎的路径。.以下是其合法的属性(缺省值加粗):limport="package.class"lcontentType="MIME-Type"lisThreadSafe="true|false"lsession="true|false"lbuffer="sizekb|none"lautoflush="true|false"\nlextends="package.class"linfo="message"lerrorPage="url"lisErrorPage="true|false"llanguage="java"JSPincludeDirective<%@includefile=”URL”%>当JSPpage被翻译成Servlet时将被包含进去的本地系统上的文件。这个URL必须是相对的。当页面被请求时才用“jsp:includeaction”调入。JSP注释<%--注释--%>当JSP转换为Servlet时将被忽略。<--注释-->TheJSP:includeAction在页面被请求(Requested)时调入文件。如果您想要在页面被转化(Translated)时将文件包含进来,则,请使用上面所提到的directive来代替。警告:在一些服务器上(Servers),被包含的文件只能是HTML或JSP,一般以文件的后缀名来判定。Thejsp:useBeanAction…..寻找或生成一个JavaBean.可能的属性是:lid="name"lscope="page|request|session|application"\nlclass="package.class"ltype="package.class"lbeanName="package.class"Thejsp:setPropertyAction设置bean的属性,通过明确的指定或使用request得到的参数。合法的属性:lname="beanName"lproperty="propertyName|*"lparam="parameterName"lvalue="val”Thejsp:getPropertyAction检索并输出bean的属性。  Thejsp:forwardAction向前请求(request)另一个页面。Thejsp:pluginAction生成特定的浏览器的OBJECT或EMBED标签,用来明确运行Applet所使用的JAVA插件(plugin)。(二)\nJSPdirectivemso-hansi-font-family:"">影响servlet类的整体结构。它常用以下形式:  <%@directiveattribute=”value”%>  而且,您可以将多个属性写在一个语句中:  <%@directiveattribute1="value1"  attribute2="value2"  attributeN="valueN"%>  有两种主要的directive:  page,允许您做一些类似importclasses的事,定义servlet的超类(Superclass),mso-hansi-font-family:"">等;  include,允许您将文件插入servlet类中(当JSP文件翻译为servlet时)。  一、JSPpageDirective语法:<%@page[language="java"][extends="package.class"][import="{package.class|.*},..."][session="true|false"][buffer="none|8kb|sizekb"][autoFlush="true|false"][isThreadSafe="true|false"][info="text"][errorPage="relativeURL"][contentType="mimeType[;charset=characterSet]"|"text/html;charset=ISO-8859-1"][isErrorPage="true|false"]%>  Pagedirectivemso-hansi-font-family:"">允许您定义一些区分大小写的属性:  (1)import=“package.class”或import=“package.class1,..,package.classN”。mso-hansi-font-family:"">您可以定想要import的packages。例如:\n<%@pageimport="java.util.*"%>import属性是这几个属性中唯一一个可以在一个JSP中出现多次的。  (2)contenType=“MIME=Type”或contentType=“MIME-Type;charset=Character-Set”mso-hansi-font-family:"">它指定输出的MIME类型。缺省为“text/html”。例如:<%@pagecontentType="text/plain"%>"在scriptlet中等价于:<%response.setContentType("text/plain");%>  (3)isThreadSafe=“true|false”.如果值为“true”(缺省)表示:将进行普通的servlet处理,多个请求将被一个servlet实例并行处理,在这种情况下,编程人员同步访问多个实例变量。值为“false”时表示:servlet将实现单线程模式(SingleThreadModel),不管请求是顺序提交还是并发出现,都将提供不同的分离的servlet实例。  (4)session=”true|false”。如果值为“true”(缺省)表示:预定义变量session(继承HttpSession)应该绑定到一个已存在的session,否则就应该创建一个并将之绑定。值为“false”时表示:将不使用session变量,如果试图使用,将在JSP向servlet转化时出现错误。  (5)buffer=“sizekb|none”。为JspWriter输出确定缓冲的大小。缺省由服务器而定,但至少要有8kb。  (6)autoflush=”true|false”。如果值为“truemso-hansi-font-family:"">”(缺省)表示:当缓冲满时将自动清空,值为“falsemso-hansi-font-family:"">”时表示:当缓冲满时递出一个异常,这很少使用。当buffer=”none”是若用falsemso-hansi-font-family:"">值是不合法的。  (7)extends=”package.class”。这将为servlet产生一个超类。请特别谨慎的使用这一功能,因为,服务器也许已经定义了一个。  (8)info=“message”。定义一个可以通过调用getServletInfo方法得到的串。  (9)errorPage=“URL”。指定一个JSPmso-hansi-font-family:"">页面来处理任何一个可抛出的但当前页面并未处理的意外错误。\n  (10)isErrorPage=“true|false”。指定当前页面是否可以处理来自另一个页面的错误,缺省为“false”。  (11)language=“java”mso-hansi-font-family:"">。指出以下将使用的语言。不过,不必为这一属性费心,因为,“javamso-hansi-font-family:"">”既是缺省又是唯一合法的选择。二JSPincludeDirective这种directive使您可以在JSP转换为servlet时将一个文件包含进来。语法:}"flush="true"/>mso-hansi-font-family:";mso-font-kerning:0pt">或}"flush="true">}"/>+  URLmso-hansi-font-family:"">通常相对于指向它的JSP页面,但是,普遍使用相对“URL”,您可以使用一个斜杠“/”作为URL的开始来告知系统URLmso-hansi-font-family:"">相对的Webserver的主路径。被包含的文件将以规则的JSP形式来解析,因此,您可以在其中使用静态HTML,scriptingelements,directives,和actions。  让我们来看一个例子,许多站点在每一个页面上包含一个小型的导航条。它通常出现在页面的顶部或左右侧,并包含在每一个页面里。这用includedirective来实现是很自然的,若用规则的HTMLmso-hansi-font-family:"">来把这些语句拷到每一个页面无疑是个梦魇。请看下列代码:JSP教程<%@includefile="/navbar.html"%>  因为文件是在页面被转换时插入的,因此,如果导航条改变了,您需要将所有指向它的JSPmso-hansi-font-family:"">页面全部重新编译一次。如果您的导航条并不常改变这样做无疑是高效的,但是,如果您的被包含文件更改频繁,则建议您使用jsp:includeaction(后面将谈到)来替代,它在页面被请求时才包含文件。(三)--JSP中”预定义变量”的使用为了简化JSP表达式和scriptlets中的代码,提供了8种自动定义的变量,有时称做implicit\nobjects(固有对象)。它们是:request,response,out,session,application,config,pageContext,和page。下面我们来详细的了解它们。  request  与request相联系的是HttpServletRequest类,使您可以得到request的参数(通过getParameter方法),request的类型(GET,POST,HEAD,等等),和引入的HTTP头(cookies,Referer,等等)。严格来说,request是类ServletRequest的一个子类而不是HttpServletRequest类的,事实上,如果request的协议不是HTTP,那麽它几乎不会工作。  response  对客户端的response与HttpServletResponse相连。请注意,因为输出流是放入缓冲的,所以可以设置HTTP状态码和response头,尽管在标准的servlets中不允许将之发送到客户端。  out  这里使用PrintWriter类来发送输出到客户端。然而,为了使response对象有效,可使用一个PrintWrite类的使用缓冲的版本JspWriter。使用session的属性pagedirective,您可以自己定义缓冲的大小,甚至可以在使用了buffer属性后关闭缓冲。也请注意,out仅用于scriptlets之中,因为JSP表达式自动的放入输出流,所以极少需要明确的声明out。  session  应用与request相联系的HttpSession类。因为session是自动创建的,即使没有一个引入的session,这种变量仍可绑定。有一个例外是,如果您用pagedirective关闭session,再试图使用session时将导致错误(在JSP页面向servlet转换时)。  application  使用ServeletContext类,通过使用getServletConfig().getContext()得到。  config  是一个ServletConfig类的对象。  pageContext\n  这是JSP中的一个新的类PageContext,用于精练特定服务器的特点时使用,如提高JspWriters的执行效率。如果您通过这个类访问而不是直接的,您的代码将仍然运行在“规则”的JSP/servlet引擎。  page  在JAVA中不是很有用,它仅仅是用来保存在脚本的语言不是JAVA时的时间。  (四)-JSPActions的使用JSPactions使用您可以动态的插入一个文件,重用JavaBeans组件,前进到另一个页面,或为Java插件生成一个HTML。可以使用的action有:(1)jsp:include--在页面被请求时包含进一个文件。(2)jsp:useBean--找到或实例化一个JavaBean。(3)jsp:setProperty--设置一个JavaBean属性。(4)jsp:getProperty--将JavaBean的属性插入到输出。(5)jsp:forward--让请求者可以向前到一个新的页面。(6)jsp:plugin--用OBJECT或EMBED标签为Javaplugins生成特定的浏览器的代码。  1、jsp:includeAction  这个action使您可以在即将生成的页面上包含进一些文件:    与includedirective不同,这个action是在页面被请求时才将文件包含进来,而,includedirective则是在JSP页面被转换为servlet时包含文件。为了提高效率,includeaction做了一点小小的牺牲,即,它不允许被包含的页面含有一般的JSP代码(例如,不可设置HTTP头),但是,它具有显著的灵活性,如下面的JSP代码,它实现将四个不同的片段插入如下的页面。每一次当标题改变的时候,您仅需修改这四个文件而无须更改主要的JSP页面。WhatsNew.jspJSP教程

What"sNewatChinesecomicsites

Hereisasummaryofourfourmostrecentnewsstories:

    \n
  当然您可以定义自己的HTML文件,但有一点请注意:  您应该将文件放到您的JSP目录下的news目录内。  (五)-JSPActions的使用下jsp:useBeanAction的使用  一、语法:}"type="package.class"}{/>|>其他元素
}  这个action使您能将一个JavaBean装入一个JSP页面。这是一个非常有用的能力,因为它使您可以使用可重用的JAVA类而不需牺牲性能。最简单的语法用于指定一个bean:    这通常意味着“实例化一个类的对象通过指定一个类,并将之与一个通过id指定名称的变量绑定”。然而,就象我们看到的,您可以指定一个scope属性来使得bean不仅仅与当前的页面相联系。在这种情形下,得到一个对已存在的bean的引用是非常有用的,而且,仅当没有相同的id和scope的bean存在时才创建一个新的。现在,您已有了bean,您可以通过jsp:setProperty来修改它,或者,通过使用之前用id指定的名字来使用scriptlet或明确的调用方法。当您说“这个bean有一个称为foo的X类型的属性”,您真正的意思是“这个类有一个称为getFoo的方法,它返回X类型的某类值,还有另一个方法称为setFoo,它以X为参数。”这jsp:setPropertyaction\n将在下一单元详细的介绍,但是现在您既可以给出一个明确的值,给出一个属性来说明此值是从request的参数继承而来,也可以仅仅列出属性来标志此值应该从与属性名同名的参数继承而来。您可以通过调用适用的getXxx方法,或更普遍的,使用jsp:getPropertyaction,来得到已存在的JSP表达式或scriptlet属性。  请注意,为bean指定的类必须在服务器的规则的类路径下,而不是用来保留当改变时自动装载的类的路径。例如,在JavaWebServer上,它和它所用的类必须到类的目录或在lib目录下的一个jar文件内,而不是在servlets的目录下。  下面让我们来看一个非常简单的例子,它装载一个bean并且设置/得到一个简单的串参数。BeanTest.jspReusingJavaBeansinJSP
ReusingJavaBeansinJSP

Message:

SimpleBean.java  以下是bean的原代码:packagehall;publicclassSimpleBean{privateStringmessage="Nomessagespecified";publicStringgetMessage(){return(message);}publicvoidsetMessage(Stringmessage){this.message=message;}\n}  运行结果为:页面输出:ReusingJavaBeansinJSP  b>Message:HelloWWW  二、jsp:useBean的详细用法  最简单的使用bean的方式是:    为了装载bean,需要用jsp:setProperty和jsp:getProperty来修改和检索bean的属性。且,还有两种别的选项。首先,您可以使用容器的格式,也就是:Body
要指出的是,Body部分应该仅在bean第一次实例化时被执行,而不是在每次被找到和使用时。Beans能够被共享,因此,并不是所有的jsp:useBean陈述都产生一个新的bean的实例。其次,除了id或class以外,还有三种属性您可以使用:scope,type,和beanName。这些属性总结如下:  属性  用法id  给一个变量命名,此变量将指向bean。如果发现存在一个具有相同的id和scope的bean则使用之而不新建一个。class  指出bean的完整的包名。scope  指明bean在之上可以被使用的前后关系。有四个可能的值:page,request,session,和application。缺省为page,表明bean仅在当前页可用(保存在当前的PageContext中)。request的一个值表明bean仅用于当前客户端的请求(保存在ServletRequest对象中)。Session的值指出在当前的HttpSession的生命周期内,对象对所有的页面可用。最后,application的值指出对象对所有共享ServletsContext的页面可以使用。使用jsp:useBean仅在没有相同的id和scope的bean时创建一个新的bean,如果已有则使用之,并忽略以jsp:useBean标志开始和结尾的代码。\ntype  指明将指向对象的变量的类型。这必须与类名相匹配或是一个超类或者是一个实现类的接口。记住,变量的名由id属性来指定。beanName  赋予bean一个名字,您应该在Beans的实例化方法中提供。它允许您给出type和一个beanName,并省略类属性。  三、jsp:setPropertyAction  语法:}"}/>  在前面我们就知道了可以使用jsp:setProperty来为一个bean的属性赋值。您可以使用两种方式实现它。其一是,在jsp:useBean后(而不是在之内)使用jsp:setProperty:...  在这种方式中,jsp:setProperty将被执行无论是否已有一个具有相同的id和scope的bean存在。另一种方式是,jsp:setProperty出现在jsp:useBean元素内,如:...
  此种情况下,jsp:setProperty仅在新的对象被实例化时才执行。  以下是四种jsp:setProperty的可用的属性:  属性\n  用法name  这是一个必选属性。它指出哪一个bean的属性将被设置。jsp:usebean必须出现在jsp:setProperty之前。property  这是一个必选属性。表明您将设置哪一个属性。然而,有一个特殊的情况:如果以"*"为值意味着,所有的名称与bean的属性匹配的request参数都将被传递到相应的属性设置方法。value  这是一个可选属性。它指定被设置的属性的值。字符串的值通过相应的对象或包的标准的valueOf方法将自动的转换为numbers,boolean,Boolean,byte,Byte,char,和Character。例如,boolean或Boolean属性的值“true”将通过Boolean.valueOf方法转化,而,一个int或Integer属性的值“42”将通过Integer.valueOf转化。您不能同时使用value和param属性,但,两个都不用是允许的。param  这是一个可选属性。它指明了bean的属性应该继承的request的参数。如果当前的request没有这样的一个参数,就什麽也不做:系统并不将null传给设置属性的方法。因此,您可以使用bean的缺省值。例如下面的这段程序执行“将numberOfItems属性设置为任意numItemsrequest参数的值,如果有这样的一个request参数,否则什么也不做。”  如果您将value和param同时缺省,这和您将param的名称设为bean的属性名相同一样。您可以通过将name的值设置为”*”和省略value和param来自动的使用与bean的属性相应的request的属性。在这种情况下,服务器将反复的查找可用的属性和request参数来匹配具有相同名字的。  四、jsp:getPropertyAction  语法:    这个属性检索出bean的属性的值并将之转化为一个字符串,然后将之插入到输出。它有两个必选属性:name,在之前用jsp:useBean引入的名称,property,必须被插入值的属性。  (六)-怎么在JSP中跳转到别一页面\n在JSP中使用jspforwardAction来实现页面的跳转功能。  语法:}"/>或}">}"/>+  这个action使您可以将request向前到另外一个页面。它只有一个属性,page。Page应有一个相对的URL组成。这可以是一个静态的值或者是能够在被请求的时候计算得到的值,就如下面两个例子一般:"/>!supportEmptyParas]>  现在以一个具体例子来说明:在test1.jsp中使用forward使其跳转到test2.jsp页面中。Test1.jspforwardtest!supportEmptyParas]>!supportEmptyParas]>!supportEmptyParas]>test2.jspforwardtest!supportEmptyParas]>!supportEmptyParas]><%out.println("这是jsp2.jsp页面产生出的输出");%>\n!supportEmptyParas]>  运行test1.jsp,可在浏览器中看见:"这是jsp2.jsp页面产生出的输出"的输出信息。但是如果你在test1.jsp和test2.jsp这两个页面中有参数传递怎么办呢?用get方式吧,不但总的长度有限制,使用现在十分不方便,而且有时候还不安全。其实我们完全可以使用jsp1.1中给forward里提供的para属性就可以解决。现以test3.jsp和test4.jsp来说明。!supportEmptyParas]>Test1.jspforwardtest!supportEmptyParas]>!supportEmptyParas]>!supportEmptyParas]>test2.jspforwardtest!supportEmptyParas]>!supportEmptyParas]><%out.println("这是jsp4.jsp页面产生出的输出"+"
");out.println("姓名:"+request.getParameter("name")+"
");out.println("地址:"+request.getParameter("address")+"
");!supportEmptyParas]>%>  运行test3.jsp,可在浏览器中看见:  "这是jsp4.jsp页面产生出的输出  姓名:powerman\n  地址:北京西大街188号"的输出信息  (七)-pluginAction的使用jsp:pluginAction使您能插入所需的特定的浏览器的OBJECT或EMBED元素来指定浏览器运行一个JAVAApplet所需的插件。语法:[[}"/>]+][textmessageforuser]示例:

Unabletoloadapplet

属性详解:属性用法type=“bean|applet”插件将执行的对象的类型。您必须在bean或applet中指定一个,因为,这个属性没有缺省值。\nclass=”classFileName”插件将执行的JAVA类文件的名称。在名称中您必须包含扩展名。且此文件必须在用“codebase”属性指明的目录下。codebase=“classFileDirectoryName”包含插件将运行的JAVA类的目录或指向这个目录的路径。缺省为此JSP文件的路径。name=”instanceName”Bean或applet的实例的名称。使得被同一个JSP文件调用的bean或applet之间的通信成为可能。archive=“URLToArchive,…”以逗号分隔的路径名列表。是那些用以codebase指定的目录下的类装载器预装载的存档文件所在的路径名。通常,这些存档文件通过网络被安全的加载,可以显著的提高applet的性能。注释和字符引用习惯  您可以使用一些特定的元素来插入注释和一些通常是作为特殊标志的字符。以下是一个总结:语法目的<%--注释--%>JSP形式的注释。将被JSP-to-scriptlet编译器所忽略。任何内嵌JSPscriptingelements,directives,或actins都将被忽略。例:<%@pagelanguage="java"%>ACommentTest

ATestofComments

<%--这部分注释将不会在查看源代码的时候看到--%>HTML形式的注释。直接传送到最终的HTML。任何内嵌JSPscriptingelements,directives,或actins都将被正常的执行。例:\n查看源代码时将看到:<\%在templatetext(静态HTML)中,当您想在页面上输出这个特殊符号(<%)时请如此书写。%\>在scriptingelements中使用,作用与上面的“在属性中的“%>”。<\%在属性中的“<%”。  \nJAVA/JSP学习系列之五(JDBC-ODBC翻页例子)一、运行前准备  建议了一个MSSQLServer7数据库DNS,名称为:Test_DB  数据库中有一个表:guestbook字段为:name(varchar),email(varchar),body(text)  数据库用户为sa密码空,可以自己修改的。二、代码<%@pagecontentType="text/html;charset=gb2312"%><%//变量声明java.sql.ConnectionsqlCon;//数据库连接对象java.sql.StatementsqlStmt;//SQL语句对象java.sql.ResultSetsqlRst;//结果集对象java.lang.StringstrCon;//数据库连接字符串java.lang.StringstrSQL;//SQL语句intintPageSize;//一页显示的记录数intintRowCount;//记录总数intintPageCount;//总页数intintPage;//待显示页码java.lang.StringstrPage;inti,j,k;//设置一页显示的记录数intPageSize=5;//取得待显示页码strPage=request.getParameter("page");\nif(strPage==null){//表明在QueryString中没有page这一个参数,此时显示第一页数据intPage=1;}else{//将字符串转换成整型intPage=java.lang.Integer.parseInt(strPage);if(intPage<1)intPage=1;}//装载JDBC-ODBC驱动程序Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");//设置数据库连接字符串strCon="jdbc:odbc:Test_DB";//连接数据库sqlCon=java.sql.DriverManager.getConnection(strCon,"sa","");//创建SQL语句对象sqlStmt=sqlCon.createStatement();//获取记录总数strSQL="selectcount(*)fromguestbook";sqlRst=sqlStmt.executeQuery(strSQL);//执行SQL语句并取得结果集sqlRst.next();//记录集刚打开的时候,指针位于第一条记录之前intRowCount=sqlRst.getInt(1);sqlRst.close();//关闭结果集\n//记算总页数intPageCount=(intRowCount+intPageSize-1)/intPageSize;//调整待显示的页码if(intPage>intPageCount)intPage=intPageCount;//设置获取数据SQL语句strSQL="selectname,email,bodyfromguestbook";//执行SQL语句并取得结果集sqlRst=sqlStmt.executeQuery(strSQL);//将记录指针定位到待显示页的第一条记录上i=(intPage-1)*intPageSize;for(j=0;jJSP数据库操作例程-数据分页显示-JDBC-ODBCjdbc-odbc留言版

<%//显示数据i=0;while(i\n姓名:<%=sqlRst.getString(1)%>邮件:<%=sqlRst.getString(2)%><%=sqlRst.getString(3)%><%i++;}%>第<%=intPage%>页  共<%=intPageCount%>页  <%if(intPage">下一页<%}%>  <%if(intPage>1){%>">上一页<%}%>\n<%//关闭结果集sqlRst.close();//关闭SQL语句对象sqlStmt.close();//关闭数据库sqlCon.close();%>三、怎么去运行?  将代码存为文件test.jspOrionApplicationServer下:Copy到orion的default-web-app目录下,通过:http://localhost:port/test.jsp访问测试对于Resin,Tomcat,JWS等等,都可以运行通过。\nJAVA/JSP学习系列之六(MySQL翻页例子)一、运行前准备  下载了mysql的jdbc驱动(一个jar文件)并加载在CLASSPATH(方法见《JAVA/JSP学习系列之一(JDK安装)》)  (如果找不到,请从本站下载)  建一个MySQL数据库test  数据库中有一个表:note,字段为:name(varchar)二、下载,安装<%@pagecontentType="text/html;charset=gb2312"%><%java.sql.ConnectionsqlCon;//数据库连接对象java.sql.StatementsqlStmt;//SQL语句对象java.sql.ResultSetsqlRst;//结果集对象java.lang.StringstrCon;//数据库连接字符串java.lang.StringstrSQL;//SQL语句intintPageSize;//一页显示的记录数intintRowCount;//记录总数intintPageCount;//总页数intintPage;//待显示页码java.lang.StringstrPage;inti;//设置一页显示的记录数intPageSize=2;//取得待显示页码\nstrPage=request.getParameter("page");if(strPage==null){//表明在QueryString中没有page这一个参数,此时显示第一页数据intPage=1;}else{//将字符串转换成整型intPage=java.lang.Integer.parseInt(strPage);if(intPage<1)intPage=1;}//装载JDBC驱动程序Class.forName("org.gjt.mm.mysql.Driver").newInstance();//连接数据库sqlCon=java.sql.DriverManager.getConnection("jdbc:mysql://localhost/test");//创建语句对象sqlStmt=sqlCon.createStatement(java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE,java.sql.ResultSet.CONCUR_READ_ONLY);//执行SQL语句strSQL="selectnamefromnote";//执行SQL语句并获取结果集sqlRst=sqlStmt.executeQuery(strSQL);//获取记录总数sqlRst.last();intRowCount=sqlRst.getRow();\n//记算总页数intPageCount=(intRowCount+intPageSize-1)/intPageSize;//调整待显示的页码if(intPage>intPageCount)intPage=intPageCount;%>JSP数据库操作例程-数据分页显示-JDBC2.0-mysql姓名<%if(intPageCount>0){//将记录指针定位到待显示页的第一条记录上sqlRst.absolute((intPage-1)*intPageSize+1);//显示数据i=0;while(i\n<%=sqlRst.getString(1)%><%sqlRst.next();i++;}}%>第<%=intPage%>页  共<%=intPageCount%>页  <%if(intPage">下一页<%}%>  <%if(intPage>1){%>">上一页<%}%><%//关闭结果集sqlRst.close();//关闭SQL语句对象sqlStmt.close();\n//关闭数据库sqlCon.close();%>三、怎么去运行?  见前文《JAVA/JSP学习系列之五(JDBC-ODBC翻页例子)》。\nJAVA/JSP学习系列之八(改写MySQL翻页例子)一、前言  其实,改写后的JDBCData-Source是运行在Servlet中的,通过JNDI去查找数据源。我用Orion试的,将本站《JAVA/JSP学习系列之六(MySQL翻页例子)》简单改写了一下。二、配置(1)JDBC  需要将用到的JDBC驱动Copy到[ORION]/lib目录下(2)data-source  在[ORION]/config/data-sources.xml文件中加入如下:〈data-sourceclass="com.evermind.sql.DriverManagerDataSource"name="mySqlDbpage"location="jdbc/HypersonicCoreDS"xa-location="jdbc/xa/HypersonicXADS"ejb-location="jdbc/mysqlDbPage"connection-driver="org.gjt.mm.mysql.Driver"username="root"password=""url="jdbc:mysql://localhost/test"inactivity-timeout="30"/〉需要注意的是:\n(1)ejb-location这个后面的“jdbc/mysqlDbPage”是JNDI要来查找的。(2)connection-driver为JDBC数据库驱动(3)url是JDBC中的URL(4)username为数据库用户名(5)password为用户密码(6)inactivity-timeout为数据库连接超时,默认为30秒对于其他的地方不要改。三、改写后的代码如下:<%@pagecontentType="text/html;charset=gb2312"%><%@pageimport="java.sql.*,javax.sql.DataSource,javax.naming.InitialContext"%><%//建立一个JNDI查找对象InitialContextJNDI_Context=newInitialContext();//JNDI查找数据源DataSourceds=(DataSource)JNDI_Context.lookup("jdbc/mysqlDbPage");//得到一个数据源连接Connectionconn=ds.getConnection();intintPageSize;//一页显示的记录数intintRowCount;//记录总数intintPageCount;//总页数intintPage;//待显示页码java.lang.StringstrPage;\ninti;//设置一页显示的记录数intPageSize=2;//取得待显示页码strPage=request.getParameter("page");if(strPage==null){//表明在QueryString中没有page这一个参数,此时显示第一页数据intPage=1;}else{//将字符串转换成整型intPage=java.lang.Integer.parseInt(strPage);if(intPage<1)intPage=1;}//得到结果stmt=conn.createStatement();ResultSetsqlRst=stmt.executeQuery("selectf1fromtest");//获取记录总数sqlRst.last();intRowCount=sqlRst.getRow();//记算总页数intPageCount=(intRowCount+intPageSize-1)/intPageSize;//调整待显示的页码\nif(intPage>intPageCount)intPage=intPageCount;%>JSP数据库操作例程-数据分页显示-JDBC2.0-mysql姓名<%if(intPageCount>0){//将记录指针定位到待显示页的第一条记录上sqlRst.absolute((intPage-1)*intPageSize+1);//显示数据i=0;while(i\n<%=sqlRst.getString(1)%><%sqlRst.next();i++;}}%>第<%=intPage%>页  共<%=intPageCount%>页  <%if(intPage">下一页<%}%>  <%if(intPage>1){%>">上一页<%}%><%//关闭结果集sqlRst.close();%>三、怎么去运行?  见前文《JAVA/JSP学习系列之五(JDBC-ODBC翻页例子)》。注意:MySQL数据库为test,中间有个表test,有个字段f1(varchar)\nJAVA/JSP学习系列之七(Orion下自定义Tag)一、前言(本文译自Orion官方站点)  本文将一步一步介绍在OrionApplicationServer下定义自己的标签,然后,实现该标签的功能,最后用一个jsp例子测试。二、建立标签,实现该标签的功能。1.命名package为:com.acme.mytags(为了和原文相同,我不做改动)packagecom.acme.mytags;2.import相关classimportjavax.servlet.jsp.*;importjavax.servlet.jsp.tagext.*;3.实现javax.servlet.jsp.tagext.Tag接口:publicclassHelloWorldimplementsTag{4.定义局部变量privatePageContextpageContext;privateTagparent;5.调用标签开始的实现方法publicintdoStartTag()throwsjavax.servlet.jsp.JspException{returnSKIP_BODY;}注意:这个方法返回SKIP_BODY,就是标签的body为空的时候,将返回这个值,否则将返回:EVAL_BODY_INCLUDE\n6.调用标签结束的实现方法publicintdoEndTag()throwsjavax.servlet.jsp.JspException{try{pageContext.getOut().write("HelloWorld!");}catch(java.io.IOExceptione){thrownewJspException("IOError:"+e.getMessage());}returnEVAL_PAGE;}这样将在jsp文件的自定义标签结束地方输出“HelloWorld!”7.还不够,我们还要写下面的方法:publicvoidrelease(){}对于本简单的例子,上面的方法不需要任何实现。8.JSP的容器要调用下面的方法:publicvoidsetPageContext(finaljavax.servlet.jsp.PageContextpageContext){this.pageContext=pageContext;}JSP容器通过上面的方法调用标签,而且上面的方法用来设置标签的PageContext.\n9.JSP的容器还要调用下面的方法:publicvoidsetParent(finaljavax.servlet.jsp.tagext.Tagparent){this.parent=parent;}JSP容器通过上面的方法设置标签的parent-Tag,因为每个标签的PageContext都要保留它的parent标签。10.最后,实现方法:publicjavax.servlet.jsp.tagext.TaggetParent(){returnparent;}}11.编译标签。三、描叙标签现在将要写一个描叙文件,描叙该标签。1.建立一个taglib.tld文件,2.taglib.tld是一个XML格式的文本文件,XML的头如下:3.描叙标签库\n1.01.1mthttp://www.orionserver.com/tutorials/tagtut/lesson1/mytags.jarMyfirstTaglibrary4.描叙标签helloWorldcom.acme.mytags.HelloWorldemptyAHelloworldTag5.结束四、打包名称为:mytags.jar其目录结构为:com/acme/mytags/HelloWorld.classMETA-INF/taglib.tld五、在jsp文件中使用自定义标签建立hello.jsp如下:<%@tagliburi="mytags.jar"prefix="mt"%>\nHelloWorld!

六、测试运行在orion目录下,建立如下结构,其中,tag是自己建立的,前面的目录是本来就有的。E:\orion\default-web-app\tag将jar文件和jsp文件全部放到这个目录下。然后,访问:http://localhost:[port]/tag/hello.jsp将出现:--------------------------------------------------------------------------------HelloWorld! --------------------------------------------------------------------------------七、恭喜,你已经成功了!\n附:mytags.jar和hello.jsp文件本站下载地址:http://www.wodejia.net/softdownload/java/orion_tag01.zip\nJSP入门教程(1)JavaServerPages(JSP)是基于Java的技术,用于创建可支持跨平台及跨Web服务器的动态网页。JSP可与微软的ActiveServerPages(ASP)相媲美,但JSP使用的是类似于HTML的标记和Java代码片段而不是用VBScript。当你使用不提供ASP本地支持的Web服务器(例如Apache或Netscape服务器)时,你就可以考虑使用JSP了。你虽然也可以为这些服务器弄一个ASP附加软件模块,但是太昂贵了。现在Sun公司并不会因你使用JSP向你收费(虽然将来可能要收),况且用于Solaris、Linux以及Windows系统的组件都很容易获得。不要把JSP与服务器端的JavaScript语言搞混了。Web服务器自动将通过JSP生成的Java代码片段转换成Java片段(servlets)。JSP也可自动控制许多功能,如过去用Perl脚本编写功能程序或像ASP这样的服务器专用API(应用编程接口)。我们马上就要开始帮助你建立一个使用JSP技术的网站范例了。准备好计算机以使用JSP你需要Java2软件开发工具箱(J2SDK),过去称之为Java开发工具箱(JDK)、JavaServer网络开发工具箱(JSWDK)、Tomcat,或者其它能支持JSP的Web服务器。升阳公司为Windows、Solaris以及Linux平台提供免费的J2SDK和JSWDK。如果你想在现有的Web服务器上使用JSP,而此服务器本身不支持JSP和Java代码片段,可以试用Allaire公司的Jrun。它可以为Netscape的EnterpriseServer和FasttrackServer、微软的InternetInformationServer(IIS)和PersonalWebServer(PWS)、Apache以及其他服务器充当Web服务器附件。你也可以使用Apache服务器的Java版,其中包含最新的JSWDK。下载并安装所需的软件目前,版本号为1.2.2-001的J2SDK可下载版以可安装的归档文件形式出现。这个大约20MB的下载量软件可提供一个彻底的Java开发环境,让你创造任何基于Java并且利用了而标准核心API的解决方案。其实,你的Web服务器使用JSP的唯一条件是要有Java编辑器。要让Web服务器知道编辑器的位置,需要JAVA_HOME环境变量设置为J2SDK安装目录。如果你已经在Windows系统中进行了安装并且接受了默认目录,请将“setJAVA_HOME=C:1.2.2”添加到你的“autoexec.bat”文件中,然后重新启动。\n安装J2SDK后,下载并安装JSWDK或者是Tomcatβ版,即基于Java的ApacheWeb服务器的β版。你把JSWDK放在什么地方是无关紧要的,只要你以后能找到它就行。通常,将其放在顶层目录中,这样你在替换JSWDK或JSDK时无需删除另一个软件。安装好这个文件后,准备工作已经就绪,你可以开始JSP开发工作了。正确地安装JSWDK后,请运行“startserver”脚本程序,以启动Web服务器,使其按照默认情况监听8080端口。你要在启动服务器之后查看您是否已正确地安装了此工具,请加载一个JSP样本文件(http://locahost:8080/examples/jsp/)。如果你能成功地运行这些样本文件,说明你已经正确地安装了这个软件。如果你在用来启动服务器的控制台窗口中看到报错信息,你就需要做些修改。最经常发生的问题是没有设置或不正确地设置了JAVA_HOME环境变量。要看到当前环境变量设置情况,要请在DOS提示符下键入“set”设置。开始在解释JSP语法前,创建一个快捷网页,显示目当前的日期和时间,并然后将之其保存为sample.jsp:FirstPage

Todayis:<%=newjava.util.Date()%>

.把这个文件以及你所有的HTML和JSP页面放在JSWDK安装目录下的网页目录里。你可以从http://localhost:8080/sample.jsp加载此网页。该网页首次被访问时,Web服务器会把JSP编译成Java代码片段,这样你就能够看到当前的日期和时间了。你已经下载、安装并配置好了开发环境,你可以开始学习JSP语法并创建自己的基于JSP的解决方案了。JSP基本语法讲完安装问题后,现在进入来讨论JSP语法。一个投机取巧的办法是下载语法卡片。如果你不熟悉Java编程,也许你想看一下Sun的指南。然而网页创建者并不需要进行太多的Java开发。除了一些方法调用,在你的JSP网页里应尽可能少用Java代码。\n记住以上提示,我们首先来看一看JSP指令和脚本元素,之后我们会解释JavaBeans和隐含对象。共有五种JSP指令和脚本元素。在JSP1.0版中,大部分JSP被一个以“<%”开头和以“%>”结尾的的标记括在其中。在更新的JSP1.1规范出台后,就有了与XML兼容的版本。JSP指令和脚本元素Directives<%@directive%>Declarations<%!declaration%>Expressions<%=expression%>CodeFragment/Scriptlet<%codefragment%>Comments<%--comment--%>指令JSP指令是为JSP引擎而设计的。他们并不直接产生任何可见输出,而只是告诉引擎如何处理其余JSP页面。这些指令始终被括在“<%@?%>”标记中。两个最重要的指令是“pagePage”和“Include”。“Taglib”指令不在讨论之列,但是在用JSP1.1创建自定义标记可以使用它。几乎在所有JSP页面顶部都会看到“Page”指令。尽管不是必须的,但它可以让你指定:到何处查找起支持作用的Java类别:<%@pageimport="java.util.Date"%>,出现Java运行问题时,将网上冲浪者引向何处:<%@pageerrorPage="errorPage.jsp"%>,还有你是否需要管理用户的会话级信息,这些信息可能来自多个网页(在下面的JavaBeans一节将对此做进一步讲)述:<%@pagesession="true"%>.“Include”指令可以把你的内容分成更多可管理的元素,比如包括一个普通页面页眉或页脚的元素。包含的网页可以是一个固定的HTML页面或更多的JSP内容:<%@includefile="filename.jsp"%>声明JSP声明用来让你定义页面级变量,以保存信息或定义JSP页面的其余部分可能需要的支持方法。如果你发现代码太多,通常最好把它们写成一个独立的Java类别。声明一般都在“<%!?%>”标记中。一定要以分号(;)结束变量声明,因为任何内容都必须是有效的Java语句:\n<%!inti=0;%>。表达式有了JSP表达式,表达式评估结果会被转换成一个字符串,并且被直接包括在输出页面之内。JSP表达式包含在“<%=?%>”标记中,没有分号,除非在加引号的字符串部分使用分号。<%=i%><%="Hello"%>.代码片段/脚本片段代码片段/脚本片段(Scriptlets)JSP代码片段或脚本片段是嵌在“<%?%>”标记中的。这种Java代码在Web服务器响应请求时就会运行。在脚本片段周围可能是原始的HTML或XML语句,在这些地方,代码片段可以使你创建条件执行代码,或要用到另外一段代码的代码。例如,以下的代码组合使用表达式和代码片段,显示H1、H2、H3和H4标记中的字符串“Hello”。代码片段并不局限于一行源代码:<%for(inti=1;i<=4;i++){%>>Hello><%}%>.注释最后一个主要的JSP元素是嵌入式注释。尽管你始终可以在文件中加入HTML注释,但用户在查看页面源代码时会看到这些注释。如果你不想让用户看到它,你就应该将其嵌入“<%--?--%>”标记中:<%--commentforserversideonly--%>.带JavaBean的JSP虽然你可以把大段的代码放在脚本片段(scriptlet)内,但是绝大多数的Java代码属于可重复使用的名为JavaBea的组件。JavaBean类似于ActiveX控件:他们都能提供常用功能并且可以重复使用。JavaBean的值是通过一些属性获得的,你可通过这些属性访问JavaBean设置。以一个人来打比方,这个人就是一个JavaBean,这个人的名字、身份证号码以及住址就是其属性。在JSP网站,你基本上就是通过玩“Connectthebeans)”来使你的网站动态化。假设JavaBean先于网站被创建出来,你需要做的第一件事就是告诉JSP页面它将要用到一个“Bean”。你可以用“”标记来做到这一点:。\n“”标记要求你用“id”属性来识鉴别Bean。这里你提供一个名字来JSP页面其余部分的Bean。除了“id”属性,你还须告诉网页面从何处查找Bean,或者它的Java类别名是什么。这种类别属性提供确认Bean的功能,其他一些方法也可以做到这一点。最后一个必需的元素是“scope”属性。有了“scope”属性的帮助,你就能告诉Bean为单一页面(默认情况)[scope="page"]、为[scope="request"]请求为会话[scope="session"],或者为整个应用程序[scope="application"]保持留信息。有了会话范围,你就能非常容易地在JSP网页面上维护购物车等项目。一旦你声名了一个JavaBean,你就可以访问它的属性来定制它。要获得属性值,请用“”标记。有了这个标记,你就能指定将要用到的Bean名称(从useBean的"id"字段得到)以及你想得到其值的属性。实际的值被放在输出中:。要改变JavaBean属性,你必须使用“”标记。对这个标记,你需要再次识别Bean和属性,以修改并额外提供新值。如果命名正确,这些值可以从一个已提交的表中直接获得:参数获得:;可以从一个参数获得,但你必须直接命名属性和参数:或者直接用一个名字称和值来设置:/>.关于JavaBean的最后一件事:为了Web服务器能找到JavaBean,你需要将其类别文件放在一个特殊位置。用JSWDK,最方便的位置是安装目录下的类别目录,如jswdk-1.0.1classes。JSP隐含对象与JSP语法相关的最后的元素是一些叫做“隐含对象”的东西。在JSP代码片段中,你可以利用这些隐含对象与JSP页面的代码片段执行环境产生互动。应尽量少访问这些内置隐含对象。但是,在一某些情况下,访问隐含对象是可被接受的。若要充分利用隐含对象,就需了解最新的JavaServletAPI。下表列出可用的隐含对象集合。隐含对象说明request客户端请求,包括从GET/POST请求传递过来的参数response网页传回客户端的反应pageContext在此管理网页属性session与请求关联的会话application代码片段的运行环境out传送响应的输出流\nconfig代码片段配置对象pageJSP网页本身exception有错的网页中未被捕获的例外这些都是用来做什么的,你又如何来使用它们呢?基本上说,在你的脚本片段里,你可以用它们进入执行JSP代码的代码片段。用不着深入了解太多的ServletAPI细节,我们来看看能用它们来做到的某些事情:你可以不用表达式,直接进入“Out”隐含对象,将某些内容输出到响应中:<%out.println("Hello");%>。用不着把一个参数直接送到JavaBean,你可以从请求对象获取参数值:<%Stringname=request.getParameter("name");out.println(name);%>.在你用JSP进行开发的过程中,进行更多的开发的过程中,如果创建了JavaBeans或发现JSP文件中加入了太多的Java源代码,你就需要创建起支持作用的Java分类(class)了,它可以协助你重复利用源代码并减少JSP网页编译时间。当你需要创建Java分类文件时,你必须:将JDSWKinstallationin目录添加到PATH中。在autoexec.bat文件PATH行的末尾,加入“C:1.2.2/bin”。用这个命令将JAR文件复制到“jrelibext:”目录中:copyc:jswdk-1.0.1libservlet.jarc:jdk1.2.2jrelibext.创建一个JSP网站现在该是把所有这些JSP语法派上用场的时候了。我们将创建一个网页,它有一个输入表单,可以使用户输入一个股票代号以获得出当前股价(有20分钟)。如果输入有误,则显示报错网页。quote.jsp首先,用这个代码创建一个quote.jsp网页并将其保存在JSWDK安装目录下的网页目录中。大多数的网页是标准的HTML,其间散布着JSP代码。第六行是一个网页指示,表示将把所有错误发送到“errorPage.jsp”文中。第13到15行是一个脚本片段,主要说明只有提供“符号”参数才会显示表格。“If”代码段在32到34行结束。第17行定义了要用的JavaBean,第18行从参数加载JavaBean的符号属性。第27行到29行显示Bean的属性。除了"if"代码段,实际上并不涉及其它Java代码。errorPage.jsp\n下一步,将下列JSP源代码保存到网页目录中的“errorPage.jsp”文件中。“这是一个错误网页”的提示为第一行,它将isErrorPage网页提示属性设置为真。上一页说了明错误网页的位置,本页则说明这就是错误网页。其它JSP文件中的JSP专用代码只有访问隐含例外对象才用到。网页只显示其值:<%@pageisErrorPage="true"%>ErrorPage

OurErrorPage

Wegotourselvesanexception:<%=exception%>Restart。Quotes.javaQuotesJavaBean利用Yahoo资源来获得股票价格。需要将此资源保存到JSWDK安装目录下“classescomjguru”目录中的“quotes.java”文件中。请在此用JSDK中的Javac编辑器来编辑它。你的第一个JSP网页。创建了这两个JSP文件,建立了JavaBean资源文件并编辑了它后,你就可以从http://localhost:8080/quote.jsp加载“quote.jsp”文件以查看结果,假设你没有更改JSWDK安装设置,转而使用别的端口。这个网页当然能做得更漂亮,但是它已实现了需要实现的功能,并且很好地展示了JSP的功能。(2)欢迎使用JavaServerPages(以下简称JSP)技术—制作动态内容网页的方法。如果你希望学习这篇教程的话,我想你一定是这项技术的新手。你可能想成为一名利用JSP技术开发网络应用程序的程序员或者网页设计师。本教程中包含了一系列JSP的技巧和一些简单的代码的写法,每一步都举了一组例子来讲述原理。我建议你在学习本教程之前先去看一看FAQ,了解如果配置你的服务器好让他能支持并运行JSP。那样你就可以跳过前边的内容直接看你感兴趣的内容了。OK,费话少说,Let’sgo!\n第一课:真正的开始现在开始做我们的第一个JSP页面。图1-1展示了一个最简单的JSP页面,接下来是两段代码。[dukebanner.html]   
helloworld.jsp<%@pageinfo="ahelloworldexample"%>Hello,World<%@includefile="dukebanner.html"%>\n 

Hello,World!

页面说明:在很多JSP文件中你都要写上这样的说明。在helloword.jsp中:<%@pageinfo="ahelloworldexample"%>傻瓜也看得出来,这句话没什么大用,只是简要的说明一下这段代码的作用。你可以在JSP文件中的任何地方写这种代码,但是好的习惯是把他写在最前面,还有,因为他是JSP标签,记住一定要放在前面,呵include说明:include用来在主JSP文件中调用本地的一个其他文件,通常是一些版权信息啦,脚本语言啦等其他任何你想在其他文件中重复使用的代码。在这个例子中就是调用了一个图片其实。大家明白这个意思就得了。随便说两句:在JSP中对写法非常敏感,不可以有一点错误。举个例子,把写成那样服务器会出现错误信息。一些如类名,包名,路径名还有其他的敏感的标签等千万不要写错。有一些JSP标签拿不准的话就去查一下JavaServerPages语法卡片。代码写好了,如何试验一下呢?我这里写的UNIX下的方法,如果你用Windows,那么换一下路径就可以了1、先建一个目录:../jswdk-1.0/examples/jsp/tutorial/helloworld2、把这些文件拷过去background.gif,duke.waving.gif,dukebanner.html,andhelloworld.jsp\n3、cd../jswdk-1.0,然后startserver4、开一个浏览器,5、http://yourMachineName:8080/examples/jsp/tutorial/helloworld/helloworld.jsp>(3)第二课:用HTML表单大多数情况下,商业的网站都要有一些表单,比如说输入一下消费者的姓名啦,地址啦,或者敲一个词来用搜索引擎来查一下啦,或者市场人员从来访者处收集一些数据供参考什么的。那些表单传回的数据怎么处理的?来访者通过表单向JSP引擎输入了数据,并保存在了request对象中,那么接下来怎么办?图2-1向你展示了数据流是如何在服务器和客户之间传递的(至少在SUN的JSPreferenceimplementation是这么做的,别的JSP引擎工作起来可能会有一点点的不同,其实大同小异,都差不多)字儿太小了,可能看不大清吧?俺来解释一下了只好。首先,JSP引擎把存放在request对象中的数据发到JSP页面指定的服务器端的组件(JavaBeans组件,servlet,或者enterprisebean),组件收到这些个数据以后,有可能再存这些数据到数据库或者其他的地方存放起来,同时,返回一个response对象给JSP引擎。JSP引擎再把response对象传给JSP页面,这时的页面包含了定义好的格式和从服务器端得到的数据。这时JSP引擎和Web服务器再发送一个整理好的完整的页面给客户,也就是这们在浏览器上看到的结果。客户和服务器间的通信协议可以用HTTP,当然也可以用其他的。Request和Response对象在你制作的JSP原代码中起作用。到于request对象到底怎么用,我要在接下来详细的讲给你听。如何创建表单用HTML定义一些有代表性的表单做成一个JSP文件,然后用JSP标签在表单和服务器端对象(通常都用Bean)传递数据。一般情况下是这么干的:1、写JSP原文件,创建一些HTML的表单并命名。\n2、在Java文件里写Bean,定义属性,GET或者SET方法来配合已经被你指定好名字的表单。3、回到JSP原文件中,增加标签来创建一个或者调用一个现成的Bean。4、增加标签设置HTML表单中需要SET方法的Bean的属性。5、增加标签设置HTML表单中需要GET方法的Bean的属性。6、如果需要处理更多的用户数据,用request对象。说了半天你可能看不懂,其实看一个例子你就懂了。先看一个简单的hello的例子吧:这段程序其实还是计算机程序里那个最经典的“hello,world”的程序,只不过呢,我使他挠了一点弯儿,使他看起来比较智能和复杂。首先你输入你的名字,然后Duke跟你说:“hello!”看看代码吧:dukebanner.html   \n
主JSP文件:hellouser.jsp<%@pageimport="hello.NameHandler"%>Hello,User<%@includefile="dukebanner.html"%> 

MynameisDuke.What'syours?


\n<%If(request.getParameter("username")!=null){%><%@includefile="response.jsp"%><%}%>回应文件:response.jsp 

Hello,!

处理数据的Bean:(namehandler.java)packagehello;publicclassNameHandler\n{privateStringusername;publicNameHandler(){username=null;}publicvoidsetUsername(Stringname){username=name;}publicStringgetUsername(){returnusername;}}建立HTML表单一个HTML的窗分为三个部分:
标签,输入方法,提交按钮发数据到服务器。一般的HTML页面里,是这么写的,在其他的页面里的action属性可能是其他特殊的CGI程序或者其他能处理数据的程序,那么在JSP里边是怎么用的呢,呵,如果你想把数据发到Bean里的话那么你可以省略action里边的东里了,直接写标签或者其他特定的JSP文件了。接下来的那些表单和普通的HTML差不多了,的方法,然后加一个提交按钮,可能还有一个Reset按钮,对了,别忘了,还得给每一个input表单加一个名字。这么写:使用GET和POST方法用GET和POST方法可以发数据到服务器,在JSP程序中GET和POST方法可以发数据到Bean、servlet、或者其他服务器端的组件。理论上说,GET是从服务器上请求数据,POST是发送数据到服务器。事实上,GET方法是把数据参数队列(query\nstring)加到一个URL上,值和表单是一一对应的。比如说,name=John。在队列里,值和表单用一个&符号分开,空格用+号替换,特殊的符号转换成十六进制的代码。因为这一队列在URL里边,这样队列的参数就能看得到,可以被记录下来,或更改。通常GET方法还限制字符的大小。事实上POST方法可以没有时间限制的传递数据到服务器,用户在浏览器端是看不到这一过程的,所以POST方法比较适合用于发送一个保密的(比如信用卡号)或者比较大量的数据到服务器。写Bean如果JSP程序用到了Bean,你就得按照JavaBeansAPI的说明设计你的Bean。记住下面两个关键部分。如果JSP程序里用标签,那你就得在Bean里边配合的GET方法。如果JSP程序进而用标签,那你就得在Bean里边配合的Set方法。设置参数到Bean或者从里边取参数将在以后的部分详细介绍。传数据到Bean把HTML表单的数据传到Bean里需要两个工作:·用标签创建或者定位到Bean·在Bean里面用设置属性值第一步用标签创建或者定位到Bean一定要用在之前,首先按照你指定的名字查找Bean,如果没找到,会给你指定一个。允许在一个JSP文件中创建一个Bean,然后再另一个文件中调用,这就给了Bean一个很广泛的运行空间。第二步在Bean里面用设置属性值。最简单的方法是把值定义成与表单名相配合。举个例子,如果你把表单名定义成“username”那么,你就在Bean里定义属性“username”然后用方法getUsername和setUsername。当然也可以定义成不同的名字,只要你不认为麻烦。谁让你记忆力好呢!Request对象用户输入的数据用来存放在Request对象里,用javax.servlet.HttpServletRequest来执行(你也可以用其他不同的工具来执行,但他们其实都是javax.servlet.HttpServletRequest的子集)\n你也可以直接用scriptlet来直接访问Request对象。Scriptlet将在下一讲里边详细的讨论,现在你只需要知道他是用脚本语言写的一段放在<%和%>之间的代码就足够了。在JSP1.0中,你必须用JavaTM程序语言作为你的脚本语言。你经常会用到如下方法处理Request对象:方法说明执行结果getRequestJavax.servlet.jsp.PageContext返回当前Request对象getParameterNamesjavax.servlet.ServletRequest返回当前Request对象参数名getParameterValuesjavax.servlet.ServletRequest返回当前Request对象参数值你将会发现其他方法包括ServletRequest,HttpServletRequest或者其他任何ServletRequest的子集。JSP引擎经常在scenes之后使用Request对象,即使你没有明确地在JSP文件中调用。从Bean中调数据到JSP页面一旦用户的数据被传到Bean,你就想重新得到数据,然后在JSP面页中显示出来。想达到这一步,你就得用到标签。传Bean名和属性名:

Hello,!,,和标签必须相配,举个例子:hellouser.jsp:response.jsp:

Hello,!\n在这个例子里,标签被放在两个文件中,但是指定的名字都是相同的,如果不同的话,那么系统会返回一个错误信息。如何运行例子我用的是UNIX主机,如果你用windows,那么改相应的路径即可。创建路径../jswdk-1.0/examples/jsp/tutorial/hellouser.把文件background.gif,duke.waving.gif,dukebanner.html,hellousr.jsp和response.jsp文件放进去。创建一个目录,../jswdk-1.0/examples/WEB-INF/jsp/beans/hello把文件NameHandler.java和NameHandler.class放进去。cd../jswdk-1.0然后startserver打开浏览器http://计算机名:8080/examples/jsp/tutorial/hellouser/hellouser.jsp(4)使用脚本在有些地方,你大概要加一些好的,成熟的程序到你的JSP页里,JSP的标签虽然很强大,但是完成某些工作还是比较费力的困难的。这时你可以使用脚本语言段来补充JSP标签。使用的JSP引擎是支持脚本语言的,SUN的JSP参考文说明,必须使用Java程序语言来编写脚本,但是其他第三方的JSP引擎允许使用其他语言来写脚本程。如何增加脚本首先,你必须了解一些增加脚本元素到JSP页中的一些基本规则1、在JSP页面里用Page指令定义脚本(默认值是Java,一般不需要定义)2、声明语法<%!……%>声明变量和方法(函数)。3、表达式语法<%=……%>定义脚本语言表达式4、脚本语法〈%……%>可以操作声明、表达式和其他类型合法的代码段在页脚本语言。5、一定要在结尾加%>标签声明、表达式、脚本使用起来有一些相似,但也有一些不同让我们用一些例子来讲述一下相同点和不同点吧。声明<%!……%>包含了一个或多个变量和方法,结尾以分号分隔。例:<%!IntI=0;%><%!Inta,b;doublec;%><%!Circlea=newcircle(2.0);%>在页面中使用变量和方法之前必须声明声明的范围通常是JSP页,但如果页面中使用INCLUDE指令包含其他页面,范围应变得扩展到被包含的页面。表达式<%=……%>可以在页面中包含任何合法的语言表达式,不用分号。例:<%=Math.sqrt(2)%>\n<%=item[I]%><%=a+b+c%><%=newjava.util.date()%>表达式和脚本的一个关键的不同点就是不需要分号。如果你需要在脚本中使用表达式就必须加分号。脚本<%……%>允许你写的任何数量的脚本语言例:<%Stringname=null;If(request.getParmeter("name")==null{%>记住在脚本中必须使用分号结尾。猜数字游戏猜数字游戏非常的有趣,而且从这里你还可以学到很多表达式的用法。代码显示用的主屏幕(numguess.jsp)<%@pageimport="num.NumberGuessBean"%>NumberGuess<%if(numguess.getSuccess()){%>Congratulations!Yougotit.Andafterjust<%=numguess.getNumGuesses()%>tries.

<%numguess.reset();%>Caretotryagain?<%}elseif(numguess.getNumGuesses()==0){%>WelcometotheNumberGuessgame.

I'mthinkingofanumberbetween1and100.

\nWhat'syourguess?<%}else{%>Goodguess,butnope.Try<%=numguess.getHint()%>.Youhavemade<%=numguess.getNumGuesses()%>guesses.

I'mthinkingofanumberbetween1and100.

What'syourguess?<%}%>操作程序(NumberGuessBean.java)//NumberGuessGame//WrittenbyJasonHunter,CTO,K&ASoftware//jasonh@kasoftware.com,http://www.servlets.com//Copyright1999,K&ASoftware//DistributedbySunMicrosystemswithpermissionpackagenum;importjava.util.*;publicclassNumberGuessBean{intanswer;booleansuccess;Stringhint;intnumGuesses;publicNumberGuessBean(){reset();}publicvoidsetGuess(Stringguess){numGuesses++;intg;try{g=Integer.parseInt(guess);}catch(NumberFormatExceptione){g=-1;\n}if(g==answer){success=true;}elseif(g==-1){hint="anumbernexttime";}elseif(ganswer){hint="lower";}}publicbooleangetSuccess(){returnsuccess;}publicStringgetHint(){return""+hint;}publicintgetNumGuesses(){returnnumGuesses;}publicvoidreset(){answer=Math.abs(newRandom().nextInt()%100)+1;success=false;numGuesses=0;}}在JSP文件中使用脚本numguess.jsp是一个非常有趣儿的用脚本写的例子,你看他的结构其实是一个很大的IF……ELSE结构,但是很一个从句又都是用HTML写的,看起来象一个大的程序段。不过你也不一定非得象numguess.jsp那样用HTML和JSP标签一起来写脚本。在<%和%>标签之间,你可以写任意多行的脚本代码,在通常情况下,尽量少用脚本来处理程序,而尽可能的使用servlets或者Beans,这样你的程序看起来会非常的清析,明了。话又说回来,怎么写JSP还得根据你的习惯和爱好,我不强迫非得使用任何一种方法,SUN的JSP详细说明书不规定脚本的长度。用标签组合脚本使用HTML和JSP标签来写脚本的时候,注意前后的标签不要忘记,一定要“封”好。说的不明白,举个例子吧:\n<%}else{%>...这时候用JSP标签吧...<%}%>开始的时候这种做法看起来可能有一点奇怪,但它以确保你JSP文件编译的时候脚本的成功转换。那么,脚本什么时候执行呢?一个JSP原文件的处理分为两个阶段:一个是HTTP的编译时候,一个是请求的处理时间。HTTP编译的时候,当用户第一次读JSP页面的时候,JSP的原代码被编译成CLASS,通常是servlet。HTML标签和JSP标签在这个时候同时被处理了,这之前用户还没有任何的请求被提交。请求处理时间是当用户在JSP页面中提交了一个请求,这时请求由客户端被request对象传到了服务器端,JSP引擎根据用户提交的值执行编译过的JSP文件或者servlet。当你在JSP页中使用脚本的时候,你必须知道他们什么时候被执行。声明在HTTP编译阶段就已经被处理了,其他脚本,表达式在编译JSP文件的时候也可用。表达式在HTTP编译的时候也被执行了。表达式的值被转换成了字符串被插入到JSP文件中一块儿被编译。其实在请求阶段,脚本也是可以利用的。如何运行例子我现在给出的都是在UNIX风格下的路径,如果你用Windows,那么改成Windows风格路径1、猜数字游戏在装TOMCAT或者JSWDK的时候就已经装好了。2、.jsp和.html文件在../jswdk-1.0.1/examples/num中3、.java和.class文件在../jswdk-1.0.1/examples/WEB-INF/jsp/bean/num中4、开浏览器,http://机器名/examples/jsp/num/numguess.jsp\nJavaServlet和JSP教程这是一个比较完整的Servlet、JSP教程,包含大量的实用资料和示例,仙人掌工作室倾情推荐。全文共十三章,主要包括:Servlet和JSP特点,安装和配置开发、运行环境,表单数据处理,HTTP请求头、应答状态处理,访问CGI变量,会话状态,JSP脚本元素、指令、动作,等等。具体请见《目录》。目录作者:仙人掌工作室                                 目    录一、Servlet和JSP概述1.1JavaServlet及其特点1.2JSP及其特点二、设置开发、运行环境2.1安装Servlet和JSP开发工具2.2安装支持Servlet的Web服务器三、第一个Servlet3.1Servlet基本结构3.2输出纯文本的简单Servlet3.2.1HelloWorld.java3.2.2Servlet的编译和安装3.2.3运行Servlet3.3输出HTML的Servlet3.4几个HTML工具函数四、处理表单数据4.1表单数据概述4.2实例:读取三个表单变量4.3实例:输出所有的表单数据五、读取HTTP请求头5.1HTTP请求头概述5.2在Servlet中读取请求头5.3实例:输出所有的请求头六、访问CGI变量6.1CGI变量概述6.2标准CGI变量的Servlet等价表示6.3实例:读取CGI变量七、HTTP应答状态7.1状态代码概述7.2设置状态代码7.3HTTP1.1状态代码及其含义7.4实例:访问多个搜索引擎八、设置HTTP应答头\n8.1HTTP应答头概述8.2常见应答头及其含义8.3实例:内容改变时自动刷新页面九、处理Cookie9.1Cookie概述9.2Servlet的CookieAPI9.2.1创建Cookie9.2.2读取和设置Cookie属性9.2.3在应答头中设置Cookie9.2.4读取保存到客户端的Cookie9.3几个Cookie工具函数9.3.1获取指定名字的Cookie值9.3.2自动保存的Cookie9.4实例:定制的搜索引擎界面  十、会话状态10.1会话状态概述10.2会话状态跟踪API10.2.1查看当前请求的会话对象10.2.2查看和会话有关的信息10.2.3在会话对象中保存数据10.3实例:显示会话信息十一、JSP及语法概要11.1概述11.2JSP语法概要表11.3关于模板文本(静态HTML)十二、脚本元素、指令和预定义变量12.1JSP脚本元素12.1.1JSP表达式12.1.2JSPScriptlet12.1.3JSP声明12.2JSP指令12.2.1page指令12.2.2include指令12.3实例:脚本元素和指令的应用12.4JSP预定义变量十三、JSP动作13.1jsp:include动作13.2jsp:useBean动作13.3关于jsp:useBean的进一步说明13.4jsp:setProperty动作13.5jsp:getProperty动作13.6jsp:forward动作13.7jsp:plugin动作附录:JSP注释和字符引用约定注:原文出处:ServletsandJavaServerPages\n一、Servlet和JSP概述1.1JavaServlet及其特点  Servlet是Java技术对CGI编程的回答。Servlet程序在服务器端运行,动态地生成Web页面。与传统的CGI和许多其他类似CGI的技术相比,JavaServlet具有更高的效率,更容易使用,功能更强大,具有更好的可移植性,更节省投资(更重要的是,Servlet程序员收入要比Perl程序员高:-):高效。在传统的CGI中,每个请求都要启动一个新的进程,如果CGI程序本身的执行时间较短,启动进程所需要的开销很可能反而超过实际执行时间。而在Servlet中,每个请求由一个轻量级的Java线程处理(而不是重量级的操作系统进程)。在传统CGI中,如果有N个并发的对同一CGI程序的请求,则该CGI程序的代码在内存中重复装载了N次;而对于Servlet,处理请求的是N个线程,只需要一份Servlet类代码。在性能优化方面,Servlet也比CGI有着更多的选择,比如缓冲以前的计算结果,保持数据库连接的活动,等等。方便。Servlet提供了大量的实用工具例程,例如自动地解析和解码HTML表单数据、读取和设置HTTP头、处理Cookie、跟踪会话状态等。功能强大。在Servlet中,许多使用传统CGI程序很难完成的任务都可以轻松地完成。例如,Servlet能够直接和Web服务器交互,而普通的CGI程序不能。Servlet还能够在各个程序之间共享数据,使得数据库连接池之类的功能很容易实现。可移植性好。Servlet用Java编写,ServletAPI具有完善的标准。因此,为I-PlanetEnterpriseServer写的Servlet无需任何实质上的改动即可移植到Apache、MicrosoftIIS或者WebStar。几乎所有的主流服务器都直接或通过插件支持Servlet。节省投资。\n不仅有许多廉价甚至免费的Web服务器可供个人或小规模网站使用,而且对于现有的服务器,如果它不支持Servlet的话,要加上这部分功能也往往是免费的(或只需要极少的投资)。  1.2JSP及其特点  JavaServerPages(JSP)是一种实现普通静态HTML和动态HTML混合编码的技术,有关JSP基础概念的说明请参见《JSP技术简介》。  许多由CGI程序生成的页面大部分仍旧是静态HTML,动态内容只在页面中有限的几个部分出现。但是包括Servlet在内的大多数CGI技术及其变种,总是通过程序生成整个页面。JSP使得我们可以分别创建这两个部分。例如,下面就是一个简单的JSP页面:<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.0Transitional//EN"><HTML><HEAD><TITLE>欢迎访问网上商店</TITLE></HEAD><BODY><H1>欢迎</H1><SMALL>欢迎,<!--首次访问的用户名字为"NewUser"--><%out.println(Utils.getUserNameFromCookie(request));%>要设置帐号信息,请点击<AHREF="Account-Settings.html">这里</A></SMALL><P>页面的其余内容。.</BODY></HTML>  下面是JSP和其他类似或相关技术的一个简单比较:JSP和ActiveServerPages(ASP)相比Microsoft的ASP是一种和JSP类似的技术。JSP和ASP相比具有两方面的优点。首先,动态部分用Java编写,而不是VBScript或其他Microsoft语言,不仅功能更强大而且更易于使用。第二,JSP应用可以移植到其他操作系统和非Microsoft的Web服务器上。JSP和纯Servlet相比JSP并没有增加任何本质上不能用Servlet实现的功能。但是,在JSP中编写静态HTML更加方便,不必再用println语句来输出每一行HTML代码。更重要的是,借助内容和外观的分离,页面制作中不同性质的任务可以方便地分开:比如,由页面设计专家进行HTML设计,同时留出供Servlet程序员插入动态内容的空间。\nJSP和服务器端包含(Server-SideInclude,SSI)相比SSI是一种受到广泛支持的在静态HTML中引入外部代码的技术。JSP在这方面的支持更为完善,因为它可以用Servlet而不是独立的程序来生成动态内容。另外,SSI实际上只用于简单的包含,而不是面向那些能够处理表单数据、访问数据库的“真正的”程序。JSP和JavaScript相比JavaScript能够在客户端动态地生成HTML。虽然JavaScript很有用,但它只能处理以客户端环境为基础的动态信息。除了Cookie之外,HTTP状态和表单提交数据对JavaScript来说都是不可用的。另外,由于是在客户端运行,JavaScript不能访问服务器端资源,比如数据库、目录信息等等。二、设置开发、运行环境2.1安装Servlet和JSP开发工具  要学习Servlet和JSP开发,首先你必须准备一个符合JavaServlet2.1/2.2和JavaServerPages1.0/1.1规范的开发环境。Sun提供免费的JavaServerWebDevelopmentKit(JSWDK),可以从http://java.sun.com/products/servlet/下载。  安装好JSWDK之后,你还要告诉javac,在编译文件的时候到哪里去寻找Servlet和JSP类。JSWDK安装指南对此有详细说明,但主要就是把servlet.jar和jsp.jar加入CLASSPATH。CLASSPATH是一个指示Java如何寻找类文件的环境变量,如果不设置CLASSPATH,Java在当前目录和标准系统库中寻找类;如果你自己设置了CLASSPATH,不要忘记包含当前目录(即在CLASSPATH中包含“.”)。  另外,为了避免和其他开发者安装到同一Web服务器上的Servlet产生命名冲突,最好把自己的Servlet放入包里面。此时,把包层次结构中的顶级目录也加入CLASSPATH会带来不少方便。请参见下文具体说明。  2.2安装支持Servlet的Web服务器  除了开发工具之外,你还要安装一个支持JavaServlet的Web服务器,或者在现有的Web服务器上安装Servlet软件包。如果你使用的是最新的Web服务器或应用服务器,很可能它已经有了所有必需的软件。请查看Web服务器的文档,或访问http://java.sun.com/products/servlet/industry.html查看支持Servlet的服务器软件清单。  虽然最终运行Servlet的往往是商业级的服务器,但是开始学习的时候,用一个能够在台式机上运行的免费系统进行开发和测试也足够了。下面是几种当前最受欢迎的产品。\nApacheTomcat.Tomcat是Servlet2.2和JSP1.1规范的官方参考实现。Tomcat既可以单独作为小型Servlet、JSP测试服务器,也可以集成到ApacheWeb服务器。直到2000年早期,Tomcat还是唯一的支持Servlet2.2和JSP1.1规范的服务器,但已经有许多其它服务器宣布提供这方面的支持。Tomcat和Apache一样是免费的。不过,快速、稳定的Apache服务器安装和配置起来有点麻烦,Tomcat也有同样的缺点。和其他商业级Servlet引擎相比,配置Tomcat的工作量显然要多一点。具体请参见http://jakarta.apache.org/。JavaServerWebDevelopmentKit(JSWDK).JSWDK是Servlet2.1和JSP1.0的官方参考实现。把Servlet和JSP应用部署到正式运行它们的服务器之前,JSWDK可以单独作为小型的Servlet、JSP测试服务器。JSWDK也是免费的,而且具有很好的稳定性,但它的安装和配置也较为复杂。具体请参见http://java.sun.com/products/servlet/download.html。AllaireJRun.JRun是一个Servlet和JSP引擎,它可以集成到NetscapeEnterprise或FastTrackServer、IIS、MicrosoftPersonalWebServer、版本较低的Apache、O'eilly的WebSite或者StarNineWebSTAR。最多支持5个并发连接的限制版本是免费的,商业版本中不存在这个限制,而且增加了远程管理控制台之类的功能。具体请参见http://www.allaire.com/products/jrun/。NewAtlanta的ServletExecServletExec是一个快速的Servlet和JSP引擎,它可以集成到大多数流行的Web服务器,支持平台包括Solaris、Windows、MacOS、HP-UX和Linux。ServletExec可以免费下载和使用,但许多高级功能和管理工具只有在购买了许可之后才可以使用。NewAtlanta还提供一个免费的Servlet调试器,该调试器可以在许多流行的JavaIDE下工作。具体请参见http://newatlanta.com/。Gefion的LiteWebServer(LWS)LWS是一个支持Servlet2.2和JSP1.1的免费小型Web服务器。Gefion还有一个免费的WAICoolRunner插件,利用该插件可以为NetscapeFastTrack和EnterpriseServer增加Servlet2.2和JSP\n1.1支持。具体请参见http://www.gefionsoftware.com/。Sun的JavaWebServer.该服务器全部用Java写成,而且是首先提供Servlet2.1和JSP1.0规范完整支持的Web服务器之一。虽然Sun现在已转向Netscape/I-PlanetServer,不再发展JavaWebServer,但它仍旧是一个广受欢迎的Servlet、JSP学习平台。要得到免费试用版本,请访问http://www.sun.com/software/jwebserver/try/.三、第一个Servlet3.1Servlet基本结构  下面的代码显示了一个简单Servlet的基本结构。该Servlet处理的是GET请求,所谓的GET请求,如果你不熟悉HTTP,可以把它看成是当用户在浏览器地址栏输入URL、点击Web页面中的链接、提交没有指定METHOD的表单时浏览器所发出的请求。Servlet也可以很方便地处理POST请求。POST请求是提交那些指定了METHOD=“POST”的表单时所发出的请求,具体请参见稍后几节的讨论。importjava.io.*;importjavax.servlet.*;importjavax.servlet.http.*;publicclassSomeServletextendsHttpServlet{  publicvoiddoGet(HttpServletRequestrequest,                    HttpServletResponseresponse)      throwsServletException,IOException{        //使用“request”读取和请求有关的信息(比如Cookies)  //和表单数据        //使用“response”指定HTTP应答状态代码和应答头    //(比如指定内容类型,设置Cookie)        PrintWriterout=response.getWriter();    //使用"out"把应答内容发送到浏览器  }}  如果某个类要成为Servlet,则它应该从HttpServlet\n继承,根据数据是通过GET还是POST发送,覆盖doGet、doPost方法之一或全部。doGet和doPost方法都有两个参数,分别为HttpServletRequest类型和HttpServletResponse类型。HttpServletRequest提供访问有关请求的信息的方法,例如表单数据、HTTP请求头等等。HttpServletResponse除了提供用于指定HTTP应答状态(200,404等)、应答头(Content-Type,Set-Cookie等)的方法之外,最重要的是它提供了一个用于向客户端发送数据的PrintWriter。对于简单的Servlet来说,它的大部分工作是通过println语句生成向客户端发送的页面。  注意doGet和doPost抛出两个异常,因此你必须在声明中包含它们。另外,你还必须导入java.io包(要用到PrintWriter等类)、javax.servlet包(要用到HttpServlet等类)以及javax.servlet.http包(要用到HttpServletRequest类和HttpServletResponse类)。  最后,doGet和doPost这两个方法是由service方法调用的,有时你可能需要直接覆盖service方法,比如Servlet要处理GET和POST两种请求时。  3.2输出纯文本的简单Servlet  下面是一个输出纯文本的简单Servlet。  3.2.1HelloWorld.javapackagehall;importjava.io.*;importjavax.servlet.*;importjavax.servlet.http.*;publicclassHelloWorldextendsHttpServlet{  publicvoiddoGet(HttpServletRequestrequest,                  HttpServletResponseresponse)      throwsServletException,IOException{    PrintWriterout=response.getWriter();    out.println("HelloWorld");  }}  3.2.2Servlet的编译和安装  不同的Web服务器上安装Servlet的具体细节可能不同,请参考Web服务器文档了解更权威的说明。假定使用JavaWeb\nServer(JWS)2.0,则Servlet应该安装到JWS安装目录的servlets子目录下。在本文中,为了避免同一服务器上不同用户的Servlet命名冲突,我们把所有Servlet都放入一个独立的包hall中;如果你和其他人共用一个服务器,而且该服务器没有“虚拟服务器”机制来避免这种命名冲突,那么最好也使用包。把Servlet放入了包hall之后,HelloWorld.java实际上是放在servlets目录的hall子目录下。  大多数其他服务器的配置方法也相似,除了JWS之外,本文的Servlet和JSP示例已经在BEAWebLogic和IBMWebSphere3.0下经过测试。WebSphere具有优秀的虚拟服务器机制,因此,如果只是为了避免命名冲突的话并非一定要用包。  对于没有使用过包的初学者,下面我们介绍编译包里面的类的两种方法。  一种方法是设置CLASSPATH,使其指向实际存放Servlet的目录的上一级目录(Servlet主目录),然后在该目录中按正常的方式编译。例如,如果Servlet的主目录是C:\JavaWebServer\servlets,包的名字(即主目录下的子目录名字)是hall,在Windows下,编译过程如下:  DOS>setCLASSPATH=C:\JavaWebServer\servlets;%CLASSPATH%  DOS>cdC:\JavaWebServer\servlets\hall  DOS>javacYourServlet.java  第二种编译包里面的Servlet的方法是进入Servlet主目录,执行“javacdirectory\YourServlet.java”(Windows)或者“javacdirectory/YourServlet.java”(Unix)。例如,再次假定Servlet主目录是C:\JavaWebServer\servlets,包的名字是hall,在Windows中编译过程如下:  DOS>cdC:\JavaWebServer\servlets  DOS>javachall\YourServlet.java  注意在Windows下,大多数JDK1.1版本的javac要求目录名字后面加反斜杠(\)。JDK1.2已经改正这个问题,然而由于许多Web服务器仍旧使用JDK1.1,因此大量的Servlet开发者仍旧在使用JDK1.1。  最后,Javac还有一个高级选项用于支持源代码和.class文件的分开放置,即你可以用javac的“-d”选项把.class文件安装到Web服务器所要求的目录。  3.2.3运行Servlet  在JavaWebServer下,Servlet应该放到JWS安装目录的servlets子目录下,而调用Servlet的URL是http://host/servlet/ServletName。注意子目录的名字是servlets(带“s”),而URL使用的是“servlet”。由于HelloWorldServlet放入包hall,因此调用它的URL应该是http://host/servlet/hall.HelloWorld。在其他的服务器上,安装和调用Servlet的方法可能略有不同。\n  大多数Web服务器还允许定义Servlet的别名,因此Servlet也可能用http://host/any-path/any-file.html形式的URL调用。具体如何进行配置完全依赖于服务器类型,请参考服务器文档了解细节。  3.3输出HTML的Servlet  大多数Servlet都输出HTML,而不象上例一样输出纯文本。要输出HTML还有两个额外的步骤要做:告诉浏览器接下来发送的是HTML;修改println语句构造出合法的HTML页面。  第一步通过设置Content-Type(内容类型)应答头完成。一般地,应答头可以通过HttpServletResponse的setHeader方法设置,但由于设置内容类型是一个很频繁的操作,因此ServletAPI提供了一个专用的方法setContentType。注意设置应答头应该在通过PrintWriter发送内容之前进行。下面是一个实例:  HelloWWW.javapackagehall;importjava.io.*;importjavax.servlet.*;importjavax.servlet.http.*;publicclassHelloWWWextendsHttpServlet{  publicvoiddoGet(HttpServletRequestrequest,                    HttpServletResponseresponse)      throwsServletException,IOException{    response.setContentType("text/html");    PrintWriterout=response.getWriter();    out.println("<!DOCTYPEHTMLPUBLIC\"-//W3C//DTDHTML4.0"+      "Transitional//EN\">\n"+                "<HTML>\n"+                "<HEAD><TITLE>HelloWWW</TITLE></HEAD>\n"+                "<BODY>\n"+                "<H1>HelloWWW</H1>\n"+                "</BODY></HTML>");  }}  3.4几个HTML工具函数\n  通过println语句输出HTML并不方便,根本的解决方法是使用JavaServerPages(JSP)。然而,对于标准的Servlet来说,由于Web页面中有两个部分(DOCTYPE和HEAD)一般不会改变,因此可以用工具函数来封装生成这些内容的代码。  虽然大多数主流浏览器都会忽略DOCTYPE行,但严格地说HTML规范是要求有DOCTYPE行的,它有助于HTML语法检查器根据所声明的HTML版本检查HTML文档合法性。在许多Web页面中,HEAD部分只包含<TITLE>。虽然许多有经验的编写者都会在HEAD中包含许多META标记和样式声明,但这里只考虑最简单的情况。  下面的Java方法只接受页面标题为参数,然后输出页面的DOCTYPE、HEAD、TITLE部分。清单如下:  ServletUtilities.javapackagehall;publicclassServletUtilities{  publicstaticfinalStringDOCTYPE=    "<!DOCTYPEHTMLPUBLIC\"-//W3C//DTDHTML4.0Transitional//EN\">";  publicstaticStringheadWithTitle(Stringtitle){    return(DOCTYPE+"\n"+           "<HTML>\n"+           "<HEAD><TITLE>"+title+"</TITLE></HEAD>\n");  }  //其他工具函数的代码在本文后面介绍}  HelloWWW2.java  下面是应用了ServletUtilities之后重写HelloWWW类得到的HelloWWW2:packagehall;importjava.io.*;importjavax.servlet.*;importjavax.servlet.http.*;publicclassHelloWWW2extendsHttpServlet{\n  publicvoiddoGet(HttpServletRequestrequest,                    HttpServletResponseresponse)      throwsServletException,IOException{    response.setContentType("text/html");    PrintWriterout=response.getWriter();    out.println(ServletUtilities.headWithTitle("HelloWWW")+                "<BODY>\n"+                "<H1>HelloWWW</H1>\n"+                "</BODY></HTML>");  }}四、处理表单数据4.1表单数据概述  如果你曾经使用过Web搜索引擎,或者浏览过在线书店、股票价格、机票信息,或许会留意到一些古怪的URL,比如“http://host/path?user=Marty+Hall&origin=bwi&dest=lax”。这个URL中位于问号后面的部分,即“user=Marty+Hall&origin=bwi&dest=lax”,就是表单数据,这是将Web页面数据发送给服务器程序的最常用方法。对于GET请求,表单数据附加到URL的问号后面(如上例所示);对于POST请求,表单数据用一个单独的行发送给服务器。  以前,从这种形式的数据提取出所需要的表单变量是CGI编程中最麻烦的事情之一。首先,GET请求和POST请求的数据提取方法不同:对于GET请求,通常要通过QUERY_STRING环境变量提取数据;对于POST请求,则一般通过标准输入提取数据。第二,程序员必须负责在“&”符号处截断变量名字-变量值对,再分离出变量名字(等号左边)和变量值(等号右边)。第三,必须对变量值进行URL反编码操作。因为发送数据的时候,字母和数字以原来的形式发送,但空格被转换成加号,其他字符被转换成“%XX”形式,其中XX是十六进制表示的字符ASCII(或者ISOLatin-1)编码值。例如,如果HTML表单中名为“users”的域值为“~hall,~gates,and~mcnealy”,则实际向服务器发送的数据为“users=%7Ehall%2C+%7Egates%2C+and+%7Emcnealy”。最后,即第四个导致解析表单数据非常困难的原因在于,变量值既可能被省略(如“param1=val1&param2=&param3=val3”),也有可能一个变量拥有一个以上的值,即同一个变量可能出现一次以上(如“param1=val1&param2=val2&param1=val3”)。  JavaServlet的好处之一就在于所有上述解析操作都能够自动完成。只需要简单地调用一下HttpServletRequest的getParameter方法、在调用参数中提供表单变量的名字(大小写敏感)即可,而且GET请求和POST请求的处理方法完全相同。  \ngetParameter方法的返回值是一个字符串,它是参数中指定的变量名字第一次出现所对应的值经反编码得到得字符串(可以直接使用)。如果指定的表单变量存在,但没有值,getParameter返回空字符串;如果指定的表单变量不存在,则返回null。如果表单变量可能对应多个值,可以用getParameterValues来取代getParameter。getParameterValues能够返回一个字符串数组。  最后,虽然在实际应用中Servlet很可能只会用到那些已知名字的表单变量,但在调试环境中,获得完整的表单变量名字列表往往是很有用的,利用getParamerterNames方法可以方便地实现这一点。getParamerterNames返回的是一个Enumeration,其中的每一项都可以转换为调用getParameter的字符串。  4.2实例:读取三个表单变量  下面是一个简单的例子,它读取三个表单变量param1、param2和param3,并以HTML列表的形式列出它们的值。请注意,虽然在发送应答内容之前必须指定应答类型(包括内容类型、状态以及其他HTTP头信息),但Servlet对何时读取请求内容却没有什么要求。  另外,我们也可以很容易地把Servlet做成既能处理GET请求,也能够处理POST请求,这只需要在doPost方法中调用doGet方法,或者覆盖service方法(service方法调用doGet、doPost、doHead等方法)。在实际编程中这是一种标准的方法,因为它只需要很少的额外工作,却能够增加客户端编码的灵活性。  如果你习惯用传统的CGI方法,通过标准输入读取POST数据,那么在Servlet中也有类似的方法,即在HttpServletRequest上调用getReader或者getInputStream,但这种方法对普通的表单变量来说太麻烦。然而,如果是要上载文件,或者POST数据是通过专门的客户程序而不是HTML表单发送,那么就要用到这种方法。  注意用第二种方法读取POST数据时,不能再用getParameter来读取这些数据。  ThreeParams.javapackagehall;importjava.io.*;importjavax.servlet.*;importjavax.servlet.http.*;importjava.util.*;publicclassThreeParamsextendsHttpServlet{  publicvoiddoGet(HttpServletRequestrequest,               HttpServletResponseresponse)      throwsServletException,IOException{    response.setContentType("text/html");    PrintWriterout=response.getWriter();\n    Stringtitle="读取三个请求参数";    out.println(ServletUtilities.headWithTitle(title)+                "<BODY>\n"+                "<H1ALIGN=CENTER>"+title+"</H1>\n"+                "<UL>\n"+                "  <LI>param1:"                +request.getParameter("param1")+"\n"+                "  <LI>param2:"                +request.getParameter("param2")+"\n"+                "  <LI>param3:"                +request.getParameter("param3")+"\n"+                "</UL>\n"+                "</BODY></HTML>");  }  publicvoiddoPost(HttpServletRequestrequest,                     HttpServletResponseresponse)      throwsServletException,IOException{    doGet(request,response);  }}  4.3实例:输出所有的表单数据  下面这个例子寻找表单所发送的所有变量名字,并把它们放入表格中,没有值或者有多个值的变量都突出显示。  首先,程序通过HttpServletRequest的getParameterNames方法得到所有的变量名字,getParameterNames返回的是一个Enumeration。接下来,程序用循环遍历这个Enumeration,通过hasMoreElements确定何时结束循环,利用nextElement得到Enumeration中的各个项。由于nextElement返回的是一个Object,程序把它转换成字符串后再用这个字符串来调用getParameterValues。  getParameterValues返回一个字符串数组,如果这个数组只有一个元素且等于空字符串,说明这个表单变量没有值,Servlet以斜体形式输出“NoValue”;如果数组元素个数大于1,说明这个表单变量有多个值,Servlet以HTML列表形式输出这些值;其他情况下Servlet直接把变量值放入表格。  ShowParameters.java  注意,ShowParameters.java用到了前面介绍过的ServletUtilities.java。\npackagehall;importjava.io.*;importjavax.servlet.*;importjavax.servlet.http.*;importjava.util.*;publicclassShowParametersextendsHttpServlet{  publicvoiddoGet(HttpServletRequestrequest,                    HttpServletResponseresponse)      throwsServletException,IOException{    response.setContentType("text/html");    PrintWriterout=response.getWriter();    Stringtitle="读取所有请求参数";    out.println(ServletUtilities.headWithTitle(title)+                "<BODYBGCOLOR=\"#FDF5E6\">\n"+                "<H1ALIGN=CENTER>"+title+"</H1>\n"+                "<TABLEBORDER=1ALIGN=CENTER>\n"+                "<TRBGCOLOR=\"#FFAD00\">\n"+                "<TH>参数名字<TH>参数值");    EnumerationparamNames=request.getParameterNames();    while(paramNames.hasMoreElements()){      StringparamName=(String)paramNames.nextElement();      out.println("<TR><TD>"+paramName+"\n<TD>");      String[]paramValues=request.getParameterValues(paramName);      if(paramValues.length==1){        StringparamValue=paramValues[0];        if(paramValue.length()==0)          out.print("<I>NoValue</I>");        else          out.print(paramValue);      }else{        out.println("<UL>");        for(inti=0;i<paramValues.length;i++){          out.println("<LI>"+paramValues[i]);        }        out.println("</UL>");      }    }    out.println("</TABLE>\n</BODY></HTML>");  }  publicvoiddoPost(HttpServletRequestrequest,                     HttpServletResponseresponse)\n      throwsServletException,IOException{    doGet(request,response);  }}  测试表单  下面是向上述Servlet发送数据的表单PostForm.html。就像所有包含密码输入域的表单一样,该表单用POST方法发送数据。我们可以看到,在Servlet中同时实现doGet和doPost这两种方法为表单制作带来了方便。<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.0Transitional//EN"><HTML><HEAD>  <TITLE>示例表单</TITLE></HEAD><BODYBGCOLOR="#FDF5E6"><H1ALIGN="CENTER">用POST方法发送数据的表单</H1><FORMACTION="/servlet/hall.ShowParameters"      METHOD="POST">  ItemNumber:  <INPUTTYPE="TEXT"NAME="itemNum"><BR>  Quantity:  <INPUTTYPE="TEXT"NAME="quantity"><BR>  PriceEach:  <INPUTTYPE="TEXT"NAME="price"VALUE="$"><BR>  <HR>  FirstName:  <INPUTTYPE="TEXT"NAME="firstName"><BR>  LastName:  <INPUTTYPE="TEXT"NAME="lastName"><BR>  MiddleInitial:  <INPUTTYPE="TEXT"NAME="initial"><BR>  ShippingAddress:  <TEXTAREANAME="address"ROWS=3COLS=40></TEXTAREA><BR>  CreditCard:<BR>    <INPUTTYPE="RADIO"NAME="cardType"                     VALUE="Visa">Visa<BR>    <INPUTTYPE="RADIO"NAME="cardType"\n                     VALUE="MasterCard">MasterCard<BR>    <INPUTTYPE="RADIO"NAME="cardType"                     VALUE="Amex">AmericanExpress<BR>    <INPUTTYPE="RADIO"NAME="cardType"                     VALUE="Discover">Discover<BR>    <INPUTTYPE="RADIO"NAME="cardType"                     VALUE="JavaSmartCard">JavaSmartCard<BR>  CreditCardNumber:  <INPUTTYPE="PASSWORD"NAME="cardNum"><BR>  RepeatCreditCardNumber:  <INPUTTYPE="PASSWORD"NAME="cardNum"><BR><BR>  <CENTER>    <INPUTTYPE="SUBMIT"VALUE="SubmitOrder">  </CENTER></FORM></BODY></HTML>五、读取HTTP请求头5.1HTTP请求头概述  HTTP客户程序(例如浏览器),向服务器发送请求的时候必须指明请求类型(一般是GET或者POST)。如有必要,客户程序还可以选择发送其他的请求头。大多数请求头并不是必需的,但Content-Length除外。对于POST请求来说Content-Length必须出现。  下面是一些最常见的请求头:Accept:浏览器可接受的MIME类型。Accept-Charset:浏览器可接受的字符集。Accept-Encoding:浏览器能够进行解码的数据编码方式,比如gzip。Servlet能够向支持gzip的浏览器返回经gzip编码的HTML页面。许多情形下这可以减少5到10倍的下载时间。Accept-Language:浏览器所希望的语言种类,当服务器能够提供一种以上的语言版本时要用到。Authorization:授权信息,通常出现在对服务器发送的WWW-Authenticate头的应答中。Connection:表示是否需要持久连接。如果Servlet看到这里的值为“Keep-Alive”,或者看到请求使用的是HTTP1.1(HTTP1.1默认进行持久连接),它就可以利用持久连接的优点,当页面包含多个元素时(例如Applet,图片),显著地减少下载所需要的时间。要实现这一点,Servlet需要在应答中发送一个Content-Length头,最简单的实现方法是:先把内容写入ByteArrayOutputStream,然后在正式写出内容之前计算它的大小。\nContent-Length:表示请求消息正文的长度。Cookie:这是最重要的请求头信息之一,参见后面《Cookie处理》一章中的讨论。From:请求发送者的email地址,由一些特殊的Web客户程序使用,浏览器不会用到它。Host:初始URL中的主机和端口。If-Modified-Since:只有当所请求的内容在指定的日期之后又经过修改才返回它,否则返回304“NotModified”应答。Pragma:指定“no-cache”值表示服务器必须返回一个刷新后的文档,即使它是代理服务器而且已经有了页面的本地拷贝。Referer:包含一个URL,用户从该URL代表的页面出发访问当前请求的页面。User-Agent:浏览器类型,如果Servlet返回的内容与浏览器类型有关则该值非常有用。UA-Pixels,UA-Color,UA-OS,UA-CPU:由某些版本的IE浏览器所发送的非标准的请求头,表示屏幕大小、颜色深度、操作系统和CPU类型。  有关HTTP头完整、详细的说明,请参见http://www.w3.org/Protocols/的HTTP规范。  5.2在Servlet中读取请求头  在Servlet中读取HTTP头是非常方便的,只需要调用一下HttpServletRequest的getHeader方法即可。如果客户请求中提供了指定的头信息,getHeader返回对应的字符串;否则,返回null。部分头信息经常要用到,它们有专用的访问方法:getCookies方法返回Cookie头的内容,经解析后存放在Cookie对象的数组中,请参见后面有关Cookie章节的讨论;getAuthType和getRemoteUser方法分别读取Authorization头中的一部分内容;getDateHeader和getIntHeader方法读取指定的头,然后返回日期值或整数值。  除了读取指定的头之外,利用getHeaderNames还可以得到请求中所有头名字的一个Enumeration对象。  最后,除了查看请求头信息之外,我们还可以从请求主命令行获得一些信息。getMethod方法返回请求方法,请求方法通常是GET或者POST,但也有可能是HEAD、PUT或者DELETE。getRequestURI方法返回URI(URI是URL的从主机和端口之后到表单数据之前的那一部分)。getRequestProtocol返回请求命令的第三部分,一般是“HTTP/1.0”或者“HTTP/1.1”。  5.3实例:输出所有的请求头  下面的Servlet实例把所有接收到的请求头和它的值以表格的形式输出。另外,该Servlet还会输出主请求命令的三个部分:请求方法,URI,协议/版本。  ShowRequestHeaders.javapackagehall;\nimportjava.io.*;importjavax.servlet.*;importjavax.servlet.http.*;importjava.util.*;publicclassShowRequestHeadersextendsHttpServlet{  publicvoiddoGet(HttpServletRequestrequest,                    HttpServletResponseresponse)      throwsServletException,IOException{    response.setContentType("text/html");    PrintWriterout=response.getWriter();    Stringtitle="显示所有请求头";    out.println(ServletUtilities.headWithTitle(title)+                "<BODYBGCOLOR=\"#FDF5E6\">\n"+                "<H1ALIGN=CENTER>"+title+"</H1>\n"+                "<B>RequestMethod:</B>"+                request.getMethod()+"<BR>\n"+                "<B>RequestURI:</B>"+                request.getRequestURI()+"<BR>\n"+                "<B>RequestProtocol:</B>"+                request.getProtocol()+"<BR><BR>\n"+                "<TABLEBORDER=1ALIGN=CENTER>\n"+                "<TRBGCOLOR=\"#FFAD00\">\n"+                "<TH>HeaderName<TH>HeaderValue");    EnumerationheaderNames=request.getHeaderNames();    while(headerNames.hasMoreElements()){      StringheaderName=(String)headerNames.nextElement();      out.println("<TR><TD>"+headerName);      out.println("    <TD>"+request.getHeader(headerName));    }    out.println("</TABLE>\n</BODY></HTML>");  }  publicvoiddoPost(HttpServletRequestrequest,                     HttpServletResponseresponse)      throwsServletException,IOException{    doGet(request,response);  }}六、访问CGI变量6.1CGI变量概述  如果你是从传统的CGI编程转而学习Java\nServlet,或许已经习惯了“CGI变量”这一概念。CGI变量汇集了各种有关请求的信息:部分来自HTTP请求命令和请求头,例如Content-Length头;部分来自Socket本身,例如主机的名字和IP地址;也有部分与服务器安装配置有关,例如URL到实际路径的映射。  6.2标准CGI变量的Servlet等价表示  下表假定request对象是提供给doGet和doPost方法的HttpServletRequest类型对象。CGI变量  含义  从doGet或doPost访问  AUTH_TYPE  如果提供了Authorization头,这里指定了具体的模式(basic或者digest)。  request.getAuthType()  CONTENT_LENGTH  只用于POST请求,表示所发送数据的字节数。  严格地讲,等价的表达方式应该是String.valueOf(request.getContentLength())(返回一个字符串)。但更常见的是用request.getContentLength()返回含义相同的整数。  CONTENT_TYPE  如果指定的话,表示后面所跟数据的类型。  request.getContentType()  DOCUMENT_ROOT  与http://host/对应的路径。  getServletContext().getRealPath("/")注意低版本Servlet规范中的等价表达方式是request.getRealPath("/")。HTTP_XXX_YYY  访问任意HTTP头。  request.getHeader("Xxx-Yyy")  PATH_INFO  URL中的附加路径信息,即URL中Servlet路径之后、查询字符串之前的那部分。  request.getPathInfo()  PATH_TRANSLATED  映射到服务器实际路径之后的路径信息。  request.getPathTranslated()  QUERY_STRING  这是字符串形式的附加到URL后面的查询字符串,数据仍旧是URL编码的。在Servlet中很少需要用到未经解码的数据,一般使用getParameter访问各个参数。  request.getQueryString()  REMOTE_ADDR  发出请求的客户机的IP地址。  request.getRemoteAddr()  REMOTE_HOST  发出请求的客户机的完整的域名,如java.sun.com。如果不能确定该域名,则返回IP地址。  request.getRemoteHost()  REMOTE_USER  如果提供了Authorization头,则代表其用户部分。它代表发出请求的用户的名字。  request.getRemoteUser()  REQUEST_METHOD  请求类型。通常是GET或者POST。但偶尔也会出现HEAD,PUT,DELETE,OPTIONS,或者TRACE.  request.getMethod()  SCRIPT_NAME  URL中调用Servlet的那一部分,不包含附加路径信息和查询字符串。  request.getServletPath()  SERVER_NAME  Web服务器名字。  request.getServerName()  SERVER_PORT  服务器监听的端口。  严格地说,等价表达应该是返回字符串的String.valueOf(request.getServerPort())。但经常使用返回整数值的request.getServerPort()。  SERVER_PROTOCOL  请求命令中的协议名字和版本(即HTTP/1.0或HTTP/1.1)。  request.getProtocol()  \nSERVER_SOFTWARE  Servlet引擎的名字和版本。  getServletContext().getServerInfo()    6.3实例:读取CGI变量  下面这个Servlet创建一个表格,显示除了HTTP_XXX_YYY之外的所有CGI变量。HTTP_XXX_YYY是HTTP请求头信息,请参见上一节介绍。  ShowCGIVariables.javapackagehall;importjava.io.*;importjavax.servlet.*;importjavax.servlet.http.*;importjava.util.*;publicclassShowCGIVariablesextendsHttpServlet{  publicvoiddoGet(HttpServletRequestrequest,                    HttpServletResponseresponse)      throwsServletException,IOException{    response.setContentType("text/html");    PrintWriterout=response.getWriter();    String[][]variables=      {{"AUTH_TYPE",request.getAuthType()},        {"CONTENT_LENGTH",String.valueOf(request.getContentLength())},        {"CONTENT_TYPE",request.getContentType()},        {"DOCUMENT_ROOT",getServletContext().getRealPath("/")},        {"PATH_INFO",request.getPathInfo()},        {"PATH_TRANSLATED",request.getPathTranslated()},        {"QUERY_STRING",request.getQueryString()},        {"REMOTE_ADDR",request.getRemoteAddr()},        {"REMOTE_HOST",request.getRemoteHost()},        {"REMOTE_USER",request.getRemoteUser()},        {"REQUEST_METHOD",request.getMethod()},        {"SCRIPT_NAME",request.getServletPath()},        {"SERVER_NAME",request.getServerName()},        {"SERVER_PORT",String.valueOf(request.getServerPort())},        {"SERVER_PROTOCOL",request.getProtocol()},        {"SERVER_SOFTWARE",getServletContext().getServerInfo()}      };    Stringtitle="显示CGI变量";    out.println(ServletUtilities.headWithTitle(title)+                "<BODYBGCOLOR=\"#FDF5E6\">\n"+                "<H1ALIGN=CENTER>"+title+"</H1>\n"+\n                "<TABLEBORDER=1ALIGN=CENTER>\n"+                "<TRBGCOLOR=\"#FFAD00\">\n"+                "<TH>CGIVariableName<TH>Value");    for(inti=0;i<variables.length;i++){      StringvarName=variables[i][0];      StringvarValue=variables[i][1];      if(varValue==null)        varValue="<I>Notspecified</I>";      out.println("<TR><TD>"+varName+"<TD>"+varValue);    }    out.println("</TABLE></BODY></HTML>");  }  publicvoiddoPost(HttpServletRequestrequest,                     HttpServletResponseresponse)      throwsServletException,IOException{    doGet(request,response);  }}七、HTTP应答状态7.1状态代码概述  Web服务器响应浏览器或其他客户程序的请求时,其应答一般由以下几个部分组成:一个状态行,几个应答头,一个空行,内容文档。下面是一个最简单的应答:HTTP/1.1200OKContent-Type:text/plainHelloWorld  状态行包含HTTP版本、状态代码、与状态代码对应的简短说明信息。在大多数情况下,除了Content-Type之外的所有应答头都是可选的。但Content-Type是必需的,它描述的是后面文档的MIME类型。虽然大多数应答都包含一个文档,但也有一些不包含,例如对HEAD请求的应答永远不会附带文档。有许多状态代码实际上用来标识一次失败的请求,这些应答也不包含文档(或只包含一个简短的错误信息说明)。  \nServlet可以利用状态代码来实现许多功能。例如,可以把用户重定向到另一个网站;可以指示出后面的文档是图片、PDF文件或HTML文件;可以告诉用户必须提供密码才能访问文档;等等。这一部分我们将具体讨论各种状态代码的含义以及利用这些代码可以做些什么。  7.2设置状态代码  如前所述,HTTP应答状态行包含HTTP版本、状态代码和对应的状态信息。由于状态信息直接和状态代码相关,而HTTP版本又由服务器确定,因此需要Servlet设置的只有一个状态代码。  Servlet设置状态代码一般使用HttpServletResponse的setStatus方法。setStatus方法的参数是一个整数(即状态代码),不过为了使得代码具有更好的可读性,可以用HttpServletResponse中定义的常量来避免直接使用整数。这些常量根据HTTP1.1中的标准状态信息命名,所有的名字都加上了SC前缀(StatusCode的缩写)并大写,同时把空格转换成了下划线。也就是说,与状态代码404对应的状态信息是“NotFound”,则HttpServletResponse中的对应常量名字为SC_NOT_FOUND。但有两个例外:和状态代码302对应的常量根据HTTP1.0命名,而307没有对应的常量。  设置状态代码并非总是意味着不要再返回文档。例如,虽然大多数服务器返回404应答时会输出简单的“FileNotFound”信息,但Servlet也可以定制这个应答。不过,定制应答时应当在通过PrintWriter发送任何内容之前先调用response.setStatus。  虽然设置状态代码一般使用的是response.setStauts(int)方法,但为了简单起见,HttpServletResponse为两种常见的情形提供了专用方法:sendError方法生成一个404应答,同时生成一个简短的HTML错误信息文档;sendRedirect方法生成一个302应答,同时在Location头中指示新文档的URL。  7.3HTTP1.1状态代码及其含义  下表显示了常见的HTTP1.1状态代码以及它们对应的状态信息和含义。  应当谨慎地使用那些只有HTTP1.1支持的状态代码,因为许多浏览器还只能够支持HTTP1.0。如果你使用了HTTP1.1特有的状态代码,最好能够检查一下请求的HTTP版本号(通过HttpServletRequest的getProtocol方法)。状态代码  状态信息  含义  100  Continue  初始的请求已经接受,客户应当继续发送请求的其余部分。(HTTP1.1新)  101  SwitchingProtocols  服务器将遵从客户的请求转换到另外一种协议(HTTP1.1新)  200  OK  一切正常,对GET和POST请求的应答文档跟在后面。如果不用setStatus设置状态代码,Servlet默认使用202状态代码。  201  Created  服务器已经创建了文档,Location头给出了它的URL。  202  Accepted  已经接受请求,但处理尚未完成。  203  Non-Authoritative\nInformation  文档已经正常地返回,但一些应答头可能不正确,因为使用的是文档的拷贝(HTTP1.1新)。  204  NoContent  没有新文档,浏览器应该继续显示原来的文档。如果用户定期地刷新页面,而Servlet可以确定用户文档足够新,这个状态代码是很有用的。  205  ResetContent  没有新的内容,但浏览器应该重置它所显示的内容。用来强制浏览器清除表单输入内容(HTTP1.1新)。  206  PartialContent  客户发送了一个带有Range头的GET请求,服务器完成了它(HTTP1.1新)。  300  MultipleChoices  客户请求的文档可以在多个位置找到,这些位置已经在返回的文档内列出。如果服务器要提出优先选择,则应该在Location应答头指明。  301  MovedPermanently  客户请求的文档在其他地方,新的URL在Location头中给出,浏览器应该自动地访问新的URL。  302  Found  类似于301,但新的URL应该被视为临时性的替代,而不是永久性的。注意,在HTTP1.0中对应的状态信息是“MovedTemporatily”,而HttpServletResponse中相应的常量是SC_MOVED_TEMPORARILY,而不是SC_FOUND。出现该状态代码时,浏览器能够自动访问新的URL,因此它是一个很有用的状态代码。为此,Servlet提供了一个专用的方法,即sendRedirect。使用response.sendRedirect(url)比使用response.setStatus(response.SC_MOVED_TEMPORARILY)和response.setHeader("Location",url)更好。这是因为:首先,代码更加简洁。第二,使用sendRedirect,Servlet会自动构造一个包含新链接的页面(用于那些不能自动重定向的老式浏览器)。最后,sendRedirect能够处理相对URL,自动把它们转换成绝对URL。注意这个状态代码有时候可以和301替换使用。例如,如果浏览器错误地请求http://host/~user(缺少了后面的斜杠),有的服务器返回301,有的则返回302。严格地说,我们只能假定只有当原来的请求是GET时浏览器才会自动重定向。请参见307。303  SeeOther  类似于301/302,不同之处在于,如果原来的请求是POST,Location头指定的重定向目标文档应该通过GET提取(HTTP1.1新)。  304  NotModified  客户端有缓冲的文档并发出了一个条件性的请求(一般是提供If-Modified-Since头表示客户只想比指定日期更新的文档)。服务器告诉客户,原来缓冲的文档还可以继续使用。  305  UseProxy  客户请求的文档应该通过Location头所指明的代理服务器提取(HTTP1.1新)。  307  TemporaryRedirect  和302(Found)相同。许多浏览器会错误地响应302应答进行重定向,即使原来的请求是POST,即使它实际上只能在POST请求的应答是303时才能重定向。由于这个原因,HTTP1.1新增了307,以便更加清除地区分几个状态代码:当出现303应答时,浏览器可以跟随重定向的GET和POST请求;如果是307应答,则浏览器只能跟随对GET请求的重定向。\n注意,HttpServletResponse中没有为该状态代码提供相应的常量。(HTTP1.1新)400  BadRequest  请求出现语法错误。  401  Unauthorized  客户试图未经授权访问受密码保护的页面。应答中会包含一个WWW-Authenticate头,浏览器据此显示用户名字/密码对话框,然后在填写合适的Authorization头后再次发出请求。  403  Forbidden  资源不可用。服务器理解客户的请求,但拒绝处理它。通常由于服务器上文件或目录的权限设置导致。  404  NotFound  无法找到指定位置的资源。这也是一个常用的应答,HttpServletResponse专门提供了相应的方法:sendError(message)。  405  MethodNotAllowed  请求方法(GET、POST、HEAD、DELETE、PUT、TRACE等)对指定的资源不适用。(HTTP1.1新)  406  NotAcceptable  指定的资源已经找到,但它的MIME类型和客户在Accpet头中所指定的不兼容(HTTP1.1新)。  407  ProxyAuthenticationRequired  类似于401,表示客户必须先经过代理服务器的授权。(HTTP1.1新)  408  RequestTimeout  在服务器许可的等待时间内,客户一直没有发出任何请求。客户可以在以后重复同一请求。(HTTP1.1新)  409  Conflict  通常和PUT请求有关。由于请求和资源的当前状态相冲突,因此请求不能成功。(HTTP1.1新)  410  Gone  所请求的文档已经不再可用,而且服务器不知道应该重定向到哪一个地址。它和404的不同在于,返回407表示文档永久地离开了指定的位置,而404表示由于未知的原因文档不可用。(HTTP1.1新)  411  LengthRequired  服务器不能处理请求,除非客户发送一个Content-Length头。(HTTP1.1新)  412  PreconditionFailed  请求头中指定的一些前提条件失败(HTTP1.1新)。  413  RequestEntityTooLarge  目标文档的大小超过服务器当前愿意处理的大小。如果服务器认为自己能够稍后再处理该请求,则应该提供一个Retry-After头(HTTP1.1新)。  414  RequestURITooLong  URI太长(HTTP1.1新)。  416  RequestedRangeNotSatisfiable  服务器不能满足客户在请求中指定的Range头。(HTTP1.1新)  500  InternalServerError  服务器遇到了意料不到的情况,不能完成客户的请求。  501  NotImplemented  服务器不支持实现请求所需要的功能。例如,客户发出了一个服务器不支持的PUT请求。  502  BadGateway  服务器作为网关或者代理时,为了完成请求访问下一个服务器,但该服务器返回了非法的应答。  503  ServiceUnavailable  服务器由于维护或者负载过重未能应答。例如,Servlet可能在数据库连接池已满的情况下返回503。服务器返回503时可以提供一个Retry-After头。  504  GatewayTimeout  由作为代理或网关的服务器使用,表示不能及时地从远程服务器获得应答。(HTTP1.1新)  505  HTTPVersionNot\nSupported  服务器不支持请求中所指明的HTTP版本。(HTTP1.1新)    7.4实例:访问多个搜索引擎  下面这个例子用到了除200之外的另外两个常见状态代码:302和404。302通过sendRedirect方法设置,404通过sendError方法设置。  在这个例子中,首先出现的HTML表单用来选择搜索引擎、搜索字符串、每页显示的搜索结果数量。表单提交后,Servlet提取这三个变量,按照所选择的搜索引擎的要求构造出包含这些变量的URL,然后把用户重定向到这个URL。如果用户不能正确地选择搜索引擎,或者利用其他表单发送了一个不认识的搜索引擎名字,则返回一个提示搜索引擎找不到的404页面。  SearchEngines.java  注意:这个Servlet要用到后面给出的SearchSpec类,SearchSpec的功能是构造适合不同搜索引擎的URL。packagehall;importjava.io.*;importjavax.servlet.*;importjavax.servlet.http.*;importjava.net.*;publicclassSearchEnginesextendsHttpServlet{  publicvoiddoGet(HttpServletRequestrequest,                    HttpServletResponseresponse)      throwsServletException,IOException{    //getParameter自动解码URL编码的查询字符串。由于我们    //要把查询字符串发送给另一个服务器,因此再次使用    //URLEncoder进行URL编码    StringsearchString=      URLEncoder.encode(request.getParameter("searchString"));    StringnumResults=      request.getParameter("numResults");    StringsearchEngine=      request.getParameter("searchEngine");    SearchSpec[]commonSpecs=SearchSpec.getCommonSpecs();    for(inti=0;i<commonSpecs.length;i++){      SearchSpecsearchSpec=commonSpecs[i];      if(searchSpec.getName().equals(searchEngine)){        Stringurl=          response.encodeURL(searchSpec.makeURL(searchString,\n                            numResults));        response.sendRedirect(url);        return;      }    }    response.sendError(response.SC_NOT_FOUND,                       "Norecognizedsearchenginespecified.");  }  publicvoiddoPost(HttpServletRequestrequest,                     HttpServletResponseresponse)      throwsServletException,IOException{    doGet(request,response);  }}  SearchSpec.javapackagehall;classSearchSpec{  privateStringname,baseURL,numResultsSuffix;  privatestaticSearchSpec[]commonSpecs=    {newSearchSpec("google",                     "http://www.google.com/search?q=",                     "&num="),      newSearchSpec("infoseek",                     "http://infoseek.go.com/Titles?qt=",                     "&nh="),      newSearchSpec("lycos",                     "http://lycospro.lycos.com/cgi-bin/pursuit?query=",                     "&maxhits="),      newSearchSpec("hotbot",                     "http://www.hotbot.com/?MT=",                     "&DC=")    };  publicSearchSpec(Stringname,                    StringbaseURL,                    StringnumResultsSuffix){    this.name=name;\n    this.baseURL=baseURL;    this.numResultsSuffix=numResultsSuffix;  }  publicStringmakeURL(StringsearchString,StringnumResults){    return(baseURL+searchString+numResultsSuffix+numResults);  }  publicStringgetName(){    return(name);  }  publicstaticSearchSpec[]getCommonSpecs(){    return(commonSpecs);  }}  SearchEngines.html  下面是调用上述Servlet的HTML表单。<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.0Transitional//EN"><HTML><HEAD>  <TITLE>访问多个搜索引擎</TITLE></HEAD><BODYBGCOLOR="#FDF5E6"><FORMACTION="/servlet/hall.SearchEngines">  <CENTER>    搜索关键字:    <INPUTTYPE="TEXT"NAME="searchString"><BR>    每页显示几个查询结果:    <INPUTTYPE="TEXT"NAME="numResults"                       VALUE=10SIZE=3><BR>    <INPUTTYPE="RADIO"NAME="searchEngine"                        VALUE="google">    Google|    <INPUTTYPE="RADIO"NAME="searchEngine"                        VALUE="infoseek">    Infoseek|\n    <INPUTTYPE="RADIO"NAME="searchEngine"                        VALUE="lycos">    Lycos|    <INPUTTYPE="RADIO"NAME="searchEngine"                        VALUE="hotbot">    HotBot    <BR>    <INPUTTYPE="SUBMIT"VALUE="Search">  </CENTER></FORM></BODY></HTML>八、设置HTTP应答头8.1HTTP应答头概述  Web服务器的HTTP应答一般由以下几项构成:一个状态行,一个或多个应答头,一个空行,内容文档。设置HTTP应答头往往和设置状态行中的状态代码结合起来。例如,有好几个表示“文档位置已经改变”的状态代码都伴随着一个Location头,而401(Unauthorized)状态代码则必须伴随一个WWW-Authenticate头。  然而,即使在没有设置特殊含义的状态代码时,指定应答头也是很有用的。应答头可以用来完成:设置Cookie,指定修改日期,指示浏览器按照指定的间隔刷新页面,声明文档的长度以便利用持久HTTP连接,……等等许多其他任务。  设置应答头最常用的方法是HttpServletResponse的setHeader,该方法有两个参数,分别表示应答头的名字和值。和设置状态代码相似,设置应答头应该在发送任何文档内容之前进行。  setDateHeader方法和setIntHeadr方法专门用来设置包含日期和整数值的应答头,前者避免了把Java时间转换为GMT时间字符串的麻烦,后者则避免了把整数转换为字符串的麻烦。  HttpServletResponse还提供了许多设置常见应答头的简便方法,如下所示:setContentType:设置Content-Type头。大多数Servlet都要用到这个方法。setContentLength:设置Content-Length头。对于支持持久HTTP连接的浏览器来说,这个函数是很有用的。addCookie:设置一个Cookie(ServletAPI中没有setCookie方法,因为应答往往包含多个Set-Cookie头)。另外,如上节介绍,sendRedirect方法设置状态代码302时也会设置Location头。  8.2常见应答头及其含义\n  有关HTTP头详细和完整的说明,请参见http://www.w3.org/Protocols/规范。应答头  说明  Allow  服务器支持哪些请求方法(如GET、POST等)。  Content-Encoding  文档的编码(Encode)方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩,但只有Unix上的Netscape和Windows上的IE4、IE5才支持它。因此,Servlet应该通过查看Accept-Encoding头(即request.getHeader("Accept-Encoding"))检查浏览器是否支持gzip,为支持gzip的浏览器返回经gzip压缩的HTML页面,为其他浏览器返回普通页面。  Content-Length  表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势,可以把输出文档写入ByteArrayOutputStram,完成后查看其大小,然后把该值放入Content-Length头,最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容。  Content-Type  表示后面的文档属于什么MIME类型。Servlet默认为text/plain,但通常需要显式地指定为text/html。由于经常要设置Content-Type,因此HttpServletResponse提供了一个专用的方法setContentTyep。  Date  当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。  Expires  应该在什么时候认为文档已经过期,从而不再缓存它?  Last-Modified  文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期,该请求将被视为一个条件GET,只有改动时间迟于指定时间的文档才会返回,否则返回一个304(NotModified)状态。Last-Modified也可用setDateHeader方法来设置。  Location  表示客户应当到哪里去提取文档。Location通常不是直接设置的,而是通过HttpServletResponse的sendRedirect方法,该方法同时设置状态代码为302。  Refresh  表示浏览器应该在多少时间之后刷新文档,以秒计。除了刷新当前文档之外,你还可以通过setHeader("Refresh","5;URL=http://host/path")让浏览器读取指定的页面。注意这种功能通常是通过设置HTML页面HEAD区的<METAHTTP-EQUIV="Refresh"CONTENT="5;URL=http://host/path">实现,这是因为,自动刷新或重定向对于那些不能使用CGI或Servlet的HTML编写者十分重要。但是,对于Servlet来说,直接设置Refresh头更加方便。注意Refresh的意义是“N秒之后刷新本页面或访问指定页面”,而不是“每隔N秒刷新本页面或访问指定页面”。因此,连续刷新要求每次都发送一个Refresh头,而发送204状态代码则可以阻止浏览器继续刷新,不管是使用Refresh头还是<METAHTTP-EQUIV="Refresh"...>。注意Refresh头不属于HTTP1.1正式规范的一部分,而是一个扩展,但Netscape和IE都支持它。\nServer  服务器名字。Servlet一般不设置这个值,而是由Web服务器自己设置。  Set-Cookie  设置和页面关联的Cookie。Servlet不应使用response.setHeader("Set-Cookie",...),而是应使用HttpServletResponse提供的专用方法addCookie。参见下文有关Cookie设置的讨论。  WWW-Authenticate  客户应该在Authorization头中提供什么类型的授权信息?在包含401(Unauthorized)状态行的应答中这个头是必需的。例如,response.setHeader("WWW-Authenticate","BASICrealm=\"executives\"")。注意Servlet一般不进行这方面的处理,而是让Web服务器的专门机制来控制受密码保护页面的访问(例如.htaccess)。  8.3实例:内容改变时自动刷新页面  下面这个Servlet用来计算大素数。因为计算非常大的数字(例如500位)可能要花不少时间,所以Servlet将立即返回已经找到的结果,同时在后台继续计算。后台计算使用一个优先级较低的线程以避免过多地影响Web服务器的性能。如果计算还没有完成,Servlet通过发送Refresh头指示浏览器在几秒之后继续请求新的内容。  注意,本例除了说明HTTP应答头的用处之外,还显示了Servlet的另外两个很有价值的功能。首先,它表明Servlet能够处理多个并发的连接,每个都有自己的线程。Servlet维护了一份已有素数计算请求的Vector表,通过查找素数个数(素数列表的长度)和数字个数(每个素数的长度)将当前请求和已有请求相匹配,把所有这些请求同步到这个列表上。第二,本例证明,在Servlet中维持请求之间的状态信息是非常容易的。维持状态信息在传统的CGI编程中是一件很麻烦的事情。由于维持了状态信息,浏览器能够在刷新页面时访问到正在进行的计算过程,同时也使得Servlet能够保存一个有关最近请求结果的列表,当一个新的请求指定了和最近请求相同的参数时可以立即返回结果。  PrimeNumbers.java  注意,该Servlet要用到前面给出的ServletUtilities.java。另外还要用到:PrimeList.java,用于在后台线程中创建一个素数的Vector;Primes.java,用于随机生成BigInteger类型的大数字,检查它们是否是素数。(此处略去PrimeList.java和Primes.java的代码。)packagehall;importjava.io.*;importjavax.servlet.*;importjavax.servlet.http.*;importjava.util.*;\npublicclassPrimeNumbersextendsHttpServlet{  privatestaticVectorprimeListVector=newVector();  privatestaticintmaxPrimeLists=30;    publicvoiddoGet(HttpServletRequestrequest,                    HttpServletResponseresponse)      throwsServletException,IOException{    intnumPrimes=ServletUtilities.getIntParameter(request,"numPrimes",50);    intnumDigits=ServletUtilities.getIntParameter(request,"numDigits",120);    PrimeListprimeList=findPrimeList(primeListVector,numPrimes,numDigits);    if(primeList==null){      primeList=newPrimeList(numPrimes,numDigits,true);      synchronized(primeListVector){        if(primeListVector.size()>=maxPrimeLists)          primeListVector.removeElementAt(0);        primeListVector.addElement(primeList);      }    }    VectorcurrentPrimes=primeList.getPrimes();    intnumCurrentPrimes=currentPrimes.size();    intnumPrimesRemaining=(numPrimes-numCurrentPrimes);    booleanisLastResult=(numPrimesRemaining==0);    if(!isLastResult){      response.setHeader("Refresh","5");    }    response.setContentType("text/html");    PrintWriterout=response.getWriter();    Stringtitle="Some"+numDigits+"-DigitPrimeNumbers";    out.println(ServletUtilities.headWithTitle(title)+                "<BODYBGCOLOR=\"#FDF5E6\">\n"+                "<H2ALIGN=CENTER>"+title+"</H2>\n"+                "<H3>Primesfoundwith"+numDigits+                "ormoredigits:"+numCurrentPrimes+".</H3>");    if(isLastResult)      out.println("<B>Donesearching.</B>");    else      out.println("<B>Stilllookingfor"+numPrimesRemaining+                  "more<BLINK>...</BLINK></B>");    out.println("<OL>");    for(inti=0;i<numCurrentPrimes;i++){      out.println("  <LI>"+currentPrimes.elementAt(i));    }    out.println("</OL>");\n    out.println("</BODY></HTML>");  }  publicvoiddoPost(HttpServletRequestrequest,                     HttpServletResponseresponse)      throwsServletException,IOException{    doGet(request,response);  }  //检查是否存在同类型请求(已经完成,或者正在计算)。  //如存在,则返回现有结果而不是启动新的后台线程。  privatePrimeListfindPrimeList(VectorprimeListVector,                             intnumPrimes,                             intnumDigits){    synchronized(primeListVector){      for(inti=0;i<primeListVector.size();i++){        PrimeListprimes=(PrimeList)primeListVector.elementAt(i);        if((numPrimes==primes.numPrimes())&&            (numDigits==primes.numDigits()))          return(primes);      }      return(null);    }  }}  PrimeNumbers.html<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.0Transitional//EN"><HTML><HEAD>  <TITLE>大素数计算</TITLE></HEAD><CENTER><BODYBGCOLOR="#FDF5E6"><FORMACTION="/servlet/hall.PrimeNumbers">  <B>要计算几个素数:</B>  <INPUTTYPE="TEXT"NAME="numPrimes"VALUE=25SIZE=4><BR>  <B>每个素数的位数:</B>  <INPUTTYPE="TEXT"NAME="numDigits"VALUE=150SIZE=3><BR>  <INPUTTYPE="SUBMIT"VALUE="开始计算"></FORM>\n</CENTER></BODY></HTML>九、处理Cookie9.1Cookie概述  Cookie是服务器发送给浏览器的体积很小的纯文本信息,用户以后访问同一个Web服务器时浏览器会把它们原样发送给服务器。通过让服务器读取它原先保存到客户端的信息,网站能够为浏览者提供一系列的方便,例如在线交易过程中标识用户身份、安全要求不高的场合避免用户重复输入名字和密码、门户网站的主页定制、有针对性地投放广告,等等。  Cookie的目的就是为用户带来方便,为网站带来增值。虽然有着许多误传,事实上Cookie并不会造成严重的安全威胁。Cookie永远不会以任何方式执行,因此也不会带来病毒或攻击你的系统。另外,由于浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB,因此Cookie不会塞满你的硬盘,更不会被用作“拒绝服务”攻击手段。  9.2Servlet的CookieAPI  要把Cookie发送到客户端,Servlet先要调用newCookie(name,value)用合适的名字和值创建一个或多个Cookie(2.1节),通过cookie.setXXX设置各种属性(2.2节),通过response.addCookie(cookie)把cookie加入应答头(2.3节)。  要从客户端读入Cookie,Servlet应该调用request.getCookies(),getCookies()方法返回一个Cookie对象的数组。在大多数情况下,你只需要用循环访问该数组的各个元素寻找指定名字的Cookie,然后对该Cookie调用getValue方法取得与指定名字关联的值,这部分内容将在2.4节讨论。  9.2.1创建Cookie  调用Cookie对象的构造函数可以创建Cookie。Cookie对象的构造函数有两个字符串参数:Cookie名字和Cookie值。名字和值都不能包含空白字符以及下列字符:  []()=,"/?@:;  9.2.2读取和设置Cookie属性  把Cookie加入待发送的应答头之前,你可以查看或设置Cookie的各种属性。下面摘要介绍这些方法:\ngetComment/setComment获取/设置Cookie的注释。getDomain/setDomain获取/设置Cookie适用的域。一般地,Cookie只返回给与发送它的服务器名字完全相同的服务器。使用这里的方法可以指示浏览器把Cookie返回给同一域内的其他服务器。注意域必须以点开始(例如.sitename.com),非国家类的域(如.com,.edu,.gov)必须包含两个点,国家类的域(如.com.cn,.edu.uk)必须包含三个点。getMaxAge/setMaxAge获取/设置Cookie过期之前的时间,以秒计。如果不设置该值,则Cookie只在当前会话内有效,即在用户关闭浏览器之前有效,而且这些Cookie不会保存到磁盘上。参见下面有关LongLivedCookie的说明。getName/setName获取/设置Cookie的名字。本质上,名字和值是我们始终关心的两个部分。由于HttpServletRequest的getCookies方法返回的是一个Cookie对象的数组,因此通常要用循环来访问这个数组查找特定名字,然后用getValue检查它的值。getPath/setPath获取/设置Cookie适用的路径。如果不指定路径,Cookie将返回给当前页面所在目录及其子目录下的所有页面。这里的方法可以用来设定一些更一般的条件。例如,someCookie.setPath("/"),此时服务器上的所有页面都可以接收到该Cookie。getSecure/setSecure获取/设置一个boolean值,该值表示是否Cookie只能通过加密的连接(即SSL)发送。getValue/setValue获取/设置Cookie的值。如前所述,名字和值实际上是我们始终关心的两个方面。不过也有一些例外情况,比如把名字作为逻辑标记(也就是说,如果名字存在,则表示true)。getVersion/setVersion获取/设置Cookie所遵从的协议版本。默认版本0(遵从原先的Netscape规范);版本1遵从RFC2109,但尚未得到广泛的支持。  9.2.3在应答头中设置Cookie  Cookie可以通过HttpServletResponse的addCookie方法加入到Set-Cookie应答头。下面是一个例子:  CookieuserCookie=newCookie("user","uid1234");  response.addCookie(userCookie);  9.2.4读取保存到客户端的Cookie  \n要把Cookie发送到客户端,先要创建Cookie,然后用addCookie发送一个Set-CookieHTTP应答头。这些内容已经在上面的2.1节介绍。从客户端读取Cookie时调用的是HttpServletRequest的getCookies方法。该方法返回一个与HTTP请求头中的内容对应的Cookie对象数组。得到这个数组之后,一般是用循环访问其中的各个元素,调用getName检查各个Cookie的名字,直至找到目标Cookie。然后对这个目标Cookie调用getValue,根据获得的结果进行其他处理。  上述处理过程经常会遇到,为方便计下面我们提供一个getCookieValue方法。只要给出Cookie对象数组、Cookie名字和默认值,getCookieValue方法就会返回匹配指定名字的Cookie值,如果找不到指定Cookie,则返回默认值。  9.3几个Cookie工具函数  下面是几个工具函数。这些函数虽然简单,但是,在和Cookie打交道的时候很有用。  9.3.1获取指定名字的Cookie值  该函数是ServletUtilities.java的一部分。getCookieValue通过循环依次访问Cookie对象数组的各个元素,寻找是否有指定名字的Cookie,如找到,则返回该Cookie的值;否则,返回参数中给出的默认值。getCookieValue能够在一定程度上简化Cookie值的提取。  publicstaticStringgetCookieValue(Cookie[]cookies,               StringcookieName,               StringdefaultValue){    for(inti=0;i<cookies.length;i++){      Cookiecookie=cookies[i];      if(cookieName.equals(cookie.getName()))        return(cookie.getValue());    }    return(defaultValue);  }  9.3.2自动保存的Cookie  下面是LongLivedCookie类的代码。如果你希望Cookie能够在浏览器退出的时候自动保存下来,则可以用这个LongLivedCookie类来取代标准的Cookie类。packagehall;importjavax.servlet.http.*;\npublicclassLongLivedCookieextendsCookie{  publicstaticfinalintSECONDS_PER_YEAR=60*60*24*365;  publicLongLivedCookie(Stringname,Stringvalue){    super(name,value);    setMaxAge(SECONDS_PER_YEAR);  }}  9.4.实例:定制的搜索引擎界面  下面也是一个搜索引擎界面的例子,通过修改前面HTTP状态代码的例子得到。在这个Servlet中,用户界面是动态生成而不是由静态HTML文件提供的。Servlet除了负责读取表单数据并把它们发送给搜索引擎之外,还要把包含表单数据的Cookie发送给客户端。以后客户再次访问同一表单时,这些Cookie的值将用来预先填充表单,使表单自动显示最近使用过的数据。  SearchEnginesFrontEnd.java  该Servlet构造一个主要由表单构成的用户界面。第一次显示的时候,它和前面用静态HTML页面提供的界面差不多。然而,用户选择的值将被保存到Cookie(本页面将数据发送到CustomizedSearchEnginesServlet,由后者设置Cookie)。用户以后再访问同一页面时,即使浏览器是退出之后再启动,表单中也会自动填好上一次搜索所填写的内容。  注意该Servlet用到了ServletUtilities.java,其中getCookieValue前面已经介绍过,headWithTitle用于生成HTML页面的一部分。另外,这里也用到了前面已经说明的LongLiveCookie类,我们用它来创建作废期限很长的Cookie。packagehall;importjava.io.*;importjavax.servlet.*;importjavax.servlet.http.*;importjava.net.*;publicclassSearchEnginesFrontEndextendsHttpServlet{  publicvoiddoGet(HttpServletRequestrequest,                 HttpServletResponseresponse)      throwsServletException,IOException{    Cookie[]cookies=request.getCookies();    StringsearchString=\n      ServletUtilities.getCookieValue(cookies,             "searchString",             "JavaProgramming");    StringnumResults=      ServletUtilities.getCookieValue(cookies,             "numResults",             "10");    StringsearchEngine=      ServletUtilities.getCookieValue(cookies,              "searchEngine",              "google");    response.setContentType("text/html");    PrintWriterout=response.getWriter();    Stringtitle="SearchingtheWeb";    out.println(ServletUtilities.headWithTitle(title)+                "<BODYBGCOLOR=\"#FDF5E6\">\n"+                "<H1ALIGN=\"CENTER\">SearchingtheWeb</H1>\n"+                "\n"+                "<FORMACTION=\"/servlet/hall.CustomizedSearchEngines\">\n"+                "<CENTER>\n"+                "SearchString:\n"+                "<INPUTTYPE=\"TEXT\"NAME=\"searchString\"\n"+                "       VALUE=\""+searchString+"\"><BR>\n"+                "ResultstoShowPerPage:\n"+                "<INPUTTYPE=\"TEXT\"NAME=\"numResults\"\n"+                "       VALUE="+numResults+"SIZE=3><BR>\n"+                "<INPUTTYPE=\"RADIO\"NAME=\"searchEngine\"\n"+                "       VALUE=\"google\""+                checked("google",searchEngine)+">\n"+                "Google|\n"+                "<INPUTTYPE=\"RADIO\"NAME=\"searchEngine\"\n"+                "       VALUE=\"infoseek\""+                checked("infoseek",searchEngine)+">\n"+                "Infoseek|\n"+                "<INPUTTYPE=\"RADIO\"NAME=\"searchEngine\"\n"+                "       VALUE=\"lycos\""+                checked("lycos",searchEngine)+">\n"+                "Lycos|\n"+                "<INPUTTYPE=\"RADIO\"NAME=\"searchEngine\"\n"+                "       VALUE=\"hotbot\""+                checked("hotbot",searchEngine)+">\n"+                "HotBot\n"+                "<BR>\n"+\n                "<INPUTTYPE=\"SUBMIT\"VALUE=\"Search\">\n"+                "</CENTER>\n"+                "</FORM>\n"+                "\n"+                "</BODY>\n"+                "</HTML>\n");  }  privateStringchecked(Stringname1,Stringname2){    if(name1.equals(name2))      return("CHECKED");    else      return("");  }}  CustomizedSearchEngines.java  前面的SearchEnginesFrontEndServlet把数据发送到CustomizedSearchEnginesServlet。本例在许多方面与前面介绍HTTP状态代码时的例子相似,区别在于,本例除了要构造一个针对搜索引擎的URL并向用户发送一个重定向应答之外,还要发送保存用户数据的Cookies。packagehall;importjava.io.*;importjavax.servlet.*;importjavax.servlet.http.*;importjava.net.*;publicclassCustomizedSearchEnginesextendsHttpServlet{  publicvoiddoGet(HttpServletRequestrequest,                    HttpServletResponseresponse)      throwsServletException,IOException{        StringsearchString=request.getParameter("searchString");    CookiesearchStringCookie=      newLongLivedCookie("searchString",searchString);    response.addCookie(searchStringCookie);    searchString=URLEncoder.encode(searchString);    StringnumResults=request.getParameter("numResults");    CookienumResultsCookie=\n      newLongLivedCookie("numResults",numResults);    response.addCookie(numResultsCookie);    StringsearchEngine=request.getParameter("searchEngine");    CookiesearchEngineCookie=      newLongLivedCookie("searchEngine",searchEngine);    response.addCookie(searchEngineCookie);    SearchSpec[]commonSpecs=SearchSpec.getCommonSpecs();    for(inti=0;i<commonSpecs.length;i++){      SearchSpecsearchSpec=commonSpecs[i];      if(searchSpec.getName().equals(searchEngine)){        Stringurl=          searchSpec.makeURL(searchString,numResults);        response.sendRedirect(url);        return;      }    }    response.sendError(response.SC_NOT_FOUND,                       "Norecognizedsearchenginespecified.");  }  publicvoiddoPost(HttpServletRequestrequest,                     HttpServletResponseresponse)      throwsServletException,IOException{    doGet(request,response);  }}十、会话状态10.1会话状态概述  HTTP协议的“无状态”(Stateless)特点带来了一系列的问题。特别是通过在线商店购物时,服务器不能顺利地记住以前的事务就成了严重的问题。它使得“购物篮”之类的应用很难实现:当我们把商品加入购物篮时,服务器如何才能知道篮子里原先有些什么?即使服务器保存了上下文信息,我们仍旧会在电子商务应用中遇到问题。例如,当用户从选择商品的页面(由普通的服务器提供)转到输入信用卡号和送达地址的页面(由支持SSL的安全服务器提供),服务器如何才能记住用户买了些什么?  这个问题一般有三种解决方法:Cookie。利用HTTPCookie来存储有关购物会话的信息,后继的各个连接可以查看当前会话,然后从服务器的某些地方提取有关该会话的完整信息。这是一种优秀的,也是应用最广泛的方法。然而,即使Servlet提供了一个高级的、使用方便的Cookie接口,仍旧有一些繁琐的细节问题需要处理:\n从其他Cookie中分别出保存会话标识的Cookie。为Cookie设置合适的作废时间(例如,中断时间超过24小时的会话一般应重置)。把会话标识和服务器端相应的信息关联起来。(实际保存的信息可能要远远超过保存到Cookie的信息,而且象信用卡号等敏感信息永远不应该用Cookie来保存。)改写URL。你可以把一些标识会话的数据附加到每个URL的后面,服务器能够把该会话标识和它所保存的会话数据关联起来。这也是一个很好的方法,而且还有当浏览器不支持Cookie或用户已经禁用Cookie的情况下也有效这一优点。然而,大部分使用Cookie时所面临的问题同样存在,即服务器端的程序要进行许多简单但单调冗长的处理。另外,还必须十分小心地保证每个URL后面都附加了必要的信息(包括非直接的,如通过Location给出的重定向URL)。如果用户结束会话之后又通过书签返回,则会话信息会丢失。隐藏表单域。HTML表单中可以包含下面这样的输入域:<INPUTTYPE="HIDDEN"NAME="session"VALUE="...">。这意味着,当表单被提交时,隐藏域的名字和数据也被包含到GET或POST数据里,我们可以利用这一机制来维持会话信息。然而,这种方法有一个很大的缺点,它要求所有页面都是动态生成的,因为整个问题的核心就是每个会话都要有一个唯一标识符。  Servlet为我们提供了一种与众不同的方案:HttpSessionAPI。HttpSessionAPI是一个基于Cookie或者URL改写机制的高级会话状态跟踪接口:如果浏览器支持Cookie,则使用Cookie;如果浏览器不支持Cookie或者Cookie功能被关闭,则自动使用URL改写方法。Servlet开发者无需关心细节问题,也无需直接处理Cookie或附加到URL后面的信息,API自动为Servlet开发者提供一个可以方便地存储会话信息的地方。  10.2会话状态跟踪API  在Servlet中使用会话信息是相当简单的,主要的操作包括:查看和当前请求关联的会话对象,必要的时候创建新的会话对象,查看与某个会话相关的信息,在会话对象中保存信息,以及会话完成或中止时释放会话对象。  10.2.1查看当前请求的会话对象  查看当前请求的会话对象通过调用HttpServletRequest的getSession方法实现。如果getSession方法返回null,你可以创建一个新的会话对象。但更经常地,我们通过指定参数使得不存在现成的会话时自动创建一个会话对象,即指定getSession的参数为true。因此,访问当前请求会话对象的第一个步骤通常如下所示:  HttpSessionsession=request.getSession(true);  10.2.2查看和会话有关的信息\n  HttpSession对象生存在服务器上,通过Cookie或者URL这类后台机制自动关联到请求的发送者。会话对象提供一个内建的数据结构,在这个结构中可以保存任意数量的键-值对。在2.1或者更早版本的ServletAPI中,查看以前保存的数据使用的是getValue("key")方法。getValue返回Object,因此你必须把它转换成更加具体的数据类型。如果参数中指定的键不存在,getValue返回null。  API2.2版推荐用getAttribute来代替getValue,这不仅是因为getAttribute和setAttribute的名字更加匹配(和getValue匹配的是putValue,而不是setValue),同时也因为setAttribute允许使用一个附属的HttpSessionBindingListener来监视数值,而putValue则不能。  但是,由于目前还只有很少的商业Servlet引擎支持2.2,下面的例子中我们仍旧使用getValue。这是一个很典型的例子,假定ShoppingCart是一个保存已购买商品信息的类:  HttpSessionsession=request.getSession(true);  ShoppingCartpreviousItems=    (ShoppingCart)session.getValue("previousItems");  if(previousItems!=null){    doSomethingWith(previousItems);  }else{    previousItems=newShoppingCart(...);    doSomethingElseWith(previousItems);  }  大多数时候我们都是根据特定的名字寻找与它关联的值,但也可以调用getValueNames得到所有属性的名字。getValuesNames返回的是一个String数组。API2.2版推荐使用getAttributeNames,这不仅是因为其名字更好,而且因为它返回的是一个Enumeration,和其他方法(比如HttpServletRequest的getHeaders和getParameterNames)更加一致。  虽然开发者最为关心的往往是保存到会话对象的数据,但还有其他一些信息有时也很有用。getID:该方法返回会话的唯一标识。有时该标识被作为键-值对中的键使用,比如会话中只保存一个值时,或保存上一次会话信息时。isNew:如果客户(浏览器)还没有绑定到会话则返回true,通常意味着该会话刚刚创建,而不是引用自客户端的请求。对于早就存在的会话,返回值为false。getCreationTime:该方法返回建立会话的以毫秒计的时间,从1970.01.01(GMT)算起。要得到用于打印输出的时间值,可以把该值传递给Date构造函数,或者GregorianCalendar的setTimeInMillis方法。getLastAccessedTime:该方法返回客户最后一次发送请求的以毫秒计的时间,从1970.01.01(GMT)算起。\ngetMaxInactiveInterval:返回以秒计的最大时间间隔,如果客户请求之间的间隔不超过该值,Servlet引擎将保持会话有效。负数表示会话永远不会超时。  10.2.3在会话对象中保存数据  如上节所述,读取保存在会话中的信息使用的是getValue方法(或,对于2.2版的Servlet规范,使用getAttribute)。保存数据使用putValue(或setAttribute)方法,并指定键和相应的值。注意putValue将替换任何已有的值。有时候这正是我们所需要的(如下例中的referringPage),但有时我们却需要提取原来的值并扩充它(如下例previousItems)。示例代码如下:  HttpSessionsession=request.getSession(true);  session.putValue("referringPage",request.getHeader("Referer"));  ShoppingCartpreviousItems=    (ShoppingCart)session.getValue("previousItems");  if(previousItems==null){    previousItems=newShoppingCart(...);  }  StringitemID=request.getParameter("itemID");  previousItems.addEntry(Catalog.getEntry(itemID));  session.putValue("previousItems",previousItems);  10.3实例:显示会话信息  下面这个例子生成一个Web页面,并在该页面中显示有关当前会话的信息。packagehall;importjava.io.*;importjavax.servlet.*;importjavax.servlet.http.*;importjava.net.*;importjava.util.*;publicclassShowSessionextendsHttpServlet{  publicvoiddoGet(HttpServletRequestrequest,                 HttpServletResponseresponse)      throwsServletException,IOException{    HttpSessionsession=request.getSession(true);    response.setContentType("text/html");    PrintWriterout=response.getWriter();    Stringtitle="SearchingtheWeb";    Stringheading;    IntegeraccessCount=newInteger(0);;\n    if(session.isNew()){      heading="Welcome,Newcomer";    }else{      heading="WelcomeBack";      IntegeroldAccessCount=        //在ServletAPI2.2中使用getAttribute而不是getValue        (Integer)session.getValue("accessCount");      if(oldAccessCount!=null){        accessCount=          newInteger(oldAccessCount.intValue()+1);      }    }    //在ServletAPI2.2中使用putAttribute    session.putValue("accessCount",accessCount);          out.println(ServletUtilities.headWithTitle(title)+                "<BODYBGCOLOR=\"#FDF5E6\">\n"+                "<H1ALIGN=\"CENTER\">"+heading+"</H1>\n"+                "<H2>InformationonYourSession:</H2>\n"+                "<TABLEBORDER=1ALIGN=CENTER>\n"+                "<TRBGCOLOR=\"#FFAD00\">\n"+                "  <TH>InfoType<TH>Value\n"+                "<TR>\n"+                "  <TD>ID\n"+                "  <TD>"+session.getId()+"\n"+                "<TR>\n"+                "  <TD>CreationTime\n"+                "  <TD>"+newDate(session.getCreationTime())+"\n"+                "<TR>\n"+                "  <TD>TimeofLastAccess\n"+                "  <TD>"+newDate(session.getLastAccessedTime())+"\n"+                "<TR>\n"+                "  <TD>NumberofPreviousAccesses\n"+                "  <TD>"+accessCount+"\n"+                "</TABLE>\n"+                "</BODY></HTML>");  }  publicvoiddoPost(HttpServletRequestrequest,                 HttpServletResponseresponse)      throwsServletException,IOException{    doGet(request,response);  }}十一、JSP及语法概要\n11.1概述  JavaServerPages(JSP)使得我们能够分离页面的静态HTML和动态部分。HTML可以用任何通常使用的Web制作工具编写,编写方式也和原来的一样;动态部分的代码放入特殊标记之内,大部分以“<%”开始,以“%>”结束。例如,下面是一个JSP页面的片断,如果我们用http://host/OrderConfirmation.jsp?title=Core+Web+Programming这个URL打开该页面,则结果显示“ThanksfororderingCoreWebProgramming”。Thanksforordering<I><%=request.getParameter("title")%></I>  JSP页面文件通常以.jsp为扩展名,而且可以安装到任何能够存放普通Web页面的地方。虽然从代码编写来看,JSP页面更象普通Web页面而不象Servlet,但实际上,JSP最终会被转换成正规的Servlet,静态HTML直接输出到和Servletservice方法关联的输出流。  JSP到Servlet的转换过程一般在出现第一次页面请求时进行。因此,如果你希望第一个用户不会由于JSP页面转换成Servlet而等待太长的时间,希望确保Servlet已经正确地编译并装载,你可以在安装JSP页面之后自己请求一下这个页面。  另外也请注意,许多Web服务器允许定义别名,所以一个看起来指向HTML文件的URL实际上可能指向Servlet或JSP页面。  除了普通HTML代码之外,嵌入JSP页面的其他成分主要有如下三种:脚本元素(ScriptingElement),指令(Directive),动作(Action)。脚本元素用来嵌入Java代码,这些Java代码将成为转换得到的Servlet的一部分;JSP指令用来从整体上控制Servlet的结构;动作用来引入现有的组件或者控制JSP引擎的行为。为了简化脚本元素,JSP定义了一组可以直接使用的变量(预定义变量),比如前面代码片断中的request就是其中一例。  注意本文以JSP1.0规范为基础。和0.92版相比,新版本的JSP作了许多重大的改动。虽然这些改动只会使JSP变得更好,但应注意1.0的JSP页面几乎和早期的JSP引擎完全不兼容。  11.2JSP语法概要表JSP元素  语法  说明  备注  JSP表达式  <%=expression%>  计算表达式并输出结果。  等价的XML表达是:<jsp:expression>expression</jsp:expression>\n可以使用的预定义变量包括:request,response,out,session,application,config,pageContext。这些预定义变量也可以在JSPScriptlet中使用。JSPScriptlet  <%code%>  插入到service方法的代码。  等价的XML表达是:<jsp:scriptlet>code</jsp:scriptlet>JSP声明  <%!code%>  代码被插入到Servlet类(在service方法之外)。  等价的XML表达是:<jsp:declaration>code</jsp:declaration>page指令  <%@pageatt="val"%>  作用于Servlet引擎的全局性指令。  等价的XML表达是<jsp:directive.pageatt="val"\>。合法的属性如下表,其中粗体表示默认值:import="package.class"contentType="MIME-Type"isThreadSafe="true|false"session="true|false"buffer="sizekb|none"autoflush="true|false"extends="package.class"info="message"errorPage="url"isErrorPage="true|false"language="java"include指令  <%@includefile="url"%>  当JSP转换成Servlet时,应当包含本地系统上的指定文件。  等价的XML表达是:<jsp:directive.includefile="url"\>.其中URL必须是相对URL。利用jsp:include动作可以在请求的时候(而不是JSP转换成Servlet时)引入文件。JSP注释  <%--comment\n--%>  注释;JSP转换成Servlet时被忽略。  如果要把注释嵌入结果HTML文档,使用普通的HTML注释标记<--comment-->。  jsp:include动作  <jsp:includepage="relativeURL"flush="true"/>  当Servlet被请求时,引入指定的文件。  如果你希望在页面转换的时候包含某个文件,使用JSPinclude指令。注意:在某些服务器上,被包含文件必须是HTML文件或JSP文件,具体由服务器决定(通常根据文件扩展名判断)。jsp:useBean动作  <jsp:useBeanatt=val*/>或者<jsp:useBeanatt=val*>...</jsp:useBean>  寻找或实例化一个JavaBean。  可能的属性包括:id="name"scope="page|request|session|application"class="package.class"type="package.class"beanName="package.class"jsp:setProperty动作  <jsp:setPropertyatt=val*/>  设置Bean的属性。既可以设置一个确定的值,也可以指定属性值来自请求参数。  合法的属性包括:name="beanName"property="propertyName|*"param="parameterName"value="val"jsp:getProperty动作  <jsp:getPropertyname="propertyName"value="val"/>  提取并输出Bean的属性。     jsp:forward动作  <jsp:forwardpage="relativeURL"/>  把请求转到另外一个页面。     jsp:plugin动作  <jsp:pluginattribute="value"*>...</jsp:plugin>  根据浏览器类型生成OBJECT或者EMBED标记,以便通过JavaPlugin运行JavaApplet。       11.3关于模板文本(静态HTML)  许多时候,JSP页面的很大一部分都由静态HTML构成,这些静态HTML也称为“模板文本”。模板文本和普通HTML几乎完全相同,它们都遵从相同的语法规则,而且模板文本也是被Servlet直接发送到客户端。此外,模板文本也可以用任何现有的页面制作工具来编写。\n  唯一的例外在于,如果要输出“<%”,则模板文本中应该写成“<\%”。  十二、脚本元素、指令和预定义变量12.1JSP脚本元素  JSP脚本元素用来插入Java代码,这些Java代码将出现在由当前JSP页面生成的Servlet中。脚本元素有三种格式:表达式格式<%=expression%>:计算表达式并输出其结果。Scriptlet格式<%code%>:把代码插入到Servlet的service方法。声明格式<%!code%>:把声明加入到Servlet类(在任何方法之外)。  下面我们详细说明它们的用法。  12.1.1JSP表达式  JSP表达式用来把Java数据直接插入到输出。其语法如下:<%=JavaExpression%>  计算Java表达式得到的结果被转换成字符串,然后插入到页面。计算在运行时进行(页面被请求时),因此可以访问和请求有关的全部信息。例如,下面的代码显示页面被请求的日期/时间:Currenttime:<%=newjava.util.Date()%>  为简化这些表达式,JSP预定义了一组可以直接使用的对象变量。后面我们将详细介绍这些隐含声明的对象,但对于JSP表达式来说,最重要的几个对象及其类型如下:request:HttpServletRequest;response:HttpServletResponse;session:和request关联的HttpSessionout:PrintWriter(带缓冲的版本,JspWriter),用来把输出发送到客户端  下面是一个例子:Yourhostname:<%=request.getRemoteHost()%>  最后,如果使用XML的话,JSP表达式也可以写成下面这种形式:<jsp:expression>JavaExpression\n</jsp:expression>  请记住XML元素和HTML不一样。XML是大小写敏感的,因此务必使用小写。有关XML语法的说明,请参见《XML教程》  12.1.2JSPScriptlet  如果你要完成的任务比插入简单的表达式更加复杂,可以使用JSPScriptlet。JSPScriptlet允许你把任意的Java代码插入Servlet。JSPScriptlet语法如下:<%JavaCode%>  和JSP表达式一样,Scriptlet也可以访问所有预定义的变量。例如,如果你要向结果页面输出内容,可以使用out变量:<%StringqueryData=request.getQueryString();out.println("AttachedGETdata:"+queryData);%>  注意Scriptlet中的代码将被照搬到Servlet内,而Scriptlet前面和后面的静态HTML(模板文本)将被转换成println语句。这就意味着,Scriptlet内的Java语句并非一定要是完整的,没有关闭的块将影响Scriptlet外的静态HTML。例如,下面的JSP片断混合了模板文本和Scriptlet:<%if(Math.random()<0.5){%>Havea<B>nice</B>day!<%}else{%>Havea<B>lousy</B>day!<%}%>  上述JSP代码将被转换成如下Servlet代码:if(Math.random()<0.5){  out.println("Havea<B>nice</B>day!");}else{  out.println("Havea<B>lousy</B>day!");}\n  如果要在Scriptlet内部使用字符“%>”,必须写成“%\>”。另外,请注意<%code%>的XML等价表达是:<jsp:scriptlet>Code</jsp:scriptlet>  12.1.3JSP声明  JSP声明用来定义插入Servlet类的方法和成员变量,其语法如下:<%!JavaCode%>  由于声明不会有任何输出,因此它们往往和JSP表达式或Scriptlet结合在一起使用。例如,下面的JSP代码片断输出自从服务器启动(或Servlet类被改动并重新装载以来)当前页面被请求的次数:<%!privateintaccessCount=0;%>自从服务器启动以来页面访问次数为:<%=++accessCount%>  和Scriptlet一样,如果要使用字符串“%>”,必须使用“%\>”代替。最后,<%!code%>的XML等价表达方式为:<jsp:declaration>Code</jsp:declaration>  12.2JSP指令  JSP指令影响Servlet类的整体结构,它的语法一般如下:<%@directiveattribute="value"%>  另外,也可以把同一指令的多个属性结合起来,例如:<%@directiveattribute1="value1"         attribute2="value2"         ...\n         attributeN="valueN"%>  JSP指令分为两种类型:第一是page指令,用来完成下面这类任务:导入指定的类,自定义Servlet的超类,等等;第二是include指令,用来在JSP文件转换成Servlet时引入其他文件。JSP规范也提到了taglib指令,其目的是让JSP开发者能够自己定义标记,但JSP1.0不支持该指令,有希望它将成为JSP1.1的主要改进之一。  12.2.1page指令  page指令的作用是定义下面一个或多个属性,这些属性大小写敏感。import="package.class",或者import="package.class1,...,package.classN":用于指定导入哪些包,例如:<%@pageimport="java.util.*"%>。import是唯一允许出现一次以上的属性。contentType="MIME-Type"或contentType="MIME-Type;charset=Character-Set":该属性指定输出的MIME类型。默认是text/html。例如,下面这个指令:<%@pagecontentType="text/plain"%>。和下面的Scriptlet效果相同:<%response.setContentType("text/plain");%>isThreadSafe="true|false"默认值true表明Servlet按照标准的方式处理,即假定开发者已经同步对实例变量的访问,由单个Servlet实例同时地处理多个请求。如果取值false,表明Servlet应该实现SingleThreadModel,请求或者是逐个进入,或者多个并行的请求分别由不同的Servlet实例处理。session="true|false"默认值true表明预定义变量session(类型为HttpSession)应该绑定到已有的会话,如果不存在已有的会话,则新建一个并绑定session变量。如果取值false,表明不会用到会话,试图访问变量session将导致JSP转换成Servlet时出错。\nbuffer="sizekb|none"该属性指定JspWriteout的缓存大小。默认值和服务器有关,但至少应该是8KB。autoflush="true|false"默认值true表明如果缓存已满则刷新它。autoflush很少取false值,false值表示如果缓存已满则抛出异常。如果buffer="none",autoflush不能取false值。extends="package.class"该属性指出将要生成的Servlet使用哪个超类。使用该属性应当十分小心,因为服务器可能已经在用自定义的超类。info="message"该属性定义一个可以通过getServletInfo方法提取的字符串。errorPage="url"该属性指定一个JSP页面,所有未被当前页面捕获的异常均由该页面处理。isErrorPage="true|false"该属性指示当前页面是否可以作为另一JSP页面的错误处理页面。默认值false。language="java"该属性用来指示所使用的语言。目前没有必要关注这个属性,因为默认的Java是当前唯一可用的语言。  定义指令的XML语法为:<jsp:directive.directiveTypeattribute=value/>  例如,下面这个指令:\n<%@pageimport="java.util.*"%>  它的XML等价表达是:<jsp:directive.pageimport="java.util.*"/>  12.2.2include指令  include指令用于JSP页面转换成Servlet时引入其他文件。该指令语法如下:<%@includefile="relativeurl"%>  这里所指定的URL是和发出引用指令的JSP页面相对的URL,然而,与通常意义上的相对URL一样,你可以利用以“/”开始的URL告诉系统把URL视为从Web服务器根目录开始。包含文件的内容也是JSP代码,即包含文件可以包含静态HTML、脚本元素、JSP指令和动作。  例如,许多网站的每个页面都有一个小小的导航条。由于HTML框架存在不少问题,导航条往往用页面顶端或左边的一个表格制作,同一份HTML代码重复出现在整个网站的每个页面上。include指令是实现该功能的非常理想的方法。使用include指令,开发者不必再把导航HTML代码拷贝到每个文件中,从而可以更轻松地完成维护工作。  由于include指令是在JSP转换成Servlet的时候引入文件,因此如果导航条改变了,所有使用该导航条的JSP页面都必须重新转换成Servlet。如果导航条改动不频繁,而且你希望包含操作具有尽可能好的效率,使用include指令是最好的选择。然而,如果导航条改动非常频繁,你可以使用jsp:include动作。jsp:include动作在出现对JSP页面请求的时候才会引用指定的文件,请参见本文后面的具体说明。  12.3实例:脚本元素和指令的应用  下面是一个使用JSP表达式、Scriptlet、声明、指令的简单例子。<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.0Transitional//EN"><HTML><HEAD><TITLE>JavaServerPages</TITLE></HEAD><BODYBGCOLOR="#FDF5E6"TEXT="#000000"LINK="#0000EE"\n      VLINK="#551A8B"ALINK="#FF0000"><CENTER><TABLEBORDER=5BGCOLOR="#EF8429">  <TR><THCLASS="TITLE">      JSP应用实例</TABLE></CENTER><P>下面是一些利用各种JSP功能生成的动态内容:<UL>  <LI><B>表达式.</B><BR>      你的主机名:<%=request.getRemoteHost()%>.  <LI><B>JSPScriptlet.</B><BR>      <%out.println("查询字符串:"+                     request.getQueryString());%>  <LI><B>声明(和表达式).</B><BR>      <%!privateintaccessCount=0;%>      服务器启动以来访问次数:<%=++accessCount%>  <LI><B>指令(和表达式).</B><BR>      <%@pageimport="java.util.*"%>      当前日期:<%=newDate()%></UL></BODY></HTML>  12.4JSP预定义变量  为了简化JSP表达式和Scriptlet的代码,JSP提供了8个预先定义的变量(或称为隐含对象)。这些变量是request、response、out、session、application、config、pageContext和page。  12.4.1request  这是和请求关联的HttpServletRequest,通过它可以查看请求参数(调用getParameter),请求类型(GET,POST,HEAD,等),以及请求的HTTP头(Cookie,Referer,等)。严格说来,如果请求所用的是HTTP之外的其他协议,request可以是ServletRequest的子类(而不是HttpServletRequest),但在实践中几乎不会用到。  12.4.2response  \n这是和应答关联的HttpServletResponse。注意,由于输出流(参见下面的out)是带缓冲的,因此,如果已经向客户端发送了输出内容,普通Servlet不允许再设置HTTP状态代码,但在JSP中却是合法的。  12.4.3out  这是用来向客户端发送内容的PrintWriter。然而,为了让response对象更为实用,out是带缓存功能的PrintWriter,即JspWriter。JSP允许通过page指令的buffer属性调整缓存的大小,甚至允许关闭缓存。  out一般只在Scriptlet内使用,这是因为JSP表达式是自动发送到输出流的,很少需要显式地引用out。  12.4.4session  这是和请求关联的HttpSession对象。前面我们已经介绍过会话的自动创建,我们知道,即使不存在session引用,这个对象也是自动绑定的。但有一个例外,这就是如果你用page指令的session属性关闭了会话,此时对session变量的引用将导致JSP页面转换成Servlet时出错。  12.4.5application  这是一个ServletContext,也可以通过getServletConfig().getContext()获得。  12.4.6config  这是当前页面的ServletConfig对象。  12.4.7pageContext  主要用来管理页面的属性。  12.4.8page  它是this的同义词,当前用处不大。它是为了Java不再是唯一的JSP编程语言而准备的占位符。  十三、JSP动作JSP动作利用XML语法格式的标记来控制Servlet引擎的行为。利用JSP动作可以动态地插入文件、重用JavaBean组件、把用户重定向到另外的页面、为Java插件生成HTML代码。  JSP动作包括:jsp:include:在页面被请求的时候引入一个文件。\njsp:useBean:寻找或者实例化一个JavaBean。jsp:setProperty:设置JavaBean的属性。jsp:getProperty:输出某个JavaBean的属性。jsp:forward:把请求转到一个新的页面。jsp:plugin:根据浏览器类型为Java插件生成OBJECT或EMBED标记。  13.1jsp:include动作  该动作把指定文件插入正在生成的页面。其语法如下:<jsp:includepage="relativeURL"flush="true"/>  前面已经介绍过include指令,它是在JSP文件被转换成Servlet的时候引入文件,而这里的jsp:include动作不同,插入文件的时间是在页面被请求的时候。jsp:include动作的文件引入时间决定了它的效率要稍微差一点,而且被引用文件不能包含某些JSP代码(例如不能设置HTTP头),但它的灵活性却要好得多。  例如,下面的JSP页面把4则新闻摘要插入一个“What'sNew?”页面。改变新闻摘要时只需改变这四个文件,而主JSP页面却可以不作修改:  WhatsNew.jsp<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.0Transitional//EN"><HTML><HEAD><TITLE>What'sNew</TITLE></HEAD><BODYBGCOLOR="#FDF5E6"TEXT="#000000"LINK="#0000EE"      VLINK="#551A8B"ALINK="#FF0000"><CENTER><TABLEBORDER=5BGCOLOR="#EF8429">  <TR><THCLASS="TITLE">      What'sNewatJspNews.com</TABLE></CENTER><P>Hereisasummaryofourfourmostrecentnewsstories:<OL>  <LI><jsp:includepage="news/Item1.html"flush="true"/>  <LI><jsp:includepage="news/Item2.html"flush="true"/>  <LI><jsp:includepage="news/Item3.html"flush="true"/>  <LI><jsp:includepage="news/Item4.html"flush="true"/></OL></BODY>\n</HTML>  13.2jsp:useBean动作  jsp:useBean动作用来装载一个将在JSP页面中使用的JavaBean。这个功能非常有用,因为它使得我们既可以发挥Java组件重用的优势,同时也避免了损失JSP区别于Servlet的方便性。jsp:useBean动作最简单的语法为:<jsp:useBeanid="name"class="package.class"/>  这行代码的含义是:“创建一个由class属性指定的类的实例,然后把它绑定到其名字由id属性给出的变量上”。不过,就象我们接下来会看到的,定义一个scope属性可以让Bean关联到更多的页面。此时,jsp:useBean动作只有在不存在同样id和scope的Bean时才创建新的对象实例,同时,获得现有Bean的引用就变得很有必要。  获得Bean实例之后,要修改Bean的属性既可以通过jsp:setProperty动作进行,也可以在Scriptlet中利用id属性所命名的对象变量,通过调用该对象的方法显式地修改其属性。这使我们想起,当我们说“某个Bean有一个类型为X的属性foo”时,就意味着“这个类有一个返回值类型为X的getFoo方法,还有一个setFoo方法以X类型的值为参数”。  有关jsp:setProperty动作的详细情况在后面讨论。但现在必须了解的是,我们既可以通过jsp:setProperty动作的value属性直接提供一个值,也可以通过param属性声明Bean的属性值来自指定的请求参数,还可以列出Bean属性表明它的值应该来自请求参数中的同名变量。  在JSP表达式或Scriptlet中读取Bean属性通过调用相应的getXXX方法实现,或者更一般地,使用jsp:getProperty动作。  注意包含Bean的类文件应该放到服务器正式存放Java类的目录下,而不是保留给修改后能够自动装载的类的目录。例如,对于JavaWebServer来说,Bean和所有Bean用到的类都应该放入classes目录,或者封装进jar文件后放入lib目录,但不应该放到servlets下。  下面是一个很简单的例子,它的功能是装载一个Bean,然后设置/读取它的message属性。  BeanTest.jsp<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.0Transitional//EN"><HTML>\n<HEAD><TITLE>ReusingJavaBeansinJSP</TITLE></HEAD><BODY><CENTER><TABLEBORDER=5>  <TR><THCLASS="TITLE">      ReusingJavaBeansinJSP</TABLE></CENTER><P><jsp:useBeanid="test"class="hall.SimpleBean"/><jsp:setPropertyname="test"          property="message"          value="HelloWWW"/><H1>Message:<I><jsp:getPropertyname="test"property="message"/></I></H1>             </BODY></HTML>  SimpleBean.java  BeanTest页面用到了一个SimpleBean。SimpleBean的代码如下:packagehall;publicclassSimpleBean{  privateStringmessage="Nomessagespecified";  publicStringgetMessage(){    return(message);  }  publicvoidsetMessage(Stringmessage){    this.message=message;  }}\n  13.3关于jsp:useBean的进一步说明  使用Bean最简单的方法是先用下面的代码装载Bean:<jsp:useBeanid="name"class="package.class"/>  然后通过jsp:setProperty和jsp:getProperty修改和提取Bean的属性。不过有两点必须注意。第一,我们还可以用下面这种格式实例化Bean:  <jsp:useBean...>    Body  </jsp:useBean>  它的意思是,只有当第一次实例化Bean时才执行Body部分,如果是利用现有的Bean实例则不执行Body部分。正如下面将要介绍的,jsp:useBean并非总是意味着创建一个新的Bean实例。  第二,除了id和class外,jsp:useBean还有其他三个属性,即:scope,type,beanName。下表简要说明这些属性的用法。属性  用法  id  命名引用该Bean的变量。如果能够找到id和scope相同的Bean实例,jsp:useBean动作将使用已有的Bean实例而不是创建新的实例。  class  指定Bean的完整包名。  scope  指定Bean在哪种上下文内可用,可以取下面的四个值之一:page,request,session和application。默认值是page,表示该Bean只在当前页面内可用(保存在当前页面的PageContext内)。request表示该Bean在当前的客户请求内有效(保存在ServletRequest对象内)。session表示该Bean对当前HttpSession内的所有页面都有效。最后,如果取值application,则表示该Bean对所有具有相同ServletContext的页面都有效。scope之所以很重要,是因为jsp:useBean只有在不存在具有相同id和scope的对象时才会实例化新的对象;如果已有id和scope都相同的对象则直接使用已有的对象,此时jsp:useBean开始标记和结束标记之间的任何内容都将被忽略。type  指定引用该对象的变量的类型,它必须是Bean类的名字、超类名字、该类所实现的接口名字之一。请记住变量的名字是由id属性指定的。  beanName  指定Bean的名字。如果提供了type属性和beanName属性,允许省略class属性。    13.4jsp:setProperty动作\n  jsp:setProperty用来设置已经实例化的Bean对象的属性,有两种用法。首先,你可以在jsp:useBean元素的外面(后面)使用jsp:setProperty,如下所示:<jsp:useBeanid="myName".../>...<jsp:setPropertyname="myName"    property="someProperty".../>  此时,不管jsp:useBean是找到了一个现有的Bean,还是新创建了一个Bean实例,jsp:setProperty都会执行。第二种用法是把jsp:setProperty放入jsp:useBean元素的内部,如下所示:<jsp:useBeanid="myName"...>  ...  <jsp:setPropertyname="myName"     property="someProperty".../></jsp:useBean>  此时,jsp:setProperty只有在新建Bean实例时才会执行,如果是使用现有实例则不执行jsp:setProperty。  jsp:setProperty动作有下面四个属性:属性  说明  name  name属性是必需的。它表示要设置属性的是哪个Bean。  property  property属性是必需的。它表示要设置哪个属性。有一个特殊用法:如果property的值是“*”,表示所有名字和Bean属性名字匹配的请求参数都将被传递给相应的属性set方法。  value  value属性是可选的。该属性用来指定Bean属性的值。字符串数据会在目标类中通过标准的valueOf方法自动转换成数字、boolean、Boolean、byte、Byte、char、Character。例如,boolean和Boolean类型的属性值(比如“true”)通过Boolean.valueOf转换,int和Integer类型的属性值(比如“42”)通过Integer.valueOf转换。value和param不能同时使用,但可以使用其中任意一个。param  param是可选的。它指定用哪个请求参数作为Bean属性的值。如果当前请求没有参数,则什么事情也不做,系统不会把null传递给Bean属性的set方法。因此,你可以让Bean自己提供默认属性值,只有当请求参数明确指定了新值时才修改默认属性值。例如,下面的代码片断表示:如果存在numItems请求参数的话,把numberOfItems属性的值设置为请求参数numItems的值;否则什么也不做。<jsp:setPropertyname="orderBean"property="numberOfItems"\nparam="numItems"/>如果同时省略value和param,其效果相当于提供一个param且其值等于property的值。进一步利用这种借助请求参数和属性名字相同进行自动赋值的思想,你还可以在property(Bean属性的名字)中指定“*”,然后省略value和param。此时,服务器会查看所有的Bean属性和请求参数,如果两者名字相同则自动赋值。  下面是一个利用JavaBean计算素数的例子。如果请求中有一个numDigits参数,则该值被传递给Bean的numDigits属性;numPrimes也类似。  JspPrimes.jsp<!DOCTYPEHTMLPUBLIC"-//W3C//DTDHTML4.0Transitional//EN"><HTML><HEAD><TITLE>在JSP中使用JavaBean</TITLE></HEAD><BODY><CENTER><TABLEBORDER=5>  <TR><THCLASS="TITLE">      在JSP中使用JavaBean</TABLE></CENTER><P><jsp:useBeanid="primeTable"class="hall.NumberedPrimes"/><jsp:setPropertyname="primeTable"property="numDigits"/><jsp:setPropertyname="primeTable"property="numPrimes"/>Some<jsp:getPropertyname="primeTable"property="numDigits"/>digitprimes:<jsp:getPropertyname="primeTable"property="numberedList"/></BODY></HTML>  注:NumberedPrimes的代码略。  13.5jsp:getProperty动作\n  jsp:getProperty动作提取指定Bean属性的值,转换成字符串,然后输出。jsp:getProperty有两个必需的属性,即:name,表示Bean的名字;property,表示要提取哪个属性的值。下面是一个例子,更多的例子可以在前文找到。<jsp:useBeanid="itemBean".../>...<UL>  <LI>Numberofitems:      <jsp:getPropertyname="itemBean"property="numItems"/>  <LI>Costofeach:      <jsp:getPropertyname="itemBean"property="unitCost"/></UL>  13.6jsp:forward动作  jsp:forward动作把请求转到另外的页面。jsp:forward标记只有一个属性page。page属性包含的是一个相对URL。page的值既可以直接给出,也可以在请求的时候动态计算,如下面的例子所示:<jsp:forwardpage="/utils/errorReporter.jsp"/><jsp:forwardpage="<%=someJavaExpression%>"/>  13.7jsp:plugin动作  jsp:plugin动作用来根据浏览器的类型,插入通过Java插件运行JavaApplet所必需的OBJECT或EMBED元素。  附录:JSP注释和字符引用约定  下面是一些特殊的标记或字符,你可以利用它们插入注释或可能被视为具有特殊含义的字符。语法  用途  <%--comment--%>  JSP注释,也称为“隐藏注释”。JSP引擎将忽略它。标记内的所有JSP脚本元素、指令和动作都将不起作用。  <!--comment-->  HTML注释,也称为“输出的注释”,直接出现在结果HTML文档中。标记内的所有JSP脚本元素、指令和动作正常执行。  <\%  在模板文本(静态HTML)中实际上希望出现“<%”的地方使用。  %\>  在脚本元素内实际上希望出现“%>”的地方使用。  \'  使用单引号的属性内的单引号。不过,你既可以使用单引号也可以使用双引号,而另外一种引号将具有普通含义。  \"  使用双引号的属性内的双引号。参见“\'”的说明。