Java Tools

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  83 随笔 :: 0 文章 :: 16 评论 :: 0 Trackbacks

2007年7月2日 #

     摘要:   阅读全文
posted @ 2008-01-23 14:26 和田雨 阅读(605) | 评论 (1)编辑 收藏

     摘要:   阅读全文
posted @ 2008-01-23 14:14 和田雨 阅读(337) | 评论 (0)编辑 收藏

下载相应的语言包,解压之后。
到Eclipse目录下建立一个 links文件夹,在文件夹下建立一个任意文件名的文件,例如link.txt.
将语言包的路径写到这个文件中,格式为path=语言包路径,注意用'/'或者'\\'代替'\'.原因是Java由Unix血缘。
posted @ 2007-11-18 14:33 和田雨 阅读(338) | 评论 (0)编辑 收藏

Java 概览(大部分内容需自己看)

如何获得JDK,JRE;网络资源介绍(SUN等,CSDN,MYBLOG)

安装JDK,设置PATH,CLASSPATH

演示简单的程序

自行演练,做三个例子,让其独立解决问题。
posted @ 2007-11-18 11:35 和田雨 阅读(279) | 评论 (0)编辑 收藏

    如果不使用IDE,对于初学者来说经常会出现  Exception in thread "main" java.lang.NoClassDefFoundError错误。
一般是以下几个原因。

建议:在你的硬盘上建立一个独立的存放代码的文件,比如D :\code,设置CLASSPATH指向这个文件夹;在创建一个专门存放类库(jar)文件夹,CLASSPAHT也指向这个文件夹,以后当你添加新的Jar包,例如JDBC驱动的时候,直接复制到这个文件夹下就可以了。

1、你的文件名对吗?
JAVA要求你的文件和你的类名严格对应的。
比如 public class HelloWorld{ .....
那么文件名一定只能是HelloWorld.java,看仔细了!

2、你的程序中有main方法吗?参数什么的对吗?
main方法的声明应该是这样子的:
public static void main(String[] args)
事实上如果你用开发工具来做的话,是不会错的。如果你用UltraEdit之类的来写程序的话,就一个字不要改的照抄吧!

3、记住,javac后面跟的参数是文件名,但java 后面跟的是类名!
这是我开始学JAVA是犯的错,java HelloWorld.class,然后就报错。后来才弄明白,应该是java HelloWorld。

4、设置了classpath了吗?
不仅是在安装JDK时要设置的内容,一般来说最好还要自己设置一个目录来存放自己的类文件的。比如d:\class目录。
设置classpath=%classpath%;d:\class
那么,现在将HelloWorld.class文件COPY到d:\class目录下,再java HelloWorld应该就OK了。

5、结合第3点和第4点
如果你的类是属于某一个包的,比如这样一个类sony.test.HelloWorld,那么按照我在第4点中说的目录来说,就应该将HelloWorld.class文件存放到d:\class\sony\test\目录下。
执行的时候应该参考第3点中说的,按类名执行:java sony.test.HelloWorld。

posted @ 2007-11-18 11:22 和田雨 阅读(366) | 评论 (0)编辑 收藏

     摘要:   阅读全文
posted @ 2007-08-09 19:31 和田雨 阅读(614) | 评论 (0)编辑 收藏

     摘要:   阅读全文
posted @ 2007-08-09 19:27 和田雨 阅读(1298) | 评论 (0)编辑 收藏

     摘要:   阅读全文
posted @ 2007-08-09 19:22 和田雨 阅读(281) | 评论 (0)编辑 收藏

我们已经知道,既可以使用主机名标识一台主帆,也可以使用IP地址标识。人们更愿意使用便于记忆的主机名标识符,而路由器则只愿使用长度固定并有层次结构的IP地址。
  
  我们可以通过多种方法来识别一个人。例如,通过出生证明上的姓名,还可以通过社会安全编号、通过驾驶执照编号。尽管这些标识都可以用来识别人,但是在某种背景下会有一种标识比其他的标识更加恰当。例如,IRS(美国的一个税收机构)中的计算机喜欢使用固定长度的社会安全编号而不是出生证上的姓名来标注。另——方面,日常生活中人们喜欢用更好记的出生证上的姓名而不是社会安全编号〔确实,你能想象出如果一个人说“嗨,我的名字是132—67—9875,请找一下我丈夫,178—87—1146”会是何等滑稽的场景)。
  
  因特网中的主机就像人一样能以多种力式标识。标识方法之一是使用主机名(hostname)。主机名(例如cnn.com,www.yahoo.com)是助记性的,人们更愿意使用。然而主机名几乎没有提供关于主机在因特网中的位置信息(主机名为sina.com.cn的主机也许是在中国境内,此外不再有别的位置信息了)。另外,主机名是由可变长度的字母数字字符构成的,路由器处理起来有困难。因此因特网中的主机也使用所谓的IP地址标识。我们将在以后深入讨论IP地址,这里只简单地说明一下。IP地址由4个字节构成,具有严格的层次结构。IP地址一般以点分十进制数格式表示,也就是说所有4个字节都以0—255之间的十进制数表示,各个字节之间以点号分隔,例如121.7.106.83。IP地址具有层次结构,当从左到右扫描某个地址时,我们得到关于其主机在因特网中所在位置的越来越明确的信息。这就像从下到上扫描某个邮政地址时,我们得到关于住宅所在位置的越来越明确的信息一样。
  
  DNS提供的服务
  
  我们已经知道,既可以使用主机名标识一台主帆,也可以使用IP地址标识。人们更愿意使用便于记忆的主机名标识符,而路由器则只愿使用长度固定民有层次结构的IP地址。为调解这两种不同的偏好,我们需要一个把主机名转换成IP地址的目录服务。这就是因特网的域名系统(Domain Name System,DNS)的主要任务。DNS既是一个在由名称服务器主机构成的层次结构中实现的分布式数据库,又是一个允许客户主机和名称服务器主机通信以使用域名转换服务的应用层协议。名称服务器主机通常是运行Berkeley Internet Name Domain(简称BIND)软件的UNIX主机。DNS协议运行在UDP之上,使用端口号53。
  
  其他应用层协议(例如HTTP,SMTP,FTP)普遍使用DNS把由用户提供的主机名转换成IP地址。作为例子,我们考虑某个用户使用运行在本地主机上的一个浏览器(也就是HTTP客户)请求http://www.yesky.com时会发生什么。为了把HTTP请求消息发送到名为www.yesky.com的web服务器主机,浏览器必须获悉这台主机的IP地址。我们知道,差不多每台主机都运行着DNS应用的客户端。浏览器从URL中抽取出主机名后把它传递给本地主机上的DNS应用客户端。DNS客户于是向某个DNS服务器发出一个包含该主机名的DNS查询消息。DNS客户最终收到一个包含与该主机名对应的IP地址的应答消息。浏览器接着打开一个到位于该IP地址的HTTP服务器的TCP连接。从这个例子中可以看出,DNS给使用它的因特网应用引入了额外延迟(有时还相当大)。所幸的是,正如我们即将讨论的那样.预期的主机名—IP地址对应关系往往高速缓存在就近的DNS名称服务器主机中,从而帮助降低了DNS访问延迟和DNS网络流量。
  
  除了从主机名到IP地址的转换,DNS还提供其他一些重要的服务:●主机别名(hody aliasing)。具有复杂主机名的主机还可以有一个或多个别名。例如,
  
  主机名为relay1.west-coast.enterprise.com的主机有两个别名:enterprise.com和www.enterprise.com。这种情况下,主机名relay1.west-coast.enterprise.com特称为正规主机名(canonical hostname),另外两个主机名则是别名主机名(alias hostname)。
  
  别名主机名往往比正规主机名更便于记忆。应用可以调用DNS获取所给定别名主机名的正规主机名和IP地址。
  
  ●邮件服务器别名(mall server aliasing)。电子邮件地址显然要求便于记忆。例如,如果Bob有一个hotmail账号,那么他的电子邮件地址可能是简单的bob@hotmail.com。然而hotmail邮件服务器的主机名要比hotmail.com复杂且不易记住。电子邮件应用可以调用DNS获取所给定别名主机名的正规主机名和IP地址。事实上,DNS允许一个公司的邮件服务器和Web服务器使用相同的别名主机名。例如,某个公司的web服务器和邮件服务器可以都称为enterprise.com。
  
  ●负载分担(load distribution)。DNS还越来越多地用于执行在多个复制成的服务器(例如复制成的Web服务器)之间的负载分担。像cnn.com那样的繁忙站点往往把Web服务器复制成多个,每个服务器运行在不向的端系统上,具有不同的IP地址。对于复制成的多个Web服务器,与其单个正规主机名相关联的是一组IP地址。DNS数据库中保存着这组IP地址。客户发出针对映射到一组IP地址的某个主机名的DNS查询后,服务器响应以整组IP地址,不过每次响应的地址顺序是轮转的。既然访问web站点时,浏览器一般把HTTP请求消息发送给内DNS客户否询到的一组IP地址中的第一个,DNS轮转于是把web站点的访问负载分担在所有复制成的服务器上。电子邮件应用也可以使用DNS轮转,这样多个邮件服务器可以有相同的别名。近来,有些公司已经以更为复杂的方式使用DNS提供web内容分发服务。
  
  DNS在RFC 1034和RFC 1035中有详细说明,并在另外若干个RFC中作了更新。DNS是一个复杂的系统,我们只在这儿讨论其操作的关键方面。感兴趣朗读者可以参见协议文档。
posted @ 2007-08-09 19:22 和田雨 阅读(340) | 评论 (0)编辑 收藏

     摘要:   阅读全文
posted @ 2007-07-31 22:00 和田雨 阅读(409) | 评论 (0)编辑 收藏

1.基本概念的理解

  绝对路径:绝对路径就是你的主页上的文件或目录在硬盘上真正的路径,(URL和物理路径)例如:
C:\xyz\test.txt 代表了test.txt文件的绝对路径。http://www.sun.com/index.htm也代表了一个
URL绝对路径。

  相对路径:相对与某个基准目录的路径。包含Web的相对路径(HTML中的相对目录),例如:在
Servlet中,"/"代表Web应用的跟目录。和物理路径的相对表示。例如:"./" 代表当前目录,
"../"代表上级目录。这种类似的表示,也是属于相对路径。

另外关于URI,URL,URN等内容,请参考RFC相关文档标准。

RFC 2396: Uniform Resource Identifiers (URI): Generic Syntax,
(http://www.ietf.org/rfc/rfc2396.txt)


2.关于JSP/Servlet中的相对路径和绝对路径。

2.1服务器端的地址

   服务器端的相对地址指的是相对于你的web应用的地址,这个地址是在服务器端解析的
(不同于html和javascript中的相对地址,他们是由客户端浏览器解析的)也就是说这时候
在jsp和servlet中的相对地址应该是相对于你的web应用,即相对于http://192.168.0.1/webapp/的。

  其用到的地方有:
 forward:servlet中的request.getRequestDispatcher(address);这个address是
在服务器端解析的,所以,你要forward到a.jsp应该这么写:
request.getRequestDispatcher(“/user/a.jsp”)这个/相对于当前的web应用webapp,
其绝对地址就是:http://192.168.0.1/webapp/user/a.jsp
sendRedirect:在jsp中<%response.sendRedirect("/rtccp/user/a.jsp");%>

2.22、客户端的地址
 
       所有的html页面中的相对地址都是相对于服务器根目录(http://192.168.0.1/)的,
而不是(跟目录下的该Web应用的目录)http://192.168.0.1/webapp/的。
 Html中的form表单的action属性的地址应该是相对于服务器根目录(http://192.168.0.1/)的,
所以,如果提交到a.jsp为:action="/webapp/user/a.jsp"或action="<%=request.getContextPath()%>"/user/a.jsp;
  提交到servlet为actiom="/webapp/handleservlet"  
   Javascript也是在客户端解析的,所以其相对路径和form表单一样。
 

  因此,一般情况下,在JSP/HTML页面等引用的CSS,Javascript.Action等属性前面最好都加上
<%=request.getContextPath()%>,以确保所引用的文件都属于Web应用中的目录。
另外,应该尽量避免使用类似".","./","../../"等类似的相对该文件位置的相对路径,这样
当文件移动时,很容易出问题。


3. JSP/Servlet中获得当前应用的相对路径和绝对路径
3.1 JSP中获得当前应用的相对路径和绝对路径
 根目录所对应的绝对路径:request.getRequestURI()
 文件的绝对路径     :application.getRealPath(request.getRequestURI());
 当前web应用的绝对路径 :application.getRealPath("/");
 取得请求文件的上层目录:new File(application.getRealPath(request.getRequestURI())).getParent()

3.2 Servlet中获得当前应用的相对路径和绝对路径
 根目录所对应的绝对路径:request.getServletPath();
 文件的绝对路径    :request.getSession().getServletContext().getRealPath
(request.getRequestURI())   
 当前web应用的绝对路径 :servletConfig.getServletContext().getRealPath("/");
     (ServletContext对象获得几种方式:
       javax.servlet.http.HttpSession.getServletContext()
       javax.servlet.jsp.PageContext.getServletContext()
       javax.servlet.ServletConfig.getServletContext()
     )

4.java 的Class中获得相对路径,绝对路径的方法
4.1单独的Java类中获得绝对路径
  根据java.io.File的Doc文挡,可知:
 默认情况下new File("/")代表的目录为:System.getProperty("user.dir")。
 一下程序获得执行类的当前路径
package org.cheng.file;
import java.io.File;

public class FileTest {
    public static void main(String[] args) throws Exception {      

  System.out.println(Thread.currentThread().getContextClassLoader().getResource(""));    

  System.out.println(FileTest.class.getClassLoader().getResource(""));       

  System.out.println(ClassLoader.getSystemResource(""));       
  System.out.println(FileTest.class.getResource(""));       
  System.out.println(FileTest.class.getResource("/")); //Class文件所在路径 
  System.out.println(new File("/").getAbsolutePath());       
  System.out.println(System.getProperty("user.dir"));   
 }
}

4.2服务器中的Java类获得当前路径(来自网络)
(1).Weblogic

WebApplication的系统文件根目录是你的weblogic安装所在根目录。
例如:如果你的weblogic安装在c:\bea\weblogic700.....
那么,你的文件根路径就是c:\.
所以,有两种方式能够让你访问你的服务器端的文件:
a.使用绝对路径:
比如将你的参数文件放在c:\yourconfig\yourconf.properties,
直接使用 new FileInputStream("yourconfig/yourconf.properties");
b.使用相对路径:
相对路径的根目录就是你的webapplication的根路径,即WEB-INF的上一级目录,将你的参数文件放

在yourwebapp\yourconfig\yourconf.properties,
这样使用:
new FileInputStream("./yourconfig/yourconf.properties");
这两种方式均可,自己选择。

(2).Tomcat

在类中输出System.getProperty("user.dir");显示的是%Tomcat_Home%/bin

(3).Resin

不是你的JSP放的相对路径,是JSP引擎执行这个JSP编译成SERVLET
的路径为根.比如用新建文件法测试File f = new File("a.htm");
这个a.htm在resin的安装目录下

(4).如何读相对路径哪?

在Java文件中getResource或getResourceAsStream均可

例:getClass().getResourceAsStream(filePath);//filePath可以是"/filename",这里的/代表web

发布根路径下WEB-INF/classes

默认使用该方法的路径是:WEB-INF/classes。已经在Tomcat中测试。

5.读取文件时的相对路径,避免硬编码和绝对路径的使用。(来自网络)
5.1 采用Spring的DI机制获得文件,避免硬编码。
   参考下面的连接内容:
   http://www.javajia.net/viewtopic.php?p=90213&
5.2 配置文件的读取
 参考下面的连接内容:
 http://dev.csdn.net/develop/article/39/39681.shtm
5.3 通过虚拟路径或相对路径读取一个xml文件,避免硬编码
 参考下面的连接内容:
 http://club.gamvan.com/club/clubPage.jsp?iPage=1&tID=10708&ccID=8
 
6.Java中文件的常用操作(复制,移动,删除,创建等)(来自网络)
 常用 java File 操作类
 http://www.easydone.cn/014/200604022353065155.htm
 
 Java文件操作大全(JSP中)
 http://www.pconline.com.cn/pcedu/empolder/gj/java/0502/559401.html

 java文件操作详解(Java中文网)
 http://www.51cto.com/html/2005/1108/10947.htm

 JAVA 如何创建\删除\修改\复制目录及文件
 http://www.gamvan.com/developer/java/2005/2/264.html

总结:
 通过上面内容的使用,可以解决在Web应用服务器端,移动文件,查找文件,复制
 删除文件等操作,同时对服务器的相对地址,绝对地址概念更加清晰。
建议参考URI,的RFC标准文挡。同时对Java.io.File. Java.net.URI.等内容了解透彻
对其他方面的理解可以更加深入和透彻。
==================================================================================

参考资料:
java/docs/

java.io.File
java.io.InputStream
java.io.OutputStream
java.io.FileInputStream
java.io.FileReader;
java.io.FileOutputStream
java.io.FileWriter;
java.net.URI
java.net.URL


绝对路径与相对路径祥解
http://www.webjx.com/htmldata/2005-02-26/1109430310.html

[『J道习练』]JSP和Servlet中的绝对路径和相对路径
http://w3china.org/blog/more.asp?name=pcthomas&id=9122&commentid=12376

JSP,Servlet,Class获得当前应用的相对路径和绝对路径
http://cy.lzu.edu.cn/cy/club/clubPage.jsp?ccStyle=0&tID=886&ccID=77

如何获得当前文件路径
http://www.matrix.org.cn/resource/article/44/44113_java.html

通过Spring注入机制,取得文件
http://www.javajia.net/viewtopic.php?p=90213&

配置文件的读取
http://dev.csdn.net/develop/article/39/39681.shtm

读取配置文件,通过虚拟路径或相对路径读取一个xml文件,避免硬编码!
http://club.gamvan.com/club/clubPage.jsp?iPage=1&tID=10708&ccID=8

常用 java File 操作类
http://www.easydone.cn/014/200604022353065155.htm

Java文件操作大全
http://www.pconline.com.cn/pcedu/empolder/gj/java/0502/559401.html

Java文件操作详解
http://www.51cto.com/html/2005/1108/10947.htm 

posted @ 2007-07-29 11:08 和田雨 阅读(13005) | 评论 (0)编辑 收藏

大家总是不喜欢看到<%和%>在jsp页面上,其实我们可以使用比较优雅的xml方式来表达
1,隐含的注释
JSP语法:<%-- 内容 --%>
XML语法:无
2,变量或函数的声明
JSP语法:<%! declaration;[declaration;]+... %>
例如:
<%! 
   int i=0;int a,b,c;
   Color red=new Color(255,0,0);
   private static final String OK="ok";
   public String getAnswer(){
       return OK;
   }
%>
XML语法:
<jsp:declaration>
    declartion;[declaration;]+...
</jsp:declaration>
例如:
<jsp:declaration>
   int i=0;int a,b,c;
   Color red=new Color(255,0,0);
   private static final String OK="ok";
   public String getAnswer(){
       return OK;
   }
</jsp:declaration>
3,表达式
JSP语法:<%=expression%>
例如:
<%=Math.round(Math.random*100)%>
XML语法:
<jsp:expression>
   expression
</jsp:expression>
例如:
<jsp:expression>
   Math.round(Math.random*100)
</jsp:expression>

4,代码片断
JSP语法:<% your java code %>
例如:
<%
   String username=request.getParameter("username").trim();
   String password=request.getParameter("password").trim();
%>
XMl语法:
<jsp:scriptlet>
   your java code
</jsp:scriptlet>
例如:
<jsp:scriptlet>
   String username=request.getParameter("username").trim();
   String password=request.getParameter("password").trim();
</jsp:scriptlet>
5,指令:include
JSP语法:<%@include file="relativeURL"%>
例如:<%@include file="static_head.html"%>
XML语法:<jsp:directive.include file="relativeURL"/>
例如:<jsp:directive.include file="static_head.html"/>
6,指令:page
JSP语法:<%@page attribtes%>
例如:<%@page import="java.util.HashMap"%>
XML语法:<jsp:directive.page attribute/>
例如:<jsp:directive.page import="java.util.HashMap"/>
7,指令:taglib
JSP语法:<%@taglib uri="URIForLibrary" prefix="tagPrefix"%>
XML语法:无
对于jsp操作而言,本来就是xml格式。
下面使用xml表达方式书写一个jsp页面

example.jsp

<jsp:directive.page contentType="text/html;charset=GBK" />
<jsp:directive.page import="java.util.*,java.text.*" />

<jsp:declartion>
    public String getCustomDate(Date,date,String pattern){
        SimpleDateFormat format=new SimpleDateFormat();
        format.applyPattern(pattern);
        return format.format(date);
    }
</jsp:declartion>
<jsp:scriptlet>
    Date date=(Date)request.getAttribute("date");
    String dateStr=getCustomDate(date,"yyyy-MM-dd,hh:mm:ss");
</jsp:scriptlet>
<div style="font-size:11pt"
  显示时间:<jsp:expression>dateStr</jsp:expression>
</div>
posted @ 2007-07-29 10:39 和田雨 阅读(252) | 评论 (0)编辑 收藏

为了给浏览者一个友好的错误提示页面,我们可以象IIS一样自定义错误页面;

步骤如下:

一、打开WEB-INF文件夹下web.xml文件;

二、在里面新增:

''404页面不存在错误

<error-page>

   <error-code>404</error-code>

   <location>/errorpage404.htm</location>

</error-page>

''505服务器内部错误

<error-page>

   <error-code>505</error-code>

   <location>/errorpage505.htm</location>

</error-page>

''java.lang.NumberFormatException异常错误,依据这个标记你可以定义好多,you can any fire:)!@

<error-page>

   <exception-type>java.lang.NumberFormatException</exception-type>

   <location>/exception.htm</location>

</error-page>

三、保存web.xml文件,reload服务即ok!

Good Luck!!

posted @ 2007-07-29 10:05 和田雨 阅读(2330) | 评论 (4)编辑 收藏

华为技术有限公司

华为技术(“华为”)是全球领先的下一代电信网络解决方案供应商,致力于向客户提供创新的满足其

需求的产品、服务和解决方案,为客户创造长期的价值和潜在的增长。

华为产品和解决方案涵盖移动(HSDPA/WCDMA/EDGE/GPRS/GSM, CDMA2000 1xEV-DO/CDMA2000 1X,

TD-SCDMA和WiMAX)、核心网(IMS, Mobile Softswitch, NGN)网络(FTTx, xDSL, 光网络, 路由器和

LAN Switch)、电信增值业务(IN, mobile data service, BOSS)和终端(UMTS/CDMA)等领域。

华为在印度、美国、瑞典、俄罗斯以及中国的北京、上海和南京等地设立了多个研究所,61000多

名员工中的48%从事研发工作。截至2006年底,华为已累计申请专利超过19000件,连续数年成为中国申

请专利最多的单位。

华为在全球建立了100多个分支机构,营销及服务网络遍及全球,能够为客户提供快速、优质的服

务。目前,华为的产品和解决方案已经应用于全球100多个国家,以及31个全球前50强的运营商,服务

全球超过10亿用户。






中兴通讯股份有限公司

中兴通讯是全球领先的综合性通信制造业上市公司,是近年全球增长最快的通信解决方案提供商之一。


中兴通讯展示大厅一隅

1985年,中兴通讯成立。1997年,中兴通讯A股在深圳证券交易所上市。2004年12月,中兴通讯作

为中国内地首家A股上市公司成功在香港上市。2005年,中兴通讯销售收入超过215亿元,其中,国际销

售收入达36%。2006年,凭借优异的全球业绩,中兴通讯跻身美国《商业周刊》 “中国十大重要海外上

市公司”和 “中国最佳品牌20强”,成为国内惟一上榜的通信设备企业。

作为在香港和深圳两地上市的大型通信制造业上市公司,中兴通讯以满足客户需求为目标,为全球

客户提供创新性、客户化的产品和服务,帮助客户实现持续赢利和成功,构建自由广阔的通信未来。凭

借在无线产品(CDMA、GSM、3G、WiMAX等)、网络产品(xDSL、NGN、光通信等)、手机终端(CDMA、

GSM、小灵通、3G等)和数据产品(路由器、以太网交换机等)四大产品领域的卓越实力,通过遍布全

球的100多个分支机构,中兴通讯产品和解决方案应用于100多个国家的500多家运营商,全球TOP 100运

营商中的30家与中兴通讯达成长期伙伴关系,并为全球近3亿人口提供优质的、高性价比的产品与服务



中兴通讯是中国重点高新技术企业、技术创新试点企业和国家863高技术成果转化基地,承担了近

30项国家“863”重大课题,是通信设备领域承担国家863课题最多的企业之一,公司每年投入的科研经

费占销售收入的10%左右,并在美国、印度、瑞典及国内设立了14个研究中心。

立足中国,放眼全球。早在1995年,中兴通讯就启动了国际化战略,是中国高科技领域最早并最为

成功实践 “走出去”战略的标杆企业。中兴通讯国际市场“十年磨一剑”,已经相继与包括和记电讯

、法国电信在内的等众多全球电信巨头建立了战略合作关系,并不断突破发达国家的高端市场。

未来,中兴通讯将以“人才国际化为根本,市场国际化为重点,资本国际化为依托”,积极迎接挑

战,全力以赴为客户创造价值和成功,打造享誉全球的中兴通讯品牌,力创世界级卓越企业。








海信集团有限公司


海信集团是特大型电子信息产业集团公司,成立于1969年,先后涉足家电、通讯、信息、房地产、服务

等领域。

海信坚持“高科技、高质量、高水平服务、创国际名牌”的发展战略,以优化产业结构为基础、技

术创新为动力、资本运营为杠杆,快速成长,迅猛发展,率先在国内构架起家电、通讯、信息为主导的

3C产业结构,主导产品为电视、空调、冰箱、冷柜、洗衣机、商用空调系统计算机、移动电话、软件开

发、网络设备等。已经形成了年产1610万台彩电、900万套空调、1000万台冰箱、70万台冷柜、330万部

手机的强大产能。2006年海信实现销售收入435亿元,在中国电子信息百强企业中名列前茅。

目前,通过收购科龙,海信已经拥海信电器(600060)和科龙电器(000921)两家在沪、深、港三

地的上市公司,同时成为国内唯一一家持有海信(HiSense)、科龙(Kelon)和容声(Ronshen)三个

中国驰名商标的企业集团。海信电器股份有限公司2001年荣获了首届“全国质量管理奖”,海信电视、

海信空调、海信电脑、海信手机、科龙空调、容声冰箱全部当选中国名牌,海信电视、海信空调、海信

电脑、海信冰箱全部被评为国家免检产品,海信电视首批获得国家出口免检资格。

海信拥有国家级企业技术中心,建有国家一流的博士后科研工作站,是全国高新技术企业、全国技

术创新基地。科学高效的技术创新体系使海信的技术始终走在国内同行的前列,2005年6月,我国第一

块自主知识产权的、产业化的数字视频媒体处理芯片在海信诞生,此举打破了国外垄断的历史。

目前,海信在南非、匈牙利、法国等地拥有生产基地,在美国、欧洲、澳洲、日本等地设有销售机

构,产品远销欧洲、美洲、非洲、东南亚等100多个国家和地区。






海尔集团公司


海尔集团是世界第四大白色家电制造商、中国最具价值品牌。旗下拥有240多家法人单位,在全球30多

个国家建立本土化的设计中心、制造基地和贸易公司,全球员工总数超过五万人,重点发展科技、工业

、贸易、金融四大支柱产业,已发展成全球营业额超过1000亿元规模的跨国企业集团。



海尔集团在首席执行官张瑞敏确立的名牌战略指导下,先后实施名牌战略、多元化战略和国际化战略,

2005年底,海尔进入第四个战略阶段——全球化品牌战略阶段,海尔品牌在世界范围的美誉度大幅提升

。1993年,海尔品牌成为首批中国驰名商标;2006年,海尔品牌价值高达749亿元,自2002年以来,海

尔品牌价值连续四年蝉联中国最有价值品牌榜首。海尔品牌旗下冰箱、空调、洗衣机、电视机、热水器

、电脑、手机、家居集成等18个产品被评为中国名牌,其中海尔冰箱、洗衣机还被国家质检总局评为首

批中国世界名牌,2005年8月30日,海尔被英国《金融时报》评为“中国十大世界级品牌”之首。2006

年,在《亚洲华尔街日报》组织评选的“亚洲企业200强”中,海尔集团连续第四年荣登“中国内地企

业综合领导力”排行榜榜首。海尔已跻身世界级品牌行列,其影响力正随着全球市场的扩张而快速上升



据中国最权威市场咨询机构中怡康统计:2006年,海尔在中国家电市场的整体份额已经达到25.5%

,依然保持份额第一。其中,海尔在白色家电市场上仍然遥遥领先,且优势更加突出;在小家电市场上

海尔表现稳健,以16%的市场份额蝉联小家电市场冠军。在智能家居集成、网络家电、数字化、大规模

集成电路、新材料等技术领域处于世界领先水平。 “创新驱动”型的海尔集团致力于向全球消费者提

供满足需求的解决方案,实现企业与用户之间的双赢。目前,海尔累计申请专利突破7000项(其中发明

专利1234项)。在自主知识产权基础上,海尔主持或参与了115项国家标准的编制修定,制定行业及其

它标准397项。海尔“防电墙”技术正式成为电热水器新国家标准,海尔空调牵头制定“家用和类似用

途空调安装规范”。在国际上,海尔热水器“防电墙”技术、海尔洗衣机双动力技术等六项技术还被纳

入IEC国际标准提案,这证明海尔的创新能力已达世界级水平。

在创新实践中,海尔探索实施的“OEC”管理模式、“市场链”管理及“人单合一”发展模式均引

起国际管理界高度关注,目前,已有美国哈佛大学、南加州大学、瑞士IMD国际管理学院、法国的欧洲

管理学院、日本神户大学等商学院专门对此进行案例研究,海尔“市场链”管理还被纳入欧盟案例库。

海尔“人单合一”发展模式为解决全球商业的库存和逾期应收提供创新思维,被国际管理界誉为“号准

全球商业脉搏”的管理模式。

面对新的全球化竞争条件,海尔确立全球化品牌战略、启动“创造资源、美誉全球”的企业精神和

“人单合一、速决速胜”的工作作风,挑战自我、挑战明天,为创出中国人自己的世界名牌而持续创新







神州数码(中国)有限公司


神州数码控股有限公司成立于2000年,是联想控股有限公司旗下的子公司之一,于2001年在香港联合交

易所主板上市。神州数码旨在以负责任和持续创新的精神,全方位提供第一流的电子商务基础建设产品

、解决方案和服务。神州数码不仅是国内第一的IT产品分销商,同时也是国内最大的专业系统集成商和

知名的全线网络产品供应商。
2004年,神州数码综合市场发展、用户需求及自身能力,在“IT服务中国”的旗帜下,进一步提出“IT

服务,随需而动”(IT Service On Demand),围绕客户需求调整业务布局,致力于供应链管理服务、

增值服务和IT服务三大竞争领域,成为能够满足客户多样化需求,具有产品、技术与服务综合能力的IT

服务供应商。


发展历程

• 2000年4月,原联想集团进行战略拆分,神州数码正式成立,由原联想科技、联想集成、联想网络整

合而成;
• 2001年6月,神州数码在香港联交所主板成功上市;
• 2002年,神州数码提出“IT服务中国”,进行以IT服务为核心的战略布局,年销售额突破130亿元人

民币;
• 2003年,神州数码全力打造集网络基础建设、应用软件服务以及供应链服务为一体的IT服务企业,先

后荣获“中国企业信息化500强”、“国内最具影响力的IT服务品牌企业”等称号。






熊猫电子集团有限公司


熊猫电子集团有限公司是一个具有65年历史的综合性大型电子骨干企业,是全国120家试点企业集团和

520家重点企业之一。公司注册资本9.8亿元,净资产13.4亿元,现有员工9000多人,主要产品有: 短

波通信系统、移动通信系统、卫星通信系统、彩电、VCD/DVD、音响、洗衣机、计算机、显示器、电源

、BP机、生产技术装备、系统网络集成业务等。产品商标熊猫牌,是全国电子行业第一个"中国驰名商

标",已有45年历史。 <BR>长期以来,“熊猫”为建立和发展民族电子工业体系,为国防和国民经济现

代化建设作出了卓越贡献。尤其是改革开放以来的20年,销售收入从5000多万元发展到100亿元,利税

从1000多万元增长到10亿元,产品门类从少数品种扩展到综合性多门类,生产方式从小批量发展到规模

化、集约化大生产,技术水平跨越三、四十年,中外合资、国际合作取得良好成绩。2000年熊猫集团销

售收入168亿元,实现利税12亿元,创历史最好水平。 ; “熊猫”拥有雄厚的技术开发实力,公司建有

5个国家级工程技术开发中心,1个博士后工作站,4个产品设计研究所,与12家进入世界500强的国际大

公司进行技术合作,与国内8所重点大学研究所进行联合开发,近几年通过技术创新,开发出具有国际

水平的新一代短波通信、移动通信、卫星通信、数字电视、DVD、机电仪一体化装备等高新技术产品,

在全国同行业中居于领先地位。; “熊猫”抓住改革开放的机遇,积极发展中外合资,先后建立了南京

爱立信熊猫通信有限公司(主要生产移动通信基站、程控交换机等)、南京爱立信熊猫移动终端有限公

司(主要生产手机等移动终端产品)、南京夏普电子有限公司(主要生产数字化彩电、音响、液晶电视

、激光头等)、南京LG熊猫电器设备有限公司(主要生产洗衣机等)等规模较大的中外合资企业,直接

引进外资6000多万美元。在中外双方的真诚合作与努力下,这些合资企业迅速发展,实现利税8亿多元

,创造了良好的经济效益与社会效益,推进了集团发展。 ; “熊猫”下一步发展规划是:突出重点,

强化优势,体现特色,把移动通信、视像产品、信息产业等做强做大,把短波通信、卫星通信和机电仪

一体化装备等做精做专,形成六大支柱产品,到2005年销售收入力争达到500亿元,为发展我国电子信

息产业作出更大贡献。





浪潮集团有限公司


浪潮集团是中国领先的行业IT应用解决方案提供商,同时,也是中国最大的服务器制造商和服务器解决

方案提供商。2002年,浪潮连续7年蝉联国产服务器第一品牌。浪潮集团拥有"浪潮信息"和"浪潮软件"

两家上市公司,业务涵盖以服务器、行业电脑为主的网络终端设备、大型行业应用软件、分行业ERP与

通信运营系统解决方案等领域,用户遍及中国金融、通信、政府、教育、制造业、烟草行业等重要领域

。2002年,浪潮集团的销售收入达到64亿元人民币。
浪潮是中国最早从事电子信息产业的企业之一,这段历史可以追溯到1968年,山东电子设备厂(浪潮的

前身)在那时开始生产计算机外围设备和低频大功率三极管。浪潮三十余年的风雨历程,就是中国IT产

业发展的缩影。作为领先的行业IT应用解决方案提供商,浪潮的想法很简单:把尖端技术产业化,使之

成为最广泛的用户价值。
浪潮对中国信息产业的贡献,不仅因为他是中国IT产业的启蒙者,更重要的是浪潮始终以超前的技术、

出色的管理和独树一帜的产品引导着中国信息产业的发展,满足行业用户对信息处理的全方位需求。
1983年,第一台浪潮微机在济南诞生。由此,浪潮将中国的PC产业带入了一个变被动为主动的新时期。

1985年,大名鼎鼎的浪潮0520A使浪潮在中国三大计算机厂商中位列第二。当时的三大厂商占据了中国

个人计算机市场大部分的市场份额。
1992年,浪潮开发出全球第一台中文寻呼机,并开发制定了全球第一个汉字寻呼标准,这一标准沿用至

今。
90年代初,国际互联网络蓬勃发展。时任浪潮集团副总工程师的孙丕恕先生大胆地预测:个人计算时代

将逐渐向网络计算时代转变,服务器作为网络的核心,将是21世纪左右网络信息技术的关键所在。
1993年,浪潮在新加坡的技术人员成功开发出了中国第一台基于10颗CPU的小型机服务器,孙丕恕先生

是这次开发的主持者。在接下来的10年中,浪潮在中国率先开始了服务器的生产与研发,打破了国外服

务器厂商在中国多年的垄断,进而开创了中国服务器产业。自1996年开始,浪潮服务器一直蝉联国产服

务器第一品牌,2000年,浪潮服务器超越了众多国际品牌,晋身中国市场前三甲。浪潮率先拥有中国最

先进、产能最大的年产10万台的服务器生产线,2000年3月成功开发出"网泰"网络安全服务器,2002年

全球率先推出了64位开放式架构服务器 - IA-64。2002年11月,旨在推动"大协同运算体系"的浪潮"天

梭工程"正式启动。2002年,浪潮连续第7年蝉联国产服务器第一品牌,并获得2002年中国服务器市场唯

一"年度最成功企业"称号。
浪潮电脑专注行业应用市场,并对行业用户的需求有深刻的理解和技术积淀。每个行业都将有相应的"

行业PC","行业PC"应用者将从浪潮电脑的产品和方案中得到最大的实惠和应用价值。浪潮电脑相信,

随着用户需求的进一步细分化,"行业PC"将是"商用PC"最终的发展趋势。
在信息产业迅速发展,激烈竞争的时代,软件与IT服务的重要性日益提高,方案提供与IT服务能力成为

IT企业的发展趋势。软硬件综合发展,注重方案的提供能力,逐步向IT服务转型是浪潮发展策略之一。

事实上,从上世纪80年代末开始,浪潮软件就伴随着浪潮电脑走向神州大地,为行业用户提供量身定做

服务。同时,浪潮的软件人员也为国产服务器的成功推广做出了不可磨灭的贡献。2000年,浪潮集团整

合了内部软件资源,并吸纳外部软件力量,实施软件产业化的发展战略,成立了浪潮齐鲁软件公司,并

于2001年在上海证券交易所挂牌上市,其后更名"浪潮软件"。"浪潮软件"定位于通信、政府、分行业

ERP等行业或领域大型应用软件开发和集成服务提供商,"浪潮软件"在通信、金融、电子政务、烟草等

领域享有声誉。在2002年"浪潮软件"被评为最具竞争力中国软件企业10强之一,并位列"中国电子政务

IT 100强"第4名。
浪潮通软是中国三大财务软件提供商之一,是最早从财务软件转型为ERP管理软件的提供商。2001年,

浪潮集团通过增持股份将浪潮通软纳入浪潮软件的一体化经营体系。2002年7月,浪潮与韩国LG-CNS公

司合资成立浪潮乐金信息系统有限责任公司,为行业用户提供行业综合解决方案。同时,浪潮软件致力

于为通信领域客户提供整体解决方案,全面提升服务,为客户赢得客户。通信领域已经成为浪潮三大产业

之一。
新世纪的浪潮提出了专注化的发展战略,浪潮集团将结合自身优势,紧紧围绕因特网,专注于两个产业

发展方向:一是以服务器为核心的嵌入式软件化硬件产品,包括服务器、PC和面向行业的解决方案;二

是以通信行业软件、分行业ERP软件为主综合应用软件,兼顾OA、金融软件。浪潮专注于以上两个目标

,致力成为中国最优秀的行业IT应用解决方案提供商。







东软集团有限公司


东软生存与发展的关键因素是不断地发展我们的员工和领导力,不断创造我们在技术、产品和服务方面

的核心竞争力,不断提高我们客户的满意度,并能够为投资者带来收益。东软的成功不仅在于是否我们

明白了这些道理,而是我们如何在执行的环节上保证这些关键因素的可靠实现。



公司概况

东软是一家以软件技术为核心,提供解决方案、数字化产品和服务的公司,在软件与行业应用的结合、

软件与数字化产品的结合、软件人才的培养和咨询服务方面形成了东软独特的经营模式。

东软认为,随着软件技术的发展和在应用领域的拓展,软件将会成为社会和人们生活的一部分。东软以

软件应用的不同业务表现形式并通过业务间的充分组合来为客户提供系统的服务,创造客户和社会的价

值,进而实现我们的价值。

东软在众多行业解决方案中表现了东软的技术领先及对各行业业务的理解,使东软不仅是技术的专家,

也是应用信息技术解决行业管理问题的专家。将软件与医疗领域、安全领域、通信领域的结合,使我们

开发出了大型医疗设备系统、网络安全产品、汽车电子和移动通讯设备软件等,使软件的价值得到了更

好的体现。通过我们的教育与培训体系,东软为客户构建了一个学习的平台,一个人力资源发展的平台

,一个理解与沟通的平台。





北京北大方正集团



持“技工贸”道路而获成功的企业。历经20年的沧桑坎坷,方正在中国高科技企业演进史上留下了深刻

而坚实的烙印。王选教授自主创新的激光照排技术,开创了中国新闻出版业“告别铅与火、迎来光与电

”的第一次革命。而今,方正持续创新的网络出版技术,正在更为广泛应用领域掀起又一次技术革命。

自主创新与持续创新是方正跳跃的灵魂、发展的源泉。

纵观改革开放近三十年的历史进程,从“科教兴国”,到“建设创新型国家”,中国对经济发展基

本驱动力的战略认识不断攀升到新的高度。方正的今天就站在这一新的高度上。建立“创新型企业”迈

向未来,这既是方正的战略远见,也是方正的现实抉择。

方方正正做人、实实在在做事,我们秉承到永远。





清华同方股份有限公司


1992年,承接中央电视台空调控制系统工程;
1993年,承接国家气象局楼宇自动化项目;
1994年起,承接毛主席纪念堂中央集中监测管理系统工程、中南海怀仁堂空调自动控制系统工程、人民

大会堂空调自控工程及后期的楼宇自控/保安监控及计算机网络工程等一系列国家重点项目;
承接伊朗德黑兰地下铁道工程机电监控系统、消防报警和气体灭火系统工程;
承接山东潍坊国际金融大厦弱电总包工程,是公司承接的第一个大型弱电总包工程;
1997年,承接北京望京地区社区智能化系统工程,获得“北京试点小区”、“小康示范小区”“99年十

个样板工程”等部级优秀项目奖;
1998年,承接昆明世界园艺博览会智能化系统重大工程;
1999年,获得建设智能化系统集成专项工程设计资质(甲级);
承接北京饭店改扩建工程智能化系统工程;承接大连星海人家社区智能化系统工程,成为智能社区的示

范工程;
2000年,获得北京市安全技术防范工程准许证书(壹级);
承接的青岛广播电视中心工程获得建设部“鲁班奖”;
承接北京国家会计学院智能化系统工程,成为智能化院校的典型工程;
2001年,受建设部委托主编《智能建筑工程质量验收规范》,是第一个关于智能建筑工程质量方面的国

家强制标准;
获得信息产业部颁发的计算机系统集成资质(壹级);
承接郑州铁路局河南境内医疗保险管理信息系统工程,承接北京市公安局数字业务宽带信息网系统工程

,显示了完成行业信息化项目实力;
2002年,获得建设部建筑业企业机电安装工程施工总承包资质(壹级);
获得国家保密局涉及国家秘密的计算机信息系统集成资质;
2003年,成为中国建筑业协会智能建筑专业委员会副主任单位,中国勘察设计协会工程智能设计分会副

会长单位;
承接南京奥林匹克体育中心智能化系统工程;
承接湖南华天大酒店贵宾楼工程、青岛海悦广场高层公寓楼工程智能化系统工程,获得建设部“鲁班奖

”;
2004年,推出ezIBS智能建筑信息集成系统软件,提出“行业整合应用”的产业思想;

2005年,ezIBS智能建筑信息集成系统通过建设领域应用系统测评和第三方认证;
市场细分,推出酒店行业智能化整体解决方案;
荣获全国绿色建筑创新奖三等奖——北京国家会计学院
荣获全国绿色建筑创新奖二等奖----北京饭店
成为中国旅游饭店业协会会员,主持《饭店智能化及集成应用标准研究》课题;
8月 清华同方与美国埃施朗公司签订了智能建筑领域战略合作协议;
9月 清华同方在德黑兰地铁FAS及FES集成项目正式启动;
10月 与建设部智能建筑技术开发推广中心,中国建筑业协会智能建筑专业委员会联合举办了“智能楼

宇自动控制系统”的深度培训;
11月 成为北京市安全防范行业协会副理事长单位;

2006年,3月同方担当中央电视台新台址建设工程A、B标段弱电工程;
RH2000分布式控制系统、RH-DⅡ型组合式冷/热量表评为“智能建筑优质产品”
6月同方海外签约伊朗地铁四号线BAS/FAS/FES系统建设、北延线BAS/FAS建设;
7月数字城市签约也门萨那国际机场智能化系统工程;
8月,数字城市科技公司成立;
9月推出同方自有品牌安防类CCTV产品系列;
10月同方安防产品评为“2006年中国安防十大品牌集成系统产品类”第八名





山东中创软件工程股份有限公司


中创软件工程股份有限公司,是全国领先的软件产品供应商、解决方案提供商和系统集成服务商,是全

国电子信息百强企业、国家规划布局内重点软件企业、国家火炬计划重点高新技术企业、国家“863计

划”软件产业国际化示范企业、中国软件欧美出口工程A级示范企业、国家“863计划”成果产业化基地

、山东中间件产业基地、中国软件二十年明星企业。2004年10月落成的中创软件昆山软件园区,已经成

为中创软件的开发、测试、培训和服务基地。
中创软件自1991年成立即专注于行业应用,为全国金融、交通、电子政务、电子商务等领域的6万

多家用户单位提供了优质的软件产品、解决方案和集成服务。2004年成功实施了交通银行、民生银行信

贷管理项目以及广东发展银行的信贷系统升级项目,成功实施了山东高速公路信息系统二期项目,在广

州成功完成了新机场高速、北环高速、东南西环高速、南部快线等联网收费系统的建设,成功完成了全

国海事信息化推广项目,承建的山东交通政务信息管理系统被国家交通部评为“省级公路资源整合示范

工程第一名”。

2005年中创软件又中标承建了中国建设银行信贷管理项目、东莞商业银行信贷管理项目,并成功实

施了山东省国税局的税控和数据分析管理系统。

以十几年丰富的应用开发经验,中创软件及商用中间件公司在国家重点支持下,通过产学研合作,

开发出了技术领先的Infor系列中间件,发起成立全国第一个中间件产业联盟,并在全国一系列大型项

目中实现了规模化应用。Infor系列中间件,2004年被评为“中国软件二十年最具应用价值的软件产品

”,并荣获“2004年度中间件综合应用奖”、“2004年度中间件最佳开发效率奖”。2005年应用服务器

InforWeb荣获“应用服务器最佳性能及开发效率奖”, 消息中间件InforBus/Q荣获“消息中间件最佳

应用奖”, 网页防篡改产品InforGuard荣获“2005年度中国信息安全值得信赖网页防篡改产品品牌”



中创软件与国际知名IT公司、咨询公司、学术机构密切交流合作,在管理、技术、工具、人才等方

面与国际接轨,促进国内外业务不断拓展,成为中国主要的欧美软件外包服务商和IBM、Intel等著名国

际公司的重要合作伙伴。2004年与IBM展开全面合作,建立了全国首家IBM授权的软件测试中心,构建了

跨区域软件开发平台,大大提升了软件开发和质量保证能力。两位UML创始人、国际软件工程大师James

Rumbaugh和Ivar Jacobson相继访问中创软件并讲学,推动了中创软件注入先进?/ca>
posted @ 2007-07-27 19:39 和田雨 阅读(820) | 评论 (0)编辑 收藏

华为技术有限公司是一家总部位于中国广东深圳市的生产销售电信设备的员工持股的民营科技公司,于1988年成立于中国深圳。华为的主要营业范围是交换,传输,无线和数据通信类电信产品,在电信领域为世界各地的客户提供网络设备、服务和解决方案。总裁任正非,董事长孙亚芳。

2004年销售额462亿元人民币,其中海外销售额22.8亿美元,并且是当年中国国内电子行业营利和纳税第一。

截至2006年5月,华为在国际市场上覆盖90多个国家和地区,全球排名前50名的运营商中,已有28家使用华为的产品和服务。

华为产品和解决方案涵盖移动(HSDPA/WCDMA/EDGE/ GPRS/GSM, CDMA2000 1X EVDO/CDMA2000 1X, TD-SCDMA和WiMAX)、核心网(IMS, 移动网软交换机, NGN) 通信网络(FTTX, xDSL, 光网络, 路由器和局域网交换机)、电信增值业务(智能网, 移动数据业务, BOSS)、终端(UMTS/CDMA)等领域。华为技术数据通信产品系列是针对企业用户的需求而设计,其中包含电信级水平的超高速骨干网络交换器、堆栈式L2/3/4网络交换器、企业级路由器、网络安全设备、VoIP网络电话设备与华为专有的QuidView相关网管软件,提供企业最佳网络整合解决方案。

华为在通信设备核心技术方面的第一次突破,是1994年推出的2000门网用大型交换机设备。华为技术有限公司在IT泡沫之前是一间籍籍无名的公司,但从IT泡沫之后该公司以中国为据点急速成长,快速吸引各界注目。华为在与思科系统公司的诉讼中获得胜利,出口大幅增加,市场不仅限于开发中国家,目前市场传言华为将与外商合作,并初次发行股票(IPO),威胁同领域的西欧企业。

华为在全球设立了包括印度、美国、瑞典、俄罗斯以及中国的北京、上海、南京等多个研究所,40000名员工中的48%从事研发工作,截止2005年年底已累计申请专利超过12500件,已连续数年成为中国申请专利最多的单位。

2006年5月8日,华为启用新的企业标示。

2006年9月,华为与3Com合资设立的网络通讯设备品牌华为3Com(Huawei-3Com)改名为H3C。

由于日益严重的外部威胁,目前该公司内部正在大规模的推行信息安全政策,然而实际上其系统仍然有着显著信息外泄漏洞。
posted @ 2007-07-27 19:29 和田雨 阅读(242) | 评论 (0)编辑 收藏

电信业务运营支持系统(BOSS),面对客户是统一的;面对电信运营商,它融合了业务支撑系统(BSS)与运营支撑系统(OSS),是一个综合的业务运营和管理平台,同时也是真正融合了传统IP数据业务与移动增值业务的综合管理平台。

OSS/BSS是电信运营商的一体化、信息资源共享的支持系统,它主要由网络管理、系统管理、计费、营业、账务和客户服务等部分组成,系统间通过统一的信息总线有机整合在一起。它不仅能在帮助运营商制订符合自身特点的运营支撑系统的同时帮助确定系统的发展方向,还能帮助用户制订系统的整合标准,改善和提高用户的服务水平。

作为一种高效的信息管理系统,OSS/BSS已在国外电信运营商中得到广泛的运用,并在实践中积累了大量的成功案例。OSS/BSS解决方案也在这一过程中趋于完善。但国内目前还没有成熟的OSS/BSS系统,电信运营商的网管系统、计费系统、营账系统、客服系统等都是各成体系,没有经过有机整合。目前全球领先的OSS/BSS供货商主要有Tibco、Portal、HP、Concord、Syndesis、SUN、Micromuse等。思科、亚信、同天等厂商还在国内开放了OSS/BSS 实验室。
posted @ 2007-07-27 19:27 和田雨 阅读(392) | 评论 (0)编辑 收藏

FSO是指文件系统对象,英文单词为File System Object。 FSO是ASP程序中的一个对文件操作的控件,通过编程,可以通过该控件对服务器进行读取、新建、修改、删除目录以及文件的操作。它是ASP编程中非常有用的一个控件,   但大家可以想一下,在一台服务器上有许多虚拟用户,如果一个用户使用FSO删除别人的文件,那不是乱了套,所以基于安全考虑,不少的虚拟主机服务商干脆关掉了这个控件,但关掉此控件,有的有户想使用ASP生成文件又变成了不可能的事,所以如果关闭了FSO会让客户很不方便。
 所以你在购买虚拟主机时,一定要看看你的服务器有没有关闭FSO权限,用了FSO,我们就可以操纵的计算机文件系统,在这里是指位于web服务器之上。所以,确认你对此拥有合适的权限。理想情况下,你可以在自己的机器上建立一个web服务器,这样就能方便地进行测试。如果运行于Windows平台,如果你还不是很了解FSO,请试一试微软公司的免费个人Web服务器PWS,或windows2000的IIS
 FSO 模型对象
Drive Object:驱动器对象 供存取磁盘或者网络驱动器
FileSystemObject Object:文件系统对象 供存取计算机的文件系统
Folder Object:文件夹对象 供存取文件夹的所有属性
TextStream Object:文本流对象 供存取文件内容 
 你可以使用上面的对象做计算机上的任何事情,也包括破坏活动 ;-( 所以,请小心使用FSO。在web环境中,存储信息是非常重要的,比如用户信息,日志文件,等等。FSO提供了一个强大且简单的方法高效率地保存数据。
开启/关闭系统FSO支持方法
windows98系统
在DOS命令行状态输入以下命令:
关闭命令:RegSvr32 /u C:\WINDOWS\SYSTEM\scrrun.dll
打开命令:RegSvr32 C:\WINDOWS\SYSTEM\scrrun.dll
win2000系统:
在CMD命令行状态输入以下命令:
关闭命令:RegSvr32 /u C:\WINNT\SYSTEM32\scrrun.dll
打开命令:RegSvr32 C:\WINNT\SYSTEM32\scrrun.dll 
posted @ 2007-07-23 16:17 和田雨 阅读(1073) | 评论 (1)编辑 收藏

     摘要:   阅读全文
posted @ 2007-07-21 13:50 和田雨 阅读(247) | 评论 (0)编辑 收藏

作者:杨晓(http://blog.sina.com.cn/u/1237288325)

一、Tomcat背景

  自从JSP发布之后,推出了各式各样的JSP引擎。Apache Group在完成GNUJSP1.0的开发以后,开始考虑在SUN的JSWDK基础上开发一个可以直接提供Web服务的JSP服务器,当然同时也支持Servlet, 这样Tomcat就诞生了
Tomcat是jakarta项目中的一个重要的子项目,其被JavaWorld杂志的编辑选为2001年度最具创新的java产品,同时它又是sun公司官方推荐的servlet和jsp容器,因此其越来越多的受到软件公司和开发人员的喜爱。servlet和jsp的最新规范都可以在tomcat的新版本中得到实现。其次,Tomcat是完全免费的软件,任何人都可以从互联网上自由地下载。Tomcat与Apache的组合相当完美。

 

二、Tomcat目录

tomcat
|---bin Tomcat:
存放启动和关闭tomcat脚本

|---conf Tomcat:存放不同的配置文件(server.xml和web.xml);
|---doc:存放Tomcat文档;
|---lib
/japser/common:存放Tomcat运行需要的库文件(JARS);
|---logs:存放Tomcat执行时的LOG文件;
|---src:存放Tomcat的源代码;
|---webapps:Tomcat的主要Web发布目录(包括
应用程序示例);
|---work:
存放jsp编译后产生的class文件



三、Tomcat类加载

    Bootstrap($JAVA_HOME/jre/lib/ext/*.jar)
System($CLASSPATH/*.class
和指定的jar)
Common($CATALINA_HOME/common
下的classes,lib,endores三个子目录)
Catalina ($CATALINA_HOME/server/
下的classeslib目录仅对Tomcat可见)
&Shared($CATALINA_HOME/shared/
下的classeslib目录以及$CATALINA_HOME/lib目录)仅对Web应用程序可见,Tomcat不可见WebApp($WEBAPP/Web-INF/*仅对该WEB应用可见classes/*.class lib/*.jar)



加载类和资源的顺序为:
1
/Web-INF/classes

2/Web-INF/lib/*.jar

3Bootstrap

4System

5$CATALINA_HOME/common/classes
6
$CATALINA_HOME/common/endores/*.jar

7$CATALINA_HOME/common/lib/*.jar

8$CATALINA_HOME/shared/classes
9
$CATALINA_HOME/shared/lib/*.jar

四、server.xml配置简介

下面讲述这个文件中的基本配置信息,更具体的配置信息请参考tomcat的文档:
    server:
          1
port 指定一个端口,这个端口负责监听关闭tomcat的请求
          2
shutdown 指定向端口发送的命令字符串
    service:
          1
name 指定service的名字
    Connector (
表示客户端和service之间的连接)
          1
port 指定服务器端要创建的端口号,并在这个断口监听来自客户端的请求
          2
minProcessors 服务器启动时创建的处理请求的线程数
          3
maxProcessors 最大可以创建的处理请求的线程数
          4
enableLookups 如果为true,则可以通过调用request.getRemoteHost()进行DNS

询来得到远程客户端的实际主机名,若为false则不进行DNS查询,而是返回其ip

地址
          5
redirectPort 指定服务器正在处理http请求时收到了一个SSL传输请求后重定向的

端口号
          6
acceptCount 指定当所有可以使用的处理请求的线程数都被使用时,可以放到处理

队列中的请求数,超过这个数的请求将不予处理
          7
connectionTimeout 指定超时的时间数(以毫秒为单位)
    Engine (
表示指定service中的请求处理机,接收和处理来自Connector的请求)
          1
defaultHost 指定缺省的处理请求的主机名,它至少与其中的一个host元素的name

属性值是一样的 

Context (表示一个web应用程序):
          1、docBase 应用程序的路径或者是WAR文件存放的路径
          2、path 表示此web应用程序的url的前缀,这样请求的url为
http://localhost:8080/path/****
          3、reloadable 这个属性非常重要,如果为true,则tomcat会自动检测应用程序的
/WEB-INF/lib 和/WEB-INF/classes目录的变化,自动装载新的应用程序,我们可
以在不重起tomcat的情况下改变应用程序
    host (表示一个虚拟主机):
          1、name 指定主机名
          2、appBase 应用程序基本目录,即存放应用程序的目录
          3、unpackWARs 如果为true,则tomcat会自动将WAR文件解压,否则不解压,直接
从WAR文件中运行应用程序
    Logger (表示日志,调试和错误信息):
          1、className 指定logger使用的类名,此类必须实现org.apache.catalina.Logger 接口
          2、prefix 指定log文件的前缀
          3、suffix 指定log文件的后缀
          4、timestamp 如果为true,则log文件名中要加入时间,如下
例:localhost_log.2001-10-04.txt
   Realm (表示存放用户名,密码及role的数据库):
          1、className 指定Realm使用的类名,此类必须实现org.apache.catalina.Realm接口
   Valve (功能与Logger差不多,其prefix和suffix属性解释和Logger 中的一样):
          1、className 指定Valve使用的类名,如用org.apache.catalina.valves.AccessLogValve
类可以记录应用程序的访问信息
    directory(指定log文件存放的位置):
    1、pattern 有两个值,common方式记录远程主机名或ip地址,用户名,日期,第一行请求的字符串,HTTP响应代码,发送的字节数。combined方式比common方式记录的值更多



五、web.xml配置简介:
1、默认(欢迎)文件的设置
在tomcat4\conf\web.xml中,<welcome-file-list>与IIS中的默认文件意思相同。
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>

2、报错文件的设置
<error-page>
<error-code>404</error-code>
<location>/notFileFound.jsp</location>
</error-page>
<error-page>
<exception-type>java.lang.NullPointerException</exception-type>
<location>/null.jsp</location>
</error-page>
如果某文件资源没有找到,服务器要报404错误,按上述配置则会调用\webapps\ROOT\notFileFound.jsp。
如果执行的某个JSP文件产生NullPointException ,则会调用\webapps\ROOT\null.jsp
 
3、会话超时的设置
设置session 的过期时间,单位是分钟;
<session-config>
<session-timeout>30</session-timeout>
</session-config>

4、过滤器的设置
<filter>
<filter-name>FilterSource</filter-name>
<filter-class>project4. FilterSource </filter-class>
</filter>
<filter-mapping>
<filter-name>FilterSource</filter-name>
<url-pattern>/WwwServlet</url-pattern>
(<url-pattern>/haha/*</url-pattern>)
</filter-mapping>
 
过滤:
1) 身份验证的过滤Authentication Filters
2) 日志和审核的过滤Logging and Auditing Filters
3) 图片转化的过滤Image conversion Filters
4) 数据压缩的过滤Data compression Filters
5) 加密过滤Encryption Filters
6) Tokenizing Filters
7) 资源访问事件触发的过滤Filters that trigger resource access events XSL/T 过滤XSL/T filters
9) 内容类型的过滤Mime-type chain Filter 注意监听器的顺序,如:先安全过滤,然后资源,然后内容类型等,这个顺序可以自己定。


六、管理
    1、用户配置
      在进行具体Tomcat管理之前,先给tomcat添加一个用户,使这个用户有权限来进行管理。
      打开conf目录下的tomcat-users.xml文件,在相应的位置添加下面一行:
    <user name="user" password="user" roles="standard,manager"/>
    然后重起tomcat,在浏览器中输入http://localhost:8080/manager/,会弹出对话框,输入上面的用户
名和密码即可。

    2、应用程序列表
      在浏览器中输入http://localhost:8080/manager/list,浏览器将会显示如下的信息:
    OK - Listed applications for virtual host localhost
    /ex:running:1
    /examples:running:1
    /webdav:running:0
    /tomcat-docs:running:0
    /manager:running:0
    /:running:0
     上面显示的信息分别为:应用程序的路径、当前状态、连接这个程序的session数
   3、重新装载应用程序
      在浏览器中输入 http://localhost:8080/manager/reload?path=/examples,浏览器显示如下:
    OK - Reloaded application at context path /examples   
这表示example应用程序装载成功,如果我们将server.xml的Context元素的reloadable属性设为true,则没必要利用这种方式重新装载应用程序,因为tomcat会自动装载。
4、显示session信息
    在浏览器中输入http://localhost:8080/manager/sessions?path=/examples,浏览器显示如下:
    OK - Session information for application at context path /examples Default maximum session inactive
interval 30 minutes
5、启动和关闭应用程序
   在浏览器中输入http://localhost:8080/manager/start?path=/examples
http://localhost:8080/manager/stop?path=/examples分别启动和关闭examples应用程序。
关于作者:
杨晓,计算机硕士,曾在NEC、Softbrain 担任java开发工作。
博客:http://blog.sina.com.cn/u/1237288325
posted @ 2007-07-21 13:40 和田雨 阅读(252) | 评论 (0)编辑 收藏

     摘要:   MySQL客户端显示汉字乱码的解决MySQL 服务端不加任何参数启动时,客户端可以默认正常显示汉字,如下面所示 代码: D:mysql5>.binmysqld-max-nt.exe --console 061106 20:35:21  InnoDB: Started; log sequence number 0 43655 061106 20:...  阅读全文
posted @ 2007-07-19 20:56 和田雨 阅读(5014) | 评论 (0)编辑 收藏

本文介绍如何利用Eclipse插件SQLExplorer在Eclipse中连接各种数据库进行操作使用。

  Eclipse是目前非常流行的开发平台,开放扩展的架构让很多程序员找到了自己个性化的工作环境。

安装

JDK:1.5.0 从http://java.sun.com上去下载安装
Eclipse:3.1.1 从http://www.eclipse.org 上去下载解压
SQLExplorer 从http://eclipsesql.sourceforge.net/

技巧

  Eclipse使用技巧之插件管理

  提示:新下载的插件PlugIn一定不要都放在原始的Eclipse目录下去,一大堆,累死你:(

  1. 前提是你已经安装好了Eclipse工具了,比如安装在E:\OpenSource\Eclipse\目录下,以下这个目录以%ECLIPSE_HOME%来进行表示;
  2. 此时默认的插件是在%ECLIPSE_HOME%\plugins目录中的;
  3. 在%ECLIPSE_HOME%下建立一个PlugInsNew的目录;

  比如:E:\OpenSource\Eclipse\PlugInsNew\

  1. 你下载了个新的插件,比如叫做:XYZ

  那么就在%ECLIPSE_HOME%\PlugInsNew\目录下建立XYZ目录,目录里面是eclipse目录,eclipse目录包含有features与plugins两个子目录;结构如下图所示:

  1. 把下载的新插件的文件放在以下相应目录中

    %ECLIPSE_HOME%\PlugInsNew\XYZ\eclipse\features
    %ECLIPSE_HOME%\PlugInsNew\ XYZ\eclipse\plugins

  2. 建立相关的.link的文件

    然后在%ECLIPSE_HOME%\links目录里建立一个XYZ.link的文件
    内容如是:path=E:/OpenSource/Eclipse/PlugInsNew/XYZ就一行这样的路径指示而已。
    这样,如果你下载了多个插件就可以如法炮制建立多个Link文件,想加载哪个插件就把哪个插件的Link文件放到%ECLIPSE_HOME%\links的目录中即可,使用与管理都很方便,建议千万不要放在默认的安装目录中;
    如果你的%ECLIPSE_HOME%与此不同,请修改XYZ.link文件里的路径

  3. 删除,关闭Eclipse

    删除%ECLIPSE_HOME%\links\XYZ.link文件即可
    删除%ECLIPSE_HOME%\PlugInsNew\XYZ整个目录及文件

  4. 重新启动Eclipse即可

使用

  SQLExplorer插件安装方法采用上一节的《Eclipse使用技巧之插件管理》
  重新启动Eclipse后,可以在菜单Window => Show View => Other => 可以看到多了SQLExplorer
展开SQLExplorer有七项内容,分别为:

  1. Aliases 别名,用来标识数据库连接串的
  2. Connection Info 连接信息,用来显示连接数据库时的相关信息,如,数据库产品名称、版本、JDBC驱动程序的名称、版本、用户名、连接串、是否自动提交等等。
  3. Connnections 显示活动的连接情况
  4. Database Structure View 显示数据库结构
  5. Drivers 配置驱动程序用
  6. SQL History 执行SQL的历史记录
  7. SQL Results 执行SQL的结果集

  下面会分别介绍这七项内容的具体使用:
如下图所示:

  菜单Window => Preferences => SQLExplorer
右边的项目中,可以设置预览表结果集时返回的行数、SQL集的返回行数、是否自动提交、还是关闭SQL编辑窗口后提交和写SQL时是否自动完成表和列名。

  菜单Window => Preferences => SQLExplorer => SQL Editor
右边的项目中,可以设置SQL编辑器里的字体属性、文本属性

  菜单Window => Show View => Other => SQLExplorer 把里面的七项内容全部显示出来,你也可以自己进行定制一下它

  开始进行配置驱动程序,打开Drivers

  默认加载成功插件后,只有JDBC ODBC Bridge是打勾可用的,其它都是打红X不可用的,想想就知道为什么了吧(^_^)

  没有想出来,接下来看看吧
右键JDBC ODBC Bridge,在弹出的菜单中点击Change the selected Driver菜单

  可以在弹出的修改驱动程序中看到如下信息,在Driver Class Name显示的是
sun.jdbc.odbc.JdbcOdbcDriver
因为从JDK1.2开始,在JDK里自动带有这个驱动程序了,所以显示为可用了:)

  接下来我们开始配置MySQL和Oracle的驱动程序:
右键MMMySQL Driver,在弹出的菜单中点击Change the selected Driver菜单

  在弹出的修改驱动程序中看到,在Driver Class Name显示的是org.gjt.mm.mysql.Driver
点击Extra Class Path => Add => 选择你的MySQL所在的路径,加入它

  加入驱动程序如下所示:

  单击List Drivers按钮,在Driver Class Name的右下框中可以看到三个驱动类名,选择你需要的一个,OK之即可。

  可以看到这下MMMySQL Driver也为打勾可以使用的状态了。

  以同样的方法配置Oracle Thin Driver驱动程序

  默认驱动名称,添加驱动程序:

  可以看到两个驱动程序的名称

  配置好驱动程序后,可以开始创建连接了,切换到Aliases别名视图
点击创建图标,如下:

  选择MMMySQL Driver

  填入别名的名称、JDBC连接串URL、用户名、密码

  确定后,在Aliases别名视图里出现刚建立的连接

  右键刚建立的数据库连接别名,在弹出的菜单选择Open…打开之。

  会弹出一个连接的确认框,你可以更改用户名与密码,也可以设置是否自动提交。

  确定后,会自动切换到Database Structure View视图上,MySQL数据库,则可以看到Database

  再打开它,可以看到数据库名,Table表及表的个数与名称了。

  选中其中的表userpwd,这个表是自己建立的,可以看到以下显示列、索引、主键、外键、结果集预览、行数共六个信息内容。

  以下显示表的列信息

  以下显示索引信息

  以下显示主键信息

    以下显示外键信息

  以下显示结果集预览的内容

  以下显示的是记录行数信息

  切换到Connection Info连接信息的视图,可以看到连接数据库时的相关信息,
如,数据库产品名称MySQL、版本4.1.0a-nt
JDBC驱动程序的名称MySQL-AB JDBC Driver、版本mysql-connector-java-3.0.16-ga
用户名test@localhost、连接串jdbc:mysql://localhost:3306/test、自动提交模式为是等等。

  显示默认的事务状态

  切换到Connnections视图,显示当前数据库活动的连接情况,有一个活动的连接。

  右上角,可以打开Open New Connection图标来打开一个新的连接,比如连接到相同的数据库,但是却是不同的用户

  右上角,单击New SQL Editor图标,创建一个新的SQL编辑器,来写你的SQL语句

  在打开的SQL编辑器中,你可以开始写SQL语句了,如下:


按Ctrl + F9 或者点击Exceute SQL图标,执行所输入的语句

这样可以在SQL Results视图中看到如下执行的结果信息

    切换到SQL History 执行SQL的历史记录的视图,可以看到你执行过的语句列表

  再写一个不同的语句,演示结果集与SQL历史记录


执行后,记录是显示在最后的执行结果是在后面,依次递增的,即1、2、3、最后一个是你最后执行的结果信息内容显示的地方,不要弄错了。

  这样在SQL History里就再增加上了一条执行语句的记录了

  同样增加一个Oracle的连接测试

  Oracle数据库,在Database Structure View视图里显示有Database、Monitor、Security、Instance四项内容


在SQL编辑器中,选择Oracle的链接,再输入查询语句测试一下

  可以在结果集里得到如果内容,在第4个标签栏里了


再更改一下SQL语句

  得到如下内容,在第4个标签栏里了

  SQL历史记录里可以看到执行过的四条信息了

  这个插件的使用应当不是很容易的,其它的应用技巧与方法继续中。

posted @ 2007-07-19 18:24 和田雨 阅读(2255) | 评论 (0)编辑 收藏

方法一
使用phpmyadmin,这是最简单的了,修改mysql库的user表,不过别忘了使用PASSWORD函数。

方法二
使用mysqladmin,这是前面声明的一个特例。
mysqladmin -u root -p password mypasswd
输入这个命令后,需要输入root的原密码,然后root的密码将改为mypasswd。
把命令里的root改为你的用户名,你就可以改你自己的密码了。
当然如果你的mysqladmin连接不上mysql server,或者你没有办法执行mysqladmin,那么这种方法就是无效的,而且mysqladmin无法把密码清空。

下面的方法都在mysql提示符下使用,且必须有mysql的root权限:
方法三
mysql> Insert INTO mysql.user (Host,User,Password)
VALUES('%','jeffrey',PASSWORD('biscuit'));
mysql> FLUSH PRIVILEGES
确切地说这是在增加一个用户,用户名为jeffrey,密码为biscuit。
在《mysql中文参考手册》里有这个例子,所以我也就写出来了。
注意要使用PASSWORD函数,然后还要使用FLUSH PRIVILEGES。

方法四
和方法三一样,只是使用了REPLACE语句
mysql> REPLACE INTO mysql.user (Host,User,Password)
VALUES('%','jeffrey',PASSWORD('biscuit'));
mysql> FLUSH PRIVILEGES

方法五
使用SET PASSWORD语句,
mysql> SET PASSWORD FOR jeffrey@"%" = PASSWORD('biscuit');
拟也必须使用PASSWORD()函数,但是不需要使用FLUSH PRIVILEGES。


方法六
使用GRANT ... IDENTIFIED BY语句
mysql> GRANT USAGE ON *.* TO jeffrey@"%" IDENTIFIED BY 'biscuit';
这里PASSWORD()函数是不必要的,也不需要使用FLUSH PRIVILEGES。


注意: PASSWORD() [不是]以在Unix口令加密的同样方法施行口令加密。
MySQL 忘记口令的解决办法
如果 MySQL 正在运行,首先杀之: killall -TERM mysqld。
启动 MySQL :bin/safe_mysqld --skip-grant-tables &
就可以不需要密码就进入 MySQL 了。
然后就是
>use mysql
>update user set password=password("new_pass") where user="root";
>flush privileges;
重新杀 MySQL ,用正常方法启动 MySQL 。


mysql密码清空
Windows:
1.用系统管理员登陆系统。
2.停止MySQL的服务。
3.进入命令窗口,然后进入MySQL的安装目录,比如我的安装目录是c:\mysql,进入C:\mysql\bin
4.跳过权限检查启动MySQL,
c:\mysql\bin>mysqld-nt --skip-grant-tables
5.重新打开一个窗口,进入c:\mysql\bin目录,设置root的新密码
c:\mysql\bin>mysqladmin -u root flush-privileges password "newpassword"
c:\mysql\bin>mysqladmin -u root -p shutdown
将newpassword替换为你要用的root的密码,第二个命令会提示你输入新密码,重复第一个命令输入的密码。
6.停止MySQL Server,用正常模式启动Mysql
7.你可以用新的密码链接到Mysql了。

Unix&Linux:
1.用root或者运行mysqld的用户登录系统;
2.利用kill命令结束掉mysqld的进程;
3.使用--skip-grant-tables参数启动MySQL Server
shell>mysqld_safe --skip-grant-tables &
4.为root@localhost设置新密码
shell>mysqladmin -u root flush-privileges password "newpassword"
5.重启MySQL Server


mysql修改密码
  mysql修改,可在mysql命令行执行如下:
  mysql -u root mysql
  mysql> Update user SET password=PASSWORD("new password") Where user='name';
  mysql> FLUSH PRIVILEGES;
  mysql> QUIT

 

教你如何将MySQL数据库的密码恢复

因为MySQL密码存储于数据库mysql中的user表中,所以只需要将我windows 2003下的MySQL中的user表拷贝过来覆盖掉就行了。

在c:\mysql\data\mysql\(linux 则一般在/var/lib/mysql/mysql/)目录下有三个user表相关文件user.frm、user.MYD、user.MYI

user.frm //user表样式文件

user.MYD //user表数据文件

user.MYI //user表索引文件

为保险起见,三个都拷贝过来,不过其实如果之前在要恢复的那个MySQL上没有更改过表结构的话,只要拷贝user.MYD就行了

然后


#. /etc/rc.d/init.d/mysql stop
#. /etc/rc.d/init.d/mysql start
#mysql -u root -p XXXXXX


好了,可以用windows 2003下mysql密码登陆了


mysql>use mysql
mysql>update user set Password=PASSWORD('xxxxxx') where User='root';


这时候会出错,提示user表只有读权限

我分析了一下原因,只这样的,因为user.*文件的权限分配是windows 2003下的,在windows 2003下我ls -l一看权限是666

在linux下我一看,拷过来后权限变成了600(其实正常情况下600就行了,只不过这里的文件属主不是mysql,拷过来后的属主变为了 root,所以会出现权限不够,这时候如果你改成权限666则可以了,当然这样不好,没有解决问题的实质),在 /var/lib/mysql/mysql/下ls -l看了一下


#chown -R mysql:mysql user.*
#chmod 600 user.*


//OK,DONE

重起一下MYSQL

重新连接


mysql>use mysql
mysql>update user set Password=PASSWORD('xxxxxx') where User='root';
mysql>FLUSH PRIVILEGES;


有一点值得注意:如果你windows 下mysql如果是默认配置的话,注意要还要执行


mysql>delete from user where User='';
mysql>delete from user where Host='%';
mysql>FLUSH PRIVILEGES;


好了,到这里恢复密码过程就完成了

这个方法么就是有点局限性,你必须也具备另外的user表文件

其他还有几种方法

其它方法一(这个是网上流传较广的方法,mysql中文参考手册上的)

1. 向mysqld server 发送kill命令关掉mysqld server(不是 kill -9),存放进程ID的文件通常在MYSQL的数据库所在的目录中。


killall -TERM mysqld


你必须是UNIX的root用户或者是你所运行的SERVER上的同等用户,才能执行这个操作。

2. 使用`--skip-grant-tables' 参数来启动 mysqld。 LINUX下:


/usr/bin/safe_mysqld --skip-grant-tables , windows下c:\mysql\bin\mysqld --skip-grant-tables


3. 然后无密码登录到mysqld server ,


>use mysql
>update user set password=password("new_pass") where user="root";
>flush privileges;


。你也可以这样做:


`
mysqladmin -h hostname -u user password 'new password''


4. 载入权限表:


`
mysqladmin -h hostname flush-privileges'


或者使用 SQL 命令


`FLUSH PRIVILEGES'


5.

killall -TERM mysqld


6.用新密码登陆

其它方法二

直接用十六进制编辑器编辑user.MYD文件

不过这个里面我要说明一点,我这里编辑的时候发现个问题,加密的密码串有些是连续存储的,有些的最后两位被切开了,后两位存储在后面其他地方.这一 点我还没想明白.还有注意一点就是编辑的是加密过的密码串,也就是说你还是需要另外有user表文件。这种方法和我最上面介绍的方法的区别在于,这种方法 直接编辑linux下的user表文件,就不需要重新改文件属主和权限了 

修正一下:我在Windows下的实际操作如下

1.关闭正在运行的MySQL。

2.打开DOS窗口,转到mysql\bin目录。

3.输入


mysqld-nt --skip-grant-tables


回车。如果没有出现提示信息,那就对了。

4.再开一个DOS窗口(因为刚才那个DOS窗口已经不能动了),转到mysql\bin目录。

5.输入mysql回车,如果成功,将出现MySQL提示符 >

6. 连接权限数据库


>use mysql;
(>是本来就有的提示符,别忘了最后的分号)


6.改密码:


> update user set password=password("123456") where user="root"; (别忘了最后的分号)


7.刷新权限(必须的步骤)


>flush privileges;


8.退出


> \q


9.注销系统,再进入,开MySQL,使用用户名root和刚才设置的新密码123456登陆。

据说可以用直接修改user表文件的方法:

关闭MySQL,Windows下打开Mysql\data\mysql,有三个文件user.frm,user.MYD,user.MYI找个知道密码的MySQL,替换相应的这三个文件,如果user表结构没改过,一般也没人去改,替换user.MYD就可以了。

也可以直接编辑user.MYD,找个十六进制编辑器,UltraEdit就有这个功能。关闭MySQL,打开user.MYD。将用户名root 后面的八个字符改为565491d704013245,新密码就是123456。或者将它们对应的十六进制数字,(左边那里,一个字符对应两个数字),改 为 00 02 02 02 02 02 02 02,这就是空密码,在编辑器右边看到的都是星号*,看起来很象小数点。重开MySQL,输入root和你的新密码。

posted @ 2007-07-19 15:43 和田雨 阅读(232) | 评论 (0)编辑 收藏

MySQL是一个真正的多用户、多线程SQL数据库服务器。MySQL是以一个客户机/服务器结构的实现,它由一个服务器守护程序mysqld和很多不同的客户程序和库组成。由于其源码的开放性及稳定性,且与网站流行编徎语言PHP的完美结合,现在很多站点都利用其当作后端数据库,使其获得了广泛应用。处于安全方面的考虑,需要为每一用户赋于对不同数据库的访问限制,以满足不同用户的要求。下面就分别讨论,供大家参考。

一、MySQL修改密码方法总结
首先要说明一点的是:一般情况下,修改MySQL密码是需要有mysql里的root权限的,这样一般用户是无法更改密码的,除非请求管理员帮助修改。

方法一

使用phpMyAdmin (图形化管理MySql数据库的工具),这是最简单的,直接用SQL语句修改mysql数据库库的user表,不过别忘了使用PASSWORD函数,插入用户用Insert命令,修改用户用Update命令,删除用Delete命令。在本节后面有数据表user字段的详细介绍。

方法二

使用mysqladmin。输入

mysqladmin -u root -p oldpassword newpasswd

执行这个命令后,需要输入root的原密码,这样root的密码将改为newpasswd。同样,把命令里的root改为你的用户名,你就可以改你自己的密码了。 当然如果你的mysqladmin连接不上mysql server,或者你没有办法执行mysqladmin,那么这种方法就是无效的,而且mysqladmin无法把密码清空。

下面的方法都在mysql提示符下使用,且必须有mysql的root权限:

方法三

mysql> INSERT INTO mysql.user (Host,User,Password) VALUES ('%','system', PASSWORD('manager'));
mysql> FLUSH PRIVILEGES

确切地说这是在增加一个用户,用户名为system,密码为manager。注意要使用PASSWORD函数,然后还要使用FLUSH PRIVILEGES来执行确认。

方法四

和方法三一样,只是使用了REPLACE语句

mysql> REPLACE INTO mysql.user (Host,User,Password)
VALUES('%','system',PASSWORD('manager'));
mysql> FLUSH PRIVILEGES

方法五

使用SET PASSWORD语句

mysql> SET PASSWORD FOR system@"%" = PASSWORD('manager');

你也必须使用PASSWORD()函数,但是不需要使用FLUSH PRIVILEGES来执行确认。

方法六

使用GRANT ... IDENTIFIED BY语句,来进行授权。

mysql> GRANT USAGE ON *.* TO system@"%" IDENTIFIED BY 'manager';

这里PASSWORD()函数是不必要的,也不需要使用FLUSH PRIVILEGES来执行确认。

注:PASSWORD()函数作用是为口令字加密,在程序中MySql自动解释。

二、MySql中访问限制的设置方法
我们采用两种方法来设置用户。

进入到Mysql执行目录下(通常是c:\mysql\bin)。输入mysqld-shareware.exe,输入mysql --user=root mysql ,不然不能添加新用户。进入到mysql>提示符下进行操作。

假设我们要建立一个超级用户,用户名为system,用户口令为manager。

方法一

用Grant 命令授权,输入的代码如下:

mysql>GRANT ALL PRIVILEGES ON *.* TO system@localhost IDENTIFIED BY 'manager' WITH GRANT OPTION;

应显示:Query OK, 0 rows affected (0.38 sec)

方法二

对用户的每一项权限进行设置:

mysql>INSERT INTO user VALUES('localhost','system',PASSWORD('manager'), 'Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y','Y');

对于3.22.34版本的MySQL,这里共14个"Y",其相应的权限如下(按字段顺序排列):
 

权限 表列名称 相应解释 使用范围 
select Select_priv 只有在真正从一个表中检索时才需要select权限 表 
insert Insert_priv 允许您把新行插入到一个存在的表中 表 
update Update_priv 允许你用新值更新现存表中行的列 表 
delete Delete_priv 允许你删除满足条件的行 表 
create Create_priv 允许你创建新的数据库和表 数据库、表或索引 
drop Drop_priv 抛弃(删除)现存的数据库和表 数据库或表 
reload Reload_priv 允许您告诉服务器再读入授权表 服务器管理 
shutdown Shutdown_priv 可能被滥用(通过终止服务器拒绝为其他用户服务) 服务器管理 
process Process_priv 允许您察看当前执行的查询的普通文本,包括设定或改变口令查询 服务器管理 
file File_priv 权限可以被滥用在服务器上读取任何可读的文件到数据库表 服务器上的文件存取 
grant Grant_priv 允许你把你自己拥有的那些权限授给其他的用户 数据库或表 
references References_priv 允许你打开和关闭记录文件 数据库或表 
index Index_priv 允许你创建或抛弃(删除)索引 表 
alter Alter_priv 允许您改变表格,可以用于通过重新命名表来推翻权限系统 表 

如果创建用户时只有select、insert、update和delete权限,则允许用户只能在一个数据库现有的表上实施操作.

下面就可以创建我们要用到的数据库了,我们直接输入. 例如:我们要创建数据库名为XinXiKu,可用如下代码:

mysql>create database XinXiKu;

应显示:Query OK, 1 row affected (0.00 sec)
posted @ 2007-07-17 17:35 和田雨 阅读(465) | 评论 (0)编辑 收藏

一、在linux中
如果 MySQL 正在运行,首先杀之: killall -TERM mysqld
启动 MySQL :/usr/bin/safe_mysqld --skip-grant-tables
就可以不需要密码就进入 MySQL 了。 
然后就是 
>use mysql 
>update user set password=password("new_pass") where user="root"; 
>flush privileges; 
重新杀 MySQL ,用正常方法启动 MySQL 。 
二、Windows: 
1.用系统管理员登陆系统。 
2.停止MySQL的服务。 
3.进入命令窗口,然后进入MySQL的安装目录,比如我的安装目录是c:\mysql,进入C:\mysql\bin 
4.跳过权限检查启动MySQL, 
c:\mysql\bin>;mysqld-nt --skip-grant-tables 
5.重新打开一个窗口,进入c:\mysql\bin目录,设置root的新密码 
c:\mysql\bin>;mysqladmin -u root flush-privileges password "newpassword" 
c:\mysql\bin>;mysqladmin -u root -p shutdown 
将newpassword替换为你要用的root的密码,第二个命令会提示你输入新密码,重复第一个命令输入的密码。 
6.停止MySQL Server,用正常模式启动Mysql 
7.你可以用新的密码链接到Mysql了。 
这种事情很少会碰到,不过如果碰到的话,不妨试试。
posted @ 2007-07-17 17:32 和田雨 阅读(1801) | 评论 (0)编辑 收藏

Introduction to XML Schema
[XML Schema
介绍]

翻译:linqingfeng
英语原文: http://www.w3schools.com/schema/default.asp


XML Schema is an XML based alternative to DTD.
[XML Schema
是一种XML文件类型定义的基本方法]

An XML schema describes the structure of an XML document.
[
每个XML Schema描述XML文档的结构]

The XML Schema language is also referred to as XML Schema Definition (XSD).
[XML Schema
也称为XML模式定义(XSD)]


What You Should Already Know
[
在学习之前你应该知道什么]

Before you study the XML Schema Language, you should have a basic understanding of XML and XML Namespaces. It will also help to have some basic understanding of DTD.
[
在你开始学习XML Schema语言之前,你应该对XMLXML的命名空间有基本的了解。那样会帮助你了解DTDDocument Type Definition文件类型定义)].


What is an XML Schema?
[XML Schema
是什么?]

The purpose of an XML Schema is to define the legal building blocks of an XML document, just like a DTD.
[XML Schema
目标是定义合法的XML文档,就像DTD那样]

An XML Schema:
[
任一个XML Schema]

  • defines elements that can appear in a document
    [
    定义在文档中的元素]
  • defines attributes that can appear in a document
    [
    定义在文档中的属性]
  • defines which elements are child elements
    [
    定义哪些元素为子元素]
  • defines the order of child elements
    [
    定义子元素的阶]
  • defines the number of child elements
    [
    定义子元素的数]
  • defines whether an element is empty or can include text
    [
    定义元素是否为空]
  • defines data types for elements and attributes
    [
    定义元素和属性的数据类型]
  • defines default and fixed values for elements and attributes
    [
    定义元素和属性的默认值和固定值]

XML Schemas are the Successors of DTDs
[XML Schemas
DTD的替代者]

We think that very soon XML Schemas will be used in most Web applications as a replacement for DTDs. Here are some reasons:
[
我们认为很快XML Schemas就会替代DTD而大量应用于Web应用。原因有如下几个:]

  • XML Schemas are extensible to future additions
    [XML Schemas
    易于未来的扩展]
  • XML Schemas are richer and more useful than DTDs
    [XML Schemas
    DTD更有用处]
  • XML Schemas are written in XML
    [XML Schemas
    本身就是XML]
  • XML Schemas support data types
    [XML Schemas
    支持数据类型]
  • XML Schemas support namespaces
    [XML Schemas
    支持命名空间]

XML Schema is a W3C Recommendation
[XML Schemas
W3C推荐使用的]

XML Schema was originally proposed by Microsoft, but became an official W3C recommendation in May 2001.
[XML Schemas
最初由微软提出,并在2001年五月成为W3C的官方指定标准]

The specification is now stable and has been reviewed by the W3C Membership.
[
其规格文档现已稳定并加入到W3C标准中]

XML Schemas - Why?
[
为什么选用XML Schemas]

 


There are a number of reasons why XML Schema is better than DTD.
[XML Schemas
有趣多方面优于DTD]


XML Schema has Support for Data Types
[XML Schemas
支持数据类型]

One of the greatest strengths of XML Schemas is the support for data types.
[XML Schemas
其中最好的方面之一就是支持数据类型]

With the support for data types:
[
对数据类型提供的支持有:]

  • It is easier to describe permissible document content
    [
    易于描述文档内容是否允许]
  • It is easier to validate the correctness of data
    [
    易于验证数据的正确性]
  • It is easier to work with data from a database
    [
    易于复合数据库操作数据]
  • It is easier to define data facets (restrictions on data)
    [
    易于定义数据约束]
  • It is easier to define data patterns (data formats)
    [
    易于格式化数据]
  • It is easier to convert data between different data types
    [
    易于数据在不同的数据类型中转换]

XML Schemas use XML Syntax
[XML Schemas
是用XML的语法]

Another great strength about XML Schemas is that they are written in XML.
[
另一个最大的优点是XML Schemas是用XML书写的]

Because XML Schemas are written in XML:
[XML Schemas
XML书写的好处有:]

  • You don't have to learn another language
    [
    不用学习另一种新的语言]
  • You can use your XML editor to edit your Schema files
    [
    能在XML的编辑器中编写XML Schemas文件]
  • You can use your XML parser to parse your Schema files
    [
    能用XML解释器去解释XML Schemas文件]
  • You can manipulate your Schema with the XML DOM
    [
    能用XML DOM(Document Object Model,文档物件模型)操作XML Schemas文件]
  • You can transform your Schema with XSLT
    [
    能用XSLT来转换XML Schemas文件]

XML Schemas Secure Data Communication
[XML Schemas
数据通信更安全]

When data is sent from a sender to a receiver it is essential that both parts have the same "expectations" about the content.
[
当数据从发送方传递给接受方时, XML Schemas会让显示的内容达到你的期望”]

With XML Schemas, the sender can describe the data in a way that the receiver will understand.
[XML Schemas
文件能让发送方的数据被接受方所解释]

A date like this: "03-11-2004" will, in some countries, be interpreted as 3. November and in other countries as 11. March, but an XML element with a data type like this:
[
例如一个日期的表述” 03-11-2004”,在某些国家会被解释为113,而在另一些国家却被解释成311],而相同的数据在XML上表述成:

<date type="date">2004-03-11</date>

ensures a mutual understanding of the content because the XML data type date requires the format YYYY-MM-DD.
[
由于XML数据类型定义了他的格式是YYYY-MM-DD,从而使能正确的表述其内容的含义]


XML Schemas are Extensible
[XML Schemas
是可扩展的]

XML Schemas are extensible, just like XML, because they are written in XML.
[XML Schemas
是可扩展的,就像普通的XML一样,因为他本身就是一个XML]

With an extensible Schema definition you can:
[
可扩展性的定义带来的好处有:]

  • Reuse your Schema in other Schemas
    [
    可从用你的规则与其他规则]
  • Create your own data types derived from standard types
    [
    从其他的标准的数据类型中创建自定义的数据类型]
  • Reference multiple schemas from the same document
    [
    同一个文档可以引用多个规则]

Well-Formed is not Enough
[
良好格式还并不足够]

A well-formed XML document is a document that conforms to the XML syntax rules:
[
一个具有良好格式的XML文档是完全符合XML的语法规则的:]

  • must begin with the XML declaration
    [
    必须由XML声明开始]
  • must have one unique root element
    [
    必须有且仅有一个根节点]
  • all start tags must match end-tags
    [
    所有标签都必须有相对的结束标签]
  • XML tags are case sensitive
    [XML
    的标签是区分大小写的]
  • all elements must be closed
    [
    所有的元素都必须是闭合的]
  • all elements must be properly nested
    [
    所有元素都必须合理的嵌套(元素不可以交叉)]
  • all attribute values must be quoted
    [
    所有的属性都必须被引用]
  • XML entities must be used for special characters
    [
    所有的XML实体都必须用特殊的字符来做]

Even if documents are Well-Formed they can still contain errors, and those errors can have serious consequences. Think of this situation: you order 5 gross of laser printers, instead of 5 laser printers. With XML Schemas, most of these errors can be caught by your validating software.
[
即使文档完全符合格式也会包含错误,而且有些错误会有严重的逻辑问题.想一想这样的情况:你订购五台激光打印机是为了来替代五台激光打印机.配合XML Schemas,大部分的错误都可以由你的软件校验出来的.]

XSD How To
[XSD
如何]


XML documents can have a reference to a DTD or an XML Schema.
[XML
文档能和一个DTD或者一个XML Schema相关]


A Simple XML Document
[
一个简单的XML文档]

Look at this simple XML document called "note.xml":
[
看看这个名为"note.xml"XML文档]

<?xml version="1.0"?>

<note>

<to>Tove</to>

<from>Jani</from>

<heading>Reminder</heading>

<body>Don't forget me this weekend!</body>

</note>

 


A Simple DTD
[
一个简单的DTD]

This is a simple DTD file called "note.dtd" that defines the elements of the XML document above ("note.xml"):
[
这个简单的DTD"note.dtd"定义了"note.xml"中的元素:]

<!ELEMENT note (to, from, heading, body)>

<!ELEMENT to (#PCDATA)>

<!ELEMENT from (#PCDATA)>

<!ELEMENT heading (#PCDATA)>

<!ELEMENT body (#PCDATA)>

Line 1 defines the note element to have four elements: "to, from, heading, body". Line 2-5 defines the to element to be of the type "#PCDATA", the from element to be of the type "#PCDATA", and so on...
[
第一行定义了note元素有四个子元素: "to, from, heading, body".而第二行到第五行分别定义了tofrom等元素为"#PCDATA"]


A Simple XML Schema
[
一个简单的XML Schema]

This is a simple XML Schema file called "note.xsd" that defines the elements of the XML document above ("note.xml"):
[
这个简单的XML Schema文件"note.xsd"同样定义了"note.xml"中的元素:]

<?xml version="1.0"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

targetNamespace="http://www.w3schools.com"

xmlns="http://www.w3schools.com"

elementFormDefault="qualified">

<xs:element name="note">

    <xs:complexType>

      <xs:sequence>

        <xs:element name="to" type="xs:string"/>

        <xs:element name="from" type="xs:string"/>

        <xs:element name="heading" type="xs:string"/>

        <xs:element name="body" type="xs:string"/>

      </xs:sequence>

    </xs:complexType>

</xs:element>

</xs:schema>

The note element is said to be of a complex type because it contains other elements. The other elements (to, from, heading, body) are said to be simple types because they do not contain other elements. You will learn more about simple and complex types in the following chapters.
[
那个note元素由于包含有其他的子元素而被定义为complex type(复合类型). 其他包含在它里面的元素(to, from, heading, body)都被定义为simple types(简单类型). 你将会在以后的章节里面学到更多关于复合类型和简单类型的知识.]


A Reference to a DTD
[DTD
的引用]

This XML document has a reference to a DTD:
[
这个XML文档引用自一个DTD:]

<?xml version="1.0"?>

<!DOCTYPE note SYSTEM

"http://www.w3schools.com/dtd/note.dtd">

<note>

<to>Tove</to>

<from>Jani</from>

<heading>Reminder</heading>

<body>Don't forget me this weekend!</body>

</note>

 


A Reference to an XML Schema
[XML Schema
的引用]

This XML document has a reference to an XML Schema:
[
这个XML文档引用自一个XML Schema文档:]

<?xml version="1.0"?>

<note

xmlns="http://www.w3schools.com"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.w3schools.com note.xsd">

 

<to>Tove</to>

<from>Jani</from>

<heading>Reminder</heading>

<body>Don't forget me this weekend!</body>

</note>

 

XSD - The <schema> Element
[XSD
中的<schema>元素]


The <schema> element is the root element of every XML Schema!
[<schema>
元素是每一个XML Schema文件的根元素!]


The <schema> Element
[<schema>
元素]

The <schema> element is the root element of every XML Schema:
[[<schema>
元素是每一个XML Schema文件的根元素:]

<?xml version="1.0"?>

<xs:schema>

...

...

</xs:schema>

The <schema> element may contain some attributes. A schema declaration often looks something like this:
[[<schema>
元素包含一些属性.一般声明如下:]

<?xml version="1.0"?>

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"

targetNamespace="http://www.w3schools.com"

xmlns="http://www.w3schools.com"

elementFormDefault="qualified">

...

...

</xs:schema>

The following fragment:
[
以下片段:]

xmlns:xs="http://www.w3.org/2001/XMLSchema"

indicates that the elements and data types used in the schema (schema, element, complexType, sequence, string, boolean, etc.) come from the "http://www.w3.org/2001/XMLSchema" namespace. It also specifies that the elements and data types that come from the "http://www.w3.org/2001/XMLSchema" namespace should be prefixed with xs:
[
说明元素和数据类型的使用模式(schema, element, complexType, sequence, string, Boolean等等)来自于"http://www.w3.org/2001/XMLSchema"命名空间.同时也可用xs:作为前缀]

This fragment:
[
如下片段:]

targetNamespace="http://www.w3schools.com"

indicates that the elements defined by this schema (note, to, from, heading, body.) come from the "http://www.w3schools.com" namespace.
[
说明元素(note, to, from, heading, body.)的使用模式来自于命名空间"http://www.w3schools.com"]

This fragment:
[
如下片段:]

xmlns="http://www.w3schools.com"

indicates that the default namespace is "http://www.w3schools.com".
[
说明默认的命名空间是"http://www.w3schools.com".]

This fragment:
[
如下片段:]

elementFormDefault="qualified"

indicates that any elements used by the XML instance document which were declared in this schema must be namespace qualified.
[
说明所有全局元素的子元素将被以缺省方式放到目标命名空间,连同全局元素或者类型一起]


Referencing a Schema in an XML Document
[
XML文件中引用Schema]

This XML document has a reference to an XML Schema:
[
如下XML文档有一个XML Schema的引用:]

<?xml version="1.0"?>

<note xmlns="http://www.w3schools.com"

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

xsi:schemaLocation="http://www.w3schools.com note.xsd">

<to>Tove</to>

<from>Jani</from>

<heading>Reminder</heading>

<body>Don't forget me this weekend!</body>

</note>

The following fragment:
[
下面片段:]

xmlns="http://www.w3schools.com"

specifies the default namespace declaration. This declaration tells the schema-validator that all the elements used in this XML document are declared in the "http://www.w3schools.com" namespace.
[
声明默认的命名空间.此声明告知模式校验器让所有XML元素都在命名空间"http://www.w3schools.com"]

Once you have the XML Schema Instance namespace available:
[
有时你可以用到XML模式实例命名空间:]

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

you can use the schemaLocation attribute. This attribute has two values. The first value is the namespace to use. The second value is the location of the XML schema to use for that namespace:
[
你能用上schemaLocation这个属性.这个属性有两个含义.第一个含义是这个命名空间被使用.第二个含义是定位XML schema用到的命名空间:]

xsi:schemaLocation="http://www.w3schools.com note.xsd"



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=705007

posted @ 2007-07-14 00:38 和田雨 阅读(392) | 评论 (0)编辑 收藏

RSS

内容目录

RSS Introduction [RSS介绍]
An introduction to what RSS is and how it can be used.
介绍什么是RSS,它的应用。

RSS History [RSS历史]
How RSS started and how it has been developed and changed
RSS是怎么诞生的,它是怎样发展和变化的.

RSS Syntax [RSS语法]
A brief introduction how to make RSS.
制作RSS的简单介绍

RSS Channel [RSS频道]
How to make and what can be included in the <channel> element.
怎么完成<channel>元素里的内容

RSS Item [RSS内容]- How to make and what can be included in the <item> element.
怎样的<item>元素才算合理的

RSS Publishing [RSS 发布]- How to publish your RSS.
怎样发布的你的RSS

RSS Readers [RSS阅读器]- How to read other site's RSS.
怎样阅读其他网站的RSS


RSS介绍
翻译:Linyupark / 2006-04-24

RSS was designed to share data like headlines, links and short descriptions of these.
RSS 将数据分成像标题,连接和简单描述的形式。(浓缩过的信息)

RSS allows fast browsing for news and updates.
RSS能快速浏览新闻和更新内容。


What You Should Already Know
哪些是你应该已经知道的

Before you continue you should have a basic understanding of the following:
在继续学习前你应该有下面这些知识点的基础:

  • HTML / XHTML
  • XML / XML 命名空间

What is RSS?
什么是RSS?

  • RSS stands for Really Simple Syndication
    RSS就是 Really Simple Syndication (真正的简单聚合)
  • RSS allows you to syndicate your site content
    RSS可以聚合你网站上的内容
  • RSS is a selected list of defined items on a site
    RSS是一站点上被选中的内容清单
  • RSS defines an easy way to share and view headlines and content
    RSS是一种更便捷的方法去查看标题和内容
  • RSS files can be automatically updated
    RSS文件可以自动的更新
  • RSS allows personalized views for different sites
    RSS允许更具人性化(个性化)的查看不同的站点
  • RSS is written in XML
    RSS是用XML来书写的

Why Use RSS?
为什么使用RSS?

RSS was designed to show selected data.
RSS 能展示被选择的数据(针对性)

Without RSS, users will have to check your site daily to check for new updates. This may be too time-consuming for many users. With an RSS feed (RSS is often called a News Feed or RSS Feed) they can check your site faster using an RSS aggregator (a site or program that gathers and sorts out several RSS feeds).
没有RSS的话,用户将必须每天到你的站上看看有没更新内容。这对于一些用户来说是很浪费时间的。通过一个RSS feed他们可以使用RSS阅读器更快的查看你的站是否有更新。

Since RSS data is small and fast-loading, it can easily be used with services like cell phones or PDA's.
因为RSS数据很小而且加载速度很快,它可以容易的在手机或PDA服务上使用。

Web-rings with similar information can easily share data on their sites to make them better and more useful.
Wb-rings通过相似的信息可以方便的共享他们站点的信息。


Who Should Use RSS?
哪些人应该使用RSS?

Webmasters who seldom update their sites do not need RSS.
站长如不是经常更新站带内信息的话可以不使用RSS

RSS can be useful for Web sites that are updated frequently, like:
RSS对于那些经常更新的站点来说是和有帮助的,像:

  • News sites - Lists news with title, date and descriptions
    新闻站
  • Site changes - Lists changed pages or new pages
    站点改动信息版块
  • Companies - Lists news and new products
    公司站 - 新闻 和 新产品
  • Calendars - Lists upcoming events and important days
    日历

The Future of RSS
展望RSS

RSS is going to be everywhere.
RSS将遍及每个角落

Thousands of sites use RSS and more people understand its usefulness every day.
数千站使用着RSS并且每天有越来越多的人明白了它的有用之处。

By using RSS, information on the Web becomes easier to find and web developers can spread their information more easily to special interest groups.
有了RSS,在WEB上的信息变的容易找到并且WEB开发者能够容易的将信息传播给兴趣小组。

Hopefully, future RSS versions will include additional fields that will make it even easier to categorize and share.
未来的RSS版本可能包含额外的领域来使分类和分享信息来的更容易。


RSS的历史
翻译:Linyupark / 2006-04-24

RSS has been released in many different versions.
RSS发布过一些不同的版本


The History of RSS
RSS的历史

Here is the history of RSS according to Dave Winer (key developer of most of the RSS formats):
依据Dave Winer(RSS开发的关键人物)的描述,RSS有这样的历史:

  • Dec. 1997 - Dave Winer developed scriptingNews
    1997年12月 Dave Winer 开发了新闻脚本
     
  • Mar. 1999 - RSS 0.90 was developed by Netscape, which supported the scriptingNews format. This was simply XML with a RDF Header
     1999年三月 - RSS 0.90 由网景公司开发,支持格式化新闻脚本.
  • Jun. 1999 - scriptingNews 2.0b1 was developed by Dave Winer at UserLand. This included Netscape's RSS 0.90 features
    新闻脚本2.0b1于 1999年6月由Dave Winer开发,包含了网景RSS 0.90的特点
     
  • Jul. 1999 - RSS 0.91 was an attempt by Netscape to move towards a more standard format and included most features from scriptingNews 2.0b1 but they removed the RDF header
    1999年7月 - RSS0.91 网景试图移入更多的标准格式并包含更多来自scriptingNews2.0b1的特性但是他们却删除了RDF头  
  • Jul. 1999 - UserLand uses RSS 0.91 and gets rid of scriptingNews
    1999年7月 - UserLand利用了没有scriptNews的RSS0.91
     
  • Netscape discontinues their RSS development
     网景中断了他们对RSS的开发
  • Jun. 2000 - Official RSS 0.91 specification release from UserLand
     UserLand公开了规范文档
  • Aug. 2000 - RSS 1.0 developed by a group lead by Rael Dornfest at O'Reilly. This format uses RDF and namespaces. Because of its name it is often confused as being a new version of 0.91, but this is a completely new format with no ties to RSS 0.91
     
  • Dec. 2000 - RSS 0.92 developed by Dave Winer at UserLand and includes optional elements
     
  • Sep. 2002 - RSS 2.0 is designed by Dave Winer after leaving Userland
     
  • Jul. 2003 - RSS 2.0 specification released through Harvard under a Creative Commons license

What Are The differences?
有什么区别?

Unlike the other RSS formats, RSS 1.0 was developed using the W3C RDF (Resource Description Framework) standard.
不像其他RSS的规格,RSS1.0使用W3C的DRF标准开发.


What RSS Version Should I Use?
我该用哪个版本的RSS?

RSS 0.91 and RSS 2.0 are easiest to understand. RSS 1.0 is more complex to learn and takes more time and bandwidth to process.
RSS0.91和RSS2.0是最容易理解的版本。1.0学起来比较复杂而且需要花更多的带宽来运作。

Our tutorial is based on RSS 2.0.
我们的教程是基于RSS2.0的


Is There an RSS Web Standard?
有RSS WEB标准吗?

There is no official standard for RSS. But about 50 % of all RSS feeds use the RSS 0.91 format. About 25 % use the RSS 1.0 format and the last 25 % is split between the other RSS 0.9x versions and RSS 2.0.
没有正式的RSS标准。但在所有的RSS feeds中大约有50% 使用RSS 0.91规格. 25%使用RSS1.0规格并且至少有25%使用 RSS 0.9x 和 RSS 2.0之间的版本。


RSS语法
翻译:Linyupark / 2006-04-24

The syntax rules of RSS 2.0 are very simple and very strict.
RSS2.0的语法规则非常简单并十分的严格。

This tutorial teaches how to create your own RSS feed and maintain it.
这个教程会教你如何建立你自己的RSS feed并维护它。


An Example RSS document
一个RSS文档的例子

RSS documents use a self-describing and simple syntax.
RSS文档使用了简单的自描述语法。

<?xml version="1.0" encoding="ISO-8859-1" ?>

<rss version="2.0">
<channel>

<title>W3Schools</title>
<link>http://www.w3schools.com</link>
<description>W3Schools Web Tutorials </description>
<item>
<title>RSS Tutorial</title>

<link>http://www.w3schools.com/rss</link>
<description>Check out the RSS tutorial
on W3Schools.com</description>
</item>
</channel>
</rss>

The first line in the document - the XML declaration - defines the XML version and the character encoding used in the document. In this case the document conforms to the 1.0 specification of XML and uses the ISO-8859-1 (Latin-1/West European) character set.
文档内的第一行为XML声明-定义了XML的版本和文档使用的字符编码。这个例子里文档使用的是XML1.0版本,编码为 ISO-8859-1.

The next line describes the RSS element of the document (like it was saying: "this is an RSS document - version 2.0"):
下面的一行描述RSS文档元素(这个就像在说:"这是一份RSS2.0版本的文档")

<rss version="2.0">

The next line describes the <channel> element of the document:
再下面的一行描述了文档的<channel>元素:

<channel>

The next three lines describe three child elements of the <channel> element (<title>, <link> and <description>):
接下来的三行描述了<channel>的三个子元素(<title>,<link>,<description>)

<title>W3Schools</title>
<link>http://www.w3schools.com</link>

<description>W3Schools Web Tutorials</description>

Then there is an <item> element. The <item> element contains the information you would like your RSS feed to show (<title>, <link> and <description>). You can add multiple <item> elements:
然后就是<item>元素,<item>元素包含了RSS展示的一些信息(<title>, <link> 和 <description>)你可以添加数个<item>元素:

<item>
<title>RSS Tutorial</title>
<link>http://www.w3schools.com/rss</link>
<description>Check out the RSS tutorial
on W3Schools.com</description>
</item>

Finally, the two last lines close the <channel> and <rss> elements:
最后的两行关闭<channel> 和 <rss> 元素:

</channel>
</rss>

Did you understand that this was an RSS feed from W3Schools? Don't you agree that RSS is pretty self-descriptive?
你明白这是个来自W3S的RSSfeed了不?难道你不认为这是个很好的RSS自我描述?


RSS is an XML Dialect
RSS 是一种XML的方言

Because RSS is XML, there are a couple of things you must remember.
因为RSS是XML,有一些事你必须了解

  • All XML elements must have a closing tag
    所有XML元素必须有关闭标签
  • XML tags are case sensitive
    XML标签区分大小写
  • All XML elements must be properly nested
    所有XML元素必须合理嵌套
  • Attribute values must always be quoted
    属性值必须在引号内

Comments in RSS
RSS注释

The syntax for writing comments in RSS is similar to that of HTML:
书写RSS注释的语法和HTML十分相似:

<!-- This is a comment -->


RSS channel元素
翻译:Linyupark / 2006-04-24

The <channel> element is where you describe your RSS feed.
<channel>元素内是描述RSS feed的地方

With RSS 2.0 there are a lot of different options for the channel element.
对于channel元素来说RSS2.0有很多不同的选择(指内部可选择的元素有很多种)


The <channel> Element
<channel>元素

The RSS <channel> element is where items are displayed. It is like an RSS headline. Channel elements normally do not change very often.
RSS的<channel>元素是项目内容显示的地方。它就像RSS的标题。一般来讲它不会频繁的改动。

There are three required elements inside the <channel> element: <title>, <link>, and <description>.
有三个内部元素是必须有的:<title>, <link>, 和 <description>.

The <title> element should contain a short description of your site and your RSS feed:
<title>元素里应该包含你的站和你的RSS feed简短的说明:

<title>W3Schools News Update</title>

The <link> element should define the link to your site's main page:
<link>元素应该定义你网站主页的链界:

<link>http://www.w3schools.com</link>

The final required element is <description>. This element should describe your RSS feed.
最后必须有的元素就是 <description>,这个元素应该描述你的RSS feed.

<description>W3Schools Web Tutorials</description>


Optional Elements in <channel>
<channel>内的可选元素

Element 元素 Description 描述
<category> Defines one or more categories the channel belongs to
定义一个或多个频道分类
<cloud> Allows notification of updates.
允许更新通告
<copyright> Notifies about copyrighted material
提醒有关版权
<docs> An URL to documentation on the RSS version the channel is using
频道所使用的RSS版本文档URL
<generator> If the channel is created using an automatic generator, this is defined here
如果频道是自动生成器产生的,就在这里定义
<image> Inserts a picture to the channel.
给频道加图片
<language> Describes what language the channel uses. By using this tag it is possible for RSS aggregators to group sites based on language.
描述了频道所使用的语言。
<lastBuildDate> Defines the last date the channel was modified
定义频道最新一次改动的时间
<managingEditor> Defines an e-mail address for the editor of the site
定义编辑站点人员的E-mail地址
<pubDate> Defines the last publication date for the channel
定义频带最新的发布时间
<rating> Parental control rating of the page
页面评估
<skipDays> Defines days where it is unnecessary for RSS aggregators to update the feed
<skipHours> Defines hours where it is unnecessary for RSS aggregators to update the feed
<textInput> Creates a text input for the channel
<ttl> (ttl = time to live) Defines how many minutes the channel can stay cached before refreshing
存活的有效时间
<webMaster> Defines an e-mail address for the webmaster of the site
定义站张的邮件地址

RSS item 元素
翻译:Linyupark / 2006-04-24

The <item> element is where you link to and describe the update on your site.
<item>元素内是你网站连接和描述更新内容的地方。

With RSS 2.0 there are a lot of different options for the item element.
在RSS 2.0 item元素里有很多不同的可选内容


The <item> Element
<item>元素

The RSS <item> is where updates are displayed. It is kind of like a headline for an article. <item> elements are created every time there is an update on your site that you would like displayed in your RSS feed.
<item>是显示RSS更新内容的地方。它像是文章的标题。当你的站点有更新时RSSfeed中的<item>元素就会被建立起来。

There are several optional <item> elements, but either the <title> or the <description> are required.
<item>元素里有几个可选的元素,但<title> 或是<description>是必须有的。

A RSS <item> should include the <title>, <link> and <description> elements.
一个RSS的<item>应该包括 <title>, <link> 和 <description>元素

The first element is your news item's title. This should be a very short description of your site and your RSS feed:
第一个元素是项目的题目。应该用十分简短的描述:

<title>W3Schools New RSS Tutorial</title>

The next element is the link to the part of your site the item is referring to:
接下来的元素项目所关联的连接:

<link>http://www.w3schools.com/rss</link>

The next line is the RSS feed description. This should describe your RSS feed item.
再下面的一行就是RSS feed的描述部分,这应该是描述你的RSS feed项目的。

<description>W3Schools RSS Tutorial</description>


Optional Elements in <item>
可选的<item>元素

Tag Description
<author> Defines the author of the item.
定义作者
<category> Places the item in one or more of the channel categories.
类别
<comments> An URL to a comment's page for the item.
针对项目的评论页URL
<enclosure> Describes a media object related to the item
描述一个与项目有关的媒体对象
<guid> GUID = Globally Unique Identifier. Defines a unique identifier to the item.针对项目定义独特的标志
<pubDate> The publication date for the item.
项目发布时间
<source> Is used to define a third party source.
转载地址(源地址)

RSS发布
翻译:Linyupark / 2006-04-24

Having a RSS feed is no good unless other people can see it.
光有RSS feed还是不够的,应该让别人能看到它


Publish Your RSS Feed
发布你的RSS feed

Publishing and getting people to notice your RSS feed is as important as making one.
发布并让人们注意到的你RSS就和你制作它一样重要

First, put your rss.xml file on the internet. You can place it on the same server as your site.
首先,将你的RSS.xml文件放到你的站点服务器上。

Next, let people know you have a RSS feed. Register your feed with an aggregator.
然后,让人们知道你有一个RSSfeed.到相关的目录站上注册你的RSS(下面是一些国外知名的RSS发布站)

  • Syndic8: The largest RSS directory. Syndicate has over 300,000 feeds listed. Register your feed here.
  • Daypop: A large news oriented RSS aggregator. Over 50,000 news oriented feeds. Register your feed here.
  • Newsisfree: A news oriented RSS aggregator. Over 18,000 feeds. Register your feed here.

Then, place this RSS Logo and this XML Logo with URL's to a page that explains briefly how other people can view your RSS feed.
接着, 将这两个含有连接到你RSS地址的图片放到你站点的页面上,人们就可以看到你的RSS feed了


Can I Manage my RSS Feed Myself?
我可以自行管理RSS feed吗?

The best way to be sure your RSS feed includes the things you want, and works the way you want, is to manage it yourself. But this can be very time consuming, especially for pages with a lot of updates.
如你想让RSS里包括一些你想要的东西或是想按你的想法来做,最好的办法就是你自己来管理它,但是这将是非常烦琐的,因为很多页有不同的东西要更新。

Your other alternative is to use a third party automated RSS.
还有种选择就是使用第三方的RSS自动生成器


Automated RSS
RSS自动生成

If you don't want to update your RSS feed yourself, there are tools and services you can use to automate it.
如果你不想自己来更新RSS,这有些工具可以为你服务。
===== 以下是几个工具的官方地址=====

One such service is MyRSSCreator (featured in "RSS for dummies"), who can offer an automated, reliable RSS service in just 10 minutes.

There are also free services such as FeedFire, who offers free creation and distribution of RSS feeds.

For users who just need an easy RSS feed for their personal website, some of the most popular blog (Web Log) managers (like Blogger and Radio) offers built in automated RSS services.


Validate
校验

You can validate your RSS feed here[校验地址]. This validator supports all RSS versions.


 

RSS阅读器
翻译:Linyupark / 2006-04-24

A RSS reader gathers your news for you.
一个RSS阅读器可以为你收集新闻

RSS readers are available for many different devices and OS.
RSS阅读器可以在很多不同的设备和操作系统上运作


RSS Readers
[下面是国外的几款阅读器]

There are a lot of different RSS readers. Some work as web services, and some are limited to windows (or Mac, PDA or UNIX). Here are a few I have tried and liked:

  • NewsGator Online - An free online based RSS aggregator. NewsGator Online also includes synchronization with Outlook Edition, viewing content on TV with Media Center Edition, as well as the publication of blogs and headlines.
     
  • RssReader - A free Windows based RSS aggregator. RssReader supports RSS versions 0.9x, 1.0 and 2.0 as well as Atom 0.1, 0.2 and 0.3.
     
  • FeedDemon - A great Windows based RSS aggregator. This is very easy to use and has a very orderly interface. But this is not freeware.
     
  • blogbot - A RSS aggregator plugin for your Outlook or Internet Explorer. The lite version for Internet Explorer is free.

There are a lot of other RSS readers out there. http://www.ourpla.net/cgi-bin/pikie.cgi?RssReaders has a big list of RSS readers, you can even add your own readers to this page or comment on the existing ones.



Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=704849

posted @ 2007-07-14 00:37 和田雨 阅读(544) | 评论 (0)编辑 收藏

使用远程桌面链接登录到终端服务器时经常会遇到“终端服务器超出最大允许链接数”诸如此类错误导致无法正常登录终端服务器,引起该问题的原因在于终端服务的缺省链接数为2个链接,并且当登录远程桌面后如果不是采用注销方式退出,而是直接关闭远程桌面窗口,那么实际上会话并没有释放掉,而是继续保留在服务器端,这样就会占用总的链接数,当这个数量达到最大允许值时就会出现上面的提示。
  解决方式:
  一、用注销来退出远程桌面而不是直接关闭窗口
  二、限制已断开链接的会话存在时间
  三、增加最多链接数,即设置可连接的数量多些

第三可采用如下几种方式来修改:

1.从终端服务配置中修改:
打开管理工具里的终端服务配置,再单击连接,双击右边的RDP-Tcp,弹出RDP-Tcp属性,选择网卡,可设置最多连接数,或者设置为无限制的连接数。当然这个值不也能太大,否则会占用较多的系统资源。不过这里修改的值好像不起作用,设置成无限制时照样还是会出现本文所说的情况。

修改会话存在时间:运行-Tscc.msc-连接-双击RDP-Tcp或右击-属性-会话-选中第一个的替代用户设置(O)-结束已断开的会话[将默认值“从不”改为一个适当的时间,比如30分钟]
  
2.打开“控制面板”,双击“添加删除程序”,单击“添加删除Windows组件”*“组件”
,在Windows组件向导对话框中选中“终端服务” * “下一步” * “应用服务器” * “下
一步”,然后按照提示即可改变终端服务的模式。 
不过听说用此法,改了应用需要许可证,90天后过期:(

3.win2003的话可以从组策略修改:
  组策略级别要高于终端服务配置,当启用组策略后终端服务配置中的相应选项会变成灰色不可修改
  运行-gpedit.msc-计算机配置-管理模板-Windows组件-终端服务
  双击右边的”限制连接数量“-选择”已启用“-填入允许的最大连接数

posted @ 2007-07-12 15:16 和田雨 阅读(324) | 评论 (0)编辑 收藏

J2EE可以说指Java在数据库信息系统上实现,数据库信息系统从早期的dBase、到Delphi/VBC/S结构,发展到B/SBrowser浏览器/Server服务器)结构,而J2EE主要是指B/S结构的实现。

J2EE又是一种框架和标准,框架类似API、库的概念,但是要超出它们。

J2EE是一个虚的大的概念,J2EE标准主要有三种子技术标准:WEB技术、EJB技术和JMS,谈到J2EE应该说最终要落实到这三个子概念上。

这三种技术的每个技术在应用时都涉及两个部分:容器部分和应用部分,Web容器也是指Jsp/Servlet容器,你如果要开发一个Web应用,无论是编译或运行,都必须要有Jsp/Servlet库或API支持(除了JDK/J2SE以外)。

Web技术中除了Jsp/Servlet技术外,还需要JavaBeansJava Class实现一些功能或者包装携带数据,所以Web技术最初简称为Jsp/Servlet+JavaBeans系统。

谈到JavaBeans技术,就涉及到组件构件技术(component),这是Java的核心基础部分,很多软件设计概念(设计模式)都是通过JavaBeans实现的。

JavaBeans不属于J2EE概念范畴中,如果一个JavaBeans对象被Web技术(也就是Jsp/Servlet)调用,那么JavaBeans就运行在J2EEWeb容器中;如果它被EJB调用,它就运行在EJB容器中。

EJB(企业JavaBeans)是普通JavaBeans的一种提升和规范,因为企业信息系统开发中需要一个可伸缩的性能和事务、安全机制,这样能保证企业系统平滑发展,而不是发展到一种规模重新更换一套软件系统。

J2EE集群原理: http://www.jdon.com/jive/article.jsp?forum=121&thread=22282

至此,JavaBeans组件发展到EJB后,并不是说以前的那种JavaBeans形式就消失了,这就自然形成了两种JavaBeans技术:EJBPOJOPOJO完全不同于EJB概念,指的是普通JavaBeans,而且这个JavaBeans不依附某种框架,或者干脆可以说:这个JavaBeans是你为这个应用程序单独开发创建的。

J2EE应用系统开发工具有很多:如JBuilderEclipse等,这些IDE首先是Java开发工具,也就是说,它们首要基本功能是可以开发出JavaBeansJava class,但是如果要开发出J2EE系统,就要落实到要么是Web技术或EJB技术,那么就有可能要一些专门模块功能,最重要的是,因为J2EE系统区分为容器和应用两个部分,所以,在任何开发工具中开发J2EE都需要指定J2EE容器。

J2EE容器分为WEB容器和EJB容器,Tomcat/ResinWeb容器;JBossEJB容器+Web容器等,其中Web容器直接使用Tomcat实现的。所以你开发的Web应用程序可以在上面两种容器运行,而你开发的Web+EJB应用则只可以在JBoss服务器上运行,商业产品Websphere/Weblogic等和JBoss属于同一种性质。

J2EE容器也称为J2EE服务器,大部分时它们概念是一致的。

如果你的J2EE应用系统的数据库连接是通过JNDI获得,也就是说是从容器中获得,那么你的J2EE应用系统基本与数据库无关,如果你在你的J2EE应用系统耦合了数据库JDBC驱动的配置,那么你的J2EE应用系统就有数据库概念色彩,作为一个成熟需要推广的J2EE应用系统,不推荐和具体数据库耦合,当然这其中如何保证J2EE应用系统运行性能又是体现你的设计水平了。

高质量的Java企业系统

衡量J2EE应用系统设计开发水平高低的标准就是:解耦性;你的应用系统各个功能是否能够彻底脱离?是否不相互依赖,也只有这样,才能体现可维护性、可拓展性的软件设计目标。

为了达到这个目的,诞生各种框架概念,J2EE框架标准将一个系统划分为WEBEJB主要部分,当然我们有时不是以这个具体技术区分,而是从设计上抽象为表现层、服务层和持久层,这三个层次从一个高度将J2EE分离开来,实现解耦目的。

因此,我们实际编程中,也要将自己的功能向这三个层次上靠,做到大方向清楚,泾渭分明,但是没有技术上约束限制要做到这点是很不容易的,因此我们还是必须借助J2EE具体技术来实现,这时,你可以使用EJB规范实现服务层和持久层,Web技术实现表现层;

EJB为什么能将服务层从Jsp/Servlet手中分离出来,因为它对JavaBeans编码有强制的约束,现在有一种对JavaBeans弱约束,使用Ioc模式实现的(当然EJB 3.0也采取这种方式),在Ioc模式诞生前,一般都是通过工厂模式来对JavaBeans约束,形成一个服务层,这也是是Jive这样开源论坛设计原理之一。

由此,将服务层从表现层中分离出来目前有两种可选架构选择:管理普通JavaBeansPOJO)框架(SpringJdonFramework)以及管理EJBEJB框架,因为EJB不只是框架,还是标准,而标准可以扩展发展,所以,这两种区别将来是可能模糊,被纳入同一个标准了。

但是,通常标准制定是为某个目的服务的,总要牺牲一些换取另外一些,所以,这两种架构会长时间并存。

前面谈了服务层框架,使用服务层框架可以将JavaBeansJsp/Servlet中分离出来,而使用表现层框架则可以将Jsp中剩余的JavaBeans完全分离,这部分JavaBeans主要负责显示相关,一般是通过标签库(taglib)实现,不同框架有不同自己的标签库,Struts是应用比较广泛的一种表现层框架。

这样,表现层和服务层的分离是通过两种框架达到目的,剩余的就是持久层框架了,通过持久层的框架将数据库存储从服务层中分离出来是其目的,持久层框架有两种方向:直接自己编写JDBCSQL语句(如iBatis);使用O/R Mapping技术实现的HibernateJDO技术;当然还有EJB中的实体Bean技术。

持久层框架目前呈现百花齐放,各有优缺点的现状,所以正如表现层框架一样,目前没有一个框架被指定为标准框架,当然,表现层框架现在又出来了一个JSF,它代表的页面组件概念是一个新的发展方向,但是复杂的实现让人有些忘而却步。

最后,你的J2EE应用系统如果采取上面提到的表现层、服务层和持久层的框架实现,基本可以在无需深刻掌握设计模式的情况下开发出一个高质量的应用系统了。

还要注意的是: 开发出一个高质量的J2EE系统还需要正确的业务需求理解,那么域建模提供了一种比较切实可行的正确理解业务需求的方法,相关详细知识可从UML角度结合理解。

当然,如果你想设计自己的行业框架,那么第一步从设计模式开始吧,因为设计模式提供你一个实现JavaBeans或类之间解耦参考实现方法,当你学会了系统基本单元JavaBeans或类之间解耦时,那么系统模块之间的解耦你就可能掌握,进而你就可以实现行业框架的提炼了,这又是另外一个发展方向了。

以上理念可以总结为一句话:

J2EE开发三件宝: Domain Model(域建模)、patterns(模式)和framework(框架)。

posted @ 2007-07-12 13:28 和田雨 阅读(394) | 评论 (2)编辑 收藏

By Benny.luo@Sun.com, 6/19/07  
SUN中国软件技术中心 罗浩/Benny Luo
 简介:
JDK 5.0, 代号老虎,在以往的Java传统上加入了许多新的设计,给Java语言带来了一些较大的变化,比如泛型,元数据,可变个数参数,静态导入类,新线程架构,自动装箱/拆箱等等新的以往没有的新特性。同时,在调试程序和解决性能各种问题方面,JDK5.0同样加入了多个分析工具来让开发者更加方便地调试他们自己的程序,它们包括了命令行调试工具,图形界面调试工具等等.
 
JDK5.0包括的调试工具:
我们在这里对JDK5.0的调试工具做大致的概念性的介绍,然后希望通过介绍我自己在实际工作中使用这些工具解决问题的实例来让大家对这些工具有更深入的了解。
 
JDK5.0里面加入了jstack, jconsole, jinfo, jmap, jdb, jstat, jps, 下面对这些工具做简单介绍:
  • jstack -- 如果java程序崩溃生成core文件,jstack工具可以用来获得core文件的java stack和native stack的信息,从而可以轻松地知道java程序是如何崩溃和在程序何处发生问题。另外,jstack工具还可以附属到正在运行的java程序中,看到当时运行的java程序的java stack和native stack的信息, 如果现在运行的java程序呈现hung的状态,jstack是非常有用的。目前只有在Solaris和Linux的JDK版本里面才有。
  • jconsole – jconsole是基于Java Management Extensions (JMX)的实时图形化监测工具,这个工具利用了内建到JVM里面的JMX指令来提供实时的性能和资源的监控,包括了Java程序的内存使用,Heap size, 线程的状态,类的分配状态和空间使用等等。
  • jinfo – jinfo可以从core文件里面知道崩溃的Java应用程序的配置信息,目前只有在Solaris和Linux的JDK版本里面才有。
  • jmap – jmap 可以从core文件或进程中获得内存的具体匹配情况,包括Heap size, Perm size等等,目前只有在Solaris和Linux的JDK版本里面才有。< /li>
  • jdb – jdb 用来对core文件和正在运行的Java进程进行实时地调试,里面包含了丰富的命令帮助您进行调试,它的功能和Sun studio里面所带的dbx非常相似,但 jdb是专门用来针对Java应用程序的。
  • jstat – jstat利用了JVM内建的指令对Java应用程序的资源和性能进行实时的命令行的监控,包括了对Heap size和垃圾回收状况的监控等等。
  • jps – jps是用来查看JVM里面所有进程的具体状态, 包括进程ID,进程启动的路径等等。

另外,还有些其他附带的工具在这里没有列出,比如Heap Analysis Tool, kill -3 方法等等,这些在JDK5.0之前就有,同样也是非常有用的性能调优工具,大家可以参照相应的文档资料来学习,在文章后面也会推荐一些相应的文档给大家作为参考。

 好,说了这么多,让我们来看看JDK5.0自带的这些工具在现实工作能给我们带来什么帮助,下面是我和ISV一起共同工作的实际例子,在这里把它们简单阐述出来,希望对大家有所帮助。

jconsole和jstack使用实例:

在做过的项目中,曾经有几个是使用jstack和jconsole来解决问题的。在下面的例子中,由于部分代码涉及到公司名字,我使用了xxx来代替。

1. 其中的一个是Web2.0的客户,由于目前Sun Microsystem公司推出的Niagara服务器系列非常适合网络方面的多线程应用,并且已经在业界非常出名,所以他们决定使用T2000服务器来测试一下如果应用到他们自己的应用是否能够获得出众的性能。
整个应用的架构如下:
Apache 2.0.59 + Resin EE 2.1.17 + Jdk 1.5.0.07 + Oracle 9
运行的操作系统:
Solaris 10 Update 3 (11/06), EIS patches包.
测试工具:
Apache benchmark tool.
在客户的测试环境中,我们分别做了Apache, Resin, Solaris的相应调整,其中包括了Apache使用Prefork模式,并且调整了httpd.conf文件里面相应的ServerLimit, ListenBacklog,Maxclient等等值,Resin服务器调整Jvm heap size, 并行回收new generation和old generation, 最大线程数,oracle连接数等等参数,Solaris操作系统做了网络和系统的相应调整,最终把整套系统搬进了生产环境,一切顺利进行,但当进入其中的一个论坛系统时却发现系统响应时间非常缓慢,用Apache Benchmark Tool加少量压力得到结果如下,由于是在生产环境下所以不敢使用大的压力:

This is ApacheBench, Version 2.0.41-dev <$Revision: 1.121.2.12 $> apache-2.0
Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Copyright (c) 2006 The Apache Software Foundation, http://www.apache.org/
Benchmarking free.xxx.com (be patient).....done
Server Software: Resin/2.1.17
Server Hostname: free.xxx.com
Server Port: 8080
Document Path: /forum/bbsMessageList.act?bbsThreadId=1580107
Document Length: 27012 bytes
Concurrency Level: 10
Time taken for tests: 92.148883 seconds
Complete requests: 100
Failed requests: 0
Write errors: 0
Keep-Alive requests: 0
Total transferred: 2722500 bytes
HTML transferred: 2701200 bytes
Requests per second: 1.09 [#/sec] (mean)
Time per request: 9214.888 [ms] (mean)
Time per request: 921.489 [ms] (mean, across all concurrent requests)
Transfer rate: 28.84 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 9089 9187 139.4 9140 9789
Waiting: 3067 3163 138.3 3117 3766
Total: 9089 9187 139.4 9140 9789
Percentage of the requests served within a certain time (ms)
50% 9140
66% 9178
75% 9189
80% 9201
90% 9281
95% 9560
98% 9739
99% 9789
100% 9789 (longest request)

每一个请求的响应时间大概去到8-9秒时间,这个是客户所不能接受的。
 
这时我们决定采用JDK5.0自带的jstack来进行trouble-shoot,首先重新做加压测试,并行请求为10个,总共100个请求,这时对Resin服务器所起的Java进程间隔10秒用jstack做一次采集工作。为什么要间隔10秒?主要是想看看在这三十秒内Java进程是否都阻塞在同一个地方。结果如下:
 
大部分的线程都阻塞在同一个java stack上面:

Thread t@38: (state = BLOCKED)
- java.lang.Object.wait(long) @bci=0 (Interpreted frame)
- java.lang.Object.wait(long) @bci=0 (Interpreted frame)
- com._xxx.vportal.common.rpc.session.RemoteServiceGroupFactory.getMaxBalanceFactoryEntry() @bci=165,
line=180 (Interpreted frame)
- com._xxx.vportal.common.rpc.session.RemoteServiceGroupFactory.getService() @bci=80, line=195 (Interpreted
frame)
- com._xxx.vportal.common.rpc.session.RemoteServiceFactory.getSynSender() @bci=1, line=331 (Interpreted
frame)
- com._xxx.vportal.common.rpc.session.RemoteServiceSupport.synRequestHardTask(java.lang.String,
java.lang.Object) @bci=6, line=35 (Interpreted frame)
- com._xxx.vportal.amus.user.client.UserClientRpcImpl.getIconSigner(int, int) @bci=36, line=90 (Interpreted frame)
- net._xxx.forum.model.user.UserInfo.getLogoPath() @bci=109, line=546 (Interpreted frame)
- sun.reflect.GeneratedMethodAccessor13.invoke(java.lang.Object, java.lang.Object[]) @bci=36 (Interpreted frame)
- sun.reflect.DelegatingMethodAccessorImpl.invoke(java.lang.Object, java.lang.Object[]) @bci=6, line=25
(Compiled frame)
- org.apache.velocity.runtime.parser.node.ASTReference.execute(java.lang.Object,
org.apache.velocity.context.InternalContextAdapter) @bci=40, line=207 (Compiled frame)
- org.apache.velocity.runtime.parser.node.ASTBlock.render(org.apache.velocity.context.InternalContextAdapter,
java.io.Writer) @bci=18, line=94 (Compiled frame)
- org.apache.velocity.runtime.parser.node.SimpleNode.render(org.apache.velocity.context.InternalContextAdapter,
java.io.Writer) @bci=18, line=271 (Interpreted frame)
-
org.apache.velocity.runtime.parser.node.ASTIfStatement.render(org.apache.velocity.context.InternalContextAdapter,
java.io.Writer) @bci=63, line=128 (Interpreted frame)
- org.apache.velocity.runtime.parser.node.ASTBlock.render(org.apache.velocity.context.InternalContextAdapter,
java.io.Writer) @bci=18, line=94 (Compiled frame)
- org.apache.velocity.runtime.directive.Foreach.render(org.apache.velocity.context.InternalContextAdapter,
java.io.Writer, org.apache.velocity.runtime.parser.node.Node) @bci=95, line=344 (Interpreted frame)
- org.apache.velocity.runtime.parser.node.ASTDirective.render(org.apache.velocity.context.InternalContextAdapter,
java.io.Writer) @bci=14, line=153 (Interpreted frame)
- org.apache.velocity.runtime.parser.node.SimpleNode.render(org.apache.velocity.context.InternalContextAdapter,
java.io.Writer) @bci=18, line=271 (Interpreted frame)
- org.apache.velocity.app.Velocity.evaluate(org.apache.velocity.context.Context, java.io.Writer, java.lang.String,
java.io.Reader) @bci=102, line=359 (Interpreted frame)
- org.apache.velocity.app.Velocity.evaluate(org.apache.velocity.context.Context, java.io.Writer,
java.lang.String, java.lang.String) @bci=18, line=253 (Interpreted frame)
- net._xxx.forum.util.velocity.VelocityUtil.getVelocityEvaluate(java.util.Map, java.io.Writer,
java.lang.String) @bci=14, line=35 (Interpreted frame)
- net._xxx.forum.action.forum.BbsMessageListAction.go() @bci=1284, line=268 (Interpreted
frame)

net._xxx.forum.action.AbstractAction.execute() @bci=1, line=39 (

..............

 和应用的开发人员交流后,发现这些有问题的程序,都是和论坛系统中Socket调用有关,当用户打开一个页面是,页面中的用户信息需要同过Socket的方式调用相册那边的数据,这个操作存在bug,经过用户的重新同步更新程序,问题解决。
 
解决后的实测的数据如下, 效果非常理想:
This is ApacheBench, Version 2.0.41-dev <$Revision: 1.121.2.12 $> apache-2.0
Copyright (c) 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Copyright (c) 2006 The Apache Software Foundation, http://www.apache.org/
Benchmarking free.xxx.com (be patient)
Completed 5000 requests
Completed 10000 requests
Completed 15000 requests
Completed 20000 requests
Completed 25000 requests
Completed 30000 requests
Completed 35000 requests
Completed 40000 requests
Completed 45000 requests
Finished 50000 requests
Server Software: Apache/2.0.59
Server Hostname: free.xxx.com
Server Port: 80
Document Path: /forum/bbsMessageList.act?bbsThreadId=1581280
Document Length: 27508 bytes
Concurrency Level: 30
Time taken for tests: 252.583749 seconds
Complete requests: 50000
Failed requests: 0
Write errors: 0
Total transferred: 1384158363 bytes
HTML transferred: 1375408188 bytes
Requests per second: 197.95 [#/sec] (mean)
Time per request: 151.550 [ms] (mean)
Time per request: 5.052 [ms] (mean, across all concurrent requests)
Transfer rate: 5351.56 [Kbytes/sec] received
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.4 0 11
Processing: 45 150 154.1 117 4349
Waiting: 41 145 153.7 112 4346
Total: 45 150 154.1 117 4349
Percentage of the requests served within a certain time (ms)
50% 117
66% 142
75% 163
80% 180
90% 241
95% 321
98% 478
99% 764
100% 4349 (longest re
2. 另外的一个是最近做的,我们有个教育行业方面的ISV上来实验室做T2000的Benchmark实验,我们花了一天的时间部署整个架构,包括安装Solaris操作系统,应用服务器,数据库。& amp; amp; lt; /div>
整个应用的架构如下:
Tomcat + Jdk 1.5 + Oracle 10g
运行的操作系统:
Solaris 10 Update 3 (11/06), EIS patches包.
测试工具:
LoadRunner 8.1
在实验的初段,Tomcat, Oracle 10g服务器都是架设在T2000上,我们在对T2000服务器的OS,网络,应用服务器做了必要的调整后,发现其表现还是不尽如人意。& #160;
 
我们使用Loadrunner做测试,用户上到了100个同时并发10个迭代时已经出现问题,有许多的请求都被阻塞住,不能正常地进行。
 
这时我们决定使用jconsole和jstack来看看系统出现了什么问题。
A. 首先我们需要在Tomcat的启动脚本catalina.sh里面加入JVM选项:
     Dcom.sun.management.jmxremote
     把tomcat服务器java进程置于JDK5.0的性能监控范围之内。
 
B. 然后我们用jconsole连接到tomcat服务器的java进程,发现基本上Heap size, 垃圾回收都比较正常,但发现tomcat服务器的大部分线程有问题,都处于被Block的状态。
     观察每条线程的stack trace, 发现它们基本上都被堵塞在uk.org.primrose.pool.core.Pool.put和uk.org.primrose.pool.core.Pool.get()的SyncHack同步机制上,我们尝试了改大数据连接池的大小,发现结果是一样的。
jdk5_1
C.这个结论在jstack的结果中同样得到了验证,使用jstack连接到tomcat服务器java进程,并观察结果。
jdk5_2

D. 最后我们决定用tomcat服务器的连接池配置来代替Primrose数据库连接池,更改以后,发现结果比较理想。

E. 后来,我们把Oracle数据库建立在另外的一台服务器X4200上,而Tomcat应用服务器依然放在T2000上,使用千兆网络交换机,同样地,使用Tomcat服务器自己的连接池配置表现较为理想。
以上两个是我对JDK5.0工具的一些实际操作的例子,在这里和大家分享一下,希望对大家有所帮助。而Sun Microsystem公司也在不断地完善JDK,在新的JDK版本里面加入越来越多的实用的方便开发者开发和调试的新特性,比如在最新的JDK6.0里面就内嵌了Dtrace里面许多关于Java程序中内存,方法,线程等等新的探针,通过这些探针和Dtrace,我们可以更加方便地调试Java程序。
文档资料:
posted @ 2007-07-10 00:04 和田雨 阅读(452) | 评论 (0)编辑 收藏

Eclipse(Eclipse 3.2)的最新版本带有Callisto,一套丰富的针对Eclipse 3.2的可选插件。Callisto包括一个功能强大的分析工具,此工具称为Eclipse测试与性能工具平台,简称TPTP。TPTP提供了一套功能全面的开源性能-测试和分析工具,包括集成的应用程序监控、测试、跟踪和分析功能,以及静态代码分析工具。对于在各类Java应用程序中找出和识别性能问题,分析工具的价值是不可估计的。在本文中,我们将探讨如何使用TPTP来保证获得高质量和高性能的代码(甚至是在单元和集成测试中)。

安装TPTP

  安装TPTP最容易的方式是使用Remote Update站点(参见图1)。打开Remote Update窗口(Help -> Software Updates -> Find and Install),然后选择Callisto Discovery Site。Eclipse将建议安装Callisto插件集。TPTP工具列在“Testing and Performance”下面。最容易也是最耗时的选择,就是安装所有建议的插件。即使不安装整个Callisto工具集,您仍然需要安装一些其他TPTP需要的组件,例如"Charting and Reporting"、"Enabling Features"和"Data Tool Performance"。

使用Eclipse Callisto分析应用程序图-1

   图 1.从远程站点安装TPTP

分析Java应用程序

  测试与性能工具平台基本上是一套分析工具。分析应用程序通常涉及到观察应用程序在压力之下的处理方式。这样做的一种常见方式是对已部署的应用程序运行一组负载测试,然后使用分析工具来记录应用程序的行为。接着,可以对结果进行研究来调查任何性能问题。这些事情通常是在项目结束时进行的,因为此时应用程序几乎已经准备好进入生产阶段了。

  TPTP非常适合这类任务。一个典型的用例是使用像JMeter这样的工具来运行负载测试,然后使用TPTP归纳工具记录和分析性能统计数据。

  然而,这并非使用TPTP分析应用程序的唯一方式。通常,越早进行测试,后面遇到的问题就越少。借助TPTP,您可以在很多上下文中分析代码,包括JUnit测试用例、Java 应用程序和web应用程序。而且它很好地集成到了Eclipse IDE中。所以,没有理由不在早期开始初步性能测试和分析工作。

  TPTP让您可以测试应用程序行为的几个方面,包括内存使用(创建了多少对象,这些对象的大小如何)、执行统计数据(应用程序在哪些地方所花的时间较多)和测试覆盖(测试期间执行代码的确切数量)。每个方面均可提供有关应用程序性能的独立信息。

  不管怎么说,内存泄漏可能而且的确存在于Java中。创建(并保存)不必要的对象会增加对内存的需求,并加重垃圾收集器的工作负担,这都会损害应用程序的性能。而且,如果运行应用程序的服务器的持续正常运行时间很长,累积下来的内存泄漏可能最终导致应用程序崩溃或服务器停止运行。这些都是留心应用程序内存泄漏情况的充分理由。

  根据80-20经验法则,80%的性能问题出现在20%的代码中。或者,换句话说,只要把精力集中在应用程序中执行最经常的部分上,就可以花费相对较少的气力使性能有实质性的提高。在这种情况下,执行统计数据就可以派上用场了。

  除此以外,TPTP还提供一些基本的测试覆盖数据。尽管这些统计数据不如Cobertura或Clover这样的专用工具提供的完整,您仍然可以通过它们快速了解性能测试正在有效地测试哪些方法。

  在本文中,我讨论的测试种类同样是没有经过优化的。优化涉及到使用像缓冲这样的技术对应用程序性能进行微调。这是一项对技术要求很高的操作,最好留到项目的最后完成。

  这里所讨论的这种初步性能测试和分析仅仅包括,确保应用程序从一开始就正确执行,以及没有编码错误或糟糕的编码实践会在后面的阶段中对性能产生不利的影响。事实上,修复内存泄漏和避免不必要的对象创建并不是优化——这只不过是调试,而且同样应该尽可能早地完成。

  让我们通过使用一些单元测试来分析一个类的方式开始。可以分析常规的单元或集成测试,或者编写针对性更强的面向性能的测试。通常,您应该尝试分析与生产代码最接近的代码。许多人使用模拟对象来代替DAO对象进行单元测试,使用这项功能强大的技术可以加速开发生命周期。如果使用这类方法,一定要使用这些测试来运行分析工具,它可以揭示有关内存使用和测试覆盖的有用信息。然而,性能测试的价值是有限的,因为对于与数据库相关的应用程序来说,其性能往往是由数据库的性能所决定的,所以在这个上下文中,应该进行所有重要的性能测试。简而言之,不要忘了分析基于实际数据库而运行的集成测试。

  出于本文的需要,我们将对以下类进行测试,这个类代表了一个到库目录的简单接口。

interface Catalog {
List<Book> findBooksByAuthor(String name);
List<Book> findAllBooks();
}

  基本的单元测试如下:

public class CatalogTest extends TestCase {
...
public Catalog getCatalog() {
...
}
public void testFindBooksByAuthor() {
List<Book> books = getCatalog().findBooksByAuthor("Lewis");
}
public void testLoadFindBooksByAuthor() {
for(int i = 0; i < 10; i++) {
List<Book> books
= getCatalog().findBooksByAuthor("Lewis");
}
}
public void testFindAll() {
List<Book> books = getCatalog().findAllBooks();
}
}

  您需要做的第一件事情就是建立一个分析。在Eclipse主菜单中选择"Run -> Profile",这将打开一个向导,您可以在其中配置不同种类的测试分析,如图2所示。

使用Eclipse Callisto分析应用程序图-2

  图 2. 创建一个TPTP分析

  在这个例子中,我们感兴趣的是JUnit测试分析。双击这一项;向导应该为每个单元测试类创建新的项。TPTP相当灵活,您可以在此屏幕中配置各个选项。例如,在Test选项卡上,可以单独分析单元测试类,也可以按照项目或软件包对它们进行分组。在Arguments选项卡上,可以指定运行时参数,而在Environment选项卡上可以定义环境变量。在Destination选项卡中,可以指定一个外部文件,用于保存分析数据以供以后使用。但是,最有用的是Monitor选项卡(参见图3),可以在上面指定要记录和研究的性能相关数据:

  • Basic Memory Analysis(基本内存分析):这个选项用于记录内存使用的统计数据,包括对象实例的数量和已经使用的全部内存。
  • Execution Time Analysis(执行时间分析):这个选项用于记录性能数据——即应用程序分别在每个方法上所花的时间长短。
  • Method Code Coverage(方法代码覆盖):这个选项用于通知在测试期间执行了哪些类和方法。

使用Eclipse Callisto分析应用程序图-3

   图 3: 在Monitor选项卡上定义要记录数据的类型。

  您可以直接从这个窗口运行分析工具,也可以使用位于要分析的测试类上的上下文菜单,方法是选择Profile As菜单项(参见图4)。

使用Eclipse Callisto分析应用程序图-4

  图 4:可以使用上下文菜单运行TPTP分析工具。

  运行分析工具可能要花上一段时间,这取决于测试用例的大小。完成之后,Eclipse将显示一个Profiling Monitor视图,可以在其中显示每种类型分析的结果的详细信息(参见图5)。

  

使用Eclipse Callisto分析应用程序图-5

  图 5: 分析结果

  Memory Statistics视图显示了应用程序创建的对象的数量。结果可以按照软件包来组织(以树视图的形式),或者显示为类或实例的一个列表。这些数据可以让您了解每种类型创建了多少个对象;应该对创建的对象(特别是高级对象,例如域对象)不正常的高数量持怀疑态度。

  用于检测内存泄漏的另一个有用工具是Object References视图。为了获得这些数据,您需要激活引用收集。启动分析之后,点击monitoring项,然后在上下文菜单中选择Collect Object References(参见图6)。接下来,通过上下文菜单(Open with -> Object References)打开Object References视图。您将获得一个类的列表,它带有对每个类的引用的次数。这可以为可能的内存泄漏提供一些线索。

使用Eclipse Callisto分析应用程序图-6

  图 6: 激活引用收集

  如图7所示,从Execution Statistics视图可以清楚地了解到应用程序执行到了哪里。"organization by"软件包可以帮助您找出执行时间最长的类和方法。点击一个方法将打开Method Invocation Details视图,它将显示有关方法被调用次数、调用地点以及它本身调用了哪些其他方法的更详细信息。尽管与一些可以向下发掘到源代码本身的商业工具相比,这个视图与源代码视图的集成度没有那么高,但是它还是可以给出一些重要线索,帮助您找出执行错误的方法。

使用Eclipse Callisto分析应用程序图-7

  图 7: Execution Statistics视图

  Coverage Statistics视图(参见图8)提供的信息是关于,您刚刚运行的测试用例使用了(因此至少在某种程度上测试了)哪些方法。覆盖统计数据是一项优秀的功能,尽管它们提供的信息的详细程度还无法与像Cobertura、Clover和jcoverage这样的专业覆盖工具相提并论(它们可以提供行精度的覆盖数据,以及行和分支覆盖的统计数据)。尽管如此,它也有自身的优点,那就是可以提供实时的覆盖结果,而目前,只有商业的代码覆盖工具,例如Clover和jcoverage,才能提供行级别的覆盖报告和完整的IDE集成。

使用Eclipse Callisto分析应用程序图-8

  图 8: Coverage Statistics视图

静态分析工具

  在TPTP工具箱中,另一件有趣的工具就是静态分析工具。Java静态分析工具,例如PMD,允许通过基于一组代码预定义规则和最佳实践检查来检查代码,从而自动验证代码质量。现在,TPTP也包含一个静态分析工具。除了提供它自己的一组静态分析规则之外,这个工具还可以提供一个一致的接口,其他工具厂商可以在这个接口中集成他们自己的规则。

  要对代码进行静态分析,需要创建分析配置。在Java视图或Analysis图标中,使用上下文菜单打开Analysis窗口,它现在应该出现在工具栏上(参见图9)。分析配置决定了要分析的代码(Scope)和应该遵循的规则(Rules)。有71条规则可供选择,例如"Avoid casting primitive types to lower precision"和"Always provide a break at the end of every case statement"。您还可以使用预定义的规则,例如"Java Quick Code Review"(在这里,71条规则中只有19条适用)。

使用Eclipse Callisto分析应用程序图-9

  图 9:建立静态分析规则

  要分析代码,使用工具栏中的Analysis图标。分析不是实时完成的,就像一些其他的类似工具一样,例如Checkstyle。然而,给出的结果很清晰(参见图10):错误在源代码视图中标出,并且按照错误类型,以树视图的形式在Analysis Results视图中列出。"Quick Fix"是一项优雅的特性,它出现在错误类型的上下文菜单中,而且如果可能,它可以自动为您纠正问题。

使用Eclipse Callisto分析应用程序图-10

  图 10: 静态代码分析结果

结束语

  Eclipse测试与性能工具平台是Eclipse IDE工具箱中极具价值的部分。它支持的性能测试的范围很宽,这有助于从第一个单元测试开始,就确保获得高质量和高性能的代码。

  TPTP无疑还比不上一些可用的商业工具,例如OptimizeIt和JProbe,后者的报告和分析功能要更加完善,而且表示通常更加精练。然而,商业的分析工具往往非常昂贵,而且很难在最严峻的环境中来验证它们的使用情况。尽管TPTP还相对较为不成熟,它仍然可算作一款功能强大的产品,毋庸置疑,它可以提供对于许多项目来说不可或缺的有价值的分析数据

参考资料

 作者简介
John Ferguson Smart 从1991年起涉足IT行业,从1999年起开始参与J2EE开发。
posted @ 2007-07-09 17:56 和田雨 阅读(276) | 评论 (0)编辑 收藏

2005年11月30日,BEA宣布,将与IBM、Oracle、SAP、Iona、Siebel和Sybase一起,支持一种构建和包装应用程序的新规范,即Service Component Architecture(服务组件架构,SCA)。

什么是SCA?

  SCA是一种规范,它使开发人员可以将注意力集中在业务逻辑的编写上。更直接地说,它是一种大大改进了的部署描述符,它可以使用任何语言而不限于Java。此外,您还可以使用编程式语言和声明式语言,比如BPEL和XSLT。SCA的特别之处在于,它对安全性、事务和可靠消息传递之类的特性使用了声明式策略的理念。

  使SCA脱颖而出的是,它是专门针对SOA设计的,而不像J2EE只是面向SOA做了修改。SCA关注的是如何描述按照各种编程模型和协议编写的组件所组成的程序集。
SCA的目标与BEA的目标是一致的:使事情(这次是应用程序的构建)更容易。SCA允许开发应用程序集而不考虑特定的中间件API或具体语言。

  SCA的核心概念是服务及其相关实现。服务由接口定义,而接口包含一组操作。服务实现可以引用其他服务,称为引用。服务可以有一个或多个属性,这些属性是可以在外部配置的数据值。

  SCA中的一个关键推动因素是Service Data Object(服务数据对象,SDO)。AquaLogic Data Services Platform一直在使用它。SDO用于表示业务数据、参数以及服务调用的返回值,当它遍历服务网络时,它还是一种表示数据的方式。注意,也可以使用XMLBeans及其它技术。

  SCA组件被组成为程序集。程序集是服务级的应用程序,它是服务的集合,这些服务被连接在一起,并进行了正确的配置。SCA程序集运行在两个级别:第一种情况,程序集是系统内的一组松散连接的组件;另一种情况,程序集是模块内的一组松散连接的组件。二者的区别在于,一般来说,模块是组件的集合,而系统是模块的集合。此外,系统对应于“大规模编程”(programming in the large或megaprogramming),而模块对应于“小规模编程”(programming in the small),比如构建当今的典型应用程序。相关例子以及更详细的说明请参见Dev2Dev站点上的SCA白皮书。

  将组件连接到它所依赖的服务的方式就是服务网络“装配”的方式。程序集已经在许多技术和框架中广为应用,比如CORBA、J2EE、ATG Dynamo和Spring,也就是说,它并不是新出现的。从这些技术中我们可以知道,程序集提供了许多重要的优点,比如更轻松的迭代开发,以及避免使业务逻辑依赖于中间件容器。SCA使用程序集解决了许多SOA开发中的重要问题,包括:

  1. 业务逻辑与底层基础架构、服务质量和传输的分离。
  2. “小规模编程”与“大规模编程”的联系。
  3. 为架构的设计、编码和操作性部署在自底向上(bottom-up)和自顶向下(top-down)两种方法中来回切换提供了一种统一的方式。

对BEA来说,它意味着什么?

  SDO 2.0规范是整个SCA技术平台的一个组成部分,它将在AquaLogic Data Services Platform (ALDSP)产品家族(已经支持SDO 1.0)的下一个主版本中实现。BEA AquaLogic Data Services Platform完全是针对SOA构建的,它自3.0版本开始就采用SCA技术。它提供企业数据服务的自动创建和维护,这可以帮助客户获得更高的生产力,进行业务优化,并更快地创造价值。BEA AquaLogic Data Services Platform提供了一个捕获与数据访问和数据更新有关的逻辑的单一位置。数据服务层提供了对相关的不同实时数据的可重用的、简化了的访问。注意,BEA Workshop中即将包含SCA支持。还有,BEA Workshop可免费下载

为什么SCA如此重要?

  SCA具有重大意义,因为它是第一项承诺提供一个组合模型以启用服务网络并支持构建下一代面向服务应用程序的技术。这一领域的每一次革新,都会导致出现一个新的抽象层,从而产生一批新的应用程序。C允许我们构建不能在汇编程序中构建的应用程序,而C++允许我们构建不能使用C构建的应用程序,Java又允许我们构建不能使用C++构建的应用程序。所有这些都是SCA的先例,简单地说,SCA就是未来用于构建大规模企业组合应用程序的技术。

posted @ 2007-07-09 17:48 和田雨 阅读(182) | 评论 (0)编辑 收藏

使用Windows操作系统的朋友对Excel(电子表格)一定不会陌生,但是要使用Java语言来操纵Excel文件并不是一件容易的事。在Web应用日益盛行的今天,通过Web来操作Excel文件的需求越来越强烈,目前较为流行的操作是在JSP或Servlet 中创建一个CSV (comma separated values)文件,并将这个文件以MIME,text/csv类型返回给浏览器,接着浏览器调用Excel并且显示CSV文件。这样只是说可以访问到Excel文件,但是还不能真正的操纵Excel文件,本文将给大家一个惊喜,向大家介绍一个开放源码项目,Java Excel API,使用它大家就可以方便地操纵Excel文件了。

JAVA EXCEL API简介

Java Excel是一开放源码项目,通过它Java开发人员可以读取Excel文件的内容、创建新的Excel文件、更新已经存在的Excel文件。使用该API非Windows操作系统也可以通过纯Java应用来处理Excel数据表。因为是使用Java编写的,所以我们在Web应用中可以通过JSP、Servlet来调用API实现对Excel数据表的访问。

现在发布的稳定版本是V2.0,提供以下功能:

  • 从Excel 95、97、2000等格式的文件中读取数据;
  • 读取Excel公式(可以读取Excel 97以后的公式);
  • 生成Excel数据表(格式为Excel 97);
  • 支持字体、数字、日期的格式化;
  • 支持单元格的阴影操作,以及颜色操作;
  • 修改已经存在的数据表;

现在还不支持以下功能,但不久就会提供了:

  1. 不能够读取图表信息;
  2. 可以读,但是不能生成公式,任何类型公式最后的计算值都可以读出;




回页首


应用示例

1 从Excel文件读取数据表

Java Excel API既可以从本地文件系统的一个文件(.xls),也可以从输入流中读取Excel数据表。读取Excel数据表的第一步是创建Workbook(术语:工作薄),下面的代码片段举例说明了应该如何操作:(完整代码见ExcelReading.java)

import java.io.*;
            import jxl.*;
            … … … …
            try
            {
            //构建Workbook对象, 只读Workbook对象
            //直接从本地文件创建Workbook
            //从输入流创建Workbook
            InputStream is = new FileInputStream(sourcefile);
            jxl.Workbook rwb = Workbook.getWorkbook(is);
            }
            catch (Exception e)
            {
            e.printStackTrace();
            }
            

一旦创建了Workbook,我们就可以通过它来访问Excel Sheet(术语:工作表)。参考下面的代码片段:

//获取第一张Sheet表
            Sheet rs = rwb.getSheet(0);
            

我们既可能通过Sheet的名称来访问它,也可以通过下标来访问它。如果通过下标来访问的话,要注意的一点是下标从0开始,就像数组一样。

一旦得到了Sheet,我们就可以通过它来访问Excel Cell(术语:单元格)。参考下面的代码片段:

//获取第一行,第一列的值
            Cell c00 = rs.getCell(0, 0);
            String strc00 = c00.getContents();
            //获取第一行,第二列的值
            Cell c10 = rs.getCell(1, 0);
            String strc10 = c10.getContents();
            //获取第二行,第二列的值
            Cell c11 = rs.getCell(1, 1);
            String strc11 = c11.getContents();
            System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());
            System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());
            System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());
            

如果仅仅是取得Cell的值,我们可以方便地通过getContents()方法,它可以将任何类型的Cell值都作为一个字符串返回。示例代码中Cell(0, 0)是文本型,Cell(1, 0)是数字型,Cell(1,1)是日期型,通过getContents(),三种类型的返回值都是字符型。

如果有需要知道Cell内容的确切类型,API也提供了一系列的方法。参考下面的代码片段:

String strc00 = null;
            double strc10 = 0.00;
            Date strc11 = null;
            Cell c00 = rs.getCell(0, 0);
            Cell c10 = rs.getCell(1, 0);
            Cell c11 = rs.getCell(1, 1);
            if(c00.getType() == CellType.LABEL)
            {
            LabelCell labelc00 = (LabelCell)c00;
            strc00 = labelc00.getString();
            }
            if(c10.getType() == CellType.NUMBER)
            {
            NmberCell numc10 = (NumberCell)c10;
            strc10 = numc10.getValue();
            }
            if(c11.getType() == CellType.DATE)
            {
            DateCell datec11 = (DateCell)c11;
            strc11 = datec11.getDate();
            }
            System.out.println("Cell(0, 0)" + " value : " + strc00 + "; type : " + c00.getType());
            System.out.println("Cell(1, 0)" + " value : " + strc10 + "; type : " + c10.getType());
            System.out.println("Cell(1, 1)" + " value : " + strc11 + "; type : " + c11.getType());
            

在得到Cell对象后,通过getType()方法可以获得该单元格的类型,然后与API提供的基本类型相匹配,强制转换成相应的类型,最后调用相应的取值方法getXXX(),就可以得到确定类型的值。API提供了以下基本类型,与Excel的数据格式相对应,如下图所示:




每种类型的具体意义,请参见Java Excel API Document。

当你完成对Excel电子表格数据的处理后,一定要使用close()方法来关闭先前创建的对象,以释放读取数据表的过程中所占用的内存空间,在读取大量数据时显得尤为重要。参考如下代码片段:

//操作完成时,关闭对象,释放占用的内存空间
            rwb.close();
            

Java Excel API提供了许多访问Excel数据表的方法,在这里我只简要地介绍几个常用的方法,其它的方法请参考附录中的Java Excel API Document。

Workbook类提供的方法

1. int getNumberOfSheets()
获得工作薄(Workbook)中工作表(Sheet)的个数,示例:

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            int sheets = rwb.getNumberOfSheets();
            

2. Sheet[] getSheets()
返回工作薄(Workbook)中工作表(Sheet)对象数组,示例:

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            Sheet[] sheets = rwb.getSheets();
            

3. String getVersion()
返回正在使用的API的版本号,好像是没什么太大的作用。

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            String apiVersion = rwb.getVersion();
            

Sheet接口提供的方法

1) String getName()
获取Sheet的名称,示例:

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            jxl.Sheet rs = rwb.getSheet(0);
            String sheetName = rs.getName();
            

2) int getColumns()
获取Sheet表中所包含的总列数,示例:

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            jxl.Sheet rs = rwb.getSheet(0);
            int rsColumns = rs.getColumns();
            

3) Cell[] getColumn(int column)
获取某一列的所有单元格,返回的是单元格对象数组,示例:

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            jxl.Sheet rs = rwb.getSheet(0);
            Cell[] cell = rs.getColumn(0);
            

4) int getRows()
获取Sheet表中所包含的总行数,示例:

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            jxl.Sheet rs = rwb.getSheet(0);
            int rsRows = rs.getRows();
            

5) Cell[] getRow(int row)
获取某一行的所有单元格,返回的是单元格对象数组,示例子:

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            jxl.Sheet rs = rwb.getSheet(0);
            Cell[] cell = rs.getRow(0);
            

6) Cell getCell(int column, int row)
获取指定单元格的对象引用,需要注意的是它的两个参数,第一个是列数,第二个是行数,这与通常的行、列组合有些不同。

jxl.Workbook rwb = jxl.Workbook.getWorkbook(new File(sourcefile));
            jxl.Sheet rs = rwb.getSheet(0);
            Cell cell = rs.getCell(0, 0);
            

2 生成新的Excel工作薄

下面的代码主要是向大家介绍如何生成简单的Excel工作表,在这里单元格的内容是不带任何修饰的(如:字体,颜色等等),所有的内容都作为字符串写入。(完整代码见ExcelWriting.java)

与读取Excel工作表相似,首先要使用Workbook类的工厂方法创建一个可写入的工作薄(Workbook)对象,这里要注意的是,只能通过API提供的工厂方法来创建Workbook,而不能使用WritableWorkbook的构造函数,因为类WritableWorkbook的构造函数为protected类型。示例代码片段如下:

import java.io.*;
            import jxl.*;
            import jxl.write.*;
            … … … …
            try
            {
            //构建Workbook对象, 只读Workbook对象
            //Method 1:创建可写入的Excel工作薄
            jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(new File(targetfile));
            //Method 2:将WritableWorkbook直接写入到输出流
            /*
            OutputStream os = new FileOutputStream(targetfile);
            jxl.write.WritableWorkbook wwb = Workbook.createWorkbook(os);
            */
            }
            catch (Exception e)
            {
            e.printStackTrace();
            }
            

API提供了两种方式来处理可写入的输出流,一种是直接生成本地文件,如果文件名不带全路径的话,缺省的文件会定位在当前目录,如果文件名带有全路径的话,则生成的Excel文件则会定位在相应的目录;另外一种是将Excel对象直接写入到输出流,例如:用户通过浏览器来访问Web服务器,如果HTTP头设置正确的话,浏览器自动调用客户端的Excel应用程序,来显示动态生成的Excel电子表格。

接下来就是要创建工作表,创建工作表的方法与创建工作薄的方法几乎一样,同样是通过工厂模式方法获得相应的对象,该方法需要两个参数,一个是工作表的名称,另一个是工作表在工作薄中的位置,参考下面的代码片段:

//创建Excel工作表
            jxl.write.WritableSheet ws = wwb.createSheet("Test Sheet 1", 0);
            

"这锅也支好了,材料也准备齐全了,可以开始下锅了!",现在要做的只是实例化API所提供的Excel基本数据类型,并将它们添加到工作表中就可以了,参考下面的代码片段:

//1.添加Label对象
            jxl.write.Label labelC = new jxl.write.Label(0, 0, "This is a Label cell");
            ws.addCell(labelC);
            //添加带有字型Formatting的对象
            jxl.write.WritableFont wf = new jxl.write.WritableFont(WritableFont.TIMES, 18, WritableFont.BOLD, true);
            jxl.write.WritableCellFormat wcfF = new jxl.write.WritableCellFormat(wf);
            jxl.write.Label labelCF = new jxl.write.Label(1, 0, "This is a Label Cell", wcfF);
            ws.addCell(labelCF);
            //添加带有字体颜色Formatting的对象
            jxl.write.WritableFont wfc = new jxl.write.WritableFont(WritableFont.ARIAL, 10, WritableFont.NO_BOLD, false,
            UnderlineStyle.NO_UNDERLINE, jxl.format.Colour.RED);
            jxl.write.WritableCellFormat wcfFC = new jxl.write.WritableCellFormat(wfc);
            jxl.write.Label labelCFC = new jxl.write.Label(1, 0, "This is a Label Cell", wcfFC);
            ws.addCell(labelCF);
            //2.添加Number对象
            jxl.write.Number labelN = new jxl.write.Number(0, 1, 3.1415926);
            ws.addCell(labelN);
            //添加带有formatting的Number对象
            jxl.write.NumberFormat nf = new jxl.write.NumberFormat("#.##");
            jxl.write.WritableCellFormat wcfN = new jxl.write.WritableCellFormat(nf);
            jxl.write.Number labelNF = new jxl.write.Number(1, 1, 3.1415926, wcfN);
            ws.addCell(labelNF);
            //3.添加Boolean对象
            jxl.write.Boolean labelB = new jxl.write.Boolean(0, 2, false);
            ws.addCell(labelB);
            //4.添加DateTime对象
            jxl.write.DateTime labelDT = new jxl.write.DateTime(0, 3, new java.util.Date());
            ws.addCell(labelDT);
            //添加带有formatting的DateFormat对象
            jxl.write.DateFormat df = new jxl.write.DateFormat("dd MM yyyy hh:mm:ss");
            jxl.write.WritableCellFormat wcfDF = new jxl.write.WritableCellFormat(df);
            jxl.write.DateTime labelDTF = new jxl.write.DateTime(1, 3, new java.util.Date(), wcfDF);
            ws.addCell(labelDTF);
            

这里有两点大家要引起大家的注意。第一点,在构造单元格时,单元格在工作表中的位置就已经确定了。一旦创建后,单元格的位置是不能够变更的,尽管单元格的内容是可以改变的。第二点,单元格的定位是按照下面这样的规律(column, row),而且下标都是从0开始,例如,A1被存储在(0, 0),B1被存储在(1, 0)。

最后,不要忘记关闭打开的Excel工作薄对象,以释放占用的内存,参见下面的代码片段:

//写入Exel工作表
            wwb.write();
            //关闭Excel工作薄对象
            wwb.close();
            

这可能与读取Excel文件的操作有少少不同,在关闭Excel对象之前,你必须要先调用write()方法,因为先前的操作都是存储在缓存中的,所以要通过该方法将操作的内容保存在文件中。如果你先关闭了Excel对象,那么只能得到一张空的工作薄了。

3 拷贝、更新Excel工作薄

接下来简要介绍一下如何更新一个已经存在的工作薄,主要是下面二步操作,第一步是构造只读的Excel工作薄,第二步是利用已经创建的Excel工作薄创建新的可写入的Excel工作薄,参考下面的代码片段:(完整代码见ExcelModifying.java)

//创建只读的Excel工作薄的对象
            jxl.Workbook rw = jxl.Workbook.getWorkbook(new File(sourcefile));
            //创建可写入的Excel工作薄对象
            jxl.write.WritableWorkbook  wwb = Workbook.createWorkbook(new File(targetfile), rw);
            //读取第一张工作表
            jxl.write.WritableSheet ws = wwb.getSheet(0);
            //获得第一个单元格对象
            jxl.write.WritableCell wc = ws.getWritableCell(0, 0);
            //判断单元格的类型, 做出相应的转化
            if(wc.getType() == CellType.LABEL)
            {
            Label l = (Label)wc;
            l.setString("The value has been modified.");
            }
            //写入Excel对象
            wwb.write();
            //关闭可写入的Excel对象
            wwb.close();
            //关闭只读的Excel对象
            rw.close();
            

之所以使用这种方式构建Excel对象,完全是因为效率的原因,因为上面的示例才是API的主要应用。为了提高性能,在读取工作表时,与数据相关的一些输出信息,所有的格式信息,如:字体、颜色等等,是不被处理的,因为我们的目的是获得行数据的值,既使没有了修饰,也不会对行数据的值产生什么影响。唯一的不利之处就是,在内存中会同时保存两个同样的工作表,这样当工作表体积比较大时,会占用相当大的内存,但现在好像内存的大小并不是什么关键因素了。

一旦获得了可写入的工作表对象,我们就可以对单元格对象进行更新的操作了,在这里我们不必调用API提供的add()方法,因为单元格已经于工作表当中,所以我们只需要调用相应的setXXX()方法,就可以完成更新的操作了。

尽单元格原有的格式化修饰是不能去掉的,我们还是可以将新的单元格修饰加上去,以使单元格的内容以不同的形式表现。

新生成的工作表对象是可写入的,我们除了更新原有的单元格外,还可以添加新的单元格到工作表中,这与示例2的操作是完全一样的。

最后,不要忘记调用write()方法,将更新的内容写入到文件中,然后关闭工作薄对象,这里有两个工作薄对象要关闭,一个是只读的,另外一个是可写入的。





回页首


小结

本文只是对Java Excel API中常用的方法作了介绍,要想更详尽地了解API,请大家参考API文档,或源代码。Java Excel API是一个开放源码项目,请大家关注它的最新进展,有兴趣的朋友也可以申请加入这个项目,或者是提出宝贵的意见。



参考资料

  1. Java Excel API 文档
  2. http://www.andykhan.com/jexcelapi/


关于作者

 

就叫我Rubber吧,我是一个Java迷,希望我们能成为朋友,我的Eamil: tim@trend.com.cn,我的联系电话0755-83501377

posted @ 2007-07-08 10:18 和田雨 阅读(258) | 评论 (0)编辑 收藏

大多数 Java 程序员都熟悉对 JAR 文件的基本操作。但是只有少数程序员了解 JAR 文件格式的 强大功能。在本文中,作者探讨了JAR 格式的许多功能和优势,包括打包、可执行的 JAR 文件、安全性和索引。

JAR 文件是什么?

JAR 文件格式以流行的 ZIP 文件格式为基础,用于将许多个文件聚集为一个文件。与 ZIP 文件不同的是,JAR 文件不仅用于压缩和发布,而且还用于部署和封装库、组件和插件程序,并可被像编译器和 JVM 这样的工具直接使用。在 JAR 中包含特殊的文件,如 manifests 和部署描述符,用来指示工具如何处理特定的 JAR。

一个 JAR 文件可以用于:

  • 用于发布和使用类库
  • 作为应用程序和扩展的构建单元
  • 作为组件、applet 或者插件程序的部署单位
  • 用于打包与组件相关联的辅助资源

JAR 文件格式提供了许多优势和功能,其中很多是传统的压缩格式如 ZIP 或者 TAR 所没有提供的。它们包括:

  • 安全性。可以对 JAR 文件内容加上数字化签名。这样,能够识别签名的工具就可以有选择地为您授予软件安全特权,这是其他文件做不到的,它还可以检测代码是否被篡改过。
  • 减少下载时间。 如果一个 applet 捆绑到一个 JAR 文件中,那么浏览器就可以在一个 HTTP 事务中下载这个 applet 的类文件和相关的资源,而不是对每一个文件打开一个新连接。
  • 压缩。JAR 格式允许您压缩文件以提高存储效率。
  • 传输平台扩展。 Java 扩展框架(Java Extensions Framework)提供了向 Java 核心平台添加功能的方法,这些扩展是用 JAR 文件打包的(Java 3D 和 JavaMail 就是由 Sun 开发的扩展例子)。
  • 包密封。存储在 JAR 文件中的包可以选择进行 密封,以增强版本一致性和安全性。密封一个包意味着包中的所有类都必须在同一 JAR 文件中找到。
  • 包版本控制。一个 JAR 文件可以包含有关它所包含的文件的数据,如厂商和版本信息。
  • 可移植性。处理 JAR 文件的机制是 Java 平台核心 API 的标准部分。

压缩的和未压缩的 JAR

jar 工具(有关细节参阅 jar 工具 )在默认情况下压缩文件。未压缩的 JAR 文件一般可以比压缩过的 JAR 文件更快地装载,因为在装载过程中要解压缩文件,但是未压缩的文件在网络上的下载时间可能更长。

META-INF 目录

大多数 JAR 文件包含一个 META-INF 目录,它用于存储包和扩展的配置数据,如安全性和版本信息。Java 2 平台识别并解释 META-INF 目录中的下述文件和目录,以便配置应用程序、扩展和类装载器:

  • MANIFEST.MF。这个 manifest 文件定义了与扩展和包相关的数据。
  • INDEX.LIST。 这个文件由 jar 工具的新选项 -i 生成,它包含在应用程序或者扩展中定义的包的位置信息。它是 JarIndex 实现的一部分,并由类装载器用于加速类装载过程。
  • xxx.SF。 这是 JAR 文件的签名文件。占位符 xxx标识了签名者。
  • xxx.DSA。 与签名文件相关联的签名程序块文件,它存储了用于签名 JAR 文件的公共签名。

jar 工具

为了用 JAR 文件执行基本的任务,要使用作为Java Development Kit 的一部分提供的 Java Archive Tool ( jar 工具)。用 jar 命令调用 jar 工具。表 1 显示了一些常见的应用:

表 1. 常见的 jar 工具用法

功能 命令
用一个单独的文件创建一个 JAR 文件 jar cf jar-file input-file...
用一个目录创建一个 JAR 文件 jar cf jar-file dir-name
创建一个未压缩的 JAR 文件 jar cf0 jar-file dir-name
更新一个 JAR 文件 jar uf jar-file input-file...
查看一个 JAR 文件的内容 jar tf jar-file
提取一个 JAR 文件的内容 jar xf jar-file
从一个 JAR 文件中提取特定的文件 jar xf jar-file archived-file...
运行一个打包为可执行 JAR 文件的应用程序 java -jar app.jar





回页首


可执行的 JAR

一个 可执行的 jar 文件是一个自包含的 Java 应用程序,它存储在特别配置的JAR 文件中,可以由 JVM 直接执行它而无需事先提取文件或者设置类路径。要运行存储在非可执行的 JAR 中的应用程序,必须将它加入到您的类路径中,并用名字调用应用程序的主类。但是使用可执行的 JAR 文件,我们可以不用提取它或者知道主要入口点就可以运行一个应用程序。可执行 JAR 有助于方便发布和执行 Java 应用程序。

创建可执行 JAR

创建一个可执行 JAR 很容易。首先将所有应用程序代码放到一个目录中。假设应用程序中的主类是 com.mycompany.myapp.Sample 。您要创建一个包含应用程序代码的 JAR 文件并标识出主类。为此,在某个位置(不是在应用程序目录中)创建一个名为 manifest 的文件,并在其中加入以下一行:

Main-Class: com.mycompany.myapp.Sample
            

然后,像这样创建 JAR 文件:

jar cmf manifest ExecutableJar.jar application-dir
            

所要做的就是这些了 -- 现在可以用 java -jar 执行这个 JAR 文件 ExecutableJar.jar。

一个可执行的 JAR 必须通过 menifest 文件的头引用它所需要的所有其他从属 JAR。如果使用了 -jar 选项,那么环境变量 CLASSPATH 和在命令行中指定的所有类路径都被 JVM 所忽略。

启动可执行 JAR

既然我们已经将自己的应用程序打包到了一个名为 ExecutableJar.jar 的可执行 JAR 中了,那么我们就可以用下面的命令直接从文件启动这个应用程序:

java -jar ExecutableJar.jar
            





回页首


包密封

密封 JAR 文件中的一个包意味着在这个包中定义的所有类都必须在同一个 JAR 文件中找到。这使包的作者可以增强打包类之间的版本一致性。密封还提供了防止代码篡改的手段。

要密封包,需要在 JAR 的 manifest 文件中为包添加一个 Name 头,然后加上值为“true”的 Sealed 头。与可执行的 JAR 一样,可以在创建 JAR 时,通过指定一个具有适当头元素的 manifest 文件密封一个 JAR,如下所示:

Name: com/samplePackage/
            Sealed: true
            

Name 头标识出包的相对路径名。它以一个“/”结束以与文件名区别。在 Name 头后面第一个空行之前的所有头都作用于在 Name 头中指定的文件或者包。在上述例子中,因为 Sealed 头出现在 Name 头后并且中间没有空行,所以 Sealed 头将被解释为只应用到包 com/samplePackage 上。

如果试图从密封包所在的 JAR 文件以外的其他地方装载密封包中的一个类,那么 JVM 将抛出一个 SecurityException

扩展打包
扩展为 Java 平台增加了功能,在 JAR 文件格式中已经加入了扩展机制。扩展机制使得 JAR 文件可以通过 manifest 文件中的 Class-Path 头指定所需要的其他 JAR 文件。

假设 extension1.jar 和 extension2.jar 是同一个目录中的两个 JAR 文件,extension1.jar 的 manifest 文件包含以下头:

Class-Path: extension2.jar
            

这个头表明 extension2.jar 中的类是 extension1.jar 中的类的 扩展类。extension1.jar 中的类可以调用 extension2.jar 中的类,并且不要求 extension2.jar 处在类路径中。

在装载使用扩展机制的 JAR 时,JVM 会高效而自动地将在 Class-Path 头中引用的 JAR 添加到类路径中。不过,扩展 JAR 路径被解释为相对路径,所以一般来说,扩展 JAR 必须存储在引用它的 JAR 所在的同一目录中。

例如,假设类 ExtensionClient 引用了类 ExtensionDemo ,它捆绑在一个名为 ExtensionClient.jar 的 JAR 文件中,而类 ExtensionDemo 则捆绑在 ExtensionDemo.jar 中。为了使 ExtensionDemo.jar 可以成为扩展,必须将 ExtensionDemo.jar 列在 ExtensionClient.jar 的 manifest 的 Class-Path 头中,如下所示:

Manifest-Version: 1.0
            Class-Path: ExtensionDemo.jar
            

在这个 manifest 中 Class-Path 头的值是没有指定路径的 ExtensionDemo.jar,表明 ExtensionDemo.jar 与 ExtensionClient JAR 文件处在同一目录中。





回页首


JAR 文件中的安全性

JAR 文件可以用 jarsigner 工具或者直接通过 java.security API 签名。一个签名的 JAR 文件与原来的 JAR 文件完全相同,只是更新了它的 manifest,并在 META-INF 目录中增加了两个文件,一个签名文件和一个签名块文件。

JAR 文件是用一个存储在 Keystore 数据库中的证书签名的。存储在 keystore 中的证书有密码保护,必须向 jarsigner 工具提供这个密码才能对 JAR 文件签名。



图 1. Keystore 数据库
Keystore 数据库

JAR 的每一位签名者都由在 JAR 文件的 META-INF 目录中的一个具有 .SF 扩展名的签名文件表示。这个文件的格式类似于 manifest 文件 -- 一组 RFC-822 头。如下所示,它的组成包括一个主要部分,它包括了由签名者提供的信息、但是不特别针对任何特定的 JAR 文件项,还有一系列的单独的项,这些项也必须包含在 menifest 文件中。在验证一个签名的 JAR 时,将签名文件的摘要值与对 JAR 文件中的相应项计算的摘要值进行比较。



清单 1. 签名 JAR 中的 Manifest 和 signature 文件
Contents of signature file META-INF/MANIFEST.MF
            Manifest-Version: 1.0
            Created-By: 1.3.0 (Sun Microsystems Inc.)
            Name: Sample.java
            SHA1-Digest: 3+DdYW8INICtyG8ZarHlFxX0W6g=
            Name: Sample.class
            SHA1-Digest: YJ5yQHBZBJ3SsTNcHJFqUkfWEmI=
            Contents of signature file META-INF/JAMES.SF
            Signature-Version: 1.0
            SHA1-Digest-Manifest: HBstZOJBuuTJ6QMIdB90T8sjaOM=
            Created-By: 1.3.0 (Sun Microsystems Inc.)
            Name: Sample.java
            SHA1-Digest: qipMDrkurQcKwnyIlI3Jtrnia8Q=
            Name: Sample.class
            SHA1-Digest: pT2DYby8QXPcCzv2NwpLxd8p4G4=
            

数字签名

一个数字签名是.SF 签名文件的已签名版本。数字签名文件是二进制文件,并且与 .SF 文件有相同的文件名,但是扩展名不同。根据数字签名的类型 -- RSA、DSA 或者 PGP -- 以及用于签名 JAR 的证书类型而有不同的扩展名。

Keystore

要签名一个 JAR 文件,必须首先有一个私钥。私钥及其相关的公钥证书存储在名为 keystores 的、有密码保护的数据库中。JDK 包含创建和修改 keystores 的工具。keystore 中的每一个密钥都可以用一个别名标识,它通常是拥有这个密钥的签名者的名字。

所有 keystore 项(密钥和信任的证书项)都是用唯一别名访问的。别名是在用 keytool -genkey 命令生成密钥对(公钥和私钥)并在 keystore 中添加项时指定的。之后的 keytool 命令必须使用同样的别名引用这一项。

例如,要用别名“james”生成一个新的公钥/私钥对并将公钥包装到自签名的证书中,要使用下述命令:

keytool -genkey -alias james -keypass jamespass
            -validity 80 -keystore jamesKeyStore
            -storepass jamesKeyStorePass
            

这个命令序列指定了一个初始密码“jamespass”,后续的命令在访问 keystore “jamesKeyStore”中与别名“james”相关联的私钥时,就需要这个密码。如果 keystore“jamesKeyStore”不存在,则 keytool 会自动创建它。

jarsigner 工具

jarsigner 工具使用 keystore 生成或者验证 JAR 文件的数字签名。

假设像上述例子那样创建了 keystore “jamesKeyStore”,并且它包含一个别名为“james”的密钥,可以用下面的命令签名一个 JAR 文件:

jarsigner -keystore jamesKeyStore -storepass jamesKeyStorePass
            -keypass jamespass -signedjar SSample.jar Sample.jar james
            

这个命令用密码“jamesKeyStorePass”从名为“jamesKeyStore”的 keystore 中提出别名为“james”、密码为“jamespass”的密钥,并对 Sample.jar 文件签名、创建一个签名的 JAR -- SSample.jar。

jarsigner 工具还可以验证一个签名的 JAR 文件,这种操作比签名 JAR 文件要简单得多,只需执行以下命令:

jarsigner -verify SSample.jar
            

如果签名的 JAR 文件没有被篡改过,那么 jarsigner 工具就会告诉您 JAR 通过验证了。否则,它会抛出一个 SecurityException , 表明哪些文件没有通过验证。

还可以用 java.util.jarjava.security API 以编程方式签名 JAR(有关细节参阅 参考资料)。也可以使用像 Netscape Object Signing Tool 这样的工具。





回页首


JAR 索引

如果一个应用程序或者 applet 捆绑到多个 JAR 文件中,那么类装载器就使用一个简单的线性搜索算法搜索类路径中的每一个元素,这使类装载器可能要下载并打开许多个 JAR 文件,直到找到所要的类或者资源。如果类装载器试图寻找一个不存在的资源,那么在应用程序或者 applet 中的所有 JAR 文件都会下载。对于大型的网络应用程序和 applet,这会导致启动缓慢、响应迟缓并浪费带宽。

从 JDK 1.3 以后,JAR 文件格式开始支持索引以优化网络应用程序中类的搜索过程,特别是 applet。JarIndex 机制收集在 applet 或者应用程序中定义的所有 JAR 文件的内容,并将这些信息存储到第一个 JAR 文件中的索引文件中。下载了第一个 JAR 文件后,applet 类装载器将使用收集的内容信息高效地装载 JAR 文件。这个目录信息存储在根 JAR 文件的 META-INF 目录中的一个名为 INDEX.LIST 的简单文本文件中。

创建一个 JarIndex
可以通过在 jar 命令中指定 -i 选项创建一个 JarIndex。假设我们的目录结构如下图所示:



图 2. JarIndex
JarIndex Demo

您将使用下述命令为 JarIndex_Main.jar、JarIndex_test.jar 和 JarIndex_test1.jar 创建一个索引文件:

jar -i JarIndex_Main.jar JarIndex_test.jar SampleDir/JarIndex_test1.jar
            

INDEX.LIST 文件的格式很简单,包含每个已索引的 JAR 文件中包含的包或者类的名字,如清单 2 所示:



清单 2. JarIndex INDEX.LIST 文件示例
JarIndex-Version: 1.0
            JarIndex_Main.jar
            sp
            JarIndex_test.jar
            Sample
            SampleDir/JarIndex_test1.jar
            org
            org/apache
            org/apache/xerces
            org/apache/xerces/framework
            org/apache/xerces/framework/xml4j
            





回页首


结束语

JAR 格式远远超出了一种压缩格式,它有许多可以改进效率、安全性和组织 Java 应用程序的功能。因为这些功能已经建立在核心平台 -- 包括编译器和类装载器 -- 中了,所以开发人员可以利用 JAR 文件格式的能力简化和改进开发和部署过程。.



参考资料

  • 您可以参阅本文在 developerWorks 全球站点上的 英文原文.

  • 参阅 jar 实用程序的命令行选项的文档。


  • Raffi Krikorian 在 ONJava 上发表的文章提供了有关 programmatically signing a JAR file的帮助。


  • 这篇关于 Java Archive Tool的文档解释了创建和操作 JAR 文件可以使用的选项。


  • 文章“ Java Web Start”( developerWorks,2001 年 9 月)描述了如何使用这种技术,以便允许应用程序可以指定所需的 JAR 文件并动态下载它们。


  • 有关 J2EE 服务器(比如 IBM WebSphere Application Server)使用 JAR 文件格式的不同方式,请参阅“ What are Java Archive (JAR) files?


  • JAR 格式是 WAR (Web Archive) 格式的基础,WAR用于在 J2EE 容器中部署 Servlet 和 JSP 应用程序。更多内容请参阅“ What are WAR files?”。


  • JAR 格式也是 EAR (Enterprise Archive) 格式的基础,EAR 用于在 J2EE 容器中部署 EJB。更多内容请参阅“ What are Enterprise Archive (EAR) files?”。


  • developerWorks Java 技术专区 上可以找到数百篇关于 Java 编程的各个方面的文章。



作者简介

Photo of Pagadala Suresh

Pagadala J. Suresh 是 IBM Global Services India 的软件工程师。他擅长的领域包括 Java 技术、WebSphere Application Server 和 WebSphere Studio Application Developer (WSAD)、Ariba Buyer。他参与了 WebSphere 的 IBM Redbook 项目。可以通过 pjsuresh@in.ibm.com 与 Pagadala 联系。


Photo of Palaniyappan Thiagarajan

Palaniyappan Thiagarajan 是位于印度班加罗尔的 IBM Global Services India 的软件工程师。他是IBM 认证的 IBM WebSphere Application Server V3.5 和 IBM DB2 UDB V7.1 Family Fundamentals 专家。可以通过 tpalaniy@in.ibm.com 与 Palaniyappan 联系。

posted @ 2007-07-08 10:17 和田雨 阅读(222) | 评论 (0)编辑 收藏

2001 年 11 月,IBM 向开源社区捐献了 Eclipse 代码,自此 Eclipse 在开发者社区得到飞速发展。2006 年 1月19日,Eclipse 国际高峰论坛也首次登陆中国,在清华大学信息科学技术大楼举行。在这次 Eclipse 大会上 developerWorks 编辑团队与 IBM 的 Eclipse 策略经理 John Kellerman 进行了座谈,聆听了他对 Eclipse 历史的追溯、对 Eclipse 架构的分析、对 Eclipse 现状分析、对未来发展的展望。

John Kellerman,IBM 软件部 Rational 产品总经理John Kellerman,IBM 软件部 Rational 产品总经理,与 1984 年加入 IBM, 一直从事应用开发工作。2000 年初,John 开始加入了 Eclipse 项目,现在任职 IBM Eclipse 策略部总经理。他现在的主要工作是在 Eclipse 基金会、Eclipse 社区中代表 IBM 的利益,与 Eclipse 成员公司合作。

developerWorks: IBM 开发工具的发展经历了从最初的 Visual Age到今天基于 Eclipse 3.0 的产品, 可以说是非常大的飞跃,您可否给我们谈一下这其中的历史?

John: 我在加入 Eclipse 项目之前,是 IBM Visual Age for SmallTalk 的产品经理。Eclipse 起源的一个非常重要的原因是,当时IBM 面临着一些挑战需要去解决,即 IBM 开发工具 Visual Age for Java 和 WebSphere Studio 很难集成到一起,而且底层的技术比较脆弱,因此进一步发展非常艰难,无法满足业界应用开发的需求。
因此,1998 年,我们开始了下一代开发工具技术探索之路,成立了一个项目开发小组,经过两年的发展,2000年,我们决定给新一代开发工具项目命名为 Eclipse,Eclipse 叫法当时只是内部使用的名称。这时候的商业目标就是希望 Eclipse 项目能够吸引开发人员,能发展起一个强大而又充满活力的商业合作伙伴(独立软件供应商)社区。同时我们意识到需要用它来对抗 Microsoft Visual Studio 的发展,因此从商业目标考虑,通过开源的方式我们最有机会达到目的。此外,IBM 推出了 Eclipse 试用计划,允许 IBM 以外的开发人员使用 Eclipse。结果证明我们的决策非常正确,Eclipse 从此在开发社区广为流传。Eclipse 也就成为这个项目的正式名称。

我们认为开源 Eclipse 不能只是简单地贡献出源码,创建一个开源项目然后放在开源许可证下;我们需要建立起多家公司的合作关系,否则就不足信服,不是真正的开源项目,而只是 IBM 的一个项目。因此在 2000 年到 2001 年之间,我做的主要工作就是去拜访一些公司,与他们谈论 Eclipse,邀请他们加入 Eclipse 项目。 您可以想象,这是非常有趣的谈话,最终,我们与 8 家公司达成了一致,其中包括一些 IBM 的竞争对手 WebGain、Borland, 还有一些盟友如 Rational(Rational 当时还没有被 IBM 收购)。

2001 年 12 月,IBM 向世界宣布了两件事,第一件是创建开源项目,既 IBM 捐赠价值 4 千万美元的源码给开源社区;另外一件事是成立 Eclipse 协会(Eclipse Consortium),这个协会由一些成员公司组成,主要任务是支持并促进 Eclipse 开源项目。

从此,我们看到了 Eclipse 本身、会员、插件(plug-in)和 Eclipse 社区飞速成长。2003 年,我们意识到这种会员模式很难进一步扩展,有些事务操作起来很困难,比如无法筹集赠款、无法成立合法理事会,这主要是因为 Eclipse 协会不是一个法律上的实体;此外,尽管 Eclipse 成功有目共睹,但仍然有些业界成员没有加入,他们认为是 Eclipse 的真正领导者是 IBM。因此我们认识到创建一个独立于 IBM 的 Eclipse 将会对 Eclipse 的发展非常有利, 于是 IBM 与其他成员公司合作起草了管理条例,准备成立 Eclipse 基金会(Eclipse Foundation)。2004 年初,Eclipse 基金会正式成立。

developerWorks: Eclipse 由一个很小的核心和核心之上的大量插件组成,这种插件式架构给当时的开发社区带来耳目一新的冲击。您能给我们介绍一下 Eclipse 插件架构吗?

John: 对于 Eclipse 插件架构,一个很好的比喻是 Web 浏览器,它也有插件,Eclipse 插件和浏览器相似,都是要解决扩展性问题。对于一个 Web 浏览器,如果你想既能浏览多媒体动画,又想浏览 PDF 文件,那么你就需要两个不同的插件,才能保证两种内容都能在 Web 浏览器中显示。 Eclipse 也一样,通过它的插件模式我们可以安装不同的插件来进行 HTML 的编辑、数据库的设计、Java 开发、C++ 开发等等,所有这些开发工作完全在一个 Eclipse 安装平台上进行。而且 Eclipse 模型所能做的远超过了这些,理解 Eclipse 插件的关键是首先要理解 Eclipse 扩展点,正是这个扩展点帮您在 Eclipse 中添加新功能。例如,如果希望在菜单中添加一项,就是通过 Eclipse 扩展点实现的。

所以当 Eclipse 插件实现了一个扩展点,就创建了一个扩展,此外,扩展还可以创建自己的扩展点。这种插件模式的扩展和扩展点是递归的,而且被证明是非常灵活的。记得当我们着手对 Eclipse 开发进行区域性调查时,最初的决定之一是:"We will eat our own dog food"(编者注:谚语,指一个公司在日常运行中使用自己的产品),即 Eclipse 团队要使用 Eclipse 进行 Eclipse 的开发。事实上,Eclipse 本身是一个核心的运行时绑定了一些在插件之上构建的插件,换句话说,Eclipse 本身就是一系列的插件。这样随着使用 Eclipse 构建 Eclipse 的经验累积,这种插件模式就变得日臻成熟。

developerWorks: IBM 对 Eclipse 已经投入了非常多的资源,IBM 在 Eclipse 的将来会扮演什么角色呢?

John: 目前 Eclipse 项目有 100 多个,IBM 参与了其中超过一半的项目,有 3 个主管在主持 Eclipse 相关的工作。2005 年我们还赞助了一些 Eclipse 创新基金和 Eclipse 奖学金。IBM 承诺在未来的几年内会一如既往地参与 Eclipse 研究,我们将会看到 IBM 研发出新项目,而在某些 Eclipse 项目上减少参与,这要看 IBM 的商业目标。最近我们提议了一个项目叫 Beacon, 是关于 Eclipse 流程框架的。我们将捐献一些 Rational Unified Process (RUP) 技术,基本上这个提议是要提供流程方面的内容。IBM 现在与一家叫做 Zend 的公司合作,希望在 Eclipse 上开发出用于 PHP 的开发工具。从商业方面看 IBM, 您会发现 Eclipse 是 Rational 软件开发平台的基础。此外由于 Eclipse 的插件模式、扩展性、集成性,我们为Rational, WebSphere, Lotus 和 Tivoli 构建了统一的平台。

developerWorks: 目前 IBM 的五大旗舰品牌都有基于 Eclipse 的产品, 这对 IBM 有什么好处?

John: 我们的目标是创建集成统一平台,我们有基于 Eclipse 构建的 DB2, Rational, WebSphere 等工具,这样客户就可以共同使用这些工具,有统一的界面风格,使用起来非常方便。对 IBM 自身来说,各个开发部门就可以关注于软件的核心性能开发,而不必要浪费资源做一些重新实现的工作,从而 DB2 开发部门就专注于数据库开发、WebSphere 专注应用服务器的开发、Rational 专注于建模、源代码管理,至于其他方面的辅助开发对于 Eclipse 来说是共同的。

developerWorks: Eclipse 是一个开源 IDE, 那如何通过 Eclipse 创造商业利益呢?

John: 对于 IBM, 在 Eclipse 技术开发方面与上百家公司合作,因为 IBM 看到了 Eclipse 作为通用的、开放的、标准的技术基础,其价值日益成长。当然,这种基础技术是不会带来创收的,没有人可以使用 Eclipse 技术来赚钱,但这并不重要,就像 Borland, Sybase 等公司都在花费钱做同样的构建工作,当然我们在这方面还有合作,但是我们还专注于将我们的核心功能组件构建在 Eclipse 平台上,例如,IBM 的 Rational Application Developer, 它是使用 Eclipse 作为基础,然后在这基础上添加了建模插件、Web 开发插件,还有其它更有价值的插件,这些插件我们是收费的。这就是使用 Eclipse 进行商业创收的模式之一。
此外,人们还可以直接为 Eclipse 构建插件,然后出售这些插件来赚钱,因为插件可以使用商业许可证发行。事实上我们建立 Eclipse 公共许可证,就是为了让 Eclipse 能很好的被商业采用,从而可以用它来赚钱,Eclipse 的所有成员公司基本都是商业软件提供商。目前全球有上百万开发人员在使用 Eclipse, 因此中国开发人员完全可以构建有价值的插件和工具,然后进行销售。同时 IBM 有一个合作伙伴计划,帮助合作伙伴宣传在 IBM 基于 Eclipse 的产品之上构建的应用。

developerWorks: 富客户端平台(RCP)目前是比较流行的应用开发模式,请谈一下 Eclipse 是如何支持富客户端的?

John: 在 Eclipse 的早期,有些用户试图除了将 Eclipse用于开发工具基础平台外,还用作更普遍的商业应用基础。他们认为一个 IDE 就是一个特殊的富客户端应用。这就是开放源码开发的魅力所在,当这些用户试着用 Eclipse 作为通用富客户端时,他们就把这些想法建议反馈给 Eclipse 开发小组。就这样 Eclipse 经过了 2.0 到 2.1 的发展,不断收到来自社区的建议和反馈,终于到了一个通用化的阶段。在3.0 版发行时,我们觉得时机成熟,于是正式声明将 Eclipse 作为通用的富客户端和 IDE。

其实最早的反馈是来自我们的 Lotus 开发小组,他们在 Eclipse 3.0 之前就意识到可以使用 Eclipse 来提供他们 Workplace 客户端的富客户端版本。从 Eclipse 3.0 到 3.1 再到 3.2, 我们看到富客户端平台应用的快速增长,同时也收到越来越多反馈帮我们完善提高。

我最欣赏的 RCP 应用之一是在EclipseCon 2005 上的一个演示,演示的是美国国家航空航天管理局(NASA)的一个项目,当时 NASA 在加利福尼亚州有个实验室叫 Jet Propulsion Laboratory (JPL),负责火星探测计划,他们的管理用户界面就是一个 Eclipse RCP 应用,通过这个应用,加利福尼亚州的工作人员就可以控制在火星上运行的火星车。在演示过程中,有人问为什么使用 Eclipse, 回答是,使用 Eclipse 这门技术,他们不用担心,而且还节省了不少纳税人的钱,因为他们只需要集中资源开发控制火星车的应用就可以了。

developerWorks: Eclipse 现在非常热门,您认为从微软的开发平台转到 Eclipse 上容易吗?

John: 这个问题可以从两方面来说明,一是软件提供商从 Visual Studio 移到 Eclipse 上,另一方面是微软平台上开发的程序的移植。

Eclipse 提供了不同的集成方法,可以是非常轻量级的集成,即只是简单的调用和返回。Eclipse可以处理 ActiveX 控件,这样 Word, Excel 就可以在 Eclipse 里打开,这是另一种集成模式。有些供应商选择把他们的工具封装在 Java 层,即保留 C/C++ 工具,封装在 Java 里与 Eclipse 进行通信。如果软件供应商希望将工具构建成 Eclipse 插件,那么我们通常建议在 Eclipse 中用 Java 重新实现,因为几乎所有的微软工具都是 C/C++ 工具。因此工具的迁移可以分三步走:轻量级集成、重量级集成和重新实现。

如果要迁移程序,Eclipse 提供了一些设施。但难易程度取决于程序是用什么工具开发的,因为最重要的部分是(版本控制系统的)代码库本身,如果代码库同时支持 Visual Studio 和 Eclipse, 那就容易的多。此外有一些公司提供了迁移系统,在 Eclipse 网站的社区部分中可以查看这些公司列表。

developerWorks: 对个体开发人员,他们如何能够参与 Eclipse 项目?

John: Eclipse 是完全开放的,任何人都可以参与,参与的最简单方法是关注它的新闻组,选择一个感兴趣的技术领域,然后下载代码,自己做些实验,并在新闻组上回答一些问题,这样随着技术的提高,就可以向 Bugzilla 提交 bug 和修复包,或去修复其他 bug。如果得到项目工作组的认可,便可以承担项目的一些义务,成为项目开发组的真正成员。此外还可以写一些文章发送到 Eclipse 组织,参加本地的 Eclipse 用户组等等方式。

如果有兴趣成立一个 Eclipse 开源项目,Eclipse 上有一个文档"Eclipse Development Process",描述了如何操作。

developerWorks: 最后请给我们谈谈 Eclipse 下个版本的情况?

John: Eclipse 3.2 计划于今年 6 月面世,名称是 Callisto, 选择 Callisto 作为名称是按社区要求的,因为这次目标是同时发布 10 个主要的 Eclipse 项目,以支持成员公司生态系统需求,他们将要把 Eclipse 框架集成到各自的软件产品中。

developerWorks: 感谢您接受我们的采访。

posted @ 2007-07-08 10:03 和田雨 阅读(255) | 评论 (0)编辑 收藏

使用 Java 语言所进行的面向对象编程变得空前普及。它使软件开发发生了某种程度上的变革,但最近的研究表明,有半数软件开发项目滞后,而三分之一的项目则超出预算。问题不在于技术,而是开发软件所使用的方法。所谓的“轻量型”或“灵活”方式,与如 Java 这样的面向对象语言的威力和灵活性结合起来,提供了一种很有意思的解决方案。最常见的灵活方式称为极端编程(Extreme Programming)或者 XP,但许多人并不真正了解它。对 Java 项目使用 XP 可以大大增加成功的机会。本文提供了 XP 的概述,并解释了它为什么很重要 -- 不是传言,也没有骗局。

在过去的十年中,CEO 们在产生稳步增加的收入方面面临巨大的压力。他们通过在许多方面采取一系列举措来解决这一问题,例如缩小公司规模、外包、再工程、企业资源规划 (ERP) 等等。这些对低效率的解决措施让 S&P 500 中的许多企业在 90 年代末能够连续几年保持两位数的收入增长。但这种方式也带来了一些负面影响。

在 Gary Hamel 所著的 Leading the Revolution(请参阅 参考资料)一书中,他声称已有一些迹象证明传统企业模式的优势已不那么明显。我们必须找到一些替代方法来为收入的持续增长提供动力。他建议唯一能让公司继续增长的办法是进行一次彻底的创新。我们认为在软件开发领域中尤其需要这样。

企业问题

如果使用标准软件开发方法,那么即使在 Java 平台上进行开发,也要做好失望的准备。如图 1 所示,最近的研究表明,有一半项目将滞后,而三分之一的项目将超过预算。这一推测比 1979 年由美国总审计局的研究结果好不了多少。



图 1. 软件项目成功和失败,过去和现在
软件项目成功和失败的统计

如果我们希望这些数字有显著提高,则需要彻底创新的方法来开发软件。有两个主要因素影响现有的方法:

  • 惧怕失败
  • 对软件本质的误解

没有人打算失败。具有讽刺意味的是为使失败最小化而创建的方法是失败的。对软件的误解是问题的根源。恐惧实际上只是一种症状。现有的方法是由那些有良好愿望但忘记了软件中的“软”的那些聪明人所创建的。他们假定开发软件就象造桥。因此他们从各种设计规范中借鉴了一些适用于“硬”物体(例如桥梁)的最优方法。结果是基于 Big Design Up-front (BDUF) 思想的反映迟钝的开发方法,软件不堪一击,人们无法使用它们。





回页首


一种解决方案:灵活方法

最近发生了一些转变,从所谓的“重量型”方法转向了“轻量型”或“灵活”方法,例如 Crystal 方法、适应性软件开发和(当前最流行的)XP。所有这些过程都有这样一个事实,即需要人们共同来开发软件。成功的软件过程必须将人们的长处最大化,将他们的缺点最小化,因为优点和缺点毋庸质疑都存在。在我们看来,XP 最出色的地方在于它能够解决所有影响参加人员的互补力量。

XP 提供了十年来最大的一次机会,给软件开发过程带来彻底变革。就象 Peopleware 作家 Tom DeMarco 所说,“XP 是当今我们所处领域中最重要的一项运动。预计它对于目前一代的重要性就象 SEI 及其能力成熟度模型对上一代的重要性一样。”

XP 规定了一组核心价值和方法,可以让软件开发人员发挥他们的专长:编写代码。XP 消除了大多数重量型过程的不必要产物,通过减慢开发速度、耗费开发人员的精力(例如干特图、状态报告,以及多卷需求文档)从目标偏离。我们认识到一个称为“极端编程”的东西可能很难作为正式的开发过程推荐给管理层,但如果您的公司从事软件行业,您应该帮助管理层绕过其名称认识到 XP 可以提供的竞争优势。

Kent Beck 在他所著的 Extreme Programming Explained: Embrace Change一书中概括了 XP 的核心价值(请参阅 参考资料)。我们对它们进行了总结:

  • 交流。 项目的问题往往可以追溯到某人在某个时刻没有和其他人一起商量某些重要问题上。使用 XP,不交流是不可能的事。
  • 简单。 XP 建议您总是尽可能围绕过程和编写代码做最简单的事情。按照 Beck 的说法,“XP 就是打赌。它打赌今天最好做些简单的事...而不是做更复杂但可能永远也不会用到的事。”
  • 反馈。 更早和经常来自客户、团队和实际最终用户的具体反馈意见为您提供更多的机会来调整您的力量。反馈可以让您把握住正确的方向,少走弯路。
  • 勇气。 勇气存在于其它三个价值的环境中。它们相互支持。需要勇气来相信一路上具体反馈比预先知道每样事物来得更好。需要勇气来在可能暴露您的无知时与团队中其他人交流。需要勇气来使系统尽可能简单,将明天的决定推到明天做。而如果没有简单的系统、没有不断的交流来扩展知识、没有掌握方向所依赖的反馈,勇气也就失去了依靠。

XP 的方法将这些价值转换成开发人员每天应做的事情。这里没什么新鲜内容。多年以来,行业认识到 XP 方法是“最优方法”。实际上,XP 中的“极端”来自两方面:

  • XP 采取经过证明的业界最优方法并将其发挥到极致。
  • XP 将这些方法以某种方式进行结合,使它们产生的结果大于各部分的总和。

这是怎样的情景呢?代码复查是个好的做法,因此始终通过成对地编写代码来做到。测试也是个好的做法,因此总是通过在编写代码之前编写测试来做到。文档很少与代码保持一致,因此只做那些最需要的事,余下的部分则取决于明确编写的代码和测试。XP 不保证人们总做正确的事,但它允许人们这样做。它将这些“极端”方法以一种相互支持的方式结合起来,显著提高了速度和有效性。





回页首


XP 的十二种方法

XP 的十二种方法(如图 2 所示)将其定义为规则。让我们仔细研究每一个方法来对“执行 XP”表示什么有个更全面的理解。



图 2. XP 的十二种方法

规划策略
有些人会指责 XP 是一种美其名的剽窃,只是一群牛仔在没有任何规则的情况下将一个系统拼凑在一起。错。XP 是为数不多的几种承认您在开始时不可能事事通晓的方法之一。无论是用户还是开发人员都是随着项目的进展过程才逐步了解事物的。只有鼓励和信奉这种更改的方法才是有效方法。状态限定方法忽视更改。而 XP 则留心更改。它倾听所用的方法就是“规划策略”,一个由 Kent Beck 创造的概念。

这一方法背后的主要思想是迅速地制定粗略计划,然后随着事物的不断清晰来逐步完善。规划策略的产物包括:一堆索引卡,每一张都包含一个客户素材,这些素材驱动项目的迭代;以及对下一两个发行版的粗略计划,如 Kent Beck 和 Martin Fowler 在他们的 Planning Extreme Programming中描述的那样(请参阅 参考资料)。让这种形式的计划得以发挥作用的关键因素是让用户做企业决策,让开发小组做技术决策。如果没有这一前提,整个过程就会土崩瓦解。

开发小组要决定:

  • 估计开发一个素材要花多长时间
  • 使用各种技术选项所花费的成本
  • 团队组织
  • 每个素材的“风险”
  • 迭代中素材开发的顺序(先开发风险最大的那一个可以减轻风险)

客户需要决定:

  • 范围(一个发行版的素材和每一次迭代的素材)
  • 发行日期
  • 优先级(根据企业价值先开发哪些特性)

规划经常发生。这样,在客户或开发人员学习新事物的同时,就为他们调整计划提供了频繁机会。

成对编程
使用 XP,成对的开发人员编写所有产品代码。这种方式听上去好象缺乏效率。Martin Fowler 说,“当人们说成对编程降低生产力时,我回答,‘那是因为大多数耗费时间的编程部分是靠输入来完成的。’”实际上,成对编程无论在经济还是其它方面都提供了许多好处:

  • 所有设计决策都牵涉到至少两个人。
  • 至少有两个人熟悉系统的每一部分。
  • 几乎不可能出现两个人同时疏忽测试或其它任务。
  • 改变各对的组合在可以在团队范围内传播知识。
  • 代码总是由至少一人复查。

研究还显示成对的编程实际上比单独编程更有效(有关详细信息,请参阅 参考资料 中 Alistair Cockburn 和 Laurie Williams 所著的 The Costs and Benefits of Pair Programming)。

测试
在 XP 中有两种测试:

  1. 单元测试
  2. 验收测试

开发人员在他们编写代码的同时编写单元测试。客户在他们定义了素材后编写验收测试。单元测试及时告诉开发人员系统在某一点上是否“工作”。验收测试告诉团队系统是否执行用户希望它执行的操作。

假设团队使用的是如 Java 这样的面向对象语言,开发人员在为一些方法编写代码之前为每种有可能失败的方法编写单元测试。然后他们编写足够的代码使之能通过测试。有时人们会发现这有点奇怪。答案很简单。编写测试首先为您提供:

  • 一组可能最完整的测试
  • 可能能工作的最简单的代码
  • 代码意图的明确景象

开发人员只有在通过所有单元测试后才可以将代码检入到源代码资源库中。单元测试使开发人员有信心相信他们的代码能够工作。这为其他开发人员留下线索,可以帮助他们理解最早的开发人员的意图(实际上,这是我们看到过的最好的文档)。单元测试还给了开发人员勇气重新划分代码,因为测试失败可以立刻告诉开发人员存在错误。应该将单元测试自动化,并提供明确的通过/失败结果。xUnit 框架(请参阅 参考资料 )做到的远不止这些,因此大多数 XP 小组都使用它们。

用户负责确保每个素材都有验收测试来确认它们。用户可以自己编写测试、可以征募组织中的其他成员(例如 QA 人员或业务分析员)编写它们,也可以将这两种方法结合起来。测试告诉他们系统是否具有应该具有的那些特性,以及是否可以正确工作。理想情况下,用户在迭代完成之前就应该写好迭代中那些素材的验收测试了。应该将验收测试自动化,并要经常运行来确保开发人员在实现新特性时没有破坏任何现有的特性。通常情况下,客户需要来自开发团队的某些帮助来编写验收测试。我们对一个项目开发一个可重用的自动验收测试框架,可以让用户在简单编辑器中输入他们的输入和所期望的输出。框架将输入转换成 XML 文件、运行文件中的测试,然后为每个测试显示“通过”或“失败”。客户喜欢这一做法。

不是所有验收测试都必须在所有情况下通过。问题是验收测试帮助客户衡量项目“完成”的情况如何。它们还可以让客户获悉有关某些事物是否可以发行的决定。

重新划分
重新划分是在不更改功能性的前提下对代码加以改进。XP 小组在进行重新划分时毫不手软。

开发人员重新划分有两个重要时机:实现特性之前和之后。开发人员尝试确定更改现有代码是否可以让新特性的开发更容易。他们查看刚刚写好的代码,看是否有方法可以对它进行简化。例如,如果他们认为有抽象的机会,就会进行重新划分来从具体实现中除去重复的代码。

XP 建议您应该编写可能运行的最简单的代码,但同时也建议您应该不断学习。重新划分让您将学到的知识加入到代码中,同时又不会破坏测试。它使您的代码简练。这意味着它可以存在相当长的时间、为以后的开发人员引入更少问题,并为他们指引正确的方向。

简单的设计
XP 的诽谤者说该过程忽略了设计。事实不是这样。问题是重量型方法建议您做的不过是提前完成大部分琐碎的设计任务。这就象是拍一张静态的地平线的照片,静止不动,然后尝试画一张如何到达那里的完美的地图。XP 说设计不应该在事物将保持不变的前提下预先仓促进行。XP 认为设计非常重要,因此应该是一个持续的事务。我们总是先尝试使用能够工作的最简单的设计,然后随着现实的不断显现来更改它。

什么是可能工作的最简单的设计?它是符合以下条件的设计(感谢 Kent Beck 为我们一一列出):

  • 运行所有测试
  • 不包含重复代码
  • 明确陈述程序员对所有代码的意图
  • 包含尽可能最少的类和方法

对简单设计的需求并不是说所有设计都很小,也不表示它们是无足轻重的。它们只不过需要尽可能简单,但是仍能工作。不要包括不使用的额外特性。我们称这样的事物为 YAGNI,表示“您将不需要它(You Aren't Going to Need It)。”不要让 YAGNI 破坏您成功的机会。

集合体代码所有权
小组中的任何人都应该有权对代码进行更改来改进它。每个人都拥有全部代码,这意味着每个人都对它负责。这种技术可以让人们对部分代码进行必要的更改而不用经过代码拥有者个人的瓶颈。每个人都负责这一事实消除了无代码所有权所带来的混乱。

“每人拥有所有代码”与“没人拥有代码”的说法并不一样。没人拥有代码时,人们可以随处进行破坏而不必负任何责任。而 XP 说,“如果是您破坏的,应该您来弥补。”我们有一些必须在每次集成之前和之后运行的单元测试。如果您破坏了某些事物,您要负责进行修补,无论它位于代码的哪一部分。这需要极端规则。可能这是名称中带有“极端”的另一个原因。

持续的集成
经常进行代码集成可以帮助您避免集成梦魇。XP 团队在一天中集成了代码几次,每次都在所有单元测试对系统运行后执行。

传统方法工作方式如下:编写大量代码后执行一次大爆炸式的集成,然后花费相当长的时间来改正问题。这种笨拙的形式的确使项目速度减缓。大爆炸式的集成给团队立即带来大量问题,并且这些问题通常都有几百种可能的原因。

如果经常进行集成,任何特定集成失败的原因都会非常明显(以前运行过测试,因此错误一定是新事物犯下的)。使用这种方法,当遇到问题时,可能的原因就相当有限。修改起来更容易,花的时间少得多,使团队保持最快速度前进。

现场客户
要使功能最理想,XP 小组需要在现场有一位客户来明确素材,并做出重要的企业决策。开发人员是不允许单独做这些事情的。让客户随时在场可以消除开发人员等待决策时出现的瓶颈。

XP 不会假装素材卡是开发人员交付必要代码所需的唯一指示。素材是对以后在客户和开发人员之间填写细节的对话的一项承诺。与将所有要求写在一个静态文档中不同,其思想是进行面对面的交流,减少产生误解的机会。

我们发现让客户在现场是可能最好的一种情形,但这不是解决问题的唯一方案。底线是客户必须随时在需要回答问题和根据企业价值为团队提供指示时有空。如果客户并非在现场专职陪伴团队的情况下就能做到这些,那很好。如果能和团队待在一起,这会更方便,但只是建议而已。

小发行版
发行版应该尽可能地小,同时仍然提供足够的企业价值以证明它们值得。

只要觉得有意义就可以发布系统。这样就尽可能早为用户提供了价值(请记住,今天的钱比明天的钱来得值钱)。小发行版将为开发人员提供具体的反馈意见,告诉他们哪些符合客户需要,哪些不符合客户需要。然后,小组可以将这些经验教训包括在其下一发行版的规划中。

一周 40 小时
Kent Beck 说他希望“...每天早晨都感到有活力有激情,每天晚上都感到疲惫而满足。”一周 40 小时工作可以让您做到这一点。确切的小时数并不重要,重要的是原则。长时间地持续工作会扼杀工作绩效。疲劳的开发人员会犯更多错误,从长期来说,将比按“正常”时间表进行的开发慢得多。

即使开发人员可以在长时间很好工作,这也不意味着他们应该这样。最终他们会厌倦,会离开他们的工作,或者产生影响他们工作绩效的非工作问题。如果您打乱了人们的生活,将会尝到它所带来的恶果。加班并不是解决项目问题的答案。实际上,它是更大问题的症状。如果您要走向灭亡,就无药可救了。

编码标准
拥有编码标准有两个目的:

  • 防止团队被一些例如事物没有以最大速度发展这种无关紧要的愚蠢争论搞得不知所措。
  • 它支持其它方法。

如果没有编码标准,重新划分代码会更加困难,按应当的频度交换对更困难,快速前进也更困难。目标应该是团队中没有人辨认得出是谁写的哪一部分代码。以团队为单位对某一标准达成协议,然后遵守这一标准。目标不是创建一个事无巨细的规则列表,而是提供将确保您的代码可以清晰交流的指导方针。编码标准开始时应该很简单,然后根据团队经验逐步进化。不要预先花费太多时间。创建能够工作的最简单标准,然后逐步发展。

系统比喻
体系结构是做什么用的?它提供了系统各种组件以及它们是如何交互的画面 -- 一种映射,可以让开发人员了解新的代码部分适合放在哪里。

XP 中的系统比喻与大多数方法称作的体系结构差不多。比喻为团队提供了一致的画面,他们可以用它来描述现有系统的工作方式、新部件适合的位置,以及它们应该采取的形式。

重要的是要记住,关键要让每个人理解系统是如何组合在一起的,而不是美丽的比喻。有时您就是无法想到一个好的比喻。想到时就太好了。





回页首


一起工作的方法

整体大于各个部分之和。您可以实现单一方法或一小部分方法,比不使用任何方法得到更大收益。但您只能在实现所有方法的情况下获得最大收益,因为它们的力量来自它们之间的交互。

最初时按照书籍来执行 XP,作为基准。一旦理解了如何进行交互,就拥有了将它们适应于自身环境所需的知识。请记住,“进行 XP”不是目的,而是到达终点的一种手段。目标是快速地开发高级软件。如果您的过程有一些变异,已称不上是在进行 XP,但结果仍能让您战胜所有竞争对手,您已经成功了。





回页首


为什么 XP 很重要

坦率地说,XP(或者任何其它灵活方法)根本就不重要。它能够产生的 结果 才是关键。如果如 XP 这样的灵活方式可以帮助您更快地开发更好的软件而少受痛苦,那么它值得考虑。

还记得我们在这篇文章开始时提到的那些令人生畏的数字吗?我们相信使用 XP 开发面向对象软件可以有机会将它们变得更好。目前我们的经验已经证实了这一信念。



参考资料



作者简介

 

Roy W. Miller 是 RoleModel Software, Inc 的软件开发人员。在 RoleModel 期间,Roy 开发了基于 Java/XML 的自动验收测试框架,并为 Dallas Semiconductor 的 TINI Java 平台创建了几个应用的原型。在加盟 RoleModel 之前,他在 Andersen Consulting(现在是 Accenture)中服务了六年,用过他们的专用重量型商业集成方法 (MIB) 及其变体。自从加盟 RoleModel 后,他获得了许多有关 XP 以及对该方法的本地适应的经验。Roy 与他人合著了 Addison-Wesley XP 系列中的一本书( XP Applied ,将于 2001 年 10 月出版),并是在 XP2001 "Business of XP" 展示会上的一名特邀专题讨论小组成员。可以通过 rmiller@rolemodelsoft.com 与 Roy 联系。


 

Christopher T. Collins 是 RoleModel Software, Inc 的高级软件开发人员。在 RoleModel 期间,Chris 参与了运行将近两年的一个 XP 项目,为新的摩托罗拉蜂窝式电话平台开发了一个嵌入式 Java 应用程序,并将 JUnit 移植到 Sun 的 J2ME 平台上运行。在加盟 RoleModel 之前,他花了 5 年的时间使用许多不同的语言为一些组织开发软件,最近的一次是为美国国防部开发应用程序。他拥有使用和适应涉及几种灵活和重量型的不同开发方法的经验,包括 RUP 和 XP。Chris 拥有美国西佛罗里达大学计算机科学和软件工程的硕士学位,目前在北卡罗来纳州立大学教授 Java 编程课程。他曾是杜克大学有关 XP 的特邀演讲人,并将在 XP2001 上介绍有关过程适应的论文。通过 ccollins@rolemodelsoft.com 与 Chris 联系。

posted @ 2007-07-06 16:34 和田雨 阅读(191) | 评论 (0)编辑 收藏

2000 年 11 月 01 日

这是篇细探 JAXP,Sun 的 Java API for XML 的文章,帮助解除了有关 JAXP 本质和服务目的的疑惑。本文讲解了 JAXP 的基本概念,演示 XML 语法分析为什么需要 JAXP,并显示如何轻易更改 JAXP 使用的语法分析器。本文还进一步讲述了 SAX 和 DOM 这两个流行的与 JAXP 相关的 Java 和 XML API。

Java 和 XML 在每一个技术领域都制造了新闻,并且对于软件开发人员来说,似乎是 1999 年和 2000 年最重要的发展。结果,Java 和 XML API 的数量激增。其中两个最流行的 DOM 和 SAX 还引起极大兴趣,而 JDOM 和数据绑定 API 也接踵而来。只透彻理解这些技术中的一个或两个就是一项艰巨任务,而正确使用所有这些技术就会使您成为专家。但在去年,另一个 API 给人留下了深刻印象,它就是 Sun 的 Java API for XML,通常称为 JAXP。如果考虑到 Sun 在其平台上还没有任何特定于 XML 的产品,那么这个进展就不足为奇。而令人惊奇的是人们对 JAXP 了解的缺乏。多数使用它的开发人员在他们所用的这个 API 的概念理解上都有错误。

什么是 JAXP?

本文假设您有 SAX 和 DOM 的基本知识。这里实在没有足够篇幅来解释 SAX、DOM 和 JAXP。如果您是 XML 语法分析的新手,那么可能要通过联机资源阅读 SAX 和 DOM,或者浏览我的书。( 参考资源 一节中有至 API 和我的书的链接。)获得基本知识后再看本文会比较好。





回页首


API 还是抽象?

在讲解代码之前,介绍一些基本概念很重要。严格地说,JAXP 是 API,但是将其称为抽象层更准确。它不提供处理 XML 的新方式,不补充 SAX 或 DOM,也不向 Java 和 XML 处理提供新功能。(如果在这点上理解有误,则本文正好适合您!)它只是使通过 DOM 和 SAX 处理一些困难任务更容易。如果在使用 DOM 和 SAX API 时遇到特定于供应商的任务,它还使通过独立于供应商的方式处理这些任务成为可能。

虽然要分别讲述所有这些特性,但是真正需要掌握的是:JAXP 不提供语法分析功能 !没有 SAX、DOM 或另一个 XML 语法分析 API,就 无法分析 XML 语法 。有很多人曾让我将 DOM、SAX 或 JDOM 与 JAXP 进行对比。但进行这些对比是不可能的,因为前三个 API 与 JAXP 的目的完全不同。SAX、DOM 和 JDOM 都分析 XML 语法。而 JAXP 却提供到达这些语法分析器和结果的方式。它自身不提供分析文档语法的新方法。如果要正确使用 JAXP,则一定要弄清这点。这将使您比其它 XML 开发人员领先一大截。

如果仍然怀疑(或认为我故弄玄虚),请从 Sun 的 Web 站点下载 JAXP 分发(请参阅 参考资料 一节),然后就会知道基本 JAXP 是什么。在包括的 jar ( jaxp.jar ) 中 只有六个类 !这个 API 会有多难哪?所有这些类( javax.xml.parsers 包的一部分)都位于现有语法分析器之上。这些类中的两个还用于错误处理。JAXP 比人们想象的要简单得多。那么,为什么还感到困惑哪?





回页首


Sun 的 JAXP 和 Sun 的语法分析器

JAXP 下载时包括 Sun 的语法分析器。所有 parser 器类作为 com.sun.xml.parser 包和相关子包的一部分位于 parser.jar 档案中。应该知道,该语法分析器(代码名为 Crimson) 是 JAXP 自身的一部分。它是 JAXP 版本的一部分,但不是 JAXP API 的一部分。令人困惑吗?有一点。换这种方式想想:JDOM 与 Apache Xerces 语法分析器一起提供。该语法分析器不是 JDOM 的一部分,但由 JDOM 使用,所以包括它,以确保 JDOM 可以单独使用。JAXP 也是如此,但不象 JDOM 那样好表达:JAXP 与 Sun 的语法分析器一起提供,以便可以立即使用。但是,很多人将 Sun 的语法分析器中包括的类当成 JAXP API 的一部分。例如,新闻组中一个常见的问题是:“怎样使用 JAXP 中的 XMLDocument 类?其目的是什么?”这个答案可有些复杂。

首先, com.sun.xml.tree.XMLDocument 类不是 JAXP 的一部分。它是 Sun 语法分析器的一部分。所以,这个问题从一开始就给人以误导。其次,JAXP 的整个意义在于在处理语法分析器时提供供应商独立性。使用 JAXP 的同一代码可以与 Sun 的 XML 语法分析器、Apache 的 Xerces XML 语法分析器和 Oracle 的 XML 语法分析器一起使用。而使用特定于 Sun 的类是个坏主意。这与 JAXP 的整个意义相背离。现在看出来这个问题怎样混淆概念了吗?语法分析器和 JAXP 发行版本(至少是 Sun 的版本)中的 API 被混为一谈,开发人员将其中一个的类和特性当成是另一个的了,反之亦然。





回页首


旧和新

关于 JAXP,最后需要指出的是:使用 JAXP 有一些缺陷。例如,JAXP 只支持 SAX 1.0 和 DOM 第一层规范。SAX 2.0 从 2000 年 5 月起就完成,DOM 第二层规范支持甚至在大多数语法分析器中存在更长时间。DOM 第二层规范还没有完成,但确实足够稳定以用于生产。这两个 API 的新版本都有重大改进,最明显的是对 XML 名称空间的支持。该支持还允许“XML Schema 确认”,这个与 XML 相关的另一热门技术。公平地说,当 JAXP 发布 1.0 最终发行版时,SAX 2.0 和 DOM 第一层规范都还没有完成。但是,由于没有包括这些新版本,确实为开发人员带来很大不便。

还可以使用 JAXP,但是也可以等待 JAXP 1.1,它支持 SAX 2.0 和 DOM第二层规范 。否则,将发现,JAXP 提供的优点以 SAX 和 DOM 最新版本中的功能为代价,并使应用程序更加难以编码。无论是否等待下一个 JAXP 发行版,都要留意这个问题。如果将 JAXP 与语法分析器一起使用,而语法分析器支持的 DOM 和 SAX 版本比 JAXP 支持的要高,则可能会有类路径问题。所以,事先留意一下,并且,一旦有 JAXP 1.1,马上升级。基本理解 JAXP 之后,让我们看一下 JAXP 依赖的 API:SAX 和 DOM。





回页首


从 SAX 开始

SAX (Simple API for XML)是用于处理 XML 的事件驱动方法。它基本由许多回调函数组成。例如,每当 SAX 语法分析器遇到元素的开始标记时就调用 startElement() 。对于字符串,将调用 characters() 回调函数,然后在元素结束标记处调用 endElement() 。还有很多回调函数用于文档处理、错误和其它词汇结构。现在知道这是怎么回事了。SAX 程序员实现一个定义这些回调函数的 SAX 接口。SAX 还实现一个名为 HandlerBase 的类,该类实现所有这些回调函数,并提供所有这些回调方法的缺省空实现。(提到这一点是因为它在后面讲到的 DOM 中很重要。)SAX 开发人员只需扩展这个类,然后实现需要插入特定逻辑的方法。所以,SAX 的关键在于为这些不同的回调函数提供代码,然后允许语法分析器在适当的时候触发这些回调函数中的每一个。

因此,典型的 SAX 过程如下:

  • 用特定供应商的语法分析器实现创建一个 SAXParser 实例
  • 注册回调实现(例如,通过使用扩展 HandlerBase 的类)
  • 开始进行语法分析,然后在触发回调实现时等待

JAXP 的 SAX 组件提供执行所有这些步骤的简单方式。如果没有 JAXP,SAX 语法分析器要直接从供应商类(如 org.apache.xerces.parsers.SAXParser )进行实例化,或者必须使用名为 ParserFactory 的帮助类。第一个方法的问题很明显:不独立于供应商。第二个方法的问题在于类厂需要一个自变量,即要使用的语法分析器类的字符串名称(还是那个 Apache 类 org.apache.xerces.parsers.SAXParser )。可以通过将不同语法分析器作为 String 传递来更改语法分析器。使用这种方法不必更改任何 import 语句,但是还是要重新编译类。这显然不是最佳解决方案。如果能够不重新编译类而更改语法分析器,可能会简单得多,是不是这样呢?

JAXP 提供了更好的替代方法:它允许将语法分析器作为 Java 系统属性来提供。当然,当从 Sun 下载版本时,将得到使用 Sun 语法分析器的 JAXP 实现。可以从 Apache XML Web 站点下载在 Apache Xerces 上构建其实现的相同 JAXP 接口。因此(无论哪一种情况),更改正在使用的语法分析器需要更改类路径设置,即从一种语法分析器实现更改到另一个,但是 要求重新编译代码。这就是 JAXP 的魔力,或抽象性。

SAX 语法分析器一瞥

JAXP SAXParserFactory 类是能够轻易更改语法分析器实现的关键所在。必须创建这个类的新实例(等一会将讲到)。创建新实例之后,类厂提供一个方法来获得支持 SAX 的语法分析器。在内部,JAXP 实现处理依赖于供应商的代码,使您的代码不受影响。这个类厂还提供其它一些优秀特性。

除创建 SAX 语法分析器实例的基本工作之外,类厂还允许设置配置选项。这些选项影响所有通过类厂获得的语法分析器实例。JAXP 1.0 中两个可用的功能是设置名称空间敏感性 ( setNamespaceAware (boolean awareness)),和打开确认 ( setValidating (boolean validating))。请记住,一旦设置了这些选项,在调用该方法之后,它们将影响 所有从 类厂获得的实例。

设置了类厂之后,调用 newSAXParser() 将返回一个随时可用的 JAXP SAXParser 类实例。这个类封装了一个下层的 SAX 语法分析器(SAX 类 org.xml.sax.Parser 的实例)。它还防止向语法分析器类添加任何特定于供应商的附加功能。(还记得以前对 XmlDocument 的讨论吗?)这个类可以开始进行实际的语法分析。以下清单显示如何创建、配置和使用 SAX 类厂。



清单 1. 使用 SAXParserFactory
import java.io.File;
            import java.io.IOException;
            import java.io.OutputStreamWriter;
            import java.io.Writer;
            // JAXP
            import javax.xml.parsers.FactoryConfigurationError;
            import javax.xml.parsers.ParserConfigurationException;
            import javax.xml.parsers.SAXParserFactory;
            import javax.xml.parsers.SAXParser;
            // SAX
            import org.xml.sax.AttributeList;
            import org.xml.sax.HandlerBase;
            import org.xml.sax.SAXException;
            public class TestSAXParsing {
            public static void main(String[] args) {
            try {
            if (args.length != 1) {
            System.err.println ("Usage: java TestSAXParsing [filename]");
            System.exit (1);
            }
            // 获得SAX 语法分析器类厂
            SAXParserFactory factory = SAXParserFactory.newInstance();
            //设置设置名称空间敏感性选项,关掉确认选项
            factory.setValidating(true);
            factory.setNamespaceAware(false);
            SAXParser parser = factory.newSAXParser();
            parser.parse(new File(args[0]), new MyHandler());
            } catch (ParserConfigurationException e) {
            System.out.println("The underlying parser does not support " +
            " the requested features.");
            } catch (FactoryConfigurationError e) {
            System.out.println("Error occurred obtaining SAX Parser Factory.");
            } catch (Exception e) {
            e.printStackTrace();
            }
            }
            }
            class MyHandler extends HandlerBase {
            //通过 DocumentHandler, ErrorHandler等实现的SAX回调函数
            }
            

请注意,在这段代码中,在使用类厂时可能发生两个特定于 JAXP 的问题:无法获得或配置 SAX 类厂,以及无法配置 SAX 语法分析器。当无法获得 JAXP 实现中指定的语法分析器或系统属性时,通常会发生第一个问题 FactoryConfigurationError 。当正在使用的语法分析器中的特性不可用时,会发生第二个问题 ParserConfigurationException 。这两个问题都容易处理,应该不会对 JAXP 的使用造成任何困难。

在获得类厂、关闭名称空间并打开“确认”之后,将获得 SAXParser ,然后开始语法分析。请注意, SAX 语法分析器的 parse() 方法取得前面提到的 SAX HandlerBase 类的一个实例。(可以通过完整的 Java 清单 查看该类的实现 。)还要传递要进行语法分析的文件。但是, SAXParser 所包含的远不止这一个方法。

使用 SAX 语法分析器

获得 SAXParser 类的实例之后,除了向语法分析器传递 File 进行语法分析之外,还可以用它做更多的事。由于如今大型应用中的应用程序组件之间通信方式,“对象实例创建者就是其使用者”这样的假定并不总是安全的。换句话说,一个组件可能创建 SAXParser 实例,而另一组件(可能由另一开发人员编码)可能需要使用那个实例。由于这个原因,提供了一些方法来确定语法分析器的设置。执行此任务的两个方法是 isValidating() ,它通知调用程序:语法分析器将要、或不要执行“确认”,以及 isNamespaceAware() ,它返回一个指示,说明语法分析器可以或不可以处理 XML 文档中的名称空间。虽然这些方法能提供有关语法分析器可以执行功能的信息,但是无法更改这些特性。必须在语法分析器类厂级别执行该操作。

另外,有多种方法来请求对文档进行语法分析。除了只接受 File 和 SAX HandlerBase 实例,SAXParser 的 parse() 方法还能以 String 形式接受 SAX InputSource 、Java InputStream 或 URL,所有这些都要与 HandlerBase 实例一起提供。所以,不同类型的输入文档可以用不同方式的语法分析来处理。

最后,可以直接通过 SAXParser 的 getParser() 方法获得和使用下层的 SAX 语法分析器( org.xml.sax.Parser 的实例)。获得这个下层实例之后,就可以获得通常的 SAX 方法。下一个清单显示 SAXParser 类(这个 JAXP 中 SAX 语法分析的核心类)的各种使用示例。



清单 2. 使用 JAXP SAXParser
 //获得SAXP的一个实例
            SAXParser saxParser = saxFactory.newSAXParser();
            //查看是否支持 Validate 选项
            boolean isValidating = saxParser.isValidating();
            //查看是否支持 namespace 选项
            boolean isNamespaceAware = saxParser.isNamespaceAware();
            // 运用一个File 和一个SAX HandlerBase 的实例进行多种形式的语法分析
            saxParser.parse(new File(args[0]), myHandlerBaseInstance);
            // 运用一个 SAX InputSource实例 和一个 SAX HandlerBase 实例
            saxParser.parse(mySaxInputSource, myHandlerBaseInstance);
            //运用一个 InputStream 实例和一个SAX HandlerBase 实例
            saxParser.parse(myInputStream, myHandlerBaseInstance);
            // 运用一个 URI 和一个SAX HandlerBase 实例
            saxParser.parse("http://www.newInstance.com/xml/doc.xml", myHandlerBaseInstance);
            //获得底层的(封装)SAX 语法分析器
            org.xml.sax.Parser parser = saxParser.getParser();
            //利用底层的语法分析器
            parser.setContentHandler(myContentHandlerInstance);
            parser.setErrorHandler(myErrorHandlerInstance);
            parser.parse(new org.xml.sax.InputSource(args[0]));
            

目前为止,关于 SAX 已经讲了很多,但是还没有揭示任何不寻常或令人惊奇的东西。事实上,JAXP 的功能很少,特别是当 SAX 也牵涉进来时。这很好,因为有最少的功能性意味着代码可移植性更强,并可以由其他开发人员与任何与 SAX 兼容的 XML 语法分析器一起使用,无论是免费(通过开放源码,希望如此)还是通过商业途径。就是这样。在 JAXP 中使用 SAX 没有更多的东西。如果已经知道 SAX,那么现在已经掌握大约 98% 的内容。只需学习两个新类和两个 Java 异常,您就可以开始了。如果从没使用过 SAX,那也很简单,现在就可以开始。





回页首


处理 DOM

如果要休息以迎接 DOM 挑战,那么先别休息。在 JAXP 中使用 DOM 的过程与 SAX 几乎相同,所要做的全部只是更改两个类名和一个返回类型,这样就差不多了。如果理解 SAX 的工作原理和 DOM 是什么,则不会有任何问题。

DOM 和 SAX 的主要差异是它们的 API 结构。SAX 包含一个基于事件的回调函数集,而 DOM 有一个内存中的树状结构。换句话说,在 SAX 中,从不需要操作数据结构(除非开发人员手工创建)。因此,SAX 不提供修改 XML 文档的功能。而 DOM 正好提供这种类型的功能。 org.w3c.dom.Document 类表示 XML 文档,它由表示元素、属性和其它 XML 结构的 DOM 节点 组成。所以,JAXP 无需触发 SAX 回调,它只负责从语法分析返回一个 DOM Document 对象。

DOM 语法分析器类厂一瞥

基本理解 DOM 以及 DOM 和 SAX 的差异之后,就没什么好说的了。以下代码看起来与 SAX 代码类似。首先,获得 DocumentBuilderFactory (与 SAX 中的方式相同)。然后,配置类厂来处理确认和名称空间(与 SAX 中的方式相同)。下一步,从类厂中检索 DocumentBuilder (它与 SAXParser 类似)(与 SAX 中的方式相同. . . 啊,您都知道了)。然后,就可以进行语法分析了,产生的 DOM Document 对象传递给打印 DOM 树的方法。



清单 3. 使用文档构建器类厂
import java.io.File;
            import java.io.IOException;
            import java.io.OutputStreamWriter;
            import java.io.Writer;
            // JAXP
            import javax.xml.parsers.FactoryConfigurationError;
            import javax.xml.parsers.ParserConfigurationException;
            import javax.xml.parsers.DocumentBuilderFactory;
            import javax.xml.parsers.DocumentBuilder;
            // DOM
            import org.w3c.dom.Document;
            import org.w3c.dom.DocumentType;
            import org.w3c.dom.NamedNodeMap;
            import org.w3c.dom.Node;
            import org.w3c.dom.NodeList;
            public class TestDOMParsing {
            public static void main(String[] args) {
            try {
            if (args.length != 1) {
            System.err.println ("Usage: java TestDOMParsing [filename]");
            System.exit (1);
            }
            // 获得 Document Builder Factory
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            //打开确认选项,关掉名称空间敏感性选项。
            factory.setValidating(true);
            factory.setNamespaceAware(false);
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document doc = builder.parse(new File(args[0]));
            // 从DOM 数中打印文档,并加一初始空格
            printNode(doc, "");
            // 在这里也可以对 DOM 文档进行修改
            } catch (ParserConfigurationException e) {
            System.out.println("The underlying parser does not support the requested features.");
            } catch (FactoryConfigurationError e) {
            System.out.println("Error occurred obtaining Document Builder Factory.");
            } catch (Exception e) {
            e.printStackTrace();
            }
            }
            private static void printNode(Node node, String indent) {
            // 打印 DOM 树
            }
            

此代码中可能会出现两个不同的问题(与 JAXP 中的 SAX 类似): FactoryConfigurationErrorParserConfigurationException 。每一个的原因与 SAX 中的相同。不是实现类 ( FactoryConfigurationError ) 中有问题,就是语法分析器不支持请求的特性 ( ParserConfigurationException )。DOM 和 SAX 的唯一差异是:在 DOM 中,用 DocumentBuilderFactory 替代 SAXParserFactory ,用 DocumentBuilder 替代 SAXParser 。就这么简单!(可以 查看完整代码清单 ,该清单包括用于打印 DOM 树的方法。)

使用 DOM 语法分析器

有了 DOM 类厂之后,就可以获得 DocumentBuilder 实例。 DocumentBuilder 实例可以使用的方法与 SAX 的非常类似。主要差异是 parse() 的变种不需要 HandlerBase 类的实例。它们返回表示语法分析之后的 XML 文档的 DOM Document 实例。另一唯一不同之处是:为类似于 SAX 的功能提供了两个方法:用 SAX ErrorHandler 实现来处理语法分析时可能出现的问题的 setErrorHandler() ,和用 SAX EntityResolver 实现来处理实体解析的 setEntityResolver() 。如果不熟悉这些概念,则需要通过联机或在我的书中学习 SAX。以下清单显示使用这些方法的示例。



清单 4. 使用 JAXP DocumentBuilder
    //获得一个 DocumentBuilder 实例
            DocumentBuilder builder = builderFactory.newDocumentBuilder();
            //查看是否支持 Validate 选项
            boolean isValidating = builder.isValidating();
            //查看是否支持 namespace 选项
            boolean isNamespaceAware = builder.isNamespaceAware();
            // 设置一个 SAX ErrorHandler
            builder.setErrorHandler(myErrorHandlerImpl);
            // 设置一个 SAX EntityResolver
            builder.setEntityResolver(myEntityResolverImpl);
            // 运用多种方法对 file 进行语法分析
            Document doc = builder.parse(new File(args[0]));
            // 运用 SAX InputSource
            Document doc = builder.parse(mySaxInputSource);
            // 运用 InputStream
            Document doc = builder.parse(myInputStream, myHandlerBaseInstance);
            // 运用 URI
            Document doc = builder.parse("http://www.newInstance.com/xml/doc.xml");
            

是不是感到 DOM 这一节有些令人厌烦?有这种想法的不止您一个,写 DOM 代码有些令人厌烦是因为它是直接取得所学的 SAX 知识,然后将其用于 DOM。因此,和朋友、同事打赌吧,说使用 JAXP 只是小菜一碟。





回页首


更改语法分析器

最后要探讨的主题是 JAXP 轻易更改类厂类使用的语法分析器的能力。更改 JAXP 使用的语法分析器实际意味着更改 类厂,因为所有 SAXParserDocumentBuilder 实例都来自这些类厂。既然确定装入哪个语法分析器的是类厂,因此,必须更改类厂。可以通过设置 Java 系统属性 javax.xml.parsers.SAXParserFactory 来更改要使用的 SAXParserFactory 接口实现。如果没有定义该属性,则返回缺省实现(供应商指定的任何语法分析器)。相同原理适用于 DocumentBuilderFactory 实现。在这种情况下,将查询 javax.xml.parsers.DocumentBuilderFactory 系统属性。就这么简单,我们已经学完了!这就是 SAXP 的全部:提供到 SAX 的挂钩,提供到 DOM 的挂钩,并允许轻易更改语法分析器。





回页首


结束语

如您所见,没多少复杂的东西。更改系统属性,通过类厂、而不是语法分析器或构建器来设置“确认”,以及弄清楚JAXP实际上不是人们通常所认为的那样,这些是使用 JAXP 的最困难部分。除了没有 SAX 2.0 和 DOM第二层规范支持之外,JAXP 在两个流行的 Java 和 XML API 之上提供一个有帮助的可插入层。它使代码独立于供应商,并允许不编译语法分析代码而更改语法分析器。那么,从 Sun、Apache XML 或其它方便之处下载 JAXP,并使用它吧!继续关注 JAXP 1.1,并增加对 SAX 2 和 DOM 2、XSLT 及更多内容的支持。您将在这里获得第一手新闻,所以,请关注 developerWorks



参考资料



关于作者

 

Brett McLaughlin 是 Lutris Technologies 的 Enhydra 策略顾问,他致力于研究分布式系统体系结构。他是 Java 和 XML (O'Reilly) 的作者。他还参与了诸如 Java Servlet、Enterprise JavaBeans 技术、XML 和B2B应用程序等技术的研究。最近,他与 Jason Hunter 一起创建了 JDOM 项目,该项目为在 Java 应用程序中使用 XML 提供了一个简单的 API。他还是 Apache Cocoon 项目、EJBoss EJB 服务器的主要开发人员,并且是 Apache Turbine 项目的共同创始人。Brett 目前在专家组从事 JAXP 1.1 的规范和发行。可以通过 brett@newInstance.com 与他联系。

posted @ 2007-07-06 12:40 和田雨 阅读(469) | 评论 (0)编辑 收藏

     摘要: 版权所有:(xiaodaoxiaodao)蓝小刀    xiaodaoxiaodao@gmail.com http://www.blogjava.net/xiaodaoxiaodao/articles/103469.html        转载请注明来源/作者 Java Web Start 入门基础教程 &nbs...  阅读全文
posted @ 2007-07-06 11:34 和田雨 阅读(312) | 评论 (0)编辑 收藏

 1. 介绍

    1)DOM(JAXP Crimson解析器)

    DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准。DOM是以层次结构组织的节点或信息片断的集合。这个层次结构允许开发人员在树中寻找特定信息。分析该结构通常需要加载整个文档和构造层次结构,然后才能做任何工作。由于它是基于信息层次的,因而DOM被认为是基于树或基于对象的。DOM以及广义的基于树的处理具有几个优点。首先,由于树在内存中是持久的,因此可以修改它以便应用程序能对数据和结构作出更改。它还可以在任何时候在树中上下导航,而不是像SAX那样是一次性的处理。DOM使用起来也要简单得多。

    2)SAX

    SAX处理的优点非常类似于流媒体的优点。分析能够立即开始,而不是等待所有的数据被处理。而且,由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优点。事实上,应用程序甚至不必解析整个文档;它可以在某个条件得到满足时停止解析。一般来说,SAX还比它的替代者DOM快许多。

    选择DOM还是选择SAX? 对于需要自己编写代码来处理XML文档的开发人员来说, 选择DOM还是SAX解析模型是一个非常重要的设计决策。 DOM采用建立树形结构的方式访问XML文档,而SAX采用的事件模型。

    DOM解析器把XML文档转化为一个包含其内容的树,并可以对树进行遍历。用DOM解析模型的优点是编程容易,开发人员只需要调用建树的指令,然后利用navigation APIs访问所需的树节点来完成任务。可以很容易的添加和修改树中的元素。然而由于使用DOM解析器的时候需要处理整个XML文档,所以对性能和内存的要求比较高,尤其是遇到很大的XML文件的时候。由于它的遍历能力,DOM解析器常用于XML文档需要频繁的改变的服务中。

    SAX解析器采用了基于事件的模型,它在解析XML文档的时候可以触发一系列的事件,当发现给定的tag的时候,它可以激活一个回调方法,告诉该方法制定的标签已经找到。SAX对内存的要求通常会比较低,因为它让开发人员自己来决定所要处理的tag.特别是当开发人员只需要处理文档中所包含的部分数据时,SAX这种扩展能力得到了更好的体现。但用SAX解析器的时候编码工作会比较困难,而且很难同时访问同一个文档中的多处不同数据。

    3)JDOM http://www.jdom.org

    JDOM的目的是成为Java特定文档模型,它简化与XML的交互并且比使用DOM实现更快。由于是第一个Java特定模型,JDOM一直得到大力推广和促进。正在考虑通过“Java规范请求JSR-102”将它最终用作“Java标准扩展”。从2000年初就已经开始了JDOM开发。

    JDOM与DOM主要有两方面不同。首先,JDOM仅使用具体类而不使用接口。这在某些方面简化了API,但是也限制了灵活性。第二,API大量使用了Collections类,简化了那些已经熟悉这些类的Java开发者的使用。

    JDOM文档声明其目的是“使用20%(或更少)的精力解决80%(或更多)Java/XML问题”(根据学习曲线假定为20%)。JDOM对于大多数Java/XML应用程序来说当然是有用的,并且大多数开发者发现API比DOM容易理解得多。JDOM还包括对程序行为的相当广泛检查以防止用户做任何在XML中无意义的事。然而,它仍需要您充分理解XML以便做一些超出基本的工作(或者甚至理解某些情况下的错误)。这也许是比学习DOM或JDOM接口都更有意义的工作。

    JDOM自身不包含解析器。它通常使用SAX2解析器来解析和验证输入XML文档(尽管它还可以将以前构造的DOM表示作为输入)。它包含一些转换器以将JDOM表示输出成SAX2事件流、DOM模型或XML文本文档。JDOM是在Apache许可证变体下发布的开放源码。

    4)DOM4J http://dom4j.sourceforge.net

    虽然DOM4J代表了完全独立的开发结果,但最初,它是JDOM的一种智能分支。它合并了许多超出基本XML文档表示的功能,包括集成的XPath支持、XML Schema支持以及用于大文档或流化文档的基于事件的处理。它还提供了构建文档表示的选项,它通过DOM4J API和标准DOM接口具有并行访问功能。从2000下半年开始,它就一直处于开发之中。

    为支持所有这些功能,DOM4J使用接口和抽象基本类方法。DOM4J大量使用了API中的Collections类,但是在许多情况下,它还提供一些替代方法以允许更好的性能或更直接的编码方法。直接好处是,虽然DOM4J付出了更复杂的API的代价,但是它提供了比JDOM大得多的灵活性。

    在添加灵活性、XPath集成和对大文档处理的目标时,DOM4J的目标与JDOM是一样的:针对Java开发者的易用性和直观操作。它还致力于成为比JDOM更完整的解决方案,实现在本质上处理所有Java/XML问题的目标。在完成该目标时,它比JDOM更少强调防止不正确的应用程序行为。

    DOM4J是一个非常非常优秀的Java XML API,具有性能优异、功能强大和极端易用使用的特点,同时它也是一个开放源代码的软件。如今你可以看到越来越多的Java软件都在使用DOM4J来读写XML,特别值得一提的是连Sun的JAXM也在用DOM4J.

    2…… 比较

    1)DOM4J性能最好,连Sun的JAXM也在用DOM4J.目前许多开源项目中大量采用DOM4J,例如大名鼎鼎的Hibernate也用DOM4J来读取XML配置文件。如果不考虑可移植性,那就采用DOM4J.

    2)JDOM和DOM在性能测试时表现不佳,在测试10M文档时内存溢出。在小文档情况下还值得考虑使用DOM和JDOM.虽然JDOM的开发者已经说明他们期望在正式发行版前专注性能问题,但是从性能观点来看,它确实没有值得推荐之处。另外,DOM仍是一个非常好的选择。DOM实现广泛应用于多种编程语言。它还是许多其它与XML相关的标准的基础,因为它正式获得W3C推荐(与基于非标准的Java模型相对),所以在某些类型的项目中可能也需要它(如在JavaScript中使用DOM)。

    3)SAX表现较好,这要依赖于它特定的解析方式-事件驱动。一个SAX检测即将到来的XML流,但并没有载入到内存(当然当XML流被读入时,会有部分文档暂时隐藏在内存中)。

3. 四种xml操作方式的基本使用方法

xml
文件:


?xml version="1.0" encoding="GB2312"?
RESULT

VALUE

   NOA1234/NO

   ADDR>四川省XXXXXXXXX号</ADDR

/VALUE

VALUE

   NOB1234/NO

   <ADDR>四川省XXXXXXXX组</ADDR

/VALUE

/RESULT


1
DOM

import java.io.*;
import java.util.*;
import org.w3c.dom.*;
import javax.xml.parsers.*;

public class MyXMLReader{
 
public static void main(String arge[]){

  
long lasting =System.currentTimeMillis();
  
try{
   
File f=new File("data_10k.xml");
   
DocumentBuilderFactory factory=DocumentBuilderFactory.newInstance();
   
DocumentBuilder builder=factory.newDocumentBuilder();
   
Document doc = builder.parse(f);
   
NodeList nl = doc.getElementsByTagName("VALUE");
   for (int i=0;i
nl.getLength();i++){
    System.out.print("车牌号码
:" + doc.getElementsByTagName("NO").item(i).getFirstChild().getNodeValue());
    System.out.println("车主地址
:" + doc.getElementsByTagName("ADDR").item(i).getFirstChild().getNodeValue());
   
}
  
}catch(Exception e){
   
e.printStackTrace();
}

2
SAX

import org.xml.sax.*;
import org.xml.sax.helpers.*;
import javax.xml.parsers.*;

public class MyXMLReader extends DefaultHandler {

 
java.util.Stack tags = new java.util.Stack();
 
public MyXMLReader() {
  
super();
}

 
public static void main(String args[]) {
  
long lasting = System.currentTimeMillis();
  
try {
   
SAXParserFactory sf = SAXParserFactory.newInstance();
   
SAXParser sp = sf.newSAXParser();
   
MyXMLReader reader = new MyXMLReader();
   
sp.parse(new InputSource("data_10k.xml"), reader);
  
} catch (Exception e) {
   
e.printStackTrace();
  
}

  System.out.println("运行时间:" + (System.currentTimeMillis() - lasting) + "毫秒
");}
  
public void characters(char ch[], int start, int length) throws SAXException {
  
String tag = (String) tags.peek();
  
if (tag.equals("NO")) {
   System.out.print("车牌号码:
" + new String(ch, start, length));
}
if (tag.equals("ADDR")) {
  System.out.println("地址
:" + new String(ch, start, length));
}
}

  
public void startElement(String uri,String localName,String qName,Attributes attrs) {
  
tags.push(qName);}
}

3
JDOM

import java.io.*;
import java.util.*;
import org.jdom.*;
import org.jdom.input.*;

public class MyXMLReader {

 
public static void main(String arge[]) {
  
long lasting = System.currentTimeMillis();
  
try {
   
SAXBuilder builder = new SAXBuilder();
   
Document doc = builder.build(new File("data_10k.xml"));
   
Element foo = doc.getRootElement();
   
List allChildren = foo.getChildren();
   for(int i=0;i
allChildren.size();i++) {
    System.out.print("车牌号码
:" + ((Element)allChildren.get(i)).getChild("NO").getText());
    System.out.println("车主地址
:" + ((Element)allChildren.get(i)).getChild("ADDR").getText());
   
}
  
} catch (Exception e) {
   
e.printStackTrace();
}

}

4
DOM4J

import java.io.*;
import java.util.*;
import org.dom4j.*;
import org.dom4j.io.*;

public class MyXMLReader {

 
public static void main(String arge[]) {
  
long lasting = System.currentTimeMillis();
  
try {
   
File f = new File("data_10k.xml");
   
SAXReader reader = new SAXReader();
   
Document doc = reader.read(f);
   
Element root = doc.getRootElement();
   
Element foo;
   
for (Iterator i = root.elementIterator("VALUE"); i.hasNext() {
    
foo = (Element) i.next();
    System.out.print("车牌号码
:" + foo.elementText("NO"));
    System.out.println("车主地址
:" + foo.elementText("ADDR"));
   
}
  
} catch (Exception e) {
   
e.printStackTrace();
}
)

posted @ 2007-07-06 11:31 和田雨 阅读(276) | 评论 (0)编辑 收藏

Sysdeo/SQLI Eclipse Tomcat Launcher plugin

在Eclipse方面使用Tomcat的插件

 

 

Plugin features

  • Starting and stopping Tomcat 4.x, 5.x and 6.x

  • Registering Tomcat process to Eclipse debugger

  • Creating a WAR project (wizard can update server.xml file)

  • Adding Java Projects to Tomcat classpath

  • Setting Tomcat JVM parameters, classpath and bootclasspath

  • Exporting a Tomcat project to a WAR File

  • Capability to use a custom Tomcat classloader to load classes in several java projects at the same classloader level than classes in a Tomcat project, see readmeDevLoader.html (Thanks Martin Kahr)

 

Support and contributions

Contact plugintomcat@sysdeo.fr

 

Download

This plugin is free and open-source

Version File Date Comment

3.2.1

10 May 2007

Works with Eclipse 3.1, 3.2 and 3.3M7
Fix a problem with HTTPS

3.2

13 April 2007

Works with Eclipse 3.1, 3.2 and 3.3M6
Tomcat 6 support added

3.2 beta3

20 November 2006

Works with Eclipse 3.2 and Eclipse 3.1
Tomcat 6 support added

3.2 beta2

25 October 2006

Works with Eclipse 3.2 and Eclipse 3.1
Tomcat 6 support added

3.1

15 September 2005

Works with Eclipse 3.0 and Eclipse 3.1 (fix a problem with Eclipse 3.1M6)

3.0

27 July 2004

Works with Eclipse 3.0 and Eclipse 2.1

2.2.1

1er August 2003

Works with Eclipse 2.1 and Eclipse 3.0 M2


Tomcat 4.x patch for JSP debugging (Thanks to Johan Compagner)

Tomcat Version File Date

4.1.24

1 April 2003

To install this patch, unzip the file in <%TOMCAT_HOME%>/classes for tomcat 4.0x, or in <%TOMCAT_HOME%>/common/classes for Tomcat 4.1.x

 

Installation

  • This plugin does not contain Tomcat.
    (Download and install Tomcat before using this plugin).
    This is a design choice not to include Tomcat in the plugin distribution, this way the same plugin version can works with any Tomcat version.

  • Download tomcatPluginVxxx.zip

  • Unzip it in your_Eclipse_Home/plugins

  • Plugin activation for Eclipse 3.x :
    - launch eclipse once using this option : -clean
    - if Tomcat icons are not shown in toolbar : select menu 'Window>Customize Perspective...>Commands', and check 'Tomcat' in 'Available command groups'

  • Set Tomcat version and Tomcat home : Workbench -> Preferences, select Tomcat and set Tomcat version and Tomcat home (Tomcat version and Tomcat home are the only required fields, other settings are there for advanced configuration).

  • This plugin launches Tomcat using the default JRE checked in Eclipe preferences window.
    To set a JDK as default JRE for Eclipse open the preference window : Window -> Preferences -> Java -> Installed JREs.
    This JRE must be a JDK (This is a Tomcat prerequisite).

  • The plugin sets itself Tomcat classpath and bootclasspath. Use Preferences -> Tomcat ->JVM Settings, only if you need specific settings.

 

Documentation and tutorials

Official documation (french) : http://www.eclipsetotale.com/articles/tomcat/tomcatPluginDocFR.html

Tutorials (english) :

 

Troubleshooting

  • In some case, despite it is correctly unzipped in Eclipse 3 'plugins' directory, the plugin is not loaded : run Eclipse with the -clean option to solve the problem.

  • ClassNotFound when using DevLoader and launching Tomcat 5.5.12 and above
    Workaround : set context definition mode (Preferences->Tomcat) to 'Context files'.
    Explanations :
    This problem is due to a change in Tomcat 5.5.12 and above (see http://issues.apache.org/bugzilla/show_bug.cgi?id=37302 ,
    We hope this bug will be fix but it seems that Tomcat developers don't care about it because it happens when a context containing its own loader is defined in server.xml file and since Tomcat 5 defining context in server.xml is not recommended)

  • ClassNotFoundException on javac/Main when accessing JSP : Tomcat is started with a JRE instead of a JDK.

  • If you have a problem with the plugin check eclipse log file (<%Your_Workspace_dir%>/.metadata/.log)

 

If you have any problem with this plugin send an email to plugintomcat@sysdeo.fr .
(Select this button : Preferences->Tomcat->JVM Settings->Dump configuration to .log file, and include in your email the corresponding lines of your .log file, it is in <%Your_Workspace_dir%>/.metadata)

posted @ 2007-07-05 16:28 和田雨 阅读(4284) | 评论 (0)编辑 收藏

这个插件是用来方面的使用 log4j组件的,非常好用,推荐!

Log4E is an Eclipse Plugin which helps you to use your logger easily in Java Projects.
The Plugin Log4E is not bound to any special logging framework. Thus you might be able to adapt to your own logger by defining your own templates using the preferences. It has active support for Log4j, Commons Logging and JDK 1.4 logging.

Use the context menu of a java file or the context menu of an editor and get to the submenu "Log4". Notice that the editor submenu provides additional tasks.

If you like this plugin, please rate it at

Download

Download the Plugin here


Overview

The following tasks can be invoked either on project, sourcefolder, package, class or method level.

Logger Declaration

 

    Inserting import statement and logger declaration of your favourite logger in your sourcecode automatically.
    Kind of logger and imports are configurable in the Preferences.

Logger Insertions

 

    Automate insertions of logger statements at certain method entries: begin of method, exit of method (before every return statement and at the end of method) and in catch blocks.
    Furthermore you can add a logger statement at any position you like supported by a wizard.
    logger levels and much more is configurable in the Preferences:Positions (Statements and Positions).

Substitution of System out's

 

    Replacement of System out's, System.err's and e.printTrackTrace Statements.
    Logger levels are configurable in the Preferences:Replacement.

Logger Modification

 

    Note that the modification task could delete a logger message under certain circumstances! Please read the Logger Modification:Algorithm section before using it.

    Modification of already exisiting logger statements: Replacing logger message with standard information, surrounding log statements with isLevelEnabled() if clauses and more.



Don't hesitate to request for new features or help at log4e@jayefem.de

posted @ 2007-07-05 16:26 和田雨 阅读(555) | 评论 (0)编辑 收藏

这个插件不错,可以监视JVM的内存使用情况,并且可以强制GC工作。


Current Version 1.0.0
. Released Feb 1st 2004

Description

Cloudgarden's MemoryManager is a small plugin for IBM's Eclipse Java IDE, which displays the current memory usage of Eclipse (letting you know when Eclipse is close to using up all it's memory allowance), and automatically invokes garbage collection when deemed necessary by a simple but effective algorithm (see below), thus preventing or reducing times of forced inactivity while the Eclipse JVM cleans up it's virtual memory space.

The plugin takes up little screen real estate, and provides a visual and numerical display of the free, total and maximum memory allocations, as well as indicating when it forced a garbage collection (it also has a button to manually force garbage collection). In the screen shot, the green region represents the free memory, the red region the used memory (which is equal to the total memory minus the free memory) and the black region represents space for expansion. The blue lines indicate when a garbage-collection happened. Scrolling of the display can be paused and re-started, and past values can be stepped through as a simple tool for analysing memory usage by applications in the workbench.

The plugin is Open Source, (the source is here) and should work on most platforms (it has been tested on Windows, Linux and Mac).


Download

The plugin is contained in this file. Simply extract and install in your eclipse folder, then start eclipse.


Usage

Show the plugin by choosing "Windows->Views->Other->MemoryManager->Memory" in the eclipse main menu. The plugin will immediately start displaying memory usage and collecting garbage when necessary. Note: If total usage is less than half of the maximum allowed space, the display will be scaled vertically by a factor of two (ie, the height of the display represents only half of the maximum memory), but once the total memory excedes half of the maximum, the height represents the maximum allowed memory usage.


Requirements

Eclipse version 2.1.2 or 3M6. If run under a 1.3 JVM, the maximum memory cannot be calculated (since there is no such method in the 1.3 API), and the display will have no black area.


Garbage collection algorithm


1) At startup, or immediately after garbage-collection, find the free memory.
2) Keep checking free memory every second or two.
3) When the free memory drops below 75% of the free memory after the last garbage collection (or at startup), do another garbage-collection.

That's it - simple, but apparently effective.
posted @ 2007-07-05 16:18 和田雨 阅读(501) | 评论 (0)编辑 收藏

作者: CNET科技资讯网
CNETNews.com.cn
2007-07-04 11:36:28
关键词: 智能手机 手机 苹果 iPhone

CNET科技资讯网7月4日国际报道 苹果iPhone已经发布有几天了,iSuppli市场研究公司为了探究其内部硬件零部件的成本,将iPhone进行了拆解。

1.屏幕

图解:苹果iPhone真机拆解 零部件成本大揭底

iPhone的屏幕为3.5英寸触摸屏,480*320像素。除了内存以及触摸屏元件外,屏幕是iPhone最昂贵的部件。iPhone屏幕的成本达到了24.5美元,按8GB型号的价格计算,这一成本占其零售价格的9.8%。iPhone的触摸屏由爱普生,夏普以及东芝松下显示器技术公司生产。

2.触摸屏元件

图解:苹果iPhone真机拆解 零部件成本大揭底

iSuppli测算,iPhone硬件的总体成本分别是,4GB型号为225.85美元,8GB版本为249.85美元。分析师Jagdish Rebello说,;两种型号的iPhone唯一的区别是闪存的不同。iPhone的触摸屏元件产自Balda以及TPK Solutions,其成本占8GB型号价格的11%。

3.电话卡插槽及闪存位置

图解:苹果iPhone真机拆解 零部件成本大揭底

底部主板。图示1的位置为电话卡插槽位置,仅能支持AT&T SIM的电话卡,而且目前iPhone仅支持AT&T服务。我们曾经尝试插入非AT&T SIM电话卡,iPhone显示不支持这些卡。图示2处,下面就是一块8MB多功能闪存。

4.电路板三明治

图解:苹果iPhone真机拆解 零部件成本大揭底

iPhone非常薄,经测量仅12毫米厚。iPhone内部的电路板设计体现了苹果的风格,电路板象被聪明的叠加在了一起,象三明治一样。为了探究各个部件,你只能分别拆开各层电路板。

5.鸟瞰图

图解:苹果iPhone真机拆解 零部件成本大揭底

图示1处为程序电路板,图示3处为无线接口电路板,图示2处为立体声耳机电路。虽然iPhone的耳机插孔是标准插孔,但当电路板被组合起来以后,普通的耳机很难插入插孔当中,有些紧。如果你想在iPhone上使用你现在的耳机,你要购买一个10美元的适配器。

6.无线接口部件

图解:苹果iPhone真机拆解 零部件成本大揭底

这块电路板上都是无线通讯部件:(A)处为四频GSM(GSM850,GSM900,GSM1800以及GSM1900-MHz)/EDGE收发器;(B)处为功率放大器;(C)处为蓝牙2.0芯片组;(D)处为无线802.11 a/b/g芯片组;(E)处为基带芯片组;(F)处为电源管理芯片组。它的成本分别是2.23美元,2.55美元,1.9美元,6美元,11.5美元以及1.4美元。

7.固定电池

图解:苹果iPhone真机拆解 零部件成本大揭底

用户无法拆下iPhone的锂离子电池。这种电池内置于iPhone当中,与无线接口电路板焊接在一起。苹果表示,这种电池可以被充电300到400次。如果要想更换电池,你需要将手机送到苹果,为此,你需要掏79美元外加6.95美元的运输费。

苹果承诺,一旦电池的电量低于原容量的50%,苹果将更换电池,但iPhone的电量显示为图示的,不是数字式的。iPhone电池的成本为5.2美元。

8.iPhone大脑第一部分

图解:苹果iPhone真机拆解 零部件成本大揭底

iPhone核心电路。闪存成本在iPhone当中所占的比例很大,4GB的三星NAND闪存成本24美元,8GB的成本为48美元。三星的NAND闪存使用了MLC(多层式储存单元)技术,虽然这种技术可以比SLC(单层式储存单元)存储更多的信息,但耗电量也比后者大。

9.iPhone大脑第二部分

图解:苹果iPhone真机拆解 零部件成本大揭底

iPhone核心电路板上还包括运动感应/加速计(图示B,成本为1.5美元),它可以让iPhone自动判断屏幕的方向;C处为24位RGB显示接口,由National制造,成本为1.3美元;D处为Wolfson Microelectronics生产的音频解码器,成本为28.25美元;左上角是iPhone照相机。

iPhone的核心处理器是620-MHz ARM1176JZF处理器,它有1GBDDR SDRAM内存,三星生产。(还有人说是Marvell生产)

10.iPhone的相机

图解:苹果iPhone真机拆解 零部件成本大揭底

iPhone的相机为200万像素,固定镜头模块,带CMOS感应器,成本估计为9.5美元。

posted @ 2007-07-04 12:12 和田雨 阅读(334) | 评论 (0)编辑 收藏

对一个简单的 JDBC 包装器的扩展及应用

developerWorks
文档选项
将此页作为电子邮件发送

将此页作为电子邮件发送

未显示需要 JavaScript 的文档选项



级别: 初级

宗锋 (zong_feng@263.net)西北大学计算机系硕士

2001 年 12 月 16 日

本文将对 《一个简单的 JDBC 包装器》中的JDBC包装器进行一些扩展,然后介绍一下其在jsp+javabean开发模式中的应用。

最近看了 《一个简单的 JDBC 包装器》,觉得这篇文章很有应用价值,我便在自己的开发中使用了它,不过这个包装器也存在一些不足,于是我对它进行了一些扩展。首先原文中的Table类缺少删除功能,我便增加了删除功能。代码如下:
public void delRow(Row row) throws SQLException {
                        String ss="";
                        ss = "delete from "+name+" where ";
                        for (int i=0; i<row.length(); ++i) {
                        String k = row.getKey( i );
                        String v = row.get( i );
                        ss += k+"='"+v+"'";
                        if (i != row.length()-1)
                        ss += " and ";
                        }
                        Connection con = database.getConnection();
                        Statement st = con.createStatement();
                        st.executeUpdate( ss );
                        }
                        public void delRow(String conditions)throws SQLException {
                        String ss="";
                        ss = "delete from "+name+" where ";
                        ss +=conditions;
                        Connection con = database.getConnection();
                        Statement st = con.createStatement();
                        st.executeUpdate( ss );
                        }

这两个函数分别用于删除一个Row和满足一定条件的记录。对于具有主关键字的表,我们可以用下面代码中的方法二来进行删除,如果没有主关键字,我们可以用方法一删除。

示例如下:
//方法一
                        Row e = table.getRow( "id=2001" );
                        table.delRow(e);
                        //方法二
                        table.delRow("id=2001");
                        

另外这个包装器没有对查询结果为NULL的情况作处理,我通过修改Table类的execute函数和RowSet类的get函数对这种情况作了处理。具体代码见附件。

下面谈谈利用这个JDBC包装器实现对数据库的封装,假定我们有一个表:student,创建表的Sql语句如下:
create table student(
                        id varchar(10) not null primary key,
                        name varchar(16) not null,
                        sex char(2) not null,
                        password varchar(16) not null,
                        department varchar(32) not null
                        )

我们对这个表进行封装,下面是Student类的主要代码:
public class Student{
                        private Row r;
                        public Student() {
                        r=new Row();
                        }
                        public Student(Row row) {
                        this.r=row;
                        }
                        private Table getTable() {
                        Database db =
                        new Database( "jdbc:mysql://localhost:3306/manger",
                        "zf", "zf" );
                        return db.getTable("student");
                        }
                        public void setName(String name){
                        r.put("name",name);
                        }
                        public void setPassword(String pass){
                        r.put("password",pass);
                        }
                        public void setId(String number){
                        r.put("id",number);
                        }
                        public void setDepart(String depart){
                        r.put("department",depart);
                        }
                        public void setSex(String sex){
                        r.put("sex",sex);
                        }
                        public String getName(){
                        return r.get("name");
                        }
                        public String getPassword(){
                        return r.get("password");
                        }
                        public String getId(){
                        return r.get("id");
                        }
                        public String getDepart(){
                        return r.get("department");
                        }
                        public String getSex(){
                        return r.get("sex");
                        }
                        /**
                        *condition表示限制条件,如果为空,则插入新记录,否则更新记录
                        */
                        public void save(String conditions) throws SQLException{
                        if(conditions==null)
                        {getTable().putRow(r);}
                        else
                        getTable().putRow(r,conditions);
                        }
                        /**
                        *由于id作为主关键字,所以我们使用字符串为参数的delRow()函数
                        */
                        public void delete()throws SQLException{
                        //getTable().delRow(this.r);
                        String conditions="";
                        conditions = "id=" + "'"+getId()+"'";
                        getTable().delRow(conditions);
                        }
                        }

下面这个类是相应的一个查询类的主要代码:
public class StudentFactory{
                        public static Student findStudentById(String id)
                        throws SQLException{
                        Row r=getTable().getRow("id="+id);
                        if(r==null)
                        return null;
                        else
                        return new Student(r);
                        }
                        public static Student[] findAllStudents()
                        throws SQLException{
                        RowSet rs=getTable().getRows("1>0");
                        if (rs==null)
                        return null;
                        else
                        Student[] stu=null;
                        stu=new Student[rs.length()];
                        for(int i=0;i<rs.length(); i++){
                        stu[i]=new Student(rs.get(i));
                        }
                        return stu;
                        }
                        }

我使用javabean来实现很多功能,这样可以减少在jsp中的java代码量,方便我们对界面的改进。我们要实现的功能为对学生的编辑,添加,删除和列表。这些功能定义在两个javabean中:下面是两个jsp文件的主要代码:
<%-- student.jsp --%>
                        <%@ page contentType="text/html;charset=GB2312" %>
                        <%@ page import="org.gjt.mm.mysql.*,manger.bean.*,manger.tools.*,manger.business.*" %>
                        <html>
                        <head>
                        <SCRIPT TYPE="text/javascript" LANGUAGE="JavaScript" >
                        <!--
                        function doDelete()
                        {
                        if(confirm('你确定删除吗?')) {
                        document.EditForm.event.value='delete';
                        document.EditForm.submit();
                        }
                        }
                        function doEdit()
                        {
                        if(confirm('你确定编辑吗?')) {
                        document.EditForm.event.value='showEdit';
                        document.EditForm.submit();
                        }
                        }
                        function showAddPage()
                        {
                        document.location='editstudent.jsp?event=showAdd';
                        }
                        </SCRIPT>
                        </head>
                        <body bgcolor="#FFFFFF" text="#000000">
                        <%
                        try {
                        Class.forName("org.gjt.mm.mysql.Driver").newInstance();
                        }
                        catch (Exception E) {
                        out.println("Unable to load driver.");
                        } %>
                        <jsp:useBean id="table" scope="page" class="manger.bean.ListStudentBean" />
                        <%
                        Student[] student=table.getStudent(pageContext);
                        int total=0;
                        int currPage=table.getCurPage();
                        int pageCount=table.getPageCount();
                        if(student!=null)
                        {total=student.length;}%>
                        <FORM NAME="EditForm" ACTION="editstudent.jsp">
                        <INPUT TYPE="HIDDEN" NAME="event" VALUE="">
                        <table width="75%" border="1">
                        <tr>
                        <td colspan="5">学生列表</td>
                        </tr>
                        <tr>
                        <td>学号</td>
                        <td>姓名</td>
                        <td>班级</td>
                        <td>备注一</td>
                        <td>选择</td>
                        </tr>
                        <%for (int i=0;i<total;i++){
                        Student current=student[i];%>
                        <tr>
                        <td><%=current.getId()%></td>
                        <td><%=current.getName()%></td>
                        <td><%=current.getDepart()%></td>
                        <td><%=current.getSex() %></td>
                        <td>
                        <input type="checkbox" name="id" value=<%=current.getId()%>>
                        </td>
                        <% } %>
                        </tr><tr>
                        <td colspan="5">
                        <INPUT TYPE="BUTTON" onclick="doEdit();" VALUE="编辑">
                        <INPUT TYPE="BUTTON" onclick="showAddPage()" VALUE="增加">
                        <INPUT TYPE="BUTTON" onclick="doDelete();" VALUE="删除">
                        </td>
                        </tr>
                        </table>
                        </form>
                        </html>

<%-- studentedit.jsp --%>
                        <jsp:useBean id="table" scope="page" class="manger.bean.EditStudentBean" />
                        <%table.processRequest(pageContext);%>
                        <p>_lt;/p>
                        <form name="EditForm" action="editstudent.jsp">
                        <INPUT TYPE="hidden" NAME="event" VALUE="<%=table.getEvent()%>" >
                        <table width="75%" border="1">
                        <tr>
                        <td colspan="2">
                        <div align="center"><b>编辑学生信息</b></div>
                        </td>
                        </tr>
                        <tr>
                        <td width="40%">学号:</td>
                        <td width="60%">
                        <input type="text" name="id" value="<%=table.getStudent().getId()%>">
                        </td>
                        </tr>
                        <%--下面的一些学生信息我们省略了--%>
                        <tr>
                        <td colspan="2">
                        <input type="submit" name="Submit" value="确定">
                        </td>
                        </tr>
                        </table>
                        </form>
                        

我的想法是在student.jsp中显示学生列表,从这个页面可以转到增加和编辑页面,也可以在这个页面中删除学生,这三个功能都是由editstudent.jsp完成的。在editstudent.jsp中非常关键的一行代码就是<%table.processRequest(pageContext);%>,这会调用EditStudentBean类的processRequest方法:下面是EditStudentBean类processRequest方法和addStudent方法的代码:
public void processRequest (PageContext pageContext)
                        throws SQLException,ServletException,IOException{
                        this.student.setId("");
                        this.student.setName("");
                        this.student.setPassword("");
                        this.student.setDepart("");
                        this.student.setSex("");
                        HttpServletRequest request=(HttpServletRequest)pageContext.getRequest();
                        String event=request.getParameter("event");
                        //this.event=event;
                        if(event==null || event.equals("")||event.equals("showAdd"))
                        {this.event="add";
                        }
                        else if(event.equals("showEdit"))
                        {this.event="edit";
                        String id=request.getParameter("id");
                        Student stu=StudentFactory.findStudentById(id);
                        this.student=stu;
                        }
                        else if(event.equals("add"))
                        {this.addStudent(pageContext);
                        }
                        else if(event.equals("delete"))
                        {this.deleteStudent(pageContext);
                        }
                        else if(event.equals("edit"))
                        {this.editStudent(pageContext);
                        }
                        }
                        public void addStudent(PageContext page)
                        throws SQLException,ServletException,IOException{
                        HttpServletRequest request=(HttpServletRequest)page.getRequest();
                        HttpServletResponse response=(HttpServletResponse)page.getResponse();
                        JspWriter out=page.getOut();
                        String id=request.getParameter("id");
                        String name=request.getParameter("name");
                        String sex=request.getParameter("sex");
                        String pass=request.getParameter("password");
                        String depart=request.getParameter("depart");
                        if (id!=null&&name!=null&&sex!=null&&pass!=null&&depart!=null
                        &&!id.equals("")&&!name.equals("")&&!sex.equals("")&&!pass.equals("")&&!depart.equals(""))
                        {Student s=new Student();
                        s.setId(id);
                        s.setName(name);
                        s.setSex(sex);
                        s.setPassword(pass);
                        s.setDepart(depart);
                        s.save(null);
                        response.sendRedirect("student.jsp");
                        }
                        else
                        {out.print("请添完所有信息");
                        }
                        }

processRequest方法的功能主要为获取提交给editstudent.jsp的event的值,根据不同的event调用不同的函数。例如event=add,则调用addStudent函数。

注意:在设置Student对象的各个属性值时,一定要按照表(见上面的SQL语句)中顺序来设置,例如在表中,id字段在name字段的前面,所以setId方法在setName方法前面,这主要是由于Table类中的putRow函数中执行插入操作的SQL语句有缺陷。在那个SQL语句中,它是按各属性在Row中的顺序来执行插入操作的。如过你不愿意按表中字段的顺序来设置Student的属性,可以对putRow函数更改如下:
String ss = "";
                        if (conditions==null) {
                        ss = "INSERT INTO "+name+'(";
                        for (int i=0;i<row.length();++i) {
                        String k = row.getKey( i );
                        ss += k;
                        if (i != row.length()-1)
                        ss += ", ";
                        }
                        ss +=") VALUES (";
                        for (int j=0; j<row.length(); ++j) {
                        String v = row.get( j );
                        ss += "'"+v+"'";
                        if (j != row.length()-1)
                        ss += ", ";
                        }
                        ss += ")";
                        }

限于篇幅,我省略了student.jsp文件中的一些内容,对这个jsp文件进行处理的bean为ListStudentBean,这个类主要实现一个函数getStudent,这个函数主要通过StudentFactory查寻到所有的学生,由于我在此函数中进行了分页处理,因此此函数只返回当前页需要的Student数组。具体的代码参看 附件

总之,我改进之后的这个JDBC封装器还是比较有效的,它能满足一般地需要,当然你也可以根据你的情况对其进行扩充,以更好地适应你的开发。

代码在Tomcat4.01+mysql3.23.43下测试通过。



关于作者

 

宗锋,男,西北大学计算机系硕士。兴趣主要集中在:java,linux,enhydra,barracuda。希望能与有共同爱好的朋友进行交流。E-mail: zong_feng@263.net.

posted @ 2007-07-03 17:22 和田雨 阅读(211) | 评论 (0)编辑 收藏

一个简单的 JDBC 包装器

一种简单程序的快速数据访问解决方案

developerWorks
文档选项
将此页作为电子邮件发送

将此页作为电子邮件发送

未显示需要 JavaScript 的文档选项



级别: 初级

Greg Travis (mito@panix.com), 自由程序员

2001 年 8 月 04 日

JDBC 提供了一种强大、全面的接口用来从 Java 程序访问数据库。对于较小的项目来说,使用 JDBC 似乎是理所当然的,它使一些程序员避免了一起使用数据库。本文描述了一种简单的包装器库,它让使用简单的数据库易如反掌。您会发现您已经开始想在编写的每一个程序中都使用 JDBC。

事情发生得很突然。您正在修改一个程序,您原以为它是个小程序,不料竟发现它已经迅速增长成为一个庞大的东西 ― 一个 项目― 而现在您已到了需要保存一些数据的时候了。

问题是,您并不是有意让程序发展到这种程度的。 您只是不由自主地做了一小部分修改。您并没有真正准备要存储或装入。而且,您的程序是个 applet,而 applet 是无法存储或装入的。

可以存储或装入它们吗?

实际上,JDBC API 允许任何 Java 程序 ― 甚至是 applet ― 连接到关系型数据库(RDBMS)上。 不幸的是,JDBC 对您的小程序来说可能有一点头重脚轻了。毕竟,如果您希望做的只是存储一点点数据的话,RDBMS 是一个强大、复杂的系统。

在本文中,我们将分析一个简单的使用 JDBC 的抽象层。对于简单的应用程序来说,它可以让您只用几行代码就实现存储和装入结构化数据。它不会处理复杂的 RDBMS 使用,但那正是我们要避免的,不是吗?

JDBC 的复杂性

JDBC 使用起来可能是一个复杂的 API。它不仅必须支持整个强大的 SQL 标准,还必须很好地隐藏不同数据库引擎之间的区别。

JDBC 的复杂还在于关系型数据库是基于 SQL 构建的,而 SQL 是要给人用的,而不是给程序用的。直接使用 JDBC 有点象同时用两种语言编程。

虽然 JDBC 的这些方面并不是什么坏事,但它们确实与我们的目标 ― 快速地存储少量数据相冲突。





回页首


简化的抽象

我们将创建一个简单的抽象层,让您不必顾虑所有繁琐的细节问题来直接使用 JDBC。如果您早已熟悉 JDBC 或关系型数据库了,那您一眼看到我们的类列表应该是很熟悉的:

  • Database
  • Table
  • RowSet
  • Row

我们这里不是在作任何实质性的事情;我们的数据模型本质上和关系型模型是一样的,但去掉了烦人的细节(同时去掉了强大的功能)。每一个类映射到一个基本的 RDBMS 概念上,同时也映射到一个 JDBC 类上。就是这种映射让我们的 API 可以在保持易用性的同时保留它的相关特性。

这种 API 的设计是基于对我们的数据存储需要的设想。如果您发现自己的程序需要一点不同的地方,您可以随意地改变这种抽象以适应您的情况。 这些类可以被认为是一种简化您工作的模式,而不是一成不变的规则。

如果您不熟悉 SQL 或者 RDBMS 技术,不必害怕。 下面的四节中,每一节都会帮助您熟悉我们的一个类,还有这些类映射到的 RDBMS 功能。

Database 类

当使用 JDBC 与数据库建立连接时,您必须告诉 JDBC 在何处可以找到实际的数据。 因为不同的数据库引擎有不同的访问方法和描述这些方法的不同语法,所以有不止一种方法来指定数据源。 在 JDBC 中,统一资源标识符(Uniform Resource Identifier,URI)字符串是用来指定数据源的,而这个字符串的结构是依赖于数据库的。

Database 类的主要目的就是封装这个字符串,还有建立连接操作时可能需要的任何用户名/密码信息。

下面是如何创建一个 Database 对象的方法:

  Database db =
                        new Database( "jdbc:postgresql://localhost/mito",
                        "mito", "" );
                        

构造函数的第一个参数是数据源的 URI。 在这个示例中,我使用了 PostgreSQL数据库引擎,而且在本机上访问了一个名为 mito 的数据库。 另外,我指定我的用户名 mito 和一个空的密码分别作为第二个和第三个参数。

一旦您创建了 Database 对象,您就可以使用它来访问数据,如我们在下一章可以看到的一样。

Table 类

我们在 API 中对简化的一个设想就是,当您从表的一行读取数据时,您会得到整行的数据。换句话说,表的一行是作为读写单独一块数据的最小单位。这并不十分有效,但效率不是我们方法中所首要考虑的。

Talbe 类让您可以读写这些行对象。您必须做的第一步是创建一个表对象,它简单得只要知道它的名称即可:

  Table table = db.getTable( "employee" );
                        

创建 Table 对象的操作实际上并没有做任何事,只是让对象记住自己的名称。要做一些实际的事,我们就需要实际地使用这个 Table 对象了。在这里,我们从表中读取一行。

  Row row = table.getRow( "id=101");
                        

注意,我们已经指定了我们只需要那些‘id’值设定为‘101’的行。通过使用 getRow() 方法,我们假定只有一行符合这个条件。在另外的情况下,我们可能需要多个行,那样我们就需要这样使用 getRows() 方法:

  RowSet rows = table.getRows( "id<103" );
                        

在这种情况下,返回的值是一个 RowSet ,而不是一个 Row。 RowSet 就是 Row 的一个集合。

在接下来的两节里,我们将讨论 RowRowSet 类。

Row 类

在我们的抽象中, Row 是在 RDBMS 中表示表中一行的名称/值对的集合。不同于 RDBMS 值可以是不同的类型, Row 仅包含一种类型,即字符串类型。 这还是为了让事情简单一点 ― 我们假定您不需要字符串类型提供的任何更强大的功能。

一旦您有了一个 Row ,就很容易从其中取出一个值,正如我们在清单 1 中看见的一样。



L清单 1. 从 Row 中获取值
  Row e = table.getRow( "id=100" );
                        String name = e.get( "name" );
                        System.out.println( "Employee name: "+name );
                        

还要注意的是 Row 是排序的,这样您就可以通过索引来取出名称/值的对。清单 2 中给出了这样的一个示例。



清单 2. 迭代整个 Row
  Row e = table.getRow( "id=100" );
                        for (int i=0; i<e.length(); ++i) {
                        String key = e.getKey( i );
                        String value = e.get( i );
                        System.out.println( key+" = "+value );
                        }
                        

当然,您还可以改变 Row 中的值。这是在数据库中更改数据所必需的 — 稍后我们会看到这一点。

RowSet 类

记住有一些查询可以返回多个 Row ,这样的话您就会得到一个 RowSetRowSet 有一点不同于基于 Vector 的包装器。你可以轻易地迭代 RowSet 中所有的 Row ,在清单 3 中可以看到这一点。



清单 3. 迭代整个 RowSet
  RowSet rs = table.get( "id<104" );
                        for (int i=0; i<rs.length(); ++i) {
                        Row row = rs.get( i );
                        // do something with the row....
                        }
                        

一个完整的示例

现在我们已经看过了所有的类,让我们来看一个完整的示例吧。在清单 4 中,我们将抽取出符合特定条件的一套记录,然后打印出它们的值。



清单 4. 一个读取数据的完整示例
    // First, get all rows meeting the criterion
                        RowSet rs = table.getRows( "id<103" );
                        // Iterate through the set
                        for (int i=0; i<rs.length(); ++i) {
                        // Grab each row in turn
                        Row row = rs.get( i );
                        // Get and print the value of the "name" field
                        String name = row.get( "name" );
                        System.out.println( "Name: "+name );
                        }
                        

如此容易!在下一节中,我们将看看怎样向数据库写入数据。





回页首


修改数据

正如前面所提到的,使用我们的 API 读写数据是以整个 为单位的。为了向数据库写入数据,您必须创建(或修改) Row 对象,然后向数据库写入那个 Row 对象。

向数据库写入数据是通过使用 Table 中的 putRow 方法。这种方法有两种变体:

  • public void putRow( Row row )
  • public void putRow( Row row, String conditions )

这两种变体分别对应于 SQL 中的 INSERTUPDATE 命令。

在第一个变体中,写一行意味着将一个全新的行插入表中。

在第二个变体中,写一行意味着修改一个现有的行。 conditions 参数使您能够指定您想要修改的是哪一行(哪些行)。

让我们来看看每种方法的一个示例。

插入一个新行

插入一个新行很简单,因为您不必指定要修改的行(一行或多行)。您只是简单地把行插入:

  table.putRow( row );
                        

您可以重新创建一个 Row ,如清单 5 所示。



清单 5. 重新创建一个 Row
  // Create an empty row object
                        Row row = new Row();
                        // Fill it up with data
                        row.put( "id", "200" );
                        row.put( "name", "Joey Capellino" );
                        

或者,您可以修改一个以前曾经从数据库中读取的一个现有的行,如清单 6 所示。



清单 6. 修改现有的 Row
  // Grab a row from the database
                        Row row = table.getRow( someConditions );
                        // Change some or all of the fields
                        row.put( "name", "Joey Capellino" );
                        

虽然通常是在插入时重新创建 Row ,更新时使用现有的 Row ,实际上您可以用任何方式来进行。

更新现有的行

正如前面的部分提到的,对于您如何 创建用来更新的 Row 是没有限制的。 但是,通常您是使用一个刚从数据库中读出的 Row

为了详细描述这一点,我们将使用一个示例(在该例子中我们读出一个员工的姓名),改变这个名字,然后将更改后的结果写回数据库,如清单 7 所示。



清单 7. 通过修改 Row 进行更新
    Row row = table.getRow( "id=104" );
                        row.put( "name", newName );
                        table.putRow( row, "id=104" );
                        

注意我们必须在调用 putRow() 中指定条件。这样才会使调用成为 更新,而不是 插入

注意,这个调用将更新 所有符合条件的行,而不是其中的一行。





回页首


结论

在本文中,我们初步认识了一种通过 JDBC 包提供一种简化的通往关系型数据库接口的 API。这种抽象保留了 JDBC 接口的很多基本关系型功能,但对其进行了简化,从而让使用非常地方便。这种简化是以效率为代价的,但当目标是简单性时,这并不是一个令人惊奇的结果。



参考资料



关于作者

 

Greg Travis 是居住在纽约的一个自由程序员。他对计算机的兴趣可能是源于“ Bionic Woman”中的一段情节 - Jamie 四处奔跑,试图逃离一所灯光和门都被邪恶的人工智能所控制的房子,人工智能还通过扬声器嘲笑她。他是一个传统观念的虔诚信徒 - 如果一个计算机程序能够工作,那完全是个巧合。可以通过 mito@panix.com与他联系。

posted @ 2007-07-03 17:16 和田雨 阅读(225) | 评论 (0)编辑 收藏

     摘要: 本文通过开发一个JSP 编辑器插件的示例,介绍了 Eclipse 中设置 JSP 断点的方法,以及如何远程调试 JSP。作为基础知识,本文的前两部分描述了 JAVA Debug 和 JSR-45 的基本原理。
  阅读全文
posted @ 2007-07-03 16:40 和田雨 阅读(397) | 评论 (0)编辑 收藏

JavaBeans的属性 

JavaBeans的属性与一般Java程序中所指的属性,或者说与所有面向对象的程序设计语言中对象的属性是一个概念,在程序中的具体体现就是类中的变量。在JavaBeans设计中,按照属性的不同作用又细分为四类:Simple, Index, Bound与Constrained属性。 

1. Simple属性 

一个简单属性表示一个伴随有一对get/set方法(C语言的过程或函数在Java程序中称为"方法")的变量。属性名与和该属性相关的get/set方法名对应。例如:如果有setX和getX方法,则暗指有一个名为"X"的属性。如果有一个方法名为isX,则通常暗指"X"是一个布尔属性(即X的值为true或false)。例如在下面这个程序中: 


public class alden1 extends Canvas { 
string ourString= "Hello"; //属性名为ourString,类型为字符串 
public alden1(){     //alden1()是alden1的构造函数, 
与C++中构造函数的意义相同 
setBackground(Color.red); 
setForeground(Color.blue); 

/* "set"属性*/ 
public void setString(String newString) { 
ourString=newString; 

/* "get"属性 */ 
public String getString() { 
return ourString; 






2. Indexed属性 

一个Indexed属性表示一个数组值。使用与该属性对应的set/get方法可取得数组中的数值。该属性也可一次设置或取得整个数组的值。例: 


public class alden2 extends Canvas { 
int[] dataSet={1,2,3,4,5,6}; // dataSet是一个indexed属性 
public alden2() { 
setBackground(Color.red); 
setForeground(Color.blue); 

/* 设置整个数组 */ 
public void setDataSet(int[] x){ 
dataSet=x; 

/* 设置数组中的单个元素值 */ 
public void setDataSet(int index, int x){ 
dataSet[index]=x; 

/* 取得整个数组值 */ 
public int[] getDataSet(){ 
return dataSet; 

/* 取得数组中的指定元素值 */ 
public int getDataSet(int x){ 
return dataSet[x]; 






3. Bound属性 

一个Bound属性是指当该种属性的值发生变化时,要通知其它的对象。每次属性值改变时,这种属性就点火一个PropertyChange事件(在Java程序中,事件也是一个对象)。事件中封装了属性名、属性的原值、属性变化后的新值。这种事件是传递到其它的Beans,至于接收事件的Beans应做什么动作由其自己定义。当PushButton的background属性与Dialog的background属性bind时,若PushButton的background属性发生变化时,Dialog的background属性也发生同样的变化。 例: 


public class alden3 extends Canvas{ 
String ourString= "Hello"; 
//ourString是一个bound属性 
private PropertyChangeSupport changes = new PropertyChangeSupport(this); 
/** 注:Java是纯面向对象的语言, 
如果要使用某种方法则必须指明是要使用哪个对象的方法, 
在下面的程序中要进行点火事件的操作, 
这种操作所使用的方法是在PropertyChangeSupport类中的。 
所以上面声明并实例化了一个changes对象, 
在下面将使用changes的firePropertyChange方法来点火ourString的属性改变事件。*/ 

public void setString(string newString){ 
String oldString = ourString; 
ourString = newString; 
/* ourString的属性值已发生变化,于是接着点火属性改变事件 */ 
changes.firePropertyChange("ourString",oldString,newString); 

public String getString(){ 
return ourString; 

/** 以下代码是为开发工具所使用的。 
我们不能预知alden3将与哪些其它的Beans组合成为一个应用, 
无法预知若alden3的ourString属性发生变化时有哪些其它的组件与此变化有关, 
因而alden3这个Beans要预留出一些接口给开发工具, 
开发工具使用这些接口, 
把其它的JavaBeans对象与alden3挂接。*/ 

public void addPropertyChangeListener(PropertyChangeLisener l){ 
changes.addPropertyChangeListener(l); 

public void removePropertyChangeListener(PropertyChangeListener l){ 
changes.removePropertyChangeListener(l); 





通过上面的代码, 

开发工具调用changes的addPropertyChangeListener方法 

把其它JavaBeans注册入ourString属性的监听者队列l中, 

l是一个Vector数组,可存储任何Java对象。 

开发工具也可使用changes的removePropertyChangeListener方法, 

从l中注销指定的对象, 

使alden3的ourString属性的改变不再与这个对象有关。 

当然,当程序员手写代码编制程序时, 

也可直接调用这两个方法, 

把其它Java对象与alden3挂接。 

4. Constrained属性 

一个JavaBeans的constrained属性,是指当这个属性的值要发生变化时,与这个属性已建立了某种连接的其它Java对象可否决属性值的改变。constrained属性的监听者通过抛出PropertyVetoException来阻止该属性值的改变。例:下面程序中的constrained属性是PriceInCents。 


public class JellyBeans extends Canvas{ 
private PropertyChangeSupport changes=new PropertyChangeSupport(this); 
private VetoableChangeSupport Vetos=new VetoableChangeSupport(this); 
/*与前述changes相同, 
可使用VetoableChangeSupport对象的实例Vetos中的方法, 
在特定条件下来阻止PriceInCents值的改变。*/ 


...... 
public void setPriceInCents(int newPriceInCents) throws PropertyVetoException { 
/*方法名中throws PropertyVetoException的作用是当有 
其它Java对象否决PriceInCents的改变时, 
要抛出例外。*/ 
/* 先保存原来的属性值*/ 

int oldPriceInCents=ourPriceInCents; 
/**点火属性改变否决事件*/ 
vetos.fireVetoableChange("priceInCents",new Integer(OldPriceInCents), 
new Integer(newPriceInCents)); 

/**若有其它对象否决priceInCents的改变, 
则程序抛出例外,不再继续执行下面的两条语句, 
方法结束。若无其它对象否决priceInCents的改变, 
则在下面的代码中把ourPriceIncents赋予新值, 
并点火属性改变事件*/ 

ourPriceInCents=newPriceInCents; 
changes.firePropertyChange("priceInCents", 
new Integer(oldPriceInCents), 
new Integer(newPriceInCents)); 


/**与前述changes相同, 
也要为PriceInCents属性预留接口, 
使其它对象可注册入PriceInCents否决改变监听者队列中, 
或把该对象从中注销 

public void addVetoableChangeListener(VetoableChangeListener l) 
{ vetos.addVetoableChangeListener(l); 

public void removeVetoableChangeListener(VetoableChangeListener l){ 
vetos.removeVetoableChangeListener(l); 

...... 





从上面的例子中可看到,一个constrained属性有两种监听者:属性变化监听者和否决属性改变的监听者。否决属性改变的监听者在自己的对象代码中有相应的控制语句,在监听到有constrained属性要发生变化时,在控制语句中判断是否应否决这个属性值的改变。 

总之,某个Beans的constrained属性值可否改变取决于其它的Beans或者是Java对象是否允许这种改变。允许与否的条件由其它的Beans或Java对象在自己的类中进行定义。 

JavaBeans的事件 

事件处理是JavaBeans体系结构的核心之一。通过事件处理机制,可让一些组件作为事件源,发出可被描述环境或其它组件接收的事件。这样,不同的组件就可在构造工具内组合在一起,组件之间通过事件的传递进行通信,构成一个应用。从概念上讲,事件是一种在"源对象"和"监听者对象"之间,某种状态发生变化的传递机制。事件有许多不同的用途,例如在Windows系统中常要处理的鼠标事件、窗口边界改变事件、键盘事件等。在Java和JavaBeans中则是定义了一个一般的、可扩充的事件机制,这种机制能够: 

对事件类型和传递的模型的定义和扩充提供一个公共框架,并适合于广泛的应用。 

与Java语言和环境有较高的集成度。 

事件能被描述环境捕获和点火。 

能使其它构造工具采取某种技术在设计时直接控制事件,以及事件源和事件监听者之间的联系。 

事件机制本身不依赖于复杂的开发工具。特别地,还应当: 

能够发现指定的对象类可以生成的事件。 

能够发现指定的对象类可以观察(监听)到的事件。 

提供一个常规的注册机制,允许动态操纵事件源与事件监听者之间的关系。 

不需要其它的虚拟机和语言即可实现。 

事件源与监听者之间可进行高效的事件传递。 

能完成JavaBeans事件模型与相关的其它组件体系结构事件模型的中立映射。 

JavaBeans事件模型的主要构成有: 事件从事件源到监听者的传递是通过对目标监听者对象的Java方法调用进行的。对每个明确的事件的发生,都相应地定义一个明确的Java方法。这些方法都集中定义在事件监听者(EventListener)接口中,这个接口要继承java.util.EventListener。实现了事件监听者接口中一些或全部方法的类就是事件监听者。 伴随着事件的发生,相应的状态通常都封装在事件状态对象中,该对象必须继承自java.util.EventObject。事件状态对象作为单参传递给应响应该事件的监听者方法中。 发出某种特定事件的事件源的标识是:遵从规定的设计格式为事件监听者定义注册方法,并接受对指定事件监听者接口实例的引用。 有时,事件监听者不能直接实现事件监听者接口,或者还有其它的额外动作时,就要在一个源与其它一个或多个监听者之间插入一个事件适配器类的实例,来建立它们之间的联系。 

事件状态对象(Event State Object) 

与事件发生有关的状态信息一般都封装在一个事件状态对象中,这种对象是java.util.EventObject的子类。按设计习惯,这种事件状态对象类的名应以Event结尾。例如: 


public class MouseMovedExampleEvent extends java.util.EventObject 

{ protected int x, y; 
/* 创建一个鼠标移动事件MouseMovedExampleEvent */ 
  MouseMovedExampleEvent(java.awt.Component source, Point location) { 
super(source); 
x = location.x; 
y = location.y; 

/* 获取鼠标位置*/ 
public Point getLocation() { 
return new Point(x, y); 
}} 




事件监听者接口(EventListener Interface)与事件监听者 

由于Java事件模型是基于方法调用,因而需要一个定义并组织事件操纵方法的方式。JavaBeans中,事件操纵方法都被定义在继承了java.util.EventListener类的EventListener接口中,按规定,EventListener接口的命名要以Listener结尾。任何一个类如果想操纵在EventListener接口中定义的方法都必须以实现这个接口方式进行。这个类也就是事件监听者。例如: 


/*先定义了一个鼠标移动事件对象*/ 
   public class MouseMovedExampleEvent 
extends java.util.EventObject { 
// 在此类中包含了与鼠标移动事件有关的状态信息 
     ... 
   } 
   /*定义了鼠标移动事件的监听者接口*/ 
   interface MouseMovedExampleListener 
extends java.util.EventListener { 
/*在这个接口中定义了鼠标移动事件监听者所应支持的方法*/ 
void mouseMoved(MouseMovedExampleEvent mme); 


在接口中只定义方法名, 
方法的参数和返回值类型。 
如:上面接口中的mouseMoved方法的 
具体实现是在下面的ArbitraryObject类中定义的。 

class ArbitraryObject implements MouseMovedExampleListener { 
    public void mouseMoved(MouseMovedExampleEvent mme) 
  { ... } 
} 
ArbitraryObject就是MouseMovedExampleEvent事件的监听者。 




事件监听者的注册与注销 

为了各种可能的事件监听者把自己注册入合适的事件源中,建立源与事件监听者间的事件流,事件源必须为事件监听者提供注册和注销的方法。在前面的bound属性介绍中已看到了这种使用过程,在实际中,事件监听者的注册和注销要使用标准的设计格式: 


public void add< ListenerType>(< ListenerType> listener); 
public void remove< ListenerType>(< ListenerType> listener); 




例如: 

首先定义了一个事件监听者接口: 


public interface 
ModelChangedListener extends java.util.EventListener { 
void modelChanged(EventObject e); 





接着定义事件源类: 


public abstract class Model { 
private Vector listeners = new Vector(); // 定义了一个储存事件监听者的数组 

/*上面设计格式中的< ListenerType>在此处即是下面的ModelChangedListener*/ 

public synchronized void addModelChangedListener(ModelChangedListener mcl) 
   { listeners.addElement(mcl); }//把监听者注册入listeners数组中 
public synchronized void removeModelChangedListener(ModelChangedListener mcl) 
     { listeners.removeElement(mcl); //把监听者从listeners中注销 
     } 
   /*以上两个方法的前面均冠以synchronized, 
是因为运行在多线程环境时, 
可能同时有几个对象同时要进行注册和注销操作, 
使用synchronized来确保它们之间的同步。 
开发工具或程序员使用这两个方法建立源与监听者之间的事件流*/ 

protected void notifyModelChanged() { 
/**事件源使用本方法通知监听者发生了modelChanged事件*/ 
    Vector l; 
     EventObject e = new EventObject(this); 
/* 首先要把监听者拷贝到l数组中, 
冻结EventListeners的状态以传递事件。 
这样来确保在事件传递到所有监听者之前, 
已接收了事件的目标监听者的对应方法暂不生效。*/ 
     synchronized(this) { 
       l = (Vector)listeners.clone(); 
     } 
     for (int i = 0; i < l.size(); i++) { 
     /* 依次通知注册在监听者队列中的每个监听者发生了modelChanged事件, 
     并把事件状态对象e作为参数传递给监听者队列中的每个监听者*/ 
((ModelChangedListener)l.elementAt(i)).modelChanged(e); 
     } 
    } 
    } 




在程序中可见事件源Model类显式地调用了接口中的modelChanged方法,实际是把事件状态对象e作为参数,传递给了监听者类中的modelChanged方法。 

适配类 

适配类是Java事件模型中极其重要的一部分。在一些应用场合,事件从源到监听者之间的传递要通过适配类来"转发"。例如:当事件源发出一个事件,而有几个事件监听者对象都可接收该事件,但只有指定对象做出反应时,就要在事件源与事件监听者之间插入一个事件适配器类,由适配器类来指定事件应该是由哪些监听者来响应。 

适配类成为了事件监听者,事件源实际是把适配类作为监听者注册入监听者队列中,而真正的事件响应者并未在监听者队列中,事件响应者应做的动作由适配类决定。目前绝大多数的开发工具在生成代码时,事件处理都是通过适配类来进行的。 

JavaBeans用户化 

JavaBeans开发者可以给一个Beans添加用户化器(Customizer)、属性编辑器(PropertyEditor)和BeansInfo接口来描述一个Beans的内容,Beans的使用者可在构造环境中通过与Beans附带在一起的这些信息来用户化Beans的外观和应做的动作。一个Beans不必都有BeansCustomizer、PrpertyEditor和BeansInfo,根据实际情况,这些是可选的,当有些Beans较复杂时,就要提供这些信息,以Wizard的方式使Beans的使用者能够用户化一个Beans。有些简单的Beans可能这些信息都没有,则构造工具可使用自带的透视装置,透视出Beans的内容,并把信息显示到标准的属性表或事件表中供使用者用户化Beans,前几节提到的Beans的属性、方法和事件名要以一定的格式命名,主要的作用就是供开发工具对Beans进行透视。当然也是给程序员在手写程序中使用Beans提供方便,使他能观其名、知其意。 

用户化器接口(Customizer Interface) 

当一个Beans有了自己的用户化器时,在构造工具内就可展现出自己的属性表。在定义用户化器时必须要实现java.Beanss.Customizer接口。例如,下面是一个"按钮"Beans的用户化一器: 


public class OurButtonCustomizer 
extends Panel implements Customizer { 
... ... 
/*当实现象OurButtonCustomizer这样的常规属性表时, 
一定要在其中实现addProperChangeListener 
和removePropertyChangeListener,这样, 
构造工具可用这些功能代码为属性事件添加监听者。*/ 
... ... 
private PropertyChangeSupport changes=new PropertyChangeSupport(this); 
public void addPropertyChangeListener(PropertyChangeListener l) { 
changes.addPropertyChangeListener(l); 
public void removePropertyChangeListener(PropertyChangeListener l) { 
changes.removePropertyChangeListener(l); 

... ... 




属性编辑器接口(PropertyEditor Interface) 

一个JavaBeans可提供PropertyEditor类,为指定的属性创建一个编辑器。这个类必须继承自java.Beanss.PropertyEditorSupport类。构造工具与手写代码的程序员不直接使用这个类,而是在下一小节的BeansInfo中实例化并调用这个类。例: 


public class MoleculeNameEditor extends java.Beanss.PropertyEditorSupport { 
public String[] getTags() { 
String resule[]={ 
"HyaluronicAcid","Benzene","buckmisterfullerine", 
"cyclohexane","ethane","water"}; 
return resule;} 





上例中是为Tags属性创建了属性编辑器,在构造工具内,可从下拉表格中选择MoleculeName的属性应是"HyaluronicAid"或是"water"。 

BeansInfo接口 

每个Beans类也可能有与之相关的BeansInfo类,在其中描述了这个Beans在构造工具内出现时的外观。BeansInfo中可定义属性、方法、事件,显示它们的名称,提供简单的帮助说明。 例如: 


public class MoleculeBeansInfo extends SimpleBeansInfo { 
public PropertyDescriptor[] getPropertyDescriptors() { 
try { 
PropertyDescriptor pd=new PropertyDescriptor("moleculeName",Molecule.class); 
/*通过pd引用了上一节的MoleculeNameEditor类,取得并返回moleculeName属性*/ 
pd.setPropertyEditorClass(MoleculeNameEditor.class); 
PropertyDescriptor result[]={pd}; 
return result; 
} catch(Exception ex) { 
System.err.println("MoleculeBeansInfo: unexpected exeption: "+ex); 
return null; 







JavaBeans持久化 

当一个JavaBeans在构造工具内被用户化,并与其它Beans建立连接之后,它的所有状态都应当可被保存,下一次被load进构造工具内或在运行时,就应当是上一次修改完的信息。为了能做到这一点,要把Beans的某些字段的信息保存下来,在定义Beans时要使它实现java.io.Serializable接口。例如: 

public class Button 
implements java.io.Serializable { 




实现了序列化接口的Beans中字段的信息将被自动保存。若不想保存某些字段的信息则可在这些字段前冠以transient或static关键字,transient和static变量的信息是不可被保存的。通常,一个Beans所有公开出来的属性都应当是被保存的,也可有选择地保存内部状态。 Beans开发者在修改软件时,可以添加字段,移走对其它类的引用,改变一个字段的private/protected/public状态,这些都不影响类的存储结构关系。然而,当从类中删除一个字段,改变一个变量在类体系中的位置,把某个字段改成transient/static,或原来是transient/static,现改为别的特性时,都将引起存储关系的变化。 

JavaBeans的存储格式 

JavaBeans组件被设计出来后,一般是以扩展名为jar的Zip格式文件存储,在jar中包含与JavaBeans有关的信息,并以MANIFEST文件指定其中的哪些类是JavaBeans。以jar文件存储的JavaBeans在网络中传送时极大地减少了数据的传输数量,并把JavaBeans运行时所需要的一些资源捆绑在一起,本章主要论述了JavaBeans的一些内部特性及其常规设计方法,参考的是JavaBeans规范1.0A版本。随着世界各大ISV对JavaBeans越来越多的支持,规范在一些细节上还在不断演化,但基本框架不会再有大的变动。
posted @ 2007-07-03 15:14 和田雨 阅读(239) | 评论 (0)编辑 收藏

一个读取xml文件内容的类 
package project.util.xml;

import java.io.*;
import java.util.*;
import javax.servlet.http.*;
import org.apache.log4j.*;
import org.jdom.*;
import org.jdom.input.*;

/**
* <p>Title: <font color="steelblue" size="10">读取xml文件信息</font></p>
* <p>Description: <font color="steelblue">从XML配置文件中获得配置信息。excerpt form jdom。</font></p>
* <p>Copyright: <font color="steelblue">Copyright (c) 2004</font></p>
* <p>Company: <font color="steelblue">Harmonious</font></p>
* @author <font color="steelblue">TZL</font>
* @version <font color="steelblue">1.0</font>
*/

public class XMLReader {
/*
#设置根的输出配置,格式为 "info [2004-05-01 22:35:30] [name]logname(b.c) [line] 86 msg-->log信息"
log4j.rootLogger=DEBUG, rootAppender
log4j.appender.rootAppender=org.apache.log4j.RollingFileAppender
log4j.appender.rootAppender.File=e:/MapXtremeSmpl.log
log4j.appender.rootAppender.MaxFileSize=1000KB
log4j.appender.rootAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.rootAppender.layout.ConversionPattern=%-5p [%d{yyyy-mm-dd HH:mm:ss}] [name] %c{2} [line] %L msg--> %m%n
*/
static public Logger log = Logger.getLogger(XMLReader.class);
protected Element m_RootElement = null;
protected String m_webAppPath = null;

/**
* <font color="orange">构造函数。</font>
* @param xmlFile <font color="steelblue">要读取的配置文件的绝对路径。</font>
*/
public XMLReader(String xmlFile) {
m_webAppPath = null;
try {
PatternLayout layout = new PatternLayout("%-5p %d{yyyy-MM-dd HH:mm:ss} [name] %c{2} [line] %L [msg] %m%n");
ConsoleAppender appender = new ConsoleAppender(/*new SimpleLayout(),*/layout, "System.err");
log.addAppender(appender);

SAXBuilder builder = new SAXBuilder();
document.nbspdoc = null;
doc = builder.build(new FileInputStream(xmlFile));
m_RootElement = doc.getRootElement();
}
catch (IOException ex) {
log.error("XMLReader构造时出现IO错误:" + ex.toString());
}
catch (JDOMException ex1) {
log.error("XMLReader构造时分析XML文件出错:" + ex1.toString());
}
catch (Exception ex) {
log.error("XMLReader 构造出错:" + ex.toString());
}
}

/**
* <font color="orange">构造函数。配置文件必须指定为发布的应用的根目录下的/XmlConfig/Config.xml。</font>
* @param servletObj <font color="steelblue">随便一个HttpServlet对象。</font>
*/
public XMLReader(HttpServlet servletObj) {
m_webAppPath = servletObj.getServletContext().getRealPath("/");
String configFileName = m_webAppPath + "XmlConfig/Config.xml";

try {
PatternLayout layout = new PatternLayout("%-5p %d{yyyy-MM-dd HH:mm:ss} [name] %c{2} [line] %L [msg] %m%n");
ConsoleAppender appender = new ConsoleAppender( /*new SimpleLayout(),*/layout, "System.err");
log.addAppender(appender);

SAXBuilder builder = new SAXBuilder();
document.nbspdoc = null;
doc = builder.build(new FileInputStream(configFileName));
m_RootElement = doc.getRootElement();
}
catch (IOException ex) {
log.error("XMLReader构造时出现IO错误(/XmlConfig/Config.xml):" + ex.toString());
}
catch (JDOMException ex1) {
log.error("XMLReader构造时分析XML文件出错(/XmlConfig/Config.xml):" + ex1.toString());
}
catch (Exception ex) {
log.error("XMLReader构造出错(/XmlConfig/Config.xml):" + ex.toString());
}
}

/**
* <font color="orange">web应用发布在web服务器的绝对路径根目录,最后已经有目录分割符。</font>
* @return <font color="tomato">返回web应用发布在web服务器的绝对路径的根目录。</font>
*/
public String getWebAppPath() {
return m_webAppPath;
}

/**
* <font color="orange">从配置文件中获得配置信息。</font>
* @param key <font color="steelblue">要获取的配置名称。</font>
* @param curRootName <font color="steelblue">查找的起始节点名称,如果为null从根开始查找。</font>
* @return <font color="tomato">配置的字符串。</font>
*/
public String getElementvalue(String curRootName, String key) {
String value = null;
Element curRoot = getElement(null, curRootName);
if (null == curRoot) {
curRoot = m_RootElement;
}
Element keyNode = getElement(curRoot, key);
if (null != keyNode) {
value = keyNode.getTextTrim();

}
return value;
}

/**
* <font color="orange">根据名字获得节点。广度遍历,递归调用。</font>
* @param nodeName <font color="steelblue">节点的名字。</font>
* @param curRoot <font color="steelblue"> 从开始查找的起始节点,如果为null从根开始查找。</font>
* @return <font color="tomato">返回从指定节点下找到的第一个节点。如果没有返回null。</font>
*/
private Element getElement(Element curRoot, String nodeName) {
Element retElement = null;

if (null == nodeName)
return m_RootElement;

if (null == curRoot) {
curRoot = m_RootElement;
}

if (null != curRoot) {
retElement = curRoot.getChild(nodeName);
if (null == retElement) {
List nestElements = curRoot.getChildren();
Iterator iterator = nestElements.iterator();
while (iterator.hasNext() && null == retElement) {
retElement = getElement( (Element) iterator.next(), nodeName);
}
}
}

return retElement;
}

/**
* <font color="orange">获得指定节点的属性。</font>
* @param elementName <font color="steelblue">节点的名称。</font>
* @param attName <font color="steelblue">要获得的属性的名称。</font>
* @return <font color="tomato">要查找的属性的值。</font>
*/
public String getElementAtrribute(String elementName, String attName)
{
Element el = getElement(null, elementName);
if (null == el)
return null;

return el.getAttributevalue(attName);
}

}
posted @ 2007-07-03 14:55 和田雨 阅读(442) | 评论 (0)编辑 收藏

引言
  在做无线项目的时候,与通讯公司的数据通讯有一部分是通过XML交互的,所以必须要动态抓取通讯公司提供的固定的Internet上的数据,便研究了一下如何抓取固定url上的数据,现与大家分享一下。

  类名GetPageCode,有一个方法GetSource,通过属性传递参数,入参控制的是要取得URL的地址,代理服务器的设置及输出方式的控制,这里大家可以再扩展自己的需要,我这里只提供了两种方式,一种是直接写到本地的某个文件中,另外一种就是返回字符串的。类里已经作了比较详细的注释,我想大家很容易就看明白了,如果实在不明白, 那就msn上问吧,MSN:yubo@x263.net。

  调用方式:
  #region 测试获取远程网页



GetPageCode gpc = new GetPageCode();
  gpc.Url="http://ppcode.com";
  gpc.ProxyState=1;//使用代理服务器,0为不使用,设置为1后下面的代理设置才起作用
  gpc.ProxyAddress="http://proxyName.com";//代理服务器地址
  gpc.ProxyPort="80";//代理服务器的端口
  gpc.ProxyAccount="proxy";//代理服务器账号
  gpc.ProxyPassword="password";//代理服务器密码
  gpc.ProxyDomain="bqc";//代理服务器域
  gpc.OutFilePath=filePath;//设置输出文件路径的地方,如果不设置,则返回字符串
  gpc.GetSource();//处理
  string tempErr=gpc.NoteMessage;//如果出错,这里会提示
  string tempCode=gpc.OutString;//返回的字符串
  #endregion
  类代码:
  using System;
  using System.Collections;
  using System.ComponentModel;
  using System.Data;
  using System.Drawing;
  using System.IO;
  using System.Net;
  using System.Text;
  using System.Web;
  namespace Test.Com
  {
   /// <summary>
   /// 功能:取得Internet上的URL页的源码
   /// 创建:2004-03-22
   /// 作者:Rexsp MSN:yubo@x263.net
  /// </summary>
   public class GetPageCode
   {
   #region 私有变量
  /// <summary>
  /// 网页URL地址
  /// </summary>
  private string url=null;
  /// <summary>
  /// 是否使用代码服务器:0 不使用  1 使用代理服务器
  /// </summary>
  private int proxyState=0;
  /// <summary>
  /// 代理服务器地址
  /// </summary>
  private string proxyAddress=null;
  /// <summary>
  /// 代理服务器端口
  /// </summary>
  private string proxyPort=null;
  /// <summary>
  /// 代理服务器用户名
  /// </summary>
  private string proxyAccount=null;
  /// <summary>
  /// 代理服务器密码
  /// </summary>
  private string proxyPassword=null;
  /// <summary>
  /// 代理服务器域
  /// </summary>
  private string proxyDomain=null;
 /// <summary>
  /// 输出文件路径
  /// </summary>
  private string outFilePath=null;
  /// <summary>
  /// 输出的字符串
  /// </summary>
  private string outString=null;
  /// <summary>
  /// 提示信息
  /// </summary>
  private string noteMessage;

  #endregion

  #region 公共属性
  /// <summary>
  /// 欲读取的URL地址
  /// </summary>
  public string Url
  {
   get{return url;}
   set{url=value;}
  }
  /// <summary>
  /// 是否使用代理服务器标志
  /// </summary>
  public int ProxyState
  {
   get{return proxyState;}
   set{proxyState=value;}
  }
  /// <summary>
  /// 代理服务器地址
  /// </summary>
  public string ProxyAddress
  {
   get{return proxyAddress;}
   set{proxyAddress=value;}
  }
  /// <summary>

  /// 代理服务器端口
  /// </summary>
  public string ProxyPort
  {
   get{return proxyPort;}
   set{proxyPort=value;}
  }
  /// <summary>
  /// 代理服务器账号
  /// </summary>
  public string ProxyAccount
  {
   get{return proxyAccount;}
   set{proxyAccount=value;}
  }
  /// <summary>
  /// 代理服务器密码
  /// </summary>
  public string ProxyPassword
  {
   get{return proxyPassword;}
   set{proxyPassword=value;}
  }
  /// <summary>
  /// 代理服务器域
  /// </summary>
  public string ProxyDomain
  {
   get{return proxyDomain;}
   set{proxyDomain=value;}
  }
  /// <summary>
  /// 输出文件路径
  /// </summary>
  public string OutFilePath
  {
   get{return outFilePath;}

  set{outFilePath=value;}
  }
  /// <summary>
  /// 返回的字符串
  /// </summary>
  public string OutString
  {
   get{return outString;}
   
  }
  /// <summary>
  /// 返回提示信息
  /// </summary>
  public string NoteMessage
  {
   get{return noteMessage;}
   
  }
  
  #endregion
  
  #region 构造函数
  public GetPageCode()
  {
  }
  #endregion

  #region 公共方法
  /// <summary>
  /// 读取指定URL地址,存到指定文件中
  /// </summary>
  public void GetSource() 
  { 
   WebRequest request = WebRequest.Create(this.url);
   //使用代理服务器的处理
   if(this.proxyState==1)
   {
    //默认读取80端口的数据

    if(this.proxyPort==null)
     this.ProxyPort="80";

    WebProxy myProxy=new WebProxy(); 
    myProxy = (WebProxy)request.Proxy; 
    myProxy.Address = new Uri(this.ProxyAddress+":"+this.ProxyPort); 
    myProxy.Credentials = new NetworkCredential(this.proxyAccount, this.proxyPassword, this.ProxyDomain);
    request.Proxy = myProxy; 
   }
   try
   
   {
    //请求服务
    WebResponse response = request.GetResponse();
    //返回信息
    Stream resStream = response.GetResponseStream(); 
    StreamReader sr = new StreamReader(resStream, System.Text.Encoding.Default);
    string tempCode= sr.ReadToEnd();
    resStream.Close(); 
    sr.Close();

    //如果输出文件路径为空,便将得到的内容赋给OutString属性
    if(this.outFilePath==null)
    {
     this.outString=tempCode;
    }
    else
    {

     FileInfo fi = new FileInfo(this.outFilePath);
     //如果存在文件则先干掉
     if(fi.Exists)
      fi.Delete();
     StreamWriter sw = new StreamWriter(this.outFilePath,true,Encoding.Default);
     sw.Write(tempCode);
     sw.Flush();
     sw.Close();
    }
   }
   catch
   {
    this.noteMessage="出错了,请检查网络是否连通;";
     }

      }
   #endregion

   }
  }
posted @ 2007-07-03 14:52 和田雨 阅读(249) | 评论 (0)编辑 收藏

<%@ page contentType="image/jpeg" import="java.awt.*, 
java.awt.image.*,java.util.*,javax.imageio.*" %> 
<% 
// 在内存中创建图象 
int width=60, height=20; 
BufferedImage image = new BufferedImage(width, height, 
BufferedImage.TYPE_INT_RGB); 

// 获取图形上下文 
Graphics g = image.getGraphics(); 

// 设定背景色 
g.setColor(new Color(0xDCDCDC)); 
g.fillRect(0, 0, width, height); 

//画边框 
g.setColor(Color.black); 
g.drawRect(0,0,width-1,height-1); 

// 取随机产生的认证码(4位数字) 
String rand = request.getParameter("rand"); 
rand = rand.substring(0,rand.indexOf(".")); 
switch(rand.length()) 

case 1: rand = "000"+rand; break; 
case 2: rand = "00"+rand; break; 
case 3: rand = "0"+rand; break; 
default: rand = rand.substring(0,4); break; 


// 将认证码存入SESSION 
session.setAttribute("rand",rand); 

// 将认证码显示到图象中 
g.setColor(Color.black); 
Integer tempNumber = new Integer(rand); 
String numberStr = tempNumber.toString(); 

g.setFont(new Font("Atlantic Inline",Font.PLAIN,18)); 
String Str = numberStr.substring(0,1); 
g.drawString(Str,8,17); 

Str = numberStr.substring(1,2); 
g.drawString(Str,20,15); 
Str = numberStr.substring(2,3); 
g.drawString(Str,35,18); 

Str = numberStr.substring(3,4); 
g.drawString(Str,45,15); 

// 随机产生88个干扰点,使图象中的认证码不易被其它程序探测到 
Random random = new Random(); 
for (int i=0;i<20;i++) 

int x = random.nextInt(width); 
int y = random.nextInt(height); 
g.drawOval(x,y,0,0); 


// 图象生效 
g.dispose(); 

// 输出图象到页面 
ImageIO.write(image, "JPEG", response.getOutputStream()); 
%>
posted @ 2007-07-03 14:51 和田雨 阅读(212) | 评论 (0)编辑 收藏

JSP/Servlet 中的汉字编码问题

developerWorks
文档选项
将此页作为电子邮件发送

将此页作为电子邮件发送

未显示需要 JavaScript 的文档选项



级别: 初级

IBM,

2001 年 3 月 01 日

网上就 JSP/Servlet 中 DBCS字符编码问题有许多优秀的文章和讨论,本文对它们作一些整理,并结合IBM WebSphere Application Server3.5(WAS)的解决方法作一些说明,希望它不是多余的。

问题的起源

每个国家(或区域)都规定了计算机信息交换用的字符编码集,如美国的扩展 ASCII码, 中国的 GB2312-80,日本的 JIS 等,作为该国家/区域内信息处理的基础,有着统一编码的重要作用。字符编码集按长度分为 SBCS(单字节字符集),DBCS(双字节字符集)两大类。早期的软件(尤其是操作系统),为了解决本地字符信息的计算机处理,出现了各种本地化版本(L10N),为了区分,引进了 LANG, Codepage 等概念。但是由于各个本地字符集代码范围重叠,相互间信息交换困难;软件各个本地化版本独立维护成本较高。因此有必要将本地化工作中的共性抽取出来,作一致处理,将特别的本地化处理内容降低到最少。这也就是所谓的国际化(I18N)。各种语言信息被进一步规范为 Locale 信息。处理的底层字符集变成了几乎包含了所有字形的 Unicode。

现在大部分具有国际化特征的软件核心字符处理都是以 Unicode 为基础的,在软件运行时根据当时的 Locale/Lang/Codepage 设置确定相应的本地字符编码设置,并依此处理本地字符。在处理过程中需要实现 Unicode 和本地字符集的相互转换,甚或以 Unicode 为中间的两个不同本地字符集的相互转换。这种方式在网络环境下被进一步延伸,任何网络两端的字符信息也需要根据字符集的设置转换成可接受的内容。

Java 语言内部是用 Unicode 表示字符的,遵守 Unicode V2.0。Java 程序无论是从/往文件系统以字符流读/写文件,还是往 URL 连接写 HTML 信息,或从 URL 连接读取参数值,都会有字符编码的转换。这样做虽然增加了编程的复杂度,容易引起混淆,但却是符合国际化的思想的。

从理论上来说,这些根据字符集设置而进行的字符转换不应该产生太多问题。而事实是由于应用程序的实际运行环境不同,Unicode 和各个本地字符集的补充、完善,以及系统或应用程序实现的不规范,转码时出现的问题时时困扰着程序员和用户。





回页首


GB2312-80,GBK,GB18030-2000 汉字字符集及 Encoding

其实解决 JAVA 程序中的汉字编码问题的方法往往很简单,但理解其背后的原因,定位问题,还需要了解现有的汉字编码和编码转换。

GB2312-80 是在国内计算机汉字信息技术发展初始阶段制定的,其中包含了大部分常用的一、二级汉字,和 9 区的符号。该字符集是几乎所有的中文系统和国际化的软件都支持的中文字符集,这也是最基本的中文字符集。其编码范围是高位0xa1-0xfe,低位也是 0xa1-0xfe;汉字从 0xb0a1 开始,结束于 0xf7fe;

GBK 是 GB2312-80 的扩展,是向上兼容的。它包含了 20902 个汉字,其编码范围是 0x8140-0xfefe,剔除高位 0x80 的字位。其所有字符都可以一对一映射到 Unicode 2.0,也就是说 JAVA 实际上提供了 GBK 字符集的支持。这是现阶段 Windows 和其它一些中文操作系统的缺省字符集,但并不是所有的国际化软件都支持该字符集,感觉是他们并不完全知道 GBK 是怎么回事。值得注意的是它不是国家标准,而只是规范。随着 GB18030-2000国标的发布,它将在不久的将来完成它的历史使命。

GB18030-2000(GBK2K) 在 GBK 的基础上进一步扩展了汉字,增加了藏、蒙等少数民族的字形。GBK2K 从根本上解决了字位不够,字形不足的问题。它有几个特点,

  • 它并没有确定所有的字形,只是规定了编码范围,留待以后扩充。
  • 编码是变长的,其二字节部分与 GBK 兼容;四字节部分是扩充的字形、字位,其编码范围是首字节 0x81-0xfe、二字节0x30-0x39、三字节 0x81-0xfe、四字节0x30-0x39。
  • 它的推广是分阶段的,首先要求实现的是能够完全映射到 Unicode 3.0 标准的所有字形。
  • 它是国家标准,是强制性的。

现在还没有任何一个操作系统或软件实现了 GBK2K 的支持,这是现阶段和将来汉化的工作内容。

Unicode 的介绍......就免了吧。

JAVA 支持的encoding中与中文编程相关的有:(有几个在JDK文档中未列出)

ASCII 7-bit, 同 ascii7
ISO8859-1 8-bit, 同 8859_1,ISO-8859-1,ISO_8859-1,latin1...
GB2312-80 同gb2312,gb2312-1980,EUC_CN,euccn,1381,Cp1381, 1383, Cp1383, ISO2022CN,ISO2022CN_GB......
GBK (注意大小写),同MS936
UTF8 UTF-8
GB18030 (现在只有IBM JDK1.3.?有支持), 同Cp1392,1392

JAVA 语言采用Unicode处理字符. 但从另一个角度来说,在java程序中也可以采用非Unicode的转码,重要的是保证程序入口和出口的汉字信息不失真。如完全采用ISO-8859-1来处理汉字也能达到正确的结果。网络上流行的许多解决方法,都属于这种类型。为了不致引起混淆,本文不对这种方法作讨论。





回页首


中文转码时'?'、乱码的由来

两个方向转换都有可能得到错误的结果:

  • Unicode-->Byte, 如果目标代码集不存在对应的代码,则得到的结果是0x3f.

    如:
    "\u00d6\u00ec\u00e9\u0046\u00bb\u00f9".getBytes("GBK") 的结果是 "?ìéF?ù", Hex 值是3fa8aca8a6463fa8b4.

    仔细看一下上面的结果,你会发现\u00ec被转换为0xa8ac, \u00e9被转换为\xa8a6... 它的实际有效位变长了!这是因为GB2312符号区中的一些符号被映射到一些公共的符号编码,由于这些符号出现在ISO-8859-1或其它一些SBCS字符集中,故它们在Unicode中编码比较靠前,有一些其有效位只有8位,和汉字的编码重叠(其实这种映射只是编码的映射,在显示时仔细不是一样的。Unicode 中的符号是单字节宽,汉字中的符号是双字节宽) . 在Unicode\u00a0--\u00ff 之间这样的符号有20个。了解这个特征非常重要!由此就不难理解为什么JAVA编程中,汉字编码的错误结果中常常会出现一些乱码(其实是符号字符), 而不全是'?'字符, 就比如上面的例子。

  • Byte-->Unicode, 如果Byte标识的字符在源代码集不存在,则得到的结果是0xfffd.

    如:
    Byte ba[] = {(byte)0x81,(byte)0x40,(byte)0xb0,(byte)0xa1}; new String(ba,"gb2312");

    结果是"?啊", hex 值是"\ufffd\u554a". 0x8140 是GBK字符,按GB2312转换表没有对应的值,取\ufffd. (请注意:在显示该uniCode时,因为没有对应的本地字符,所以也适用上一种情况,显示为一个"?".)

实际编程中,JSP/Servlet 程序得到错误的汉字信息,往往是这两个过程的叠加,有时甚至是两个过程叠加后反复作用的结果.





回页首


JSP/Servlet 汉字编码问题及在 WAS 中的解决办法

4.1 常见的 encoding 问题的现象

网上常出现的 JSP/Servlet encoding 问题一般都表现在 browser 或应用程序端,如:

  • 浏览器中看到的 Jsp/Servlet 页面中的汉字怎么都成了 ’?’ ?
  • 浏览器中看到的 Servlet 页面中的汉字怎么都成了乱码?
  • JAVA 应用程序界面中的汉字怎么都成了方块?
  • Jsp/Servlet 页面无法显示 GBK 汉字。
  • JSP 页面中内嵌在<%...%>,<%=...%>等Tag包含的 JAVA code 中的中文成了乱码,但页面的其它汉字是对的。
  • Jsp/Servlet 不能接收 form 提交的汉字。
  • JSP/Servlet 数据库读写无法获得正确的内容。

隐藏在这些问题后面的是各种错误的字符转换和处理(除第3个外,是因为 Java font 设置错误引起的)。解决类似的字符 encoding 问题,需要了解 Jsp/Servlet 的运行过程,检查可能出现问题的各个点。

4.2 JSP/Servlet web 编程时的 encoding 问题

运行于Java 应用服务器的 JSP/Servlet 为 Browser 提供 HTML 内容,其过程如下图所示:




其中有字符编码转换的地方有:

  • JSP 编译。Java 应用服务器将根据 JVM 的 file.encoding 值读取 JSP 源文件,编译生成 JAVA 源文件,再根据 file.encoding 值写回文件系统。如果当前系统语言支持 GBK,那么这时候不会出现 encoding 问题。如果是英文的系统,如 LANG 是 en_US 的 Linux, AIX 或 Solaris,则要将 JVM 的 file.encoding 值置成 GBK 。系统语言如果是 GB2312,则根据需要,确定要不要设置 file.encoding,将 file.encoding 设为 GBK 可以解决潜在的 GBK 字符乱码问题

  • Java 需要被编译为 .class 才能在 JVM 中执行,这个过程存在与a.同样的 file.encoding 问题。从这里开始 servlet 和 jsp 的运行就类似了,只不过 Servlet 的编译不是自动进行的。对于JSP程序, 对产生的JAVA 中间文件的编译是自动进行的(在程序中直接调用sun.tools.javac.Main类). 因此如果在这一步出现问题的话, 也要检查encoding和OS的语言环境,或者将内嵌在JSP JAVA Code 中的静态汉字转为 Unicode, 要么静态文本输出不要放在 JAVA code 中。对于Servlet, javac 编译时手工指定-encoding 参数就可以了。

  • Servlet 需要将 HTML 页面内容转换为 browser 可接受的 encoding 内容发送出去。依赖于各 JAVA App Server 的实现方式,有的将查询 Browser 的 accept-charset 和 accept-language 参数或以其它猜的方式确定 encoding 值,有的则不管。因此采用固定encoding 也许是最好的解决方法。对于中文网页,可在 JSP 或 Servlet 中设置 contentType="text/html; charset=GB2312";如果页面中有GBK字符,则设置为contentType="text/html; charset=GBK",由于IE 和 Netscape对GBK的支持程度不一样,作这种设置时需要测试一下。因为16位 JAVA char在网络传送时高8位会被丢弃,也为了确保Servlet页面中的汉字(包括内嵌的和servlet运行过程中得到的)是期望的内码,可以用 PrintWriter out=res.getWriter() 取代 ServletOutputStream out=res.getOutputStream(). PrinterWriter 将根据contentType中指定的charset作转换 (ContentType需在此之前指定!); 也可以用OutputStreamWriter封装 ServletOutputStream 类并用write(String)输出汉字字符串。对于 JSP,JAVA Application Server 应当能够确保在这个阶段将嵌入的汉字正确传送出去。

  • 这是解释 URL 字符 encoding 问题。如果通过 get/post 方式从 browser 返回的参数值中包含汉字信息, servlet 将无法得到正确的值。SUN的 J2SDK 中,HttpUtils.parseName 在解析参数时根本没有考虑 browser 的语言设置,而是将得到的值按 byte 方式解析。这是网上讨论得最多的 encoding 问题。因为这是设计缺陷,只能以 bin 方式重新解析得到的字符串;或者以 hack HttpUtils 类的方式解决。参考文章 2 均有介绍,不过最好将其中的中文 encoding GB2312、 CP1381 都改为 GBK,否则遇到 GBK 汉字时,还是会有问题。 Servlet API 2.3 提供一个新的函数 HttpServeletRequest.setCharacterEncoding 用于在调用 request.getParameter(“param_name”) 前指定应用程序希望的 encoding,这将有助于彻底解决这个问题。

4.3 IBM Websphere Application Server 中的解决方法

WebSphere Application Server 对标准的 Servlet API 2.x 作了扩展,提供较好的多语言支持。运行在中文的操作系统中,可以不作任何设置就可以很好地处理汉字。下面的说明只是对WAS是运行在英文的系统中,或者需要有GBK支持时有效。

上述c,d情况,WAS 都要查询 Browser 的语言设置,在缺省状况下, zh, zh-cn 等均被映射为 JAVA encoding CP1381(注意: CP1381 只是等同于 GB2312 的一个 codepage,没有 GBK 支持)。这样做我想是因为无法确认 Browser 运行的操作系统是支持GB2312, 还是 GBK,所以取其小。但是实际的应用系统还是要求页面中出现 GBK 汉字,最著名的是朱总理名字中的“?F"(rong2 ,0xe946,\u9555),所以有时还是需要将 Encoding/Charset 指定为 GBK。当然 WAS 中变更缺省的 encoding 没有上面说的那么麻烦,针对 a,b,参考文章 5,在 Application Server 的命令行参数中指定 -Dfile.encoding=GBK 即可; 针对 d,在 Application Server 的命令行参数中指定-Ddefault.client.encoding=GBK。如果指定了-Ddefault.client.encoding=GBK,那么c情况下可以不再指定charset。

上面列出的问题中还有一个关于Tag<%...%>,<%=...%>中的 JAVA 代码里包含的静态文本未能正确显示的问题,在WAS中的解决方法是除了设置正确的file.encoding, 还需要以相同方法设置-Duser.language=zh -Duser.region=CN。这与JAVA locale的设置有关。

4.4 数据库读写时的 encoding 问题

JSP/Servlet 编程中经常出现 encoding 问题的另一个地方是读写数据库中的数据。

流行的关系数据库系统都支持数据库 encoding,也就是说在创建数据库时可以指定它自己的字符集设置,数据库的数据以指定的编码形式存储。当应用程序访问数据时,在入口和出口处都会有 encoding 转换。对于中文数据,数据库字符编码的设置应当保证数据的完整性. GB2312,GBK,UTF-8 等都是可选的数据库 encoding;也可以选择 ISO8859-1 (8-bit),那么应用程序在写数据之前须将 16Bit 的一个汉字或 Unicode 拆分成两个 8-bit 的字符,读数据之后则需将两个字节合并起来,同时还要判别其中的 SBCS 字符。没有充分利用数据库 encoding 的作用,反而增加了编程的复杂度,ISO8859-1不是推荐的数据库 encoding。JSP/Servlet编程时,可以先用数据库管理系统提供的管理功能检查其中的中文数据是否正确。

然后应当注意的是读出来的数据的 encoding,JAVA 程序中一般得到的是 Unicode。写数据时则相反。

4.5 定位问题时常用的技巧

定位中文encoding问题通常采用最笨的也是最有效的办法――在你认为有嫌疑的程序处理后打印字符串的内码。通过打印字符串的内码,你可以发现什么时候中文字符被转换成Unicode,什么时候Unicode被转回中文内码,什么时候一个中文字成了两个 Unicode 字符,什么时候中文字符串被转成了一串问号,什么时候中文字符串的高位被截掉了……

取用合适的样本字符串也有助于区分问题的类型。如:”aa啊aa?@aa” 等中英相间、GB、GBK特征字符均有的字符串。一般来说,英文字符无论怎么转换或处理,都不会失真(如果遇到了,可以尝试着增加连续的英文字母长度)。





回页首


结束语

其实 JSP/Servlet 的中文encoding 并没有想像的那么复杂,虽然定位和解决问题没有定规,各种运行环境也各不尽然,但后面的原理是一样的。了解字符集的知识是解决字符问题的基础。不过,随着中文字符集的变化,不仅仅是 java 编程,中文信息处理中的问题还是会存在一段时间的。



参考资料



关于作者

 

IBM has authored this article

posted @ 2007-07-03 14:49 和田雨 阅读(265) | 评论 (0)编辑 收藏

作者:Chris Schalk

AJAX 核心基础技术的真实概述。

2006 年 4 月发布

迄今为止,您可能已经听过太多有关 AJAX 的宣传报道,而且很多产品都宣称它们支持或“兼容”AJAX。但是,很多人可能一直无法从技术角度对 AJAX 的实质进行简单、严谨的阐释。本文将摈弃所有华丽词藻,从平实的角度对构成 AJAX 的核心基础进行概述。

 

首先,AJAX 是个新事物吗?

并非如此。远程 Javascript 在最近掀起了一轮热潮。利用它,开发人员能够使用 XML 数据与服务器交互。而 AJAX 就是一种远程 Javascript。AJAX 之所以成为可能,是因为现在许多主要的浏览器都提供可进行独立 XML HTTP 请求的对象。Internet Explorer 5 以及更高版本提供了一个 XMLHTTP 对象,而基于 Mozilla 的浏览器则提供了一个 XMLHttpRequest 对象。这些对象都能够从服务器请求 XML 数据,并以类似的方式处理这些数据。所有能够动态提供 XML 的技术都可以使用服务器端 AJAX 组件。任何动态 Web 技术(从 PHP 到 Servlet)都可以充当 AJAX 服务器。

 

远程 Javascript 与 AJAX 的缺点之一是,页面作者(设计最终页面的人员)必须编写相当数量的 Javascript 代码来管理 XMLHTTP 交互。幸好,JavaServer Faces (JSF) 为此提供了一个解决方案,从而使 AJAX 更加易于使用。

Ajax Under The Hood

只有了解了 AJAX 客户端-服务器事务中涉及的核心 AJAX 体系结构后,方可进一步理解与其他技术(如 JSF)集成的更为高级的 AJAX 示例。

AJAX 目前可以提供以下两种核心技术:

  • 支持 Javascript 和支持 XMLHTTP 和 XMLHttpRequest 对象的浏览器
  • 能够以 XML 响应的 HTTP 服务器技术

因为所有流行的浏览器都支持 Javascript 和必要的 XMLHTTP 请求对象,且几乎所有 Web 服务器技术均可生成 XML(或任何标记),所以核心 AJAX 技术普遍适用。

最简单的 AJAX 应用程序实质上就是一个带有 Javascript 函数的标准 HTML 用户界面,该界面可与能动态生成 XML 的 HTTP 服务器进行交互。任何动态 Web 技术(从 CGI 到 Servlet,以及本文稍后将谈到的 JSF)都可充当服务器端 AJAX 技术。

核心 AJAX 应用程序的主要组件包括:

  • HTML 页面,其中包含:
    • 与 AJAX Javascript 函数交互的 UI 元素
    • 与 AJAX 服务器交互的 Javascript 函数
  • 可处理 HTTP 请求并以 XML 标记响应的服务器端 Web 技术。

这些元素如图 1 所示。

图 1

图 1 核心 AJAX 体系结构

了解了关键元素后,我们就可以设计一个包含输入域、按钮或任何可链接至 Javascript 的元素的 HTML 用户界面了。例如,按下按钮可激活某个 Javascript 函数,或者更深入些,在用户向输入域键入内容时可激活某个 Javascript 函数。为此,您可以将 onkeyup= 赋予 Javascript 函数的值来处理输入域中的数据。例如,当发生 onkeyup 事件(即键入内容)时,输入域“searchField”将调用 Javascript 函数 lookup( )。

<input type="text" id="searchField"
size="20" onkeyup="lookup('searchField');">

除了响应用户界面交互(例如键入)外,AJAX Javascript 函数还可根据自己的计时器进行独立操作。(可以使用该方法执行 AJAX autosave(自动保存)特性。)

如何发出 XML HTTP 请求

现在,我们来了解如何调用 AJAX Javascript 代码。请看以下 Javascript 代码,该代码可发出一个 XML HTTP 请求:

if (window.XMLHttpRequest) {
req = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
req = new
ActiveXObject("Microsoft.XMLHTTP");
}   

利用该代码断,主要的浏览器(Internet Explorer 和 Mozilla/Safari)都可向服务器发出独立的 HTTP 请求。该代码首先检查浏览器是否支持上文提及的两个支持的 XMLHTTP 对象,然后对其中之一进行实例化。

一旦对 XMLHttpRequest(或 Microsoft 的 XMLHTTP)进行了实例化,即可以通过完全相同的方式对其进行操作。

要初始化到服务器的连接,需使用以下 open 方法:

req.open("GET", url, true);

第一个参数是 HTTP 方法(GET POST)。第二个参数是服务器(或使用 POST 的表单操作)的 URL;第三个参数为 true,则表明可进行异步调用(“A”代表 AJAX)。这意味着该浏览器可以在实现请求的同时继续执行其他操作。open 方法中若为 false 值,则表明为非异步处理或顺序处理。我们不建议如此,这是因为您的浏览器会在返回响应前停止操作。

使用 open 初始化连接后,可进行 onreadystatechange 调用(只适用于异步调用)。这将注册一个回调函数,一旦请求完成就会调用该函数:

req.onreadystatechange = processXMLResponse;

在完成请求后,将调用处理 XML 响应的 processXMLResponse( ) 函数。可以通过 onreadystatechange 语句以内联方式声明回调函数:

req.onreadystatechange = processXMLResponse() {
// process request
};

还可使用 req.setRequestHeader 指定任何标题内容,如:

req.setRequestHeader("Cookie", "someKey=true");

一旦完全初始化了 XMLHTTP 请求对象 (req),就可使用 send( ) 初始化对服务器的调用:

req.send(null);

对于 GET 请求,使用 null 值或空字符串“”。

POST 请求包含一个带有表单数据的字符串参数。它们也要求在请求的标题中设置 Content-Type。以下两行演示了如何执行 AJAX POST 请求:

req.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded";
req.send("name=scott&email=stiger@foocorp.com");

完成请求后调用的回调函数通常具有确保请求不会发生错误的代码。这可通过检查 readyState 以及 HTTP 请求的整体状态来实现。(readystate 为 4 表示 XMLHTTP 请求完整,而 200 表示请求成功(404 含义正好相反)。

function processXMLResponse() {
if (req.readyState == 4) {
if (request.status != 200) {
// Process the XML response
}
}
}

XML 响应的处理是通过使用标准 Javascript DOM 方法完成的。例如,要从输入的 XML 流中抽取员工姓名:

<employee>
Chris
</employee>

您可以使用以下代码:

var name = req.responseXML.getElementsByTagName("employee")[0];

分析更为复杂的 XML 会使用如下代码迭代元素:

for (i=0;i<elements.length;i++) {
for (j=0;j<elements[i].childNodes.length;j++) {
var ElementData = elements[i].childNodes[j].firstChild.nodeValue;
}
}

结合使用 XMLHttpRequest 和 HTML

请注意,通过 XMLHttpRequest 获得 XML 响应无需总是格式良好和有效。因此,AJAX 服务器端组件可以直接将 HTML 内容发送至客户端。然后,JavaScript 可使用 req.responseText 方法/属性(它只是将内容作为字符串进行检索)检索该 HTML 内容。可以使用该 HTML 字符串文本以任何方式更改页面。例如,对于以下 HTML 流:

<h2>Hello there!</h2>
<p> This is <b>HTML</b></p>

可使用以下语句检索至一个字符串中:

var HTMLcontent = req.responseText;

之后通过 id="div1" 添加至指定的 HTML DIV。

document.getElementById("div1").innerHTML += HTMLcontent;

JSF 如何支持 AJAX

JSF 及其以组件为中心的体系结构通过允许 JSF 组件全权处理 Javascript 和嵌入式 AJAX“管件”解决了 AJAX 的固有难题。JSF 页面作者甚至无需关注客户端与服务器之间的 AJAX 交互。它们只需像使用其他 JSF 组件那样使用支持 AJAX 的 JSF 组件即可,而且感觉更好。JSF 与 AJAX 的结合使用前途光明!

保持关注!

posted @ 2007-07-02 19:55 和田雨 阅读(242) | 评论 (0)编辑 收藏

Java SE 6中的JDBC 4.0增强

时间:2006-10-25
作者:Srini Penchikala
浏览次数: 5760
本文关键字:JDOJDBCSQLJJavaSrini Penchikalajdbcmustangjava se 6RowIDdriverannotation驱动程序注释
文章工具
推荐给朋友 推荐给朋友
打印文章 打印文章

   Java Platform, Standard Edition(Java SE)版本6(代码名称Mustang)现在已经推出了第二个beta版本,并计划于今年十月份交付使用。Java SE 6包括几处对Java Database ConnectivityJDBC)API的增强。这些增强将被发布为JDBC 4.0版本。新JDBC功能的主要目标是提供更为简单的设计方式和更好的开发人员体验。本文概要说明了JDBC 4.0增强,以及它们给企业Java开发人员带来的好处。我们将借助一个使用Apache Derby作为后端数据库而构建的贷款处理示例应用程序,对新的JDBC功能进行探讨。

Java SE 6.0

  Java SE 6.0版本的主要目标是提供兼容性、稳定性和高质量。这个版本中有几处有趣的增强,尤其是在监控与管理(JMX)、Web service、脚本语言支持(使用Rhino脚本引擎JSR 223将JavaScript技术与Java源代码集成在一起)、数据库连接性、注释支持和安全性方面。JDBC API中还增加了几个新功能,从新的RowId支持到更多的SQLException子类。

JDBC 4.0功能

  借助Mustang中包含的Java SE Service Provider机制,Java开发人员不再需要使用像Class.forName()这样的代码显式地加载JDBC驱动程序,就能注册JDBC驱动程序。通过在调用DriverManager.getConnection()方法时自动定位合适的驱动程序,DriverManager类可以做到这一点。这个功能是向后兼容的,所以无需修改现有的JDBC代码。

   在访问关系数据库的Java应用程序中,通过最小化我们需要编写的“模板”代码,JDBC 4.0还改善了开发人员体验。它还提供实用程序类,以改进JDBC驱动程序的注册和卸载机制,以及管理数据源和连接对象。

   借助JDBC 4.0,Java开发人员现在可以使用Annotations指定SQL查询,从而利用Java SE 5.0(Tiger)版本中提供的元数据支持。基于注释的SQL查询允许在Java代码中使用Annotation关键字指定SQL查询字符串。这样,我们就不必在两个不同文件中查看JDBC代码以及这些代码中调用的数据库查询了。例如,如果有一个叫做getActiveLoans()的方法,用于获取贷款处理数据库中的当前贷款,可以使用@Query(sql="SELECT * FROM LoanApplicationDetails WHERE LoanStatus = 'A'")注释来修饰它。

   此外,Java SE 6开发工具包(JDK 6)的最后版本——与运行时环境(JRE 6)相反——将会有一个基于与它绑定在一起的Apache Derby的数据库。这将帮助开发人员理解新的JDBC功能,而不必单独下载、安装和配置数据库产品。

   JDBC 4.0中加入的主要功能包括:

  • 自动加载JDBC驱动程序类。
  • 连接管理增强。
  • 支持RowId SQL 类型。
  • 使用Annotations的DataSet SQL实现。
  • 处理增强的SQL异常。
  • 支持SQL XML。

  还存在其他功能,比如对大对象(BLOB/CLOB)的改进支持和National Character Set Support。接下来的内容将会详细分析这些功能。

自动加载JDBC驱动程序

  在JDBC 4.0中,调用getConnection方法时,不再需要使用Class.forName()显式地加载JDBC驱动程序,因为DriverManager将会试着从初始化时加载的以及使用与当前应用程序相同的类加载器显式加载的JDBC驱动程序中,找出合适的驱动程序来。

   DriverManager方法getConnection和getDrivers已经增强为支持Java SE Service Provider机制(SPM)。根据SPM,服务被定义为一组众所周知的接口和抽象类,而服务提供程序则是服务的特定实现。它还指定在META-INF/services目录中保存服务提供程序配置文件。JDBC 4.0驱动程序必须包含文件META-INF/services/java.sql.Driver。这个文件包含JDBC驱动程序的java.sql.Driver实现的名称。例如,要加载JDBC驱动程序以连接到Apache Derby数据库,META-INF/services/java.sql.Driver文件就要包含以下项:

   org.apache.derby.jdbc.EmbeddedDriver

   让我们尽快了解如何使用这项新功能加载JDBC驱动程序管理器。下面的列表显示了加载JDBC驱动程序通常使用的示例代码。我们假定需要连接到一个Apache Derby数据库,因为我们在文章后面提到的示例应用程序中将使用这个数据库:

 Class.forName("org.apache.derby.jdbc.EmbeddedDriver");
Connection conn

=DriverManager.getConnection(jdbcUrl, jdbcUser, jdbcPassword);

  但是在JDBC 4.0中,我们不需要Class.forName()这一行。我们只要调用getConnection()就可以获得数据库连接。

   注意,这仅适用于在单机模式中获得数据库连接。如果使用某种数据库连接池来管理连接,代码将会有所区别。

连接管理

  在JDBC 4.0之前,我们依赖于JDBC URL来定义数据源连接。现在有了JDBC 4.0,我们只要提供一组参数(比如主机名称和端口号)给标准的连接工厂机制,就能获得到任意数据源的连接。Connection和Statement接口中加入了新的方法,以便在管理池环境中的Statement对象时可以支持连接状态跟踪改进和更大的灵活性。元数据工具(JSR-175)用于管理活动连接。我们还可以获得元数据信息,比如活动连接的状态,还可以把连接指定为标准的(Connection,用于单机应用程序)、池化的(PooledConnection)或者甚至是用于XA事务的分布式连接(XAConnection)。注意,我们没有直接使用XAConnection。它是由诸如WebLogic、WebSphere或JBoss这样的Java EE应用服务器内部的事务管理器来使用的。

RowId支持

  RowID接口被添加到JDBC 4.0中以支持ROWID数据类型,Oracle和DB2等数据库也支持这种数据类型。当有多条记录没有惟一标识符列,而且需要在不允许复制的Collection(比如Hashtable)中保存查询输出时,RowId很有用。我们可以使用ResultSet的getRowId()方法来获得RowId,并使用PreparedStatement的setRowId()方法在查询中使用RowId。

   关于RowId对象要记住的一件重要事情是,分别在PreparedStatement和ResultSet中使用set或update方法时,RowId对象的值无法在数据源之间移植,可以认为它是特定于数据源的。所以,禁止在不同的Connection和ResultSet对象之间共享它。

   DatabaseMetaData中的getRowIdLifetime()方法可用于确定RowId对象的生存期有效性。表1中列出了返回值或行id可能取的值。

RowId 值 描述
ROWID_UNSUPPORTED 不支持ROWID数据类型。
ROWID_VALID_OTHER RowID的生存期依赖于数据库厂商实现。
ROWID_VALID_TRANSACTION 只要在数据库表中行未被删除,RowID的生存期在当前的事务中。
ROWID_VALID_SESSION 只要在数据库表中行未被删除,RowID的生存期在当前会话的持续时间中。
ROWID_VALID_FOREVER 只要在数据库表中行未被删除,RowID的生存期是无限的。

基于注释的SQL查询

  JDBC 4.0规范利用注释(Java SE 5中加入)允许开发人员把SQL查询与Java类关联在一起,同时不用编写大量的代码。此外,通过使用Generics(JSR 014)和元数据(JSR 175)API,我们可以把SQL查询与Java对象关联在一起,从而指定查询输入和输出参数。我们还可以把查询结果绑定到Java类,以加速对查询输出的处理。我们无需编写通常用于把查询结果填充到Java对象中的所有代码。在Java代码中指定SQL查询时,有2种主要的注释:Select和Update。

Select注释

  Select注释用于在Java类中指定选择查询,以便使用get方法从数据库表中获取数据。表2显示了Select注释的各种属性以及它们的用法。

名称 类型 描述
sql String SQL Select查询字符串。
value String 与sql属性相同。
tableName String 在其上调用sql的数据库表的名称。
readOnly、connected、 scrollable Boolean 标志,分别用于指示返回的DataSet是只读的还是可更新的,是否连接到后端数据库,在connected模式中使用时是否可以滚动。
allColumnsMapped Boolean 标志,用于指示sql注释元素中的列名是否一对一地映射到DataSet中的字段。

  下面是Select注释的一个例子,用于从贷款数据库获得所有当前贷款:

interface LoanAppDetailsQuery extends BaseQuery {
@Select("SELECT * FROM LoanDetais where LoanStatus = 'A'")
DataSet<LoanApplication> getAllActiveLoans();
}

  sql注释也支持I/O参数(参数标记由一个问号后面跟一个整数来表示)。下面是参数化sql查询的一个例子:

interface LoanAppDetailsQuery extends BaseQuery {
@Select(sql="SELECT * from LoanDetails
where borrowerFirstName= 1 and borrowerLastName= 2")
DataSet<LoanApplication> getLoanDetailsByBorrowerName(String borrFirstName,
String borrLastName);
}

Update注释

  Update注释用于修饰Query接口方法,用于更新数据库表中的一条或多条记录。每个Update注释都必须包含一个sql注释类型的元素。下面是Update注释的一个例子:

interface LoanAppDetailsQuery extends BaseQuery {
@Update(sql="update LoanDetails set LoanStatus = 1
where loanId = 2")
boolean updateLoanStatus(String loanStatus, int loanId);
}

处理增强的SQL异常

  异常处理是Java编程的一个重要组成部分,特别是当连接到后端关系数据库或在后端关系数据库上运行查询的时候。我们一直使用SQLException类来指示与数据库相关的错误。JDBC 4.0在SQLException处理方面有几处增强。下面是JDBC 4.0版本中的一些增强,在处理SQLExceptions时它们可以为开发人员带来更好的体验:

  • 新的SQLException子类
  • 支持因果关系
  • 支持增强的for-each循环

新的SQLException类

  JDBC 4.0中创建了SQLException的新子类,以便为Java程序员提供一种编写更多可移植错误处理代码的手段。JDBC 4.0中引入了2类新的SQLException:

  • SQL非瞬时异常
  • SQL瞬时异常

  非瞬时异常:同一项操作重试失败时抛出此异常,直到SQLException的原因得到纠正为止。表3显示了JDBC 4.0中加入的新异常类,它们都是SQLNonTransientException的子类(SQLState类值定义在SQL 2003规范中。):

异常类 SQLState值
SQLFeatureNotSupportedException 0A
SQLNonTransientConnectionException 08
SQLDataException 22
SQLIntegrityConstraintViolationException 23
SQLInvalidAuthorizationException 28
SQLSyntaxErrorException 42

  瞬时异常:当操作在没有任何应用程序级功能进行干涉的情况下重试,前面失败的JDBC操作能够成功时抛出此异常。表4中列出了对SQLTransientException进行扩展的新异常。

异常类 SQLState值
SQLTransientConnectionException 08
SQLTransactionRollbackException 40
SQLTimeoutException None

因果关系

  现在,SQLException类支持配备有异常机制(也称为Cause工具)的Java SE,这种机制让我们能够处理JDBC操作中抛出的多种异常(如果后端数据库支持多异常功能)。这种场景发生在执行一条可能抛出多个SQLException的语句时。

   我们可以使用SQLException中的getNextException()方法,通过异常链进行迭代。下面给出一些用于处理SQLException因果关系的示例代码:

catch(SQLException ex) {
while(ex != null) {
LOG.error("SQL State:" + ex.getSQLState());
LOG.error("Error Code:" + ex.getErrorCode());
LOG.error("Message:" + ex.getMessage());
Throwable t = ex.getCause();
while(t != null) {
LOG.error("Cause:" + t);
t = t.getCause();
}
ex = ex.getNextException();
}
}

增强的For-Each循环

  SQLException类实现了Iterable接口,为Java SE 5中加入的for-each循环功能提供支持。循环的导航将遍历SQLException及其原因。下面给出一个代码片段,对SQLException中加入的for-each循环进行了说明。

catch(SQLException ex) {
for(Throwable e : ex ) {
LOG.error("Error occurred: " + e);
}
}

对国家字符集转换的支持

  下面列出了处理国家字符集(National Character Set)时JDBC类中所做的增强:

  • JDBC数据类型:加入了新的JDBC数据类型,比如NCHAR、NVARCHAR、LONGNVARCHAR和NCLOB。
  • PreparedStatement:加入了新方法setNString、setNCharacterStream和setNClob。
  • CallableStatement:加入了新方法getNClob、getNString和getNCharacterStream。
  • ResultSet:接口加入了新方法updateNClob、updateNString和updateNCharacterStream。

对大对象(BLOB和CLOB)的增强支持

  下面列出了JDBC 4.0中对处理LOB所做的增强:

  • Connection:加入了新方法(createBlob()、createClob()和createNClob())以创建BLOB、CLOB和NCLOB对象的新实例。
  • PreparedStatement:加入了新方法setBlob()、setClob()和setNClob(),以便使用InputStream对象插入BLOB对象,以及使用Reader对象插入CLOB和NCLOB对象。
  • LOB:Blob、Clob和NClob接口中加入了新方法(free()),以便释放这些对象占用的资源。

  现在,让我们看一看java.sql和javax.jdbc包中加入的一些新类,以及它们提供了哪些服务。

JDBC 4.0 API:新类

RowId (java.sql)

  正如前面提过的那样,这个接口代表着数据库中的一个SQL ROWID值。ROWID是一个内置的SQL数据类型,用于识别数据库表中的特定数据行。ROWID通常用在这样的查询中:该查询从输出行没有惟一ID列的表中返回行。

   CallableStatement、PreparedStatement和ResultSet接口中的方法,比如getRowId和setRowId,允许程序员访问SQL ROWID值。接口还提供一个方法(叫做getBytes())把ROWID的值返回为字节数组。DatabaseMetaData接口有一个叫做getRowIdLifetime的新方法,可用于确定RowId对象的生存期。RowId的作用域可以是下列3种类型之一:

  • 在其中创建RowId的数据库事务的持续时间。
  • 在其中创建RowId的会话的持续时间。
  • 数据库表中的标识行,只要它尚未被删除。

DataSet (java.sql)

  DataSet接口为从执行SQL查询返回的数据提供类型安全的视图。DataSet可以在已连接或未连接模式中进行操作。当在已连接模式中使用时,其功能类似于ResultSet。而在未连接模式中使用时,DataSet的功能则类似于CachedRowSet。因为DataSet扩展了List接口,我们可以遍历查询返回的行。

   现有的类中还加入了几个新方法,比如Connection(createSQLXML、isValid)和ResultSet(getRowId)。

示例应用程序

  本文中使用的示例应用程序是一个贷款处理应用程序,它包含一个贷款查找页面,用户可以在这个页面上输入一个贷款ID以获得有关贷款的详细信息,然后提交表单。贷款查找页面调用一个控制器对象,而此控制器对象又调用DAO对象来访问后端数据库,从而获得有关贷款的详细信息。这些详细信息包括借款人姓名、贷款金额和贷款到期时间,它们均会显示在一个贷款详细信息页面上。在后端数据库中,我们使用一个叫做LoanApplicationDetails的表来保存贷款应用程序的详细信息。

   示例应用程序的用例是获得特定贷款ID的贷款详细信息。在注册贷款并针对抵押产品和利率锁定它之后,就可以获得这些贷款详细信息了。贷款处理应用程序的项目细节如表5所示。

名称
Project Name JdbcApp
Project Directory c:\dev\projects\JdbcApp
DB Directory c:\dev\dbservers\apache\derby
JDK Directory c:\dev\java\jdk_1.6.0
IDE Directory c:\dev\tools\eclipse
Database Apache Derby 10.1.2.1
JDK 6.0 (beta 2 release)
IDE Eclipse 3.1
Unit Testing JUnit 4
Build Ant 1.6.5

  下表列出了连接贷款详细信息Apache Derby数据库时需要用到的JDBC参数。这些参数都保存在一个叫做derby.properties的文本文件中,该文件位于项目基目录下的etc/jdbc目录中(参见表6)。

名称
JDBC Driver File LoanApp\META-INF\services\java.sql.driver
Driver org.apache.derby.ClientDriver
URL jdbc:derby:derbyDB
User Id user1
Password user1

  注意:Apache Derby数据库提供2类JDBC驱动程序:Embedded Driver(org.apache.derby.jdbc.EmbeddedDriver)和Client/Server Driver(org.apache.derby.jdbc.ClientDriver)。在示例应用程序中我使用的是Client/Server Driver版本。

   下面给出用于启动Derby数据库服务器和使用ij工具创建新数据库的命令。

   要启动Derby Network Server,打开一个命令提示,然后运行以下命令(修改DERBY_INSTALL和JAVA_HOME环境变量,从而影响本地环境)。

set DERBY_INSTALL=C:\dev\dbservers\db-derby-10.1.2.1-bin
set JAVA_HOME=C:\dev\java\jdk1.6.0
set DERBY_INSTALL=C:\dev\dbservers\db-derby-10.1.3.1-bin
set CLASSPATH=%CLASSPATH%;%DERBY_INSTALL%\lib\derby.jar;
%DERBY_INSTALL%\lib\derbytools.jar;
%DERBY_INSTALL%\lib\derbynet.jar;
cd %DERBY_INSTALL%\frameworks\ NetworkServer\bin
startNetworkServer.bat

  要连接到数据库服务器和创建测试数据库,打开另一个命令提示,然后运行以下命令。确保修改了DERBY_INSTALL和JAVA_HOME环境变量,以适应环境。

set JAVA_HOME=C:\dev\java\jdk1.6.0
set DERBY_INSTALL=C:\dev\dbservers\db-derby-10.1.3.1-bin
set CLASSPATH=%DERBY_INSTALL%\lib\derbyclient.jar;
%DERBY_INSTALL%\lib\derbytools.jar;.
%JAVA_HOME%\bin\java org.apache.derby.tools.ij
connect 'jdbc:derby://localhost:1527/LoanDB;create=true';

测试

  用于编译Java源代码的classpath设置应该包含位于项目主目录下lib目录中的derby.jarjunit4.jar文件。我们还需要在classpath中包含etcetc/jdbcetc/log4j目录,这样应用程序就能访问JDBC属性和Log4J配置文件。我创建了一个Ant编译脚本(位于JdbcApp/build目录中),以自动化编译和打包Java代码的任务。

   用于测试贷款详细信息数据访问对象的测试类叫做LoanAppDetailsDAOTest。我们传入参数(比如贷款ID和借款人姓名)以获得贷款详细信息。

   下面的内容给出了一些代码示例,这些代码是关于JDBC 4.0规范的JDBC驱动程序自动加载和给予注释的SQL查询功能的。

自动加载JDBC驱动程序

  BaseDAO抽象类有一个叫做getConnection的方法,用于获得一个数据库连接。下面的代码片段显示了这个方法(注意,我们不必注册JDBC驱动程序)。只要java.sql.Driver文件中存在正确的驱动程序名称(org.apache.derby.jdbc.ClientDriver),就可以自动加载JDBC驱动程序。

protected Connection getConnection() throws DAOException {
// Load JDBC properties first
if (jdbcUrl == null || jdbcUser == null ||
jdbcPassword == null) {
loadJdbcProperties();
}
// Get Connection
Connection conn = null;
try {
conn = DriverManager.getConnection(jdbcUrl, jdbcUser,
jdbcPassword);
} catch (SQLException sqle) {
throw new DAOException("Error in getting a DB connection.",
sqle);
}
return conn;
}

SQL注释

  LoanAppDetailsQuery接口有带有注释的SQL查询,用于获得当前贷款(条件是loanstatus="A")的列表和基于贷款人姓名的贷款详细信息(在一个贷款人借贷多笔款项的情况下)。我们在本文的前面部分曾见过这些SQL注释。下面给出的示例代码说明了如何调用使用Annotation定义的SQL查询。

public DataSet<LoanAppDetails> getAllActiveLoans() throws Exception {
// Get Connection
Connection conn = getConnection();
LoanAppDetailsQuery query = null;
DataSet<LoanAppDetails> loanDetails = null;
query = QueryObjectFactory.createQueryObject(
LoanAppDetailsQuery.class, conn);
loanDetails = query.getAllActiveLoans();
return loanDetails;
}
public DataSet<LoanAppDetails> getLoanDetailsByBorrowerName(
String borrFirstName, String borrLastName) throws Exception {
// Get Connection
Connection conn = getConnection();
LoanAppDetailsQuery query = null;
DataSet<LoanAppDetails> loanDetails = null;
query = QueryObjectFactory.createQueryObject(
LoanAppDetailsQuery.class, conn);
loanDetails = query.getLoanDetailsByBorrowerName(
borrFirstName,borrLastName);
return loanDetails;
}

结束语

  在使用SQL时,JDBC 4.0可以提供开发的简便性并改善开发人员体验。JDBC 4.0的另一个目标是提供企业级的JDBC功能,把API公开给涵盖范围更广的工具集,以便更好地管理JDBC资源。此外,JDBC 4.0 API为JDBC驱动程序提供了一条迁移路径,从而与J2EE Connector架构(JCA)保持兼容。这使得JDBC厂商能够继续实现JDBC技术Connector API。当在企业级面向服务架构(Service Oriented Architecture,SOA)应用程序中使用JDBC数据源时,这一点很重要,因为在企业级SOA应用程序中,可以把JDBC数据源部署为企业服务总线(Enterprise Service Bus,ESB)架构中的另一个适配器,而不必为JDBC数据源编写ESB特定实现代码。

   在本文中,我们讨论了JDBC 4.0中的增强,比如RowId支持、JDBC驱动程序加载和基于Annotations的SQL。JDBC 4.0中还将加入其他功能,以便在未来支持SQL 2003规范。要了解有关JDBC 4.0规范的更多信息,请参考规范文档。

参考资料

 作者简介
Srini Penchikala 是一位在Flagstar银行工作的信息系统学科问题专家。
posted @ 2007-07-02 19:39 和田雨 阅读(683) | 评论 (0)编辑 收藏

主要特色:
1.使用最新的JDBC3.0数据库驱动。
2.大幅度减化了JSP的反复调用JavaBean,可以直接写SQL,无须再使用连接数据库连接池。
3.将大量的工作交给JavaBean做,JSP负责页面控制。
4.最大特色是极其简单,程序编写也极其简单,非常适合初学者。
5.使用的是tomcat数据库连接池,方便快速。
请提供E_mail,为大家分享,如有高手,请指点不是。
本人E_mail:c841@163.com,望多提意见。
****************************************文件名《page.jsp》*******************************************************************
文件名《page.jsp》
<%@ page language="java" import="java.sql.*, my.*" %>
<%@ page contentType="text/html; charset=gb2312" %>
<jsp:useBean id="pagi" scope="page" class="my.Pagi"/>
<html>
<body>
<table  align="center" border=1>
<%
String CountQuery="select count(*) from 商品资料";
String query = "select * from 商品资料";
ResultSet rs = pagi.querySql(CountQuery,query, request);
String footer = pagi.PageFooter();
%>
<tr>
<td >商品编号</font></td>
<td >商品名称</font></td>
</tr>
<%
if (pagi.intPageCount>0)
{
int i=0;
while (rs.next())
{
i++;
if (i>((pagi.intPage-1)*pagi.intPageSize) &&(i<=pagi.intPage*pagi.intPageSize))
{
%>
<tr>
<td><%=rs.getString(1)%></td>
<td><%=rs.getString(2)%></td>
</tr>
<%
}
}
}
out.println("<tr><td colspan=2>"+footer+"</td></tr>");
rs.close();
pagi.close_all();
%>
</table>
</body>
</html>
****************************************文件名《pagi.java》*********************************************************
文件名《pagi.java》
package my;
import java.util.*;
import java.sql.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import my.DB.*;
public class Pagi
{
ResultSet CountTopicrs=null; //初始化总记录数Rs变量
ResultSet Pagirs=null; //初始化分页时Rs变量
public int intCountTopic=0; //主题总数
public int intPageSize;//每页显示主题数
public int intPageCount;//总页数
public int intPage=1; //当前页数
public String nowPage; // int i;
public String HttpFile;//初始化当前页intPage变量,以准确便获取当前页。 //当前的地址栏的文件
DB db; //定义Linkdb类的一个对象。
public Pagi()//定义构造器,初始化每页显示的主题数和数据库的连接。
{
intPageSize=4;  //每页显示的记录数目
db = new DB();
}
//Countsql:总记录的Query字符串。[形式为select count(*) from tablename]
//Pagisql :要分页的Query字符串。[形式为select * from tablename where ...]
//request :参数传递过程中的变量。[用来控制翻页时的pages变量]
public ResultSet querySql(String Countsql,String Pagisql,HttpServletRequest request)throws Exception
{
HttpFile=request.getRequestURI();  //获取当前文件名。
nowPage=request.getParameter("pages");  //获取当前页,将数值赋予intPage变量。[分页栏中必须要有pages参数]
if (nowPage==null)
{
intPage=1;
}
else
{
intPage=Integer.parseInt(nowPage);
if (intPage<1)
intPage=1;
}
CountTopicrs=db.executeQuery(Countsql); //@@@@@@@@@@@@获取总记录数的结果集。
if (CountTopicrs.next())
{
intCountTopic=CountTopicrs.getInt(1);
}
intPageCount = (intCountTopic+intPageSize-1)/intPageSize;  //获取总页数。
if (intPage>intPageCount)//如果当前页大于总页数,则当前页等于总页数。
{
intPage=intPageCount;
}
CountTopicrs.close();  //关闭总主题数的数据集。
db.close_all();
Pagirs=db.executeQuery(Pagisql);  //@@@@@@@@@@@@@@@获取执行分页的结果集。
return Pagirs;
}//end querySql function.
public int getCountTopic()//获取记录总数。
{
return intCountTopic;
}
public int getPageCount() //获取总页数。
{
return intPageCount;
}
public int getIntPage()  //获取当前页数。
{
return intPage;
}
public String PageFooter()
{
String str = "";
int next, prev;
prev=intPage-1;
next=intPage+1;
str += "查询到<font color=red>"+getCountTopic()+"</font>条记录"+
"    共<font color=red>"+getPageCount()+"</font>页";
str +=" 第<font color=red>"+getIntPage()+"</font>页 ";
if(intPage>1)
str += " <A href=" + HttpFile + "?pages=1"+">首页</A> ";
else
str += " 首页 ";
if(intPage>1)
str += " <A href=" + HttpFile + "?pages=" + prev + ">上一页</A> ";
else
str += " 上一页 ";
if(intPage<intPageCount)
str += " <A href=" + HttpFile + "?pages=" + next + ">下一页</A> ";
else
str += " 下一页 ";
if(intPageCount>1&&intPage!=intPageCount)
str += " <A href=" + HttpFile + "?pages=" + intPageCount +
">尾页</A>";
else
str += " 尾页 ";
return str;
}
public void close_all()
{
db.close_all();
}
}
************************************************文件名《DB.java》********************************************************
文件名《DB.java》
package my;
import java.sql.*;
import javax.naming.*;
import javax.sql.DataSource;
//一个用于查找数据源的工具类。
public class DB {
private Connection con=null;
private Statement stmt=null;
ResultSet rs=null;
public  ResultSet executeQuery(String sql) throws Exception
{
rs=null;
try
{
Context initCtx = new javax.naming.InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource)envCtx.lookup("jdbc/bn");
con=ds.getConnection();
stmt=con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);
rs=stmt.executeQuery (sql);
}
catch(SQLException e){throw e;}
catch(NamingException e){throw e;}
return rs;
}
//执行Insert,Update语句
public void executeUpdate(String sql) throws Exception
{
stmt = null;
rs=null;
try
{
Context initCtx = new javax.naming.InitialContext();
Context envCtx = (Context) initCtx.lookup("java:comp/env");
DataSource ds = (DataSource)envCtx.lookup("jdbc/bn");
con=ds.getConnection();
stmt=con.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,ResultSet.CONCUR_READ_ONLY);
stmt.executeQuery(sql);
stmt.close();
con.close();
}
catch(SQLException ex)
{
System.err.println("执行SQL语句出错: " + ex.getMessage());
}
}
// 关闭stmt和关闭连接
public void close_all()
{
try{
stmt.close();
con.close();
}
catch(SQLException e){e.printStackTrace();}
}
}
***************************************《tomcat中的数据库连接池的设置》********************************************************************************
……
……
……
<Context path="/SQL" docBase="D:\SQL_JSP" debug="0" reloadable="true" crossContext="true">
<Resource name="jdbc/bn" auth="Container" type="javax.sql.DataSource"/>
<ResourceParams name="jdbc/bn">
<parameter>
<name>factory</name>
<value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
</parameter>
<parameter>
<name>driverClassName</name>
<value>com.microsoft.jdbc.sqlserver.SQLServerDriver</value>
</parameter>
<parameter>
<name>url</name>
<value>jdbc:microsoft:sqlserver://127.0.0.1:1433;DatabaseName=jspdev</value>
</parameter>
<parameter>
<name>username</name>
<value>cyg</value>
</parameter>
<parameter>
<name>password</name>
<value>325345353</value>
</parameter>
<parameter>
<name>maxActive</name>
<value>20</value>
</parameter>
<parameter>
<name>maxIdle</name>
<value>20</value>
</parameter>
<parameter>
<name>maxWait</name>
<value>-1</value>
</parameter>
</ResourceParams>
</Context>
</Host>
</Engine>
</Service>
</Server>
posted @ 2007-07-02 19:23 和田雨 阅读(953) | 评论 (0)编辑 收藏

目前所有的B/S系统应用可以分为:有状态(statefull)和无状态(stateless)两大类别。 有状态是指在整个系统的处理过程中要保留记住一些信息,而无状态则相反,每次request都是独立的连接,不需要在每个request之间共享数据等等。

对于这两种应用,通常第一考虑是性能要最优,性能是我们选择IT技术的主要依据之一。

为达到最大化的性能,对于Java系统,以前通常的作法是使用对象池,这样节约对象生成时的性能开销,也就是说系统启动时,预先生成一定数目的对象实例在内存中,需要使用时,从对象池中取出实例,用完,归还对象池,对于有状态的应用,可以使用相关持久化(persistence)策略来保存状态。

下一步,如何并行访问对象池将是非常重要,java的多线程技术为我们提供了实现可能,线程的创建销毁也是可能非常耗时的,那么,无疑象使用对象池一样,我们必须使用线程池来实现多线程并行计算的最优化。

使用线程池和对象池,每次客户端请求发生一次就从线程池中借用一个线程,处理完这个请求就将线程返回线程池,同样,使用线程快速的访问对象,对象也是从对象池中借用,用完就还回对象池。 整个这样的架构设计在性能上是最优的。

有了性能保证,安全机制、事务机制、集群(cluster)技术也将是选择IT技术的主要依据。

J2EE就是这样一个实现上述多种考量的综合标准框架系统,在具体使用中,也许我们对所有这些考量的要求并不都一样重视,比如:如果纯粹追求性能是第一,可以忽视事务机制,那么,完整的J2EE技术也许就并不适合你。

那么我们先看看J2EE是如何从性能上保证我们的应用系统以最快速度运行的,也就是说J2EE中必然应该有上述线程池和对象池的实现技术,servlet实际是基于线程池的更好的线程容器;EJB是基于对象池的更好的对象容器。

看看Servler的架构图:

 

当client1发生请求时servlet容器会从线程池中分配一个线程给这个request.


再看看EJB的架构图:



instance Pool作为一个对象实例池,维持着EJB实例,当然这个对象池是用生命周期的,简单的说 EJB=对象池+远程对象池

但是,EJB还整合了相当的其它增强功能,如安全 事务机制等,这些对于一般应用都是必需的,当然你还必须根据你的需要来选择是否使用J2EE,如果你的应用对安全 事务机制没有要求,直接使用线程池和对象池技术肯定获得最好的性能。

所以,根据Servler和EJB的原理,我们已经可以规划我们的应用,什么可以放在servlet,或什么需要放在EJB中实现:

线程的本质决定了servlet只适合一些轻量的应用,如分析简单XML文档, 通过JDBC访问数据源,使用JMS或JavaMail处理简单的信息Message,或使用JTS/JTA处理简单的事务机制,注意这些用词都是"简单"的,一旦复杂了,就要使用EJB了。

下面从客户端和服务器端两个方面来具体考量这两个技术的使用,这里的客户端不一定是指最终客户端,因为J2EE是多层结构,中间层可能在多个服务器上实现,如果一个服务器上的服务是供另外一个服务器上的应用访问的,那么后者我们也称为客户端。

根据应用的复杂程度和要求不同,分下列情况:

1.在WEB层可以实现的一些应用

如果你的系统没有很复杂的事务处理,或访问很多企业原有的资源,那么可以借助javabean这样的一些Help性质的类来实现你的应用,但是,这样的方案不是最干净clean, 最有效efficient, 或最有扩展性的scalable。

否则,将所有核心计算放置入EJB中。

2.所有的复杂商务计算核心都在EJB中完成

如果你的客户端和服务器端之间有防火墙,那么目前能够无障碍通过防火墙的协议只有Http了(Web Service也是基于http就是这个道理),既然使用http了,而Servlet是基于Http协议的,那么就需要通过servlet来访问EJB,这是我们最普遍的应用情况。

但是,如果你的客户端和服务器端可以放置在一个网络内,之间没有防火墙,那么就不必使用Servlet,直接使用Java调用RMI来访问EJB,这样性能是最好的,这时的Servlet大概只有用于控制Jsp的页面的输出了(MVC模式中的控制作用)。

如果是非java客户端,可以通过CORBA组件来访问EJB。

3.如果你的应用对速度要求很高,要求非常快,对于事务处理等方面几乎无要求

直接使用J2SE,加上线程池和对象池技术,将会使你的java系统性能发挥极致。Jakarta.Apache.org有这两种技术的源码,线程池可以从Servlet容器Tomcat的源码中发现。

posted @ 2007-07-02 19:19 和田雨 阅读(219) | 评论 (0)编辑 收藏

  1.RequestDispatcher.forward()

  是在服务器端起作用,当使用forward()时,Servlet engine传递HTTP请求从当前的Servlet or JSP到另外一个Servlet,JSP 或普通HTML文件,也即你的form提交至a.jsp,在a.jsp用到了forward()重定向至b.jsp,此时form提交的所有信息在b.jsp都可以获得,参数自动传递.

  但forward()无法重定向至有frame的jsp文件,可以重定向至有frame的html文件,同时forward()无法在后面带参数传递,比如servlet?name=frank,这样不行,可以程序内通过response.setAttribute("name",name)来传至下一个页面.

  重定向后浏览器地址栏URL不变.

  例:在servlet中进行重定向

  public void doPost(HttpServletRequest request,HttpServletResponse response)

  throws ServletException,IOException

  {

  response.setContentType("text/html; charset=gb2312");

  ServletContext sc = getServletContext();

  RequestDispatcher rd = null;

  rd = sc.getRequestDispatcher("/index.jsp");   //定向的页面

  rd.forward(request, response);

  }

  通常在servlet中使用,不在jsp中使用。

  2.response.sendRedirect()

  是在用户的浏览器端工作,sendRedirect()可以带参数传递,比如servlet?name=frank传至下个页面,同时它可以重定向至不同的主机上,sendRedirect()可以重定向有frame.的jsp文件.

  重定向后在浏览器地址栏上会出现重定向页面的URL

  例:在servlet中重定向

  public void doPost(HttpServletRequest request,HttpServletResponse response)

  throws ServletException,IOException

  {

  response.setContentType("text/html; charset=gb2312");

  response.sendRedirect("/index.jsp");

  }

  由于response是jsp页面中的隐含对象,故在jsp页面中可以用response.sendRedirect()直接实现重定位。

  注意:

  (1).使用response.sendRedirect时,前面不能有HTML输出。

  这并不是绝对的,不能有HTML输出其实是指不能有HTML被送到了浏览器。事实上现在的server都有cache机制,一般在8K(我是说JSP SERVER),这就意味着,除非你关闭了cache,或者你使用了out.flush()强制刷新,那么在使用sendRedirect之前,有少量的HTML输出也是允许的。

  (2).response.sendRedirect之后,应该紧跟一句return;

  我们已经知道response.sendRedirect是通过浏览器来做转向的,所以只有在页面处理完成后,才会有实际的动作。既然你已经要做转向了,那么后的输出还有什么意义呢?而且有可能会因为后面的输出导致转向失败。

  比较:

  (1).Request Dispatcher.forward()是容器中控制权的转向,在客户端浏览器地址栏中不会显示出转向后的地址;

  (2).response.sendRedirect()则是完全的跳转,浏览器将会得到跳转的地址,并重新发送请求链接。这样,从浏览器的地址栏中可以看到跳转后的链接地址。

  前者更加高效,在前者可以满足需要时,尽量使用RequestDispatcher.forward()方法.

  注:在有些情况下,比如,需要跳转到一个其它服务器上的资源,则必须使用HttpServletResponse.sendRequest()方法。

  3.<jsp:forward page="" />

  它的底层部分是由RequestDispatcher来实现的,因此它带有RequestDispatcher.forward()方法的印记。

  如果在<jsp:forward>之前有很多输出,前面的输出已使缓冲区满,将自动输出到客户端,那么该语句将不起作用,这一点应该特别注意。

  另外要注意:它不能改变浏览器地址,刷新的话会导致重复提交

  4.修改HTTP header的Location属性来重定向

  通过设置直接修改地址栏来实现页面的重定向。
jsp文件代码如下:

  <%

  response.setStatus(HttpServletResponse.SC_MOVED_PERMANENTLY);

  String newLocn = "/newpath/jsa.jsp";

  response.setHeader("Location",newLocn);

  %>

  5.JSP中实现在某页面停留若干秒后,自动重定向到另一页面

  在html文件中,下面的代码:

  <meta http-equiv="refresh" content="300; url=target.jsp">

  它的含义:在5分钟之后正在浏览的页面将会自动变为target.html这一页。代码中300为刷新的延迟时间,以秒为单位。targer.html为你想转向的目标页,若为本页则为自动刷新本页。

  由上可知,可以通过setHeader来实现某页面停留若干秒后,自动重定向到另一页面。

  关键代码:

  String content=stayTime+";URL="+URL;

  response.setHeader("REFRESH",content);

  如果总结得不够全面,请各位发表自己的意见或经验。
posted @ 2007-07-02 13:38 和田雨 阅读(284) | 评论 (0)编辑 收藏

http://www.stcore.com/java/2006-11-17/1163704120d113828.html
posted @ 2007-07-02 13:34 和田雨 阅读(230) | 评论 (0)编辑 收藏

由于工作需要,最近在找一些解决方案,发现Listener是一个很好的东西, 能够监听到session,application的create,destroy,可以监听到session,application

 属性绑定的变化,考虑了一下,可以应用在"在线人数统计","数据缓存"等各个方面,

下面是整理的一些资料.



Listener是Servlet的监听器,它可以监听客户端的请求、服务端的操作等。通过监听器,可以自动激发一些操作,比如监听在线的用户的数量。当增加一个HttpSession时,就激发sessionCreated(HttpSessionEvent se)方法,这样就可以给在线人数加1。常用的监听接口有以下几个:

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

ServletContextListener监听ServletContext。当创建ServletContext时,激发contextInitialized(ServletContextEvent sce)方法;当销毁ServletContext时,激发contextDestroyed(ServletContextEvent sce)方法。

HttpSessionListener监听HttpSession的操作。当创建一个Session时,激发session Created(HttpSessionEvent se)方法;当销毁一个Session时,激发sessionDestroyed (HttpSessionEvent se)方法。

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

下面我们开发一个具体的例子,这个监听器能够统计在线的人数。在ServletContext初始化和销毁时,在服务器控制台打印对应的信息。当ServletContext里的属性增加、改变、删除时,在服务器控制台打印对应的信息。

要获得以上的功能,监听器必须实现以下3个接口:

HttpSessionListener

ServletContextListener

ServletContextAttributeListener

我们看具体的代码,见示例14-9。

【程序源代码】

1 // ==================== Program Discription =====================

2 // 程序名称:示例14-9 : EncodingFilter .java

3 // 程序目的:学习使用监听器

4 // ==============================================================

5 import javax.servlet.http.*;

6 import javax.servlet.*;

7

8 public class OnLineCountListener implements HttpSessionListener,

ServletContextListener,ServletContextAttributeListener

9 {

10  private int count;

11  private ServletContext context = null;

12  

13  public OnLineCountListener()

14  {

15   count=0;

16   //setContext();

17  }

18  //创建一个session时激发

19  public void sessionCreated(HttpSessionEvent se)

20  {

21   count++;

22   setContext(se);

23   

24  }

25  //当一个session失效时激发

26  public void sessionDestroyed(HttpSessionEvent se)

27  {

28   count--;

29   setContext(se);

30  }

31  //设置context的属性,它将激发attributeReplaced或attributeAdded方法

32  public void setContext(HttpSessionEvent se)

33  {

34   se.getSession().getServletContext().
posted @ 2007-07-02 13:33 和田雨 阅读(1074) | 评论 (2)编辑 收藏

1.RequestDispatcher.forward()

是在服务器端起作用,当使用forward()时,Servlet engine传递HTTP请求从当前的Servlet or JSP到另外一个Servlet,JSP 或普通HTML文件,也即你的form提交至a.jsp,在a.jsp用到了forward()重定向至b.jsp,此时form提交的所有信息在 b.jsp都可以获得,参数自动传递.

但forward ()无法重定向至有frame的jsp文件,可以重定向至有frame的html文件,同时forward()无法在后面带参数传递,比如 servlet?name=frank,这样不行,可以程序内通过response.setAttribute("name",name)来传至下一个页面.

重定向后浏览器地址栏URL不变.

例:servlet文件中重定向
CODE

public void doPost(HttpServletRequest request,HttpServletResponse response)

       throws ServletException,IOException

{

       response.setContentType("text/html; charset=gb2312");

       ServletContext sc = getServletContext();

       RequestDispatcher rd = null;

       rd = sc.getRequestDispatcher("/index.jsp");

       rd.forward(request, response);
}


2.response.sendRedirect()

是在用户的浏览器端工作,sendRedirect()可以带参数传递,比如servlet?name=frank传至下个页面,同时它可以重定向至不同的主机上,且在浏览器地址栏上会出现重定向页面的URL.

sendRedirect()可以重定向有frame的jsp文件.

例:servlet文件中重定向
CODE

public void doPost(HttpServletRequest request,HttpServletResponse response)

       throws ServletException,IOException

{

       response.setContentType("text/html; charset=gb2312");

       response.sendRedirect("/index.jsp");

}

posted @ 2007-07-02 13:31 和田雨 阅读(249) | 评论 (0)编辑 收藏

创建型模式

1、FACTORY—追MM少不了请吃饭了,麦当劳的鸡翅和肯德基的鸡翅都是MM爱吃的东西,虽然口味有所不同,但不管你带MM去麦当劳或肯德基,只管向服务员说“来四个鸡翅”就行了。麦当劳和肯德基就是生产鸡翅的Factory

工厂模式:客户类和工厂类分开。消费者任何时候需要某种产品,只需向工厂请求即可。消费者无须修改就可以接纳新产品。缺点是当产品修改时,工厂类也要做相应的修改。如:如何创建及如何向客户端提供。

2、BUILDER—MM最爱听的就是“我爱你”这句话了,见到不同地方的MM,要能够用她们的方言跟她说这句话哦,我有一个多种语言翻译机,上面每种语言都有一个按键,见到MM我只要按对应的键,它就能够用相应的语言说出“我爱你”这句话了,国外的MM也可以轻松搞掂,这就是我的“我爱你”builder。(这一定比美军在伊拉克用的翻译机好卖)

建造模式:将产品的内部表象和产品的生成过程分割开来,从而使一个建造过程生成具有不同的内部表象的产品对象。建造模式使得产品内部表象可以独立的变化,客户不必知道产品内部组成的细节。建造模式可以强制实行一种分步骤进行的建造过程。

3、FACTORY METHOD—请MM去麦当劳吃汉堡,不同的MM有不同的口味,要每个都记住是一件烦人的事情,我一般采用Factory Method模式,带着MM到服务员那儿,说“要一个汉堡”,具体要什么样的汉堡呢,让MM直接跟服务员说就行了。

工厂方法模式:核心工厂类不再负责所有产品的创建,而是将具体创建的工作交给子类去做,成为一个抽象工厂角色,仅负责给出具体工厂类必须实现的接口,而不接触哪一个产品类应当被实例化这种细节。

4、PROTOTYPE—跟MM用QQ聊天,一定要说些深情的话语了,我搜集了好多肉麻的情话,需要时只要copy出来放到QQ里面就行了,这就是我的情话prototype了。(100块钱一份,你要不要)

原始模型模式:通过给出一个原型对象来指明所要创建的对象的类型,然后用复制这个原型对象的方法创建出更多同类型的对象。原始模型模式允许动态的增加或减少产品类,产品类不需要非得有任何事先确定的等级结构,原始模型模式适用于任何的等级结构。缺点是每一个类都必须配备一个克隆方法。

5、SINGLETON—俺有6个漂亮的老婆,她们的老公都是我,我就是我们家里的老公Sigleton,她们只要说道“老公”,都是指的同一个人,那就是我(刚才做了个梦啦,哪有这么好的事)

单例模式:单例模式确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例单例模式。单例模式只应在有真正的“单一实例”的需求时才可使用。

结构型模式

6、ADAPTER—在朋友聚会上碰到了一个美女Sarah,从香港来的,可我不会说粤语,她不会说普通话,只好求助于我的朋友kent了,他作为我和Sarah之间的Adapter,让我和Sarah可以相互交谈了(也不知道他会不会耍我)

适配器(变压器)模式:把一个类的接口变换成客户端所期待的另一种接口,从而使原本因接口原因不匹配而无法一起工作的两个类能够一起工作。适配类可以根据参数返还一个合适的实例给客户端。

7、BRIDGE—早上碰到MM,要说早上好,晚上碰到MM,要说晚上好;碰到MM穿了件新衣服,要说你的衣服好漂亮哦,碰到MM新做的发型,要说你的头发好漂亮哦。不要问我“早上碰到MM新做了个发型怎么说”这种问题,自己用BRIDGE组合一下不就行了

桥梁模式:将抽象化与实现化脱耦,使得二者可以独立的变化,也就是说将他们之间的强关联变成弱关联,也就是指在一个软件系统的抽象化和实现化之间使用组合/聚合关系而不是继承关系,从而使两者可以独立的变化。

8、COMPOSITE—Mary今天过生日。“我过生日,你要送我一件礼物。”“嗯,好吧,去商店,你自己挑。”“这件T恤挺漂亮,买,这条裙子好看,买,这个包也不错,买。”“喂,买了三件了呀,我只答应送一件礼物的哦。”“什么呀,T恤加裙子加包包,正好配成一套呀,小姐,麻烦你包起来。”“……”,MM都会用Composite模式了,你会了没有?

合成模式:合成模式将对象组织到树结构中,可以用来描述整体与部分的关系。合成模式就是一个处理对象的树结构的模式。合成模式把部分与整体的关系用树结构表示出来。合成模式使得客户端把一个个单独的成分对象和由他们复合而成的合成对象同等看待。

9、DECORATOR—Mary过完轮到Sarly过生日,还是不要叫她自己挑了,不然这个月伙食费肯定玩完,拿出我去年在华山顶上照的照片,在背面写上“最好的的礼物,就是爱你的Fita”,再到街上礼品店买了个像框(卖礼品的MM也很漂亮哦),再找隔壁搞美术设计的Mike设计了一个漂亮的盒子装起来……,我们都是Decorator,最终都在修饰我这个人呀,怎么样,看懂了吗?

装饰模式:装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案,提供比继承更多的灵活性。动态给一个对象增加功能,这些功能可以再动态的撤消。增加由一些基本功能的排列组合而产生的非常大量的功能。
10、FACADE—我有一个专业的Nikon相机,我就喜欢自己手动调光圈、快门,这样照出来的照片才专业,但MM可不懂这些,教了半天也不会。幸好相机有Facade设计模式,把相机调整到自动档,只要对准目标按快门就行了,一切由相机自动调整,这样MM也可以用这个相机给我拍张照片了。

门面模式:外部与一个子系统的通信必须通过一个统一的门面对象进行。门面模式提供一个高层次的接口,使得子系统更易于使用。每一个子系统只有一个门面类,而且此门面类只有一个实例,也就是说它是一个单例模式。但整个系统可以有多个门面类。

11、FLYWEIGHT—每天跟MM发短信,手指都累死了,最近买了个新手机,可以把一些常用的句子存在手机里,要用的时候,直接拿出来,在前面加上MM的名字就可以发送了,再不用一个字一个字敲了。共享的句子就是Flyweight,MM的名字就是提取出来的外部特征,根据上下文情况使用。

享元模式:FLYWEIGHT在拳击比赛中指最轻量级。享元模式以共享的方式高效的支持大量的细粒度对象。享元模式能做到共享的关键是区分内蕴状态和外蕴状态。内蕴状态存储在享元内部,不会随环境的改变而有所不同。外蕴状态是随环境的改变而改变的。外蕴状态不能影响内蕴状态,它们是相互独立的。将可以共享的状态和不可以共享的状态从常规类中区分开来,将不可以共享的状态从类里剔除出去。客户端不可以直接创建被共享的对象,而应当使用一个工厂对象负责创建被共享的对象。享元模式大幅度的降低内存中对象的数量。

12、PROXY—跟MM在网上聊天,一开头总是“hi,你好”,“你从哪儿来呀?”“你多大了?”“身高多少呀?”这些话,真烦人,写个程序做为我的Proxy吧,凡是接收到这些话都设置好了自动的回答,接收到其他的话时再通知我回答,怎么样,酷吧。

代理模式:代理模式给某一个对象提供一个代理对象,并由代理对象控制对源对象的引用。代理就是一个人或一个机构代表另一个人或者一个机构采取行动。某些情况下,客户不想或者不能够直接引用一个对象,代理对象可以在客户和目标对象直接起到中介的作用。客户端分辨不出代理主题对象与真实主题对象。代理模式可以并不知道真正的被代理对象,而仅仅持有一个被代理对象的接口,这时候代理对象不能够创建被代理对象,被代理对象必须有系统的其他角色代为创建并传入。

行为模式

13、CHAIN OF RESPONSIBLEITY—晚上去上英语课,为了好开溜坐到了最后一排,哇,前面坐了好几个漂亮的MM哎,找张纸条,写上“Hi,可以做我的女朋友吗?如果不愿意请向前传”,纸条就一个接一个的传上去了,糟糕,传到第一排的MM把纸条传给老师了,听说是个老处女呀,快跑!

责任链模式:在责任链模式中,很多对象由每一个对象对其下家的引用而接

起来形成一条链。请求在这个链上传递,直到链上的某一个对象决定处理此请求。客户并不知道链上的哪一个对象最终处理这个请求,系统可以在不影响客户端的情况下动态的重新组织链和分配责任。处理者有两个选择:承担责任或者把责任推给下家。一个请求可以最终不被任何接收端对象所接受。

14、COMMAND—俺有一个MM家里管得特别严,没法见面,只好借助于她弟弟在我们俩之间传送信息,她对我有什么指示,就写一张纸条让她弟弟带给我。这不,她弟弟又传送过来一个COMMAND,为了感谢他,我请他吃了碗杂酱面,哪知道他说:“我同时给我姐姐三个男朋友送COMMAND,就数你最小气,才请我吃面。”,:-(

命令模式:命令模式把一个请求或者操作封装到一个对象中。命令模式把发出命令的责任和执行命令的责任分割开,委派给不同的对象。命令模式允许请求的一方和发送的一方独立开来,使得请求的一方不必知道接收请求的一方的接口,更不必知道请求是怎么被接收,以及操作是否执行,何时被执行以及是怎么被执行的。系统支持命令的撤消。

15、INTERPRETER—俺有一个《泡MM真经》,上面有各种泡MM的攻略,比如说去吃西餐的步骤、去看电影的方法等等,跟MM约会时,只要做一个Interpreter,照着上面的脚本执行就可以了。

解释器模式:给定一个语言后,解释器模式可以定义出其文法的一种表示,并同时提供一个解释器。客户端可以使用这个解释器来解释这个语言中的句子。解释器模式将描述怎样在有了一个简单的文法后,使用模式设计解释这些语句。在解释器模式里面提到的语言是指任何解释器对象能够解释的任何组合。在解释器模式中需要定义一个代表文法的命令类的等级结构,也就是一系列的组合规则。每一个命令对象都有一个解释方法,代表对命令对象的解释。命令对象的等级结构中的对象的任何排列组合都是一个语言。



16、ITERATOR—我爱上了Mary,不顾一切的向她求婚。

Mary:“想要我跟你结婚,得答应我的条件”

我:“什么条件我都答应,你说吧”

Mary:“我看上了那个一克拉的钻石”

我:“我买,我买,还有吗?”

Mary:“我看上了湖边的那栋别墅”

我:“我买,我买,还有吗?”

Mary:“你的小弟弟必须要有50cm长”

我脑袋嗡的一声,坐在椅子上,一咬牙:“我剪,我剪,还有吗?”

……

迭代子模式:迭代子模式可以顺序访问一个聚集中的元素而不必暴露聚集的内部表象。多个对象聚在一起形成的总体称之为聚集,聚集对象是能够包容一组对象的容器对象。迭代子模式将迭代逻辑封装到一个独立的子对象中,从而与聚集本身隔开。迭代子模式简化了聚集的界面。每一个聚集对象都可以有一个或一个以上的迭代子对象,每一个迭代子的迭代状态可以是彼此独立的。迭代算法可以独立于聚集角色变化。

17、MEDIATOR—四个MM打麻将,相互之间谁应该给谁多少钱算不清楚了,幸亏当时我在旁边,按照各自的筹码数算钱,赚了钱的从我这里拿,赔了钱的也付给我,一切就OK啦,俺得到了四个MM的电话。

调停者模式:调停者模式包装了一系列对象相互作用的方式,使得这些对象不必相互明显作用。从而使他们可以松散偶合。当某些对象之间的作用发生改变时,不会立即影响其他的一些对象之间的作用。保证这些作用可以彼此独立的变化。调停者模式将多对多的相互作用转化为一对多的相互作用。调停者模式将对象的行为和协作抽象化,把对象在小尺度的行为上与其他对象的相互作用分开处理。

18、MEMENTO—同时跟几个MM聊天时,一定要记清楚刚才跟MM说了些什么话,不然MM发现了会不高兴的哦,幸亏我有个备忘录,刚才与哪个MM说了什么话我都拷贝一份放到备忘录里面保存,这样可以随时察看以前的记录啦。

备忘录模式:备忘录对象是一个用来存储另外一个对象内部状态的快照的对象。备忘录模式的用意是在不破坏封装的条件下,将一个对象的状态捉住,并外部化,存储起来,从而可以在将来合适的时候把这个对象还原到存储起来的状态。

19、OBSERVER—想知道咱们公司最新MM情报吗?加入公司的MM情报邮件组就行了,tom负责搜集情报,他发现的新情报不用一个一个通知我们,直接发布给邮件组,我们作为订阅者(观察者)就可以及时收到情报啦

观察者模式:观察者模式定义了一种一队多的依赖关系,让多个观察者对象同时监听某一个主题对象。这个主题对象在状态上发生变化时,会通知所有观察者对象,使他们能够自动更新自己。

20、STATE—跟MM交往时,一定要注意她的状态哦,在不同的状态时她的行为会有不同,比如你约她今天晚上去看电影,对你没兴趣的MM就会说“有事情啦”,对你不讨厌但还没喜欢上的MM就会说“好啊,不过可以带上我同事么?”,已经喜欢上你的MM就会说“几点钟?看完电影再去泡吧怎么样?”,当然你看电影过程中表现良好的话,也可以把MM的状态从不讨厌不喜欢变成喜欢哦。

状态模式:状态模式允许一个对象在其内部状态改变的时候改变行为。这个对象看上去象是改变了它的类一样。状态模式把所研究的对象的行为包装在不同的状态对象里,每一个状态对象都属于一个抽象状态类的一个子类。状态模式的意图是让一个对象在其内部状态改变的时候,其行为也随之改变。状态模式需要对每一个系统可能取得的状态创立一个状态类的子类。当系统的状态变化时,系统便改变所选的子类。
21、STRATEGY—跟不同类型的MM约会,要用不同的策略,有的请电影比较好,有的则去吃小吃效果不错,有的去海边浪漫最合适,单目的都是为了得到MM的芳心,我的追MM锦囊中有好多Strategy哦。

策略模式:策略模式针对一组算法,将每一个算法封装到具有共同接口的独立的类中,从而使得它们可以相互替换。策略模式使得算法可以在不影响到客户端的情况下发生变化。策略模式把行为和环境分开。环境类负责维持和查询行为类,各种算法在具体的策略类中提供。由于算法和环境独立开来,算法的增减,修改都不会影响到环境和客户端。

22、TEMPLATE METHOD——看过《如何说服女生上床》这部经典文章吗?女生从认识到上床的不变的步骤分为巧遇、打破僵局、展开追求、接吻、前戏、动手、爱抚、进去八大步骤(Template method),但每个步骤针对不同的情况,都有不一样的做法,这就要看你随机应变啦(具体实现);

模板方法模式:模板方法模式准备一个抽象类,将部分逻辑以具体方法以及具体构造子的形式实现,然后声明一些抽象方法来迫使子类实现剩余的逻辑。不同的子类可以以不同的方式实现这些抽象方法,从而对剩余的逻辑有不同的实现。先制定一个顶级逻辑框架,而将逻辑的细节留给具体的子类去实现。

23、VISITOR—情人节到了,要给每个MM送一束鲜花和一张卡片,可是每个MM送的花都要针对她个人的特点,每张卡片也要根据个人的特点来挑,我一个人哪搞得清楚,还是找花店老板和礼品店老板做一下Visitor,让花店老板根据MM的特点选一束花,让礼品店老板也根据每个人特点选一张卡,这样就轻松多了;

访问者模式:访问者模式的目的是封装一些施加于某种数据结构元素之上的操作。一旦这些操作需要修改的话,接受这个操作的数据结构可以保持不变。访问者模式适用于数据结构相对未定的系统,它把数据结构和作用于结构上的操作之间的耦合解脱开,使得操作集合可以相对自由的演化。访问者模式使得增加新的操作变的很容易,就是增加一个新的访问者类。访问者模式将有关的行为集中到一个访问者对象中,而不是分散到一个个的节点类中。当使用访问者模式时,要将尽可能多的对象浏览逻辑放在访问者类中,而不是放到它的子类中。访问者模式可以跨过几个类的等级结构访问属于不同的等级结构的成员类。

posted @ 2007-07-02 13:19 和田雨 阅读(221) | 评论 (0)编辑 收藏

1,前言
2,摘要
3,如何使用本文
4,Google简介
5,搜索入门
6,初阶搜索
  6.1,搜索结果要求包含两个及两个以上关键字
  6.2,搜索结果要求不包含某些特定信息
  6.3,搜索结果至少包含多个关键字中的任意一个
7,杂项语法
  7.1,通配符问题
  7.2,关键字的字母大小写
  7.3,搜索整个短语或者句子
  7.4,搜索引擎忽略的字符以及强制搜索
8,进阶搜索
  8.1,对搜索的网站进行限制
  8.2,查询某一类文件
  8.3,搜索的关键字包含在URL链接中
  8.4,搜索的关键字包含在网页标题中
  8.5,搜索的关键字包含在网页“锚”内
9,其他罕用语法
  9.1,搜索所有链接到某个URL地址的网页
  9.2,查找与某个页面结构内容相似的页面
  9.3,从Google服务器上缓存页面中查询信息
10,图片搜索
11,目录检索
12,新闻组搜索
13,Google的其他杰出功能
  13.1,网页快照
  13.2,集成化的工具条
  13.3,单词英文解释
  13.4,网页翻译
  13.5,单词纠错
  13.6,搜索结果过滤
14,Google尚未发布的一些新特性和功能
  14.1,对网页更新日期做出限定
  14.2,新闻搜索
  14.3,分类广告搜索
  14.4,其它Google的最新发展动态
  14.5,一个有趣的地方
15,后记

——————————————————————————————————

[B]1,前言[/B]

  我是在2000年上半年知道Google的。在这之前,我搜索英文信息通常用AltaVista,而搜索中文信息则常用Sina。但自使用了Google之后,它便成为我的Favorite Search engine了。这也得感谢新浪网友曹溪,因为当初正是因为他的大力推介,才使我识得了Google。

  记得1996年夏季的时候,当我第一次接触Internet,便被扑面而来的魔力征服了。那种天涯咫尺的感觉,真是妙不可言。在经历了疯狂的WWW冲浪和如痴如醉的BBS沉迷之后,我意识到Internet对我影响至深的还是在于学习方式的变迁。

  如何来描述这种变迁呢?以前的学习,一般需要预先在肚子里存储下足够的知识,必要时,就从海量的信息中提取所需的部分。这种学习方式造就了很多“才高八斗,学富五车”的大才子。但是,到了信息领域大大超出“四书五经”的新时期,预先无目的的吞下海量信息的学习方式就有些不合时宜了。比方说,我们到了大型的图书城,往往有一种不知所措的感觉。旧有的学习方式需要变更以适应这个信息爆炸的年代。目的明确的去学习,即先知道要学什么,然后有目的的去寻找答案,这种方式看上去更加有效率。我不妨把这称为“即学式”,相应的,旧有的称为“预学式”。

  不过,“即学式”的实施是有前提的。首先,要求学习者拥有一个包罗万象的信息库,以供随时抽取各种目的信息;其次,是需要一个强劲的信息检索工具,以便高效率的从信息库中提取信息。很明显,Internet可以充当那个海量的信息库,而搜索引擎,则正是寻找光明之火的绝好工具。

  “公欲善其事,必先利其器”。Internet只有一个,而搜索引擎则有N多个。有搜索高手说,所谓搜索,就是“在正确的地方使用正确的工具和正确的方法寻找正确的内容”。但是,对于普通人而言,掌握诸多搜索引擎的可能性似乎不大。用一两个相对强劲的具代表性的工具达到绝大多数搜索目的更为人们所迫切希望。不同的时期,涌现出不同的强者。就目前而言,我们非常幸运的有了: *****Google******


[B]2,摘要[/B]

  本文简要的介绍了Google的历史和特点,Google的基本搜索语法和高级搜索语法,Google的特色功能,包括图片搜索、新闻组搜索和集成工具条等。尽管本文名为“Google搜索从入门到精通”,但事实上,本文只能算是对Google的一个并不十分完全的介绍而已。:)


3,如何使用本文

  阅读本文最好具备一些最基本的布尔代数基础,如“与”、“或”、“非”等。不过,即便你没有这方面的知识,也不必在意。对那些实例进行练习,你的疑惑就会迎刃而解。对于刚刚接触网络搜索的读者而言,也许你应该从头到尾的阅读本文;但对于那些有一定搜索基础的读者而言,只需要跳跃着寻找自己所需要的信息就可以了。此外,你也可以参考中文Google大全:http://www.Google.com/intl/zh-CN/about.html,以及搜索帮助:http://www.google.com/intl/zh-CN/help.html,那是官方Google使用手册以及问题解答中心。


4,Google简介

  Google(www.Google.com)是一个搜索引擎,由两个斯坦福大学博士生Larry Page与Sergey Brin于1998年9月发明,Google Inc. 于1999年创立。2000年7月份,Google替代Inktomi成为Yahoo公司的搜索引擎,同年9月份,Google成为中国网易公司的搜索引擎。98年至今,Google已经获得30多项业界大奖。到Google的新闻中心(http://www.Google.com/press/index.html),你可以找到关于一切关于Google的历史和新闻资料。

  Google的成功得益于其强大的功能和独到的特点:

Google检索网页数量达24亿,搜索引擎中排名第一;
Google支持多达132种语言,包括简体中文和繁体中文;
Google网站只提供搜索引擎功能,没有花里胡哨的累赘;
Google速度极快,年初时据说有15000多台服务器,200多条T3级宽带;
Google的专利网页级别技术PageRank能够提供准确率极高的搜索结果;
Google智能化的“手气不错”功能,提供可能最符合要求的网站;
Google的“网页快照”功能,能从Google服务器里直接取出缓存的网页。
Google具有独到的图片搜索功能;
Google具有强大的新闻组搜索功能;
Google具有二进制文件搜索功能(PDF,DOC,SWF等);
Google还有很多尚在开发阶段的令人吃惊的设想和功能。
等等


5,搜索入门

  要用Google做搜索,当然首先要进Google网站--www.Google.com;不过,163.com和yahoo.com.cn使用的实际上也是Google搜索引擎,只是对搜索结果进行了编排,而且无法提供一些特色功能,如图片搜索等。因此,如果你要搜索网页的话,就直接使用Google.com吧。

  第一次进入Google,它会根据你的操作系统,确定语言界面。需要提醒的是,Google是通过cookie来存储页面设定的,所以,如果你的系统禁用cookie,就无法对Google界面进行个人设定了。

  Google的首页很清爽,LOGO下面,排列了四大功能模块:网站、图像、新闻组和目录服务。默认是网站搜索。现在进行第一次搜索实践,假定你是个搜索新手,想要了解一下搜索引擎的来龙去脉和搜索技巧。在搜索框内输入一个关键字“搜索引擎”, 选中“搜索中文(简体)网页”选项,然后点击下面的“Google搜索”按钮(或者直接回车),结果就出来了。 搜索:“搜索引擎”
结果:已搜索有关搜索引擎的中文(简体)网页。 共约有707,000项查询结果,这是第1-10项 。 搜索用时0.08秒。

  仔细看一下搜索结果的前十项,就会发现绝大部分链接是搜索引擎本身,而不是对搜索引擎的或者搜索技巧方面的介绍。

  注意:文章中搜索语法外面的引号仅起引用作用,不能带入搜索栏内。


6,初阶搜索

  上例是最基本的搜索,即查询包含单个关键字的信息。但是,你可以发现,上例中,单个关键字“搜索引擎”,搜索得的信息浩如烟海,而且绝大部分并不符合自己的要求,怎么办呢?我们需要进一步缩小搜索范围和结果。

6.1,搜索结果要求包含两个及两个以上关键字

  一般搜索引擎需要在多个关键字之间加上“ ”,而Google无需用明文的“ ”来表示逻辑“与”操作,只要空格就可以了。现在,我们需要了解一下搜索引擎的历史,因此期望搜得的网页上有“搜索引擎”和“历史”两个关键字。

示例:搜索所有包含关键词“搜索引擎”和“历史”的中文网页
搜索:“搜索引擎 历史”
结果:已搜索有关搜索引擎 历史的中文(简体)网页。 共约有78,600项查询结果,这是第1-10项 。 搜索用时0.36秒。

  用了两个关键字,查询结果已经从70多万项减少到7万多项。但查看一下搜索结果,发现前列的绝大部分结果还是不符合要求,大部分网页涉及的“历史”,并不是我们所需要的“搜索引擎的历史”。 怎么办呢?删除与搜索引擎不相关的“历史”。我们发现,这部分无用的资讯,总是和“文化”这个词相关的,另外一些常见词是“中国历史”、“世界历史”、“历史书籍”等。

6.2,搜索结果要求不包含某些特定信息

  Google用减号“-”表示逻辑“非”操作。“A –B”表示搜索包含A但没有B的网页。

示例:搜索所有包含“搜索引擎”和“历史”但不含“文化”、“中国历史”和“世界历史”的中文网页
搜索:“搜索引擎 历史 -文化 -中国历史 -世界历史”
结果:已搜索有关搜索引擎 历史 -文化 -中国历史 -世界历史的中文(简体)网页。 共约有36,800项查询结果,这是第1-10项 。 搜索用时0.22秒。

  我们看到,通过去掉不相关信息,搜索结果又减少了将近一半。第一个搜索结果是:

搜索引擎直通车≡搜索引擎发展历史
搜索引擎直通车, ... 搜索引擎专业介绍站点. ...
www.se-express.com/about/about.htm - 14k - 网页快照 - 类似网页

非常符合搜索要求。另外,第八项搜索结果:

463搜索王
本站检索 整个网站 在此输入关键词. 你的当前
位置:首页 >> Internet搜索手册 >> 搜索引擎的历史. ...
www.cnco.net/search/history.htm - 21k - 网页快照 - 类似网页

  也符合搜索要求。但是,10个结果只有两个符合要求,未免太少了点。不过,在没有更好的策略之前,不妨先点开一个结果看看。点开se-express.com的这个名为“搜索引擎发展历史”的网页,我们发现,搜索引擎的历史,是与互联网早期的文件检索工具“Archie”息息相关的。此外,搜索引擎似乎有个核心程序,叫“蜘蛛”,而最早成型的搜索引擎是“Lycos”,使搜索引擎深入人心的是“Yahoo”。了解了这些信息,我们就可以进一步的让搜索结果符合要求了。

  注意:这里的“ ”和“-”号,是英文字符,而不是中文字符的“+”和“-”。此外,操作符与作用的关键字之间,不能有空格。比如“搜索引擎 - 文化”,搜索引擎将视为关键字为“搜索引擎”和“文化”的逻辑“与”操作,中间的“-”被忽略。

6.3,搜索结果至少包含多个关键字中的任意一个。

  Google用大写的“OR”表示逻辑“或”操作。搜索“A OR B”,意思就是说,搜索的网页中,要么有A,要么有B,要么同时有A和B。在上例中,我们希望搜索结果中最好含有“archie”、“lycos”、“蜘蛛”等关键字中的一个或者几个,这样可以进一步的精简搜索结果。

示例:搜索如下网页,要求必须含有“搜索引擎”和“历史”,没有“文化”,可以含有以下关键字中人任何一个或者多个:“Archie”、“蜘蛛”、“Lycos”、“Yahoo”。
搜索:“搜索引擎 历史 archie OR 蜘蛛 OR lycos OR yahoo -文化”
结果:已搜索有关搜索引擎 历史 archie OR 蜘蛛 OR lycos OR yahoo -文化的中文(简体)网页。 共约有8,400项查询结果,这是第1-10项 。 搜索用时0.16秒。

  我们看到,搜索结果缩小到8千多项,前20项结果中,大部分都符合搜索要求。如果你想了解一下解搜索引擎的历史发展,就不妨研究一下现在搜索到的结果吧。 注意:“与”操作必须用大写的“OR”,而不是小写的“or”。

  在上面的例子中,我介绍了搜索引擎最基本的语法“与”“非”和“或”,这三种搜索语法Google分别用“ ”(空格)、“-”和“OR”表示。顺着上例的思路,你也可以了解到如何缩小搜索范围,迅速找到目的资讯的一般方法:目标信息一定含有的关键字(用“ ”连起来),目标信息不能含有的关键字(用“-”去掉),目标信息可能含有的关键字(用“OR”连起来)。


7,杂项语法

7.1,通配符问题

  很多搜索引擎支持通配符号,如“*”代表一连串字符,“?”代表单个字符等。Google对通配符支持有限。它目前只可以用“*”来替代单个字符,而且包含“*”必须用""引起来。比如,“"以*治国"”,表示搜索第一个为“以”,末两个为“治国”的四字短语,中间的“*”可以为任何字符。

7.2,关键字的字母大小写

  Google对英文字符大小写不敏感,“GOD”和“god”搜索的结果是一样的。

7.3,搜索整个短语或者句子

  Google的关键字可以是单词(中间没有空格),也可以是短语(中间有空格)。但是,用短语做关键字,必须加英文引号,否则空格会被当作“与”操作符。

示例:搜索关于第一次世界大战的英文信息。
搜索:“"world war I"”
结果:已向英特网搜索"world war i". 共约有937,000项查询结果,这是第1-10项 。 搜索用时0.06秒。

7.4,搜索引擎忽略的字符以及强制搜索

  Google对一些网路上出现频率极高的英文单词,如“i”、“com”、“www”等,以及一些符号如“*”、“.”等,作忽略处理。

示例:搜索关于www起源的一些历史资料。
搜索:“www的历史 internet”
结果:以下的字词因为使用过于频繁,没有被列入搜索范围: www 的. 已搜索有关www的历史 internet的中文(简体)网页。 共约有75,100项查询结果,这是第1-10项 。 搜索用时0.22秒。

  我们看到,搜索“www的历史 internet”,但搜索引擎把“www”和“的”都省略了。于是上述搜索只搜索了“历史”和“internet”。这显然不符合要求。这里我顺便说一点搜索引擎分词的知识。当我们在搜索“www的历史”的时候,搜索引擎实际上把这个短语分成三部分,“www”、“的”和“历史”分别来检索,这就是搜索引擎的分词。所以尽管你输入了连续的“www的历史”,但搜索引擎还是把这个短语当成三个关键字分别检索。

  如果要对忽略的关键字进行强制搜索,则需要在该关键字前加上明文的“+”号。
搜索:“+www +的历史 internet”
结果:已搜索有关+www +的历史 internet的中文(简体)网页。 共约有25,000项查询结果,这是第1-10项 。 搜索用时0.05秒。

  另一个强制搜索的方法是把上述的关键字用英文双引号引起来。在上例“”world war I””中,“I”其实也是忽略词,但因为被英文双引号引起来,搜索引擎就强制搜索这一特定短语。

搜索:“"www的历史" internet”
结果:已搜索有关"www的历史" internet的中文(简体)网页。 共约有7项查询结果,这是第1-6项 。 搜索用时0.26秒。

  我们看到,这一搜索事实上把“www的历史”作为完整的一个关键字。显然,包含这样一个特定短语的网页并不是很多,不过,每一项都很符合要求。

  注意:大部分常用英文符号(如问号,句号,逗号等)无法成为搜索关键字,加强制也不行。


8,进阶搜索

  上面已经探讨了Google的一些最基础搜索语法。通常而言,这些简单的搜索语法已经能解决绝大部分问题了。不过,如果想更迅速更贴切找到需要的信息,你还需要了解更多的东西。

8.1,对搜索的网站进行限制

  “site”表示搜索结果局限于某个具体网站或者网站频道,如“www.sina.com.cn”、“edu.sina.com.cn”,或者是某个域名,如“com.cn”、“com”等等。如果是要排除某网站或者域名范围内的页面,只需用“-网站/域名”。

示例:搜索中文教育科研网站(edu.cn)上关于搜索引擎技巧的页面。
搜索:“搜索引擎 技巧 site:edu.cn”
结果:已搜索有关搜索引擎 技巧 site:edu.cn的中文(简体)网页。 共约有608项查询结果,这是第1-10项 。 搜索用时0.05秒。

示例:上著名IT门户网站ZDNET和CNET搜索一下关于搜索引擎技巧方面的资讯。
搜索:“"search engine" tips site:www.zdnet.com OR site:www.cnet.com”
结果:已在www.zdnet.com内搜索有关"search engine" tips OR site:www.cnet.com的网页。 共约有1,040项查询结果,这是第1-10项 。 搜索用时0.09秒。

  注意,在这里Google有个小BUG。“已在www.zdnet.com内搜索…”,其实应该表述成“已在www.zdnet.com和www.cnet.com内搜索…”。

示例:搜索新浪科技频道中关于搜索引擎技巧的信息。
搜索:“搜索引擎 技巧 site:tech.sina.com.cn”
结果:已在tech.sina.com.cn搜索有关搜索引擎 技巧 的中文(简体)网页。 共约有163项查询结果,这是第1-10项 。 搜索用时0.07秒。

  注意:site后的冒号为英文字符,而且,冒号后不能有空格,否则,“site:”将被作为一个搜索的关键字。此外,网站域名不能有“http://”前缀,也不能有任何“/”的目录后缀;网站频道则只局限于“频道名.域名”方式,而不能是“域名/频道名”方式。

8.2,在某一类文件中查找信息

  “filetype:”是Google开发的非常强大实用的一个搜索语法。也就是说,Google不仅能搜索一般的文字页面,还能对某些二进制文档进行检索。目前,Google已经能检索微软的Office文档如.xls、.ppt、.doc,.rtf,WordPerfect文档,Lotus1-2-3文档,Adobe的.pdf文档,ShockWave的.swf文档(Flash动画)等。其中最实用的文档搜索是PDF搜索。PDF是ADOBE公司开发的电子文档格式,现在已经成为互联网的电子化出版标准。目前Google检索的PDF文档大约有2500万左右,大约占所有索引的二进制文档数量的80%。PDF文档通常是一些图文并茂的综合性文档,提供的资讯一般比较集中全面。

示例:搜索几个资产负债表的Office文档。
搜索:“资产负债表 filetype:doc OR filetype:xls OR filetype:ppt”
结果:已搜索有关资产负债表 filetype:doc OR filetype:xls OR filetype:ppt的中文(简体)网页。 共约有481项查询结果,这是第1-10项 。 搜索用时0.04秒。

  注意,下载的Office文件可能含有宏病毒,谨慎操作。

示例:搜索一些关于搜索引擎知识和技巧方面的PDF文档
搜索:?quot;search engine" tips OR tutorial filetype:pdf”
结果:已向英特网搜索"search engine" tips OR tutorial filetype:pdf. 共约有12,600项查询结果,这是第1-10项 。 搜索用时0.22秒。

  我们来看其中的一个结果:

[PDF]Search Engines Tips
文档类型: PDF/Adobe Acrobat - HTML 版
... http://www.google.com/press/zeitgeist.html See what people are searching on at Google.com
* Search Engine Watch http://searchenginewatch.com/ Some free tips ...
www.allvertical.com/PromoKits/SearchEngineTips.pdf - 类似网页

  可以看到,Google用[PDF]来标记这是一个PDF的文档检索,另外,它还给出了该PDF文档的HTML版本,该HTML版保留了文档的文字内容和结构,但没有图片。

8.3,搜索的关键字包含在URL链接中

  “inurl”语法返回的网页链接中包含第一个关键字,后面的关键字则出现在链接中或者网页文档中。有很多网站把某一类具有嗤?属性的资源名称显示在目录名称或者网页名称中,比如“MP3”、“GALLARY”等,于是,就可以用INURL语法找到这些相关资源链接,然后,用第二个关键词确定是否有某项具体资料。INURL语法和基本搜索语法的最大区别在于,前者通常能提供非常精确的专题资料。 示例:查找MIDI曲“沧海一声笑”。
搜索:“inurl:midi “沧海一声笑””
结果:已搜索有关inurl:midi "沧海一声笑"的中文(简体)网页。 共约有27项查询结果,这是第1-10项 。 搜索用时0.34秒。

  注意:“inurl:”后面不能有空格,Google也不对URL符号如“/”进行搜索。例如,Google会把“cgi-bin/phf”中的“/”当成空格处理。

  “allinurl”语法返回的网页的链接中包含所有作用关键字。这个查询的关键字只集中于网页的链接字符串。

示例:查找可能具有PHF安全漏洞的公司网站。通常这些网站的CGI-BIN目录中含有PHF脚本程序(这个脚本是不安全的),表现在链接中就是“域名/cgi-bin/phf”。
搜索:“allinurl:"cgi-bin" phf +com”
结果:已向英特网搜索allinurl:"cgi-bin" phf +com. 共约有51项查询结果,这是第1-10项 。 搜索用时0.11秒。

8.4,搜索的关键字包含在网页标题中

  “intitle”和“allintitle”的用法类似于上面的inurl和allinurl,只是后者对URL进行查询,而前者对网页的标题栏进行查询。网页标题,就是HTML标记语言title中之间的部分。网页设计的一个原则就是要把主页的关键内容用简洁的语言表示在网页标题中。因此,只查询标题栏,通常也可以找到高相关率的专题页面。 示例:查找日本明星藤原纪香的照片集。
搜索:“intitle:藤原纪香 "写真集"”
结果:已搜索有关intitle:藤原纪香 "写真集"的中文(简体)网页。 共约有315项查询结果,这是第1-10项 。 搜索用时0.15秒。

8.5,搜索的关键字包含在网页的“锚”(anchor)链点内

  所谓“锚”,就是在同一个网页中快速切换链接点。与URL和TITLE类似,Google提供了两种对anchor的检索,“inanchor”和“allincnchor”。对此不作详述。


9,其他罕用语法

9.1,搜索所有链接到某个URL地址的网页

  如果你拥有一个个人网站,估计很想知道有多少人对你的网站作了链接。而“link”语法就能让你迅速达到这个目的。

示例:搜索所有含指向华军软件园“www.newhua.com”链接的网页。
搜索:“link:www.newhua.com”
结果:搜索有链接到www.newhua.com的网页 。 共约有920项查询结果,这是第1-10项 。 搜索用时0.12秒。

  注意:“link”不能与其他语法相混合操作,所以“link:”后面即使有空格,也将被Google忽略。另外还要说明的是,link只列出Google索引链接很小一部分,而非全部,所以如果你用Google没有搜到链到你的主页的链接,也不必灰心丧气。

  除了上述功能,link语法还有其它妙用。一般说来,做友情链接的网站都有相似地方。这样,你可以通过这些友情链接,找到一大批具有相似内容的网站。比如说,你是个天文爱好者,你发现某网站非常不错,那么,可以用link语法查一下与之做链接的网站,也许可以找到更多符合你兴趣的内容。

9.2,查找与某个页面结构内容相似的页面

  “related”用来搜索结构内容方面相似的网页。例:搜索所有与中文新浪网主页相似的页面(如网易首页,搜狐首页,中华网首页等),“related:wwwsina.com.cn/index.shtml”。我到现在也不明白这个语法有什么作用,如果有谁知道,请不吝指教。预先感谢。:)

9.3,从Google服务器上缓存页面中查询信息

  “cache”用来搜索Google服务器上某页面的缓存,通常用于查找某些已经被删除的死链接网页,相当于使用普通搜索结果页面中的“网页快照”功能。

  其它罕用语法如info、stock等不一一介绍,有兴趣的读者可以参阅Google大全。


10,图片搜索

  Google自称可以检索390,000,000张图片,并称自己为“互联网上最好用的图像搜索工具”。从使用结果来看,Google的图片搜索的确不错,但个人以为比AltaVista的还是要差一些,主要体现在检索图片数量比不上AV,匹配度比AV的图片搜索器也差了些。但AltaVista国内用户无法正常访问,因此对中国用户而言,Google的图片搜索引擎已经是最好的了。

  Google首页点击“图像”链接就进入了Google的图像搜索界面“images.Google.com”。你可以在关键字栏位内输入描述图像内容的关键字,如“britney spears”,就会搜索到大量的小甜甜布兰妮的图片。我目前尚不是很清楚图片的排列标准,不过以观察来看,似乎图片文件名完全符合关键字的结果排列比较考前,然后才按照普通的页面搜索时的标准排列。

  Google给出的搜索结果具有一个直观的缩略图(THUMBNAIL),以及对该缩略图的简单描述,如图像文件名称,以及大小等。点击缩略图,页面分成两祯,上祯是图像之缩略图,以及页面链接,而下祯,则是该图像所处的页面。屏幕右上角有一个“Remove Frame”的按钮,可以把框架页面迅速切换到单祯的结果页面,非常方便。

  Google图像搜索目前支持的语法包括基本的搜索语法如“ ”、“-”、“OR”、“site”和 “filetype:”。其中“filetype:”的后缀只能是几种限定的图片类似,如JPG,GIF等。

示例:查找新浪网上本拉登的图片
搜索:“拉登 OR 拉丹 site:sina.com.cn”
结果:搜索有关 拉登 OR 拉丹 site:sina.com.cn 的图片。 共有6项查询结果,这是第1-6项。 搜索用时0.36秒。

  这里我想说明一点的是,images.google.com作为专门的图片搜索引擎,实际上有其特殊的用途。

  举个例子,互联网上本拉登的照片成千上万,但是,它们都是分散的,往往随机的分布于各种新闻报道中。如果用搜索图片库的方式(最容易想到的如“Ben Ladin photo”),来搜索本拉登的照片,显然是不恰当的,因为很少有人专门为拉登建一个在线相册。在这个时候,images.google.com就派上用场了。

  但是,如果查找的图片在网上有很多主题“gallary”,如诸多电影电视明星的照片,则明显就不适合用images.google.com来查找了。

  images.google.com对于很多报纸杂志的编辑,绝对是一个雪中送炭式的工具。比如要在某个版面上插一张专题图片,用google的图片搜索功能几秒钟就可以搞定。

  综上,可以有这样的一般性结论:如果要搜索的图片是分散的,则用google图片搜索;如果要搜索的图片通常是处于某个图片集合中的,则不适合用google图片搜索。


11,目录检索

  如果不想搜索广泛的网页,而是想寻找某些专题网站,可以访问Google的分类目录“http://directory.Google.com/”,中文目录是“http://directory.Google.com/Top/World/Chinese_Simplified/”。分类的网站目录一般由专人负责,分类明确,信息集中。因此读者应该养成这样的习惯:首先考虑所需要的信息能否在一个专门主题的网站上找到。不过需要说明的是,用目录检索,往往需要用户对查询的领域很熟悉。否则,连查询的内容属于哪个类目都不知道,目录浏览也就无从谈及了。 目前Google使用的分类目录采用了ODP的内容。“Open Directory Project”是网景公司所主持的一项大型公共网页目录。由全世界各地的义务编辑人员来审核挑选网页,并依照网页的性质及内容来分门别类。因此,在某一目录门类中进行搜索往往能有更高的命中率。另外,Google根据其专业的“网页级别”(PageRank)技术对目录中登录的网站进行了排序,可以让一般的检索更具高效率。

示例:查找一下介绍搜索引擎方面的中文网站
搜索:先进入中文简体分类目录,再进入“计算机”目录,再进入“互联网络”子目录,再进入“搜寻”子目录。我们看到在“World > Chinese Simplified > 计算机 > 互联网络 > 搜寻”下,还有两个子目录“分类目录 (33) 搜索引擎 (10)”,以及6个相关网站。显然,这些都是我们所需要的信息。

  除了用鼠标层层点入,也可以在目录中检索。比如,在上例的“互联网络”目录下,选中“只在互联网络中搜索”选项,在搜索栏内填入“搜索引擎”进行搜索。
结果:在分类Google 网页目录项中搜索搜索引擎。 共约有387项查询结果,这是第11-20项 。 搜索用时0.09秒。

  可以看到,上述查询结果比普通的检索更有效,因为在分类“互联网络”下进行搜索剔除了很多不相关的诸如新闻之类的无效信息。不过,对于中文用户而言,现在最大的问题是志愿的中文目录编辑太少,导致收录站点太少,因此搜索结果范围显得过于狭隘。但愿这个问题能随着Google以及ODP项目在国内名声的响亮而能得到改观。


12,新闻组搜索

  新闻组有详尽的分类主题,某些主题还有专人管理和编辑,具有大量的有价值信息。由于新闻组包含的信息实在是海量,因此不利用工具进行检索是不大可能的。DEJA一直是新闻组搜索引擎中的佼佼者。2001年2月份,Google将DEJA收购并提供了所有DEJA的功能。现在,除了搜索之外,Google还支持新闻组的WEB方式浏览和张贴功能。

  进入Google新闻组“http://groups.Google.com/”,你有两种信息查找方式。一种是一层层的点击进入特定主题讨论组,另一种则是直接搜索。现在,我们进行一个最简单的搜索试验,查找一下新闻组中关于山顶洞人的讨论信息。

搜索:“山顶洞人”
结果:在各群组内搜索 山顶洞人 共约有2,400项查询结果,这是第1-10项 。 搜索用时0.94秒。 搜索结果默认按照“留言内容”排列,但是你也可以点击“依照日期”按钮,让帖子按照发布日期排列。

  因为新闻组中的帖子实在是多,而且又涉及一些普通搜索所没有的语法,所以建议使用“高级群组搜寻”进入高级搜索界面。新闻组高级搜索提供留言内容、分类主题、标题、留言者、留言代码、语言和发布日期作为条件进行搜索。其中作者项指作者发帖所用的唯一识别号电子信箱。


13,Google的其他杰出功能

13.1网页快照

  网页快照是Google抓下来缓存在服务器上的网页。它有三个作用:

第一, 如果原地址打开很慢,那么可以直接查看Google缓存页面,因为Google服务器速度极快。

第二, 如果原链接已经死掉或者因为网络的原因暂时链接不通,那么可以通过Google快照看到该页面信息。当然,快照内容不是该页最新页面。

第三, 如果打开的页面信息量巨大,一下子找不到关键词所在位置,那么可以通过Google快照,因为快照中Google用黄色表明关键字位置。

13.2,集成化的工具条

  为了方便搜索者,Google提供了工具条,集成于浏览器中,用户无需打开Google主页就可以在工具条内输入关键字进行搜索。此外,工具条还提供了其他许多功能,如显示页面PageRank等。最方便的一点在于用户可以快捷的在Google主页、目录服务、新闻组搜索、高级搜索和搜索设定之间切换。欲安装Google的工具条,可以访问“http://toolbar.Google.com/”,按页面提示可以自动下载并安装。不过,Google工具条目前只支持IE5.0以上版本。

  对于经常进行网络搜索者而言,Google工具条实在是必备的东西!!

13.3,单词英文解释

  写英文文章的时候,最头疼的事情就是对某个英文单词的用法不确定。现在有了Google,一切就迎刃而解了!无论你是想查找某个生词的意思还是想了解某个单词的用法,均可使用在线词典。

  进入英文Google,输入你要查的单词。举个例子,我想查一下suggest的用法。结果如下:“Searched the web for suggest. Results 1 - 10 of about 8,000,000. Search took 0.08 seconds. ”注意看上面句子中,单词suggest下出现了一个横线,点击这个链接,就跳转到另外一个网站“http://www.dictionary.com/”,Google已经把单词提交给该网站的查询脚本。看看这个网站所提供的详尽解释吧。:)

13.4,网页翻译

  你懂英文,但是你不见得就懂德文、法文、拉丁文。如果搜索出来的页面是这些语言怎么办?呵呵,Google提供了网页翻译功能!!虽然目前只支持有限的拉丁语、法语、西班牙语、德语和葡萄牙文,但是我不得不承认,这是个杰出功能。

  试着做以下搜索:“big bang site:fr”。这个表示查找关于宇宙大爆炸的法文网页。看第一条结果:

The Big Bang Website - [ Translate this page ]
... A propos de Big Bang. Le dernier numéro en date. Les anciens numéros. Autres
activités. Concerts progressifs en France. Emissions de radio. Liens.
perso.club-internet.fr/calyx/bigbang/ - 3k - Cached - Similar pages

  有点晕。没关系,点击“Translate this page”按钮。再看结果,嗯,大致能看明白,这原来是个叫“big bang”的乐队的网站,与大爆炸无关...

  机器翻译是一个很前沿的人工智能课题,想指望翻译出来的结果跟专门用英语撰写的内容是不可能的。但西文间的互相转译比中英文机译强得多得多了。至少能看明白。

13.5,单词纠错

  笔者记忆力很差,英文单词经常拼写错误。但Google有纠错功能。比如在写上文的时候,我要用到英文单词“tutorial”,我只是依稀记得好像是“tatorial”的样子,但不肯定,于是用Google查了一下,它马上提醒:“您要找的会不会是: tutorial ”,呵呵,正是这个单词。

13.6,繁简转换

  对中文用户而言,常希望能同时检索繁体和简体信息。Google能做到这一点。Google默认使用繁简自动转换功能,因此你输入的简体关键字也将被转换成繁体做检索。这样省了不少力气。当然,如果你不希望这样的话,也可以在“使用偏好”中把这个选项关掉。

13.7,搜索结果过滤

  网络上的成人内容浩如烟海,而且很多站点具有欺骗或者其他不良企图,浏览者很容易掉入其中的陷阱。为此,Google新设立了成人内容过滤功能,见Google的设置页面,http://www.Google.com/preferences,最底下有一个选项SafeSearch Filtering。不过,中文状态下的Google尚没有这个功能。


14,Google尚未发布的一些新特性和功能

14.1,对网页更新日期做出限定“daterange:”

  评价一个搜索引擎的好坏,更新频率是一个很关键因素。通常情况下,我们总希望能找到最新的网页。Google已经开发了对更新日期做限定的搜索语法,但目前还未公布。而且比较麻烦的是,Google现在支持的日期格式为julian(凯撒日)格式,把通用日期数值切换成julian格式需要借助第三方网站:http://www.tesre.bo.cnr.it/~mauro/JD/。不过,在下面这个自称是“Google终极搜索界面”的网页上,你已经可以利用Google的这项新特性了,它自动提供日期转换功能。

Google Ultimate Interface:http://www.faganfinder.com/google.html

  Google为什么要这样做呢?也许是在测试阶段,不想让太多人使用吧。:)

14.2,新闻搜索“http://news.google.com/”

  Google的新闻搜索尚在B测试阶段,但使用起来已经非常不错了。新闻首页按头条新闻,各国新闻,以及不同领域做了分类。你可以通过Google搜索各大门户和新闻网站的新闻,简单、快捷、方便。遗憾的是,目前Google新闻只检索英文信息。

14.3,分类广告搜索“http://catalogs.google.com/”

  这也在B测试阶段。主要是对电子分类广告做检索。广告页为JPG图片格式。

14.4,其它Google的最新发展动态

  想了解Google公司的工程师们都在忙些什么吗?去看一下Google实验室(http://labs.google.com/)吧。Google的最新设想都在这个地方向访问者展现出来。现在处于发展和试验阶段的新功能有:术语查询、语音查询、键盘查询等等。

  网络工程师和程序员可以看看这个地方:http://www.google.com/apis/,我想可以让你喜出望外的。

14.5,一个有趣的地方

  想看看世界各国网民都用Google搜索什么信息么?到http://www.google.com/press/zeitgeist.html看一下就知道了。从这些资讯中,你大致可以了解到世界热点和流行时尚走向。:)


15,后记

  这个文章4.0版本与3.0版本相比,变更很大,主要把一些与Google无关的东西删除了,另外随Google的变化作了一些修正,并增加了一些Google尚未发布的新功能。关于搜索技巧和搜索实例,是各个搜索引擎共通的东西,是搜索者长期的经验积累,要写出来,是件工程很浩大的事情,因此在这个小文章中我就不献丑了。

  随着时间的推移,我发现搜索已经成为网络生活的一部分。工作需要搜索技术文档、客户信息;购物需要搜索商品信息和指南;娱乐需要搜索相关背景资料和图片。搜索已经变得无处不在,而Google则相应的成了工作和生活中的一个必备工具。套用雅虎的一句广告词,我们也许应该这样说:“今天你Google了吗?”
posted @ 2007-07-02 13:17 和田雨 阅读(5603) | 评论 (0)编辑 收藏

软件需求说明书
1. 引言
1.1 项目名称
1.2 项目背景和内容概要
(项目的委托单位、开发单位、主管部门、与其它项目的关系,与其他机构的关系等)

1.3 相关资料、缩略语、定义
(相关项目计划、合同及上级机关批文,引用的文件、采用的标准等)
(缩写词和名词定义)
2. 任务概述
2.1 目标
(项目的开发目标和应用目标。如果是其他系统的一部分,则说明其关系)
2.2 范围
(包含的业务,不包含的业务)
2.3 假定条件与约束限制
(尽量列出开展本项目的假定和约束,例如:经费限制,开发期限,设备条件,用户现
场环境准备等)
3.业务流程
4.数据描述
4.1 原始数据描述
a. 静态数据
b. 动态数据
4.2 数据流向图
4.3 数据概念模型和描述
5.功能需求
5.1 功能描述
6.界面要求
6.1报表格式
6.2图形要求
6.3输入输出要求
7.接口要求
(描述与本系统相连的系统的接口的数据格式,数据交换协议,接口功能等)
8.性能需求
8.1数据精确度
(例如,数据内部精度,外部显示精度)
8. 2数据量
8. 3时间特性要求
(根据所开发系统的特点,规定系统对时间的特性的要求。例如:
系统响应时间、界面更新处理时间、数据转换与传输时间)
9. 运行环境需求
9.1网络和硬件设备平台
(网络拓扑图及设备类型描述)
操作系统平台
数据库系统平台
10.1编程工具
10.2其它支撑软件
11. 其它专门需求
11.1安装和操作
11.2安全保密
11.3维护服务
posted @ 2007-07-02 13:13 和田雨 阅读(4627) | 评论 (1)编辑 收藏

法则1:优先使用(对象)组合,而非(类)继承

[ Favor Composition Over Inheritance ]




  • 组合



    1.(对象)组合是一种通过创建一个组合了其它对象的对象,从而获得新功能的复用方法。

    2.将功能委托给所组合的一个对象,从而获得新功能。

    3.有些时候也称之为"聚合"(aggregation)或"包容"(containment),尽管有些作者对这些术语赋予了专门的含义

    4.例如:

    a.聚合:一个对象拥有另一个对象或对另一个对象负责(即一个对象包含另一个对象或是另一个对象的一部分),并且聚合对象和其所有者具有相同的生命周期。(译者注:即所谓的"同生共死"关系,可参见GOF的Design
    Patterns: Elements of Reusable Object-Oriented Software的引言部分。)

    b.包容:一种特殊类型的组合,对于其它对象而言,容器中的被包含对象是不可见的,其它对象仅能通过容器对象来访问被包含对象。(Coad)





    5.包含可以通过以下两种方式实现:

    a.根据引用(By reference)

    b.根据值(By value)

    6.C++允许根据值或引用来实现包含。

    7.但是在Java中,一切皆为对象的引用!




  • 组合的优点和缺点






    1.优点:

    a.容器类仅能通过被包含对象的接口来对其进行访问。

    b."黑盒"复用,因为被包含对象的内部细节对外是不可见。

    c.对装性好。

    d.实现上的相互依赖性比较小。(译者注:被包含对象与容器对象之间的依赖关系比较少)

    e.每一个类只专注于一项任务。

    f.通过获取指向其它的具有相同类型的对象引用,可以在运行期间动态地定义(对象的)组合。




    2.缺点:

    a.从而导致系统中的对象过多。

    b为了能将多个不同的对象作为组合块(composition block)来使用,必须仔细地对接口进行定义。




  • 继承





    1.(类)继承是一种通过扩展一个已有对象的实现,从而获得新功能的复用方法。

    2.泛化类(超类)可以显式地捕获那些公共的属性和方法。

    3.特殊类(子类)则通过附加属性和方法来进行实现的扩展。





  • 继承的优点和缺点





    1.优点:

    a.容易进行新的实现,因为其大多数可继承而来。

    b.易于修改或扩展那些被复用的实现。

    2.缺点:

    a.破坏了封装性,因为这会将父类的实现细节暴露给子类。

    b. "白盒"复用,因为父类的内部细节对于子类而言通常是可见的。

    c.当父类的实现更改时,子类也不得不会随之更改。

    d.从父类继承来的实现将不能在运行期间进行改变。





  • Coad规则





    仅当下列的所有标准被满足时,方可使用继承:

    a.子类表达了"是一个…的特殊类型",而非"是一个由…所扮演的角色"。

    b子类的一个实例永远不需要转化(transmute)为其它类的一个对象。

    c.子类是对其父类的职责(responsibility)进行扩展,而非重写或废除(nullify)。

    d.子类没有对那些仅作为一个工具类(utility class)的功能进行扩展。

    e.对于一个位于实际的问题域(Problem Domain)的类而言,其子类特指一种角色(role),交易(transaction)或设备(device)。





  • 继承/组合示例1







1."是一个…的特殊类型",而非"是一个由…所扮演的角色"

-->失败。乘客是人所扮演的一种角色。代理人亦然。

2.永远不需要转化

-->失败。随着时间的发展,一个Person的子类实例可能会从Passenger转变成Agent,再到Agent Passenger。

3.扩展,而非重写和废除

-->通过。

4.不要扩展一个工具类

-->通过。

5.在问题域内,特指一种角色,交易或设备

-->失败。Person不是一种角色,交易或设备。






继承并非适用于此处!


使用组合进行挽救!





  • 继承/组合示例2







1."是一个…的特殊类型",而非"是一个由…所扮演的角色"

-->通过。乘客和代理人都是特殊类型的人所扮演的角色。

2.永远不需要转化

-->通过。一个Passenger对象将保持不变;Agent对象亦然。

3.扩展,而非重写和废除

-->通过。

4.不要扩展一个工具类

-->通过。

5.在问题域内,特指一种角色,交易或设备

-->通过。PersonRole是一种类型的角色。




继承适用于此处!




  • 继承/组合示例3







1."是一个…的特殊类型",而非"是一个由…所扮演的角色"

-->通过。预订和购买都是一种特殊类型的交易。

2.永远不需要转化

-->通过。一个Reservation对象将保持不变;Purchase对象亦然。

3.扩展,而非重写和废除

-->通过。

4.不要扩展一个工具类

-->通过。

5.在问题域内,特指一种角色,交易或设备

-->通过。是一种交易。




继承适用于此处!




  • 继承/组合示例4







1."是一个…的特殊类型",而非"是一个由…所扮演的角色"

-->失败。预订不是一种特殊类型的observable。

2.永远不需要转化

-->通过。一个Reservation对象将保持不变。

3.扩展,而非重写和废除

-->通过。

4.不要扩展一个工具类

-->失败。Observable就是一个工具类。

5.在问题域内,特指一种角色,交易或设备

-->不适用。Observable是一个工具类,并非一个问题域的类。。




继承并非适用于此处!




  • 继承/组合总结





    1.组合与继承都是重要的重用方法

    2.在OO开发的早期,继承被过度地使用

    3.随着时间的发展,我们发现优先使用组合可以获得重用性与简单性更佳的设计

    4.当然可以通过继承,以扩充(enlarge)可用的组合类集(the set of composable classes)。

    5.因此组合与继承可以一起工作

    6.但是我们的基本法则是:

    优先使用对象组合,而非(类)继承

    [ Favor Composition Over Inheritance ]

    法则2:针对接口编程,而非(接口的)实现

    [ Program To An Interface, Not An Implementation ]





    • 接口



      1.接口是一个对象在对其它的对象进行调用时所知道的方法集合。

      2.一个对象可以有多个接口(实际上,接口是对象所有方法的一个子集)

      3.类型是对象的一个特定的接口。

      4.不同的对象可以具有相同的类型,而且一个对象可以具有多个不同的类型。

      5.一个对象仅能通过其接口才会被其它对象所了解。

      6.某种意义上,接口是以一种非常局限的方式,将"是一种…"表达为"一种支持该接口的…"。

      7.接口是实现插件化(pluggability)的关键





    • 实现继承和接口继承



      1.实现继承(类继承):一个对象的实现是根据另一个对象的实现来定义的。

      2.接口继承(子类型化):描述了一个对象可在什么时候被用来替代另一个对象。

      3.C++的继承机制既指类继承,又指接口继承。

      4.C++通过继承纯虚类来实现接口继承。

      5.Java对接口继承具有单独的语言构造方式-Java接口。

      6.Java接口构造方式更加易于表达和实现那些专注于对象接口的设计。





    • 接口的好处



      1.优点

      a.Client不必知道其使用对象的具体所属类。

      b.一个对象可以很容易地被(实现了相同接口的)的另一个对象所替换。

      c.对象间的连接不必硬绑定(hardwire)到一个具体类的对象上,因此增加了灵活性。

      e.松散藕合(loosens coupling)。

      f.增加了重用的可能性。

      e.提高了(对象)组合的机率,因为被包含对象可以是任何实现了一个指定接口的类。

      2.缺点:

      a.设计的复杂性略有增加

      (译者注:接口表示"…像…"(LikeA)的关系,继承表示"…是…"(IsA)的关系,组合表示"…有…"(HasA)的关系。)





    • 接口实例








    该方法是指其它的一些类可以进行交通工具的驾驶,而不必关心其实际上是(汽车,轮船,潜艇或是其它任何实现了IManeuverabre的对象)。


    法则3:开放-封闭法则(OCP)

    软件组成实体应该是可扩展的,但是不可修改的。

    [ Software Entities Should Be Open For Extension, Yet Closed For Modification
    ]




    • 开放-封闭法则





      1.开放-封闭法则认为我们应该试图去设计出永远也不需要改变的模块。

      2我们可以添加新代码来扩展系统的行为。我们不能对已有的代码进行修改。

      3.符合OCP的模块需满足两个标准:

      4.可扩展,即"对扩展是开放的"(Open For Extension)-模块的行为可以被扩展,以需要满足新的需求。

      5.不可更改,即"对更改是封闭的"(Closed for Modification)-模块的源代码是不允许进行改动的。

      6.我们能如何去做呢?

      a.抽象(Abstraction)

      b.多态(Polymorphism)

      c.继承(Inheritance)

      d.接口(Interface)


      7. 一个软件系统的所有模块不可能都满足OCP,但是我们应该努力最小化这些不满足OCP的模块数量。

      8.开放-封闭法则是OO设计的真正核心。

      9.符合该法则便意味着最高等级的复用性(reusability)和可维护性(maintainability)。





    • OCP示例



      1. 考虑下面某类的方法:





      2.以上函数的工作是在制订的部件数组中计算各个部件价格的总和。

      3.若Part是一个基类或接口且使用了多态,则该类可很容易地来适应新类型的部件,而不必对其进行修改。

      4.其将符合OCP


      5. 但是在计算总价格时,若财务部颁布主板和内存应使用额外费用,则将如何去做。

      6.下列的代码是如何来做的呢?





      7.这符合OCP吗?

      8.当每次财务部提出新的计价策略,我们都不得不要修改totalPrice()方法!这并非"对更改是封闭的"。显然,策略的变更便意味着我们不得不要在一些地方修改代码的,因此我们该如何去做呢?

      9.为了使用我们第一个版本的totalPrice(),我们可以将计价策略合并到Part的getPrice()方法中。


      10.这里是Part和ConcretePart类的示例:



      11. 但是现在每当计价策略发生改变,我们就必须修改Part的每个子类!

      12.一个更好的思路是采用一个PricePolicy类,通过对其进行继承以提供不同的计价策略:




      13.看起来我们所做的就是将问题推迟到另一个类中。但是使用该解决方案,我们可通过改变Part对象,在运行期间动态地来设定计价的策略。

      14.另一个解决方案是使每个ConcretePart从数据库或属性文件中获取其当前的价格。





    • 单选法则



      单选法则(the Single Choice Principle)是OCP的一个推论。

      无论在什么时候,一个软件系统必须支持一组备选项,理想情况下,在系统中只能有一个类能够知道整个的备选项集合。


      法则4:Liskov替换法则(LSP)

      使用指向基类(超类)的引用的函数,必须能够在不知道具体派生类(子类)对象类型的情况下使用它们。

      [ Function Thar Use Referennces To Base(Super) Classes Must Be Able To Use Objects
      Of Derived(Sub) Classes Without Knowing It ]




      • Liskov替换法则





        1.显而易见,Liskov替换法则(LSP)是根据我所熟知的"多态"而得出的。

        2.例如:



        方法drawShape应该可与Sharp超类的任何子类一起工作(或者,若Sharp为Java接口,则该方法可与任何实现了Sharp接口的类一起工作)

        但是当我们在实现子类时必须要谨慎对待,以确保我们不会无意中违背了LSP。





        3.若一个函数未能满足LSP,那么可能是因为它显式地引用了超类的一些或所有子类。这样的函数也违背了OCP,因为当我们创建一个新的子类时,会不得不进行代码的修改。




      • LSP示例



        1. 考虑下面Rectangle类:



        2.现在,Square类会如何呢?显然,一个正方形是一个四边形,因此Square类应该从Rectangle类派生而来,对否?让我们看一看!

        3.观察可得:

        a.正方形不需要将高和宽都作为属性,但是总之它将继承自Rectangle。因此,每一个Square对象会浪费一点内存,但这并不是一个主要问题。

        b.继承而来的setWidth()和setHeight()方法对于Square而言并非真正地适合,因为一个正方形的高和宽是相同。因此我们将需要重写setWidth()和setHeight()方法。不得不重写这些简单的方法有可能是一种不恰当的继承使用方式。


        3.Square类如下:










      4. 看起来都还不错。但是让我们检验一下!








      5. 测试程序输出:







      6.看上去好像我们违背了LSP!


      7.这里的问题出在哪里呢?编写testLsp()方法的程序员做了一个合理的假设,即改变Rectangle的宽而保持它的高不变。

      8.在将一个Square对象传递给这样一个方法时产生了问题,显然是违背了LSP

      9.Square和Rectangle类是相互一致和合法的。尽管程序员对基类作了合理的假设,但其所编写的方法仍然会导致设计模型的失败。

      10.不能孤立地去看待解决方案,必须根据设计用户所做的合理假设来看待它们。


      11. 一个数学意义上的正方形可能是一个四边形,但是一个Square对象不是一个Rectangle对象,因为一个Square对象的行为与一个Rectangle对象的行为是不一致的!

      12.从行为上来说,一个Square不是一个Rectangle!一个Square对象与一个Rectangle对象之间不具有多态的特征。






      • 总结





        1.Liskov替换法则(LSP)清楚地表明了ISA关系全部都是与行为有关的。

        2.为了保持LSP(并与开放-封闭法则一起),所有子类必须符合使用基类的client所期望的行为。

        3.一个子类型不得具有比基类型(base type)更多的限制,可能这对于基类型来说是合法的,但是可能会因为违背子类型的其中一个额外限制,从而违背了LSP!

        4.LSP保证一个子类总是能够被用在其基类可以出现的地方!

posted @ 2007-07-02 13:11 和田雨 阅读(205) | 评论 (0)编辑 收藏

     摘要: 摘要:单点登录(SSO)的技术被越来越广泛地运用到各个领域的软件系统当中。本文从业务的角度分析了单点登录的需求和应用领域;从技术本身的角度分析了单点登录技术的内部机制和实现手段,并且给出Web-SSO和桌面SSO的实现、源代码和详细讲解;还从安全和性能的角度对现有的实现技术进行进一步分析,指出相应的风险和需要改进的方面。本文除了从多个方面和角度给出了对单点登录(SSO)的全面分析,还并且讨论了如何...  阅读全文
posted @ 2007-07-02 13:10 和田雨 阅读(629) | 评论 (0)编辑 收藏

     摘要: JDK 1.4以前,Java的IO操作集中在java.io这个包中,是基于流的阻塞(blocking)API。对于大多数应用来说,这样的API使用很方便,然而,一些对性能要求较高的应用,尤其是服务端应用,往往需要一个更为有效的方式来处理IO。从JDK 1.4起,NIO API作为一个基于缓冲区,并能提供非阻塞(non-blocking)IO操作的API被引入。本文对其进行深入的介绍。  ...  阅读全文
posted @ 2007-07-02 13:07 和田雨 阅读(254) | 评论 (0)编辑 收藏

1 定义头和根元素

部署描述符文件就像所有XML文件一样,必须以一个XML头开始。这个头声明可以使用的XML版本并给出文件的字符编码。
DOCYTPE声明必须立即出现在此头之后。这个声明告诉服务器适用的servlet规范的版本(如2.2或2.3)并指定管理此文件其余部分内容的语法的DTD(Document Type Definition,文档类型定义)。
所有部署描述符文件的顶层(根)元素为web-app。请注意,XML元素不像HTML,他们是大小写敏感的。因此,web-App和WEB-APP都是不合法的,web-app必须用小写。

1 定义头和根元素

XML 元素不仅是大小写敏感的,而且它们还对出现在其他元素中的次序敏感。例如,XML头必须是文件中的第一项,DOCTYPE声明必须是第二项,而web- app元素必须是第三项。在web-app元素内,元素的次序也很重要。服务器不一定强制要求这种次序,但它们允许(实际上有些服务器就是这样做的)完全 拒绝执行含有次序不正确的元素的Web应用。这表示使用非标准元素次序的web.xml文件是不可移植的。
下面的列表给出了所有可直接出现在web-app元素内的合法元素所必需的次序。例如,此列表说明servlet元素必须出现在所有servlet-mapping元素之前。请注意,所有这些元素都是可选的。因此,可以省略掉某一元素,但不能把它放于不正确的位置。
l icon icon元素指出IDE和GUI工具用来表示Web应用的一个和两个图像文件的位置。
l display-name display-name元素提供GUI工具可能会用来标记这个特定的Web应用的一个名称。
l description description元素给出与此有关的说明性文本。
l context-param context-param元素声明应用范围内的初始化参数。
l filter 过滤器元素将一个名字与一个实现javax.servlet.Filter接口的类相关联。
l filter-mapping 一旦命名了一个过滤器,就要利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。
l listener servlet API的版本2.3增加了对事件监听程序的支持,事件监听程序在建立、修改和删除会话或servlet环境时得到通知。Listener元素指出事件监听程序类。
l servlet 在向servlet或JSP页面制定初始化参数或定制URL时,必须首先命名servlet或JSP页面。Servlet元素就是用来完成此项任务的。
l servlet-mapping 服务器一般为servlet提供一个缺省的URL:http://host/webAppPrefix/servlet/ServletName。但是,常常会更改这个URL,以便servlet可以访问初始化参数或更容易地处理相对URL。在更改缺省URL时,使用servlet-mapping元素。
l session-config 如果某个会话在一定时间内未被访问,服务器可以抛弃它以节省内存。可通过使用HttpSession的setMaxInactiveInterval方法 明确设置单个会话对象的超时值,或者可利用session-config元素制定缺省超时值。
l mime-mapping 如果Web应用具有想到特殊的文件,希望能保证给他们分配特定的MIME类型,则mime-mapping元素提供这种保证。
l welcom-file-list welcome-file-list元素指示服务器在收到引用一个目录名而不是文件名的URL时,使用哪个文件。
l error-page error-page元素使得在返回特定HTTP状态代码时,或者特定类型的异常被抛出时,能够制定将要显示的页面。
l taglib taglib元素对标记库描述符文件(Tag Libraryu Descriptor file)指定别名。此功能使你能够更改TLD文件的位置,而不用编辑使用这些文件的JSP页面。
l resource-env-ref resource-env-ref元素声明与资源相关的一个管理对象。
l resource-ref resource-ref元素声明一个资源工厂使用的外部资源。
l security-constraint security-constraint元素制定应该保护的URL。它与login-config元素联合使用
l login-config 用login-config元素来指定服务器应该怎样给试图访问受保护页面的用户授权。它与sercurity-constraint元素联合使用。
l security-role security-role元素给出安全角色的一个列表,这些角色将出现在servlet元素内的security-role-ref元素的role-name子元素中。分别地声明角色可使高级IDE处理安全信息更为容易。
l env-entry env-entry元素声明Web应用的环境项。
l ejb-ref ejb-ref元素声明一个EJB的主目录的引用。
l ejb-local-ref ejb-local-ref元素声明一个EJB的本地主目录的应用。

3 分配名称和定制的UL

在web.xml中完成的一个最常见的任务是对servlet或JSP页面给出名称和定制的URL。用servlet元素分配名称,使用servlet-mapping元素将定制的URL与刚分配的名称相关联。
3.1 分配名称
为 了提供初始化参数,对servlet或JSP页面定义一个定制URL或分配一个安全角色,必须首先给servlet或JSP页面一个名称。可通过 servlet元素分配一个名称。最常见的格式包括servlet-name和servlet-class子元素(在web-app元素内),如下所示:
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>moreservlets.TestServlet</servlet-class>
</servlet> 
这 表示位于WEB-INF/classes/moreservlets/TestServlet的servlet已经得到了注册名Test。给 servlet一个名称具有两个主要的含义。首先,初始化参数、定制的URL模式以及其他定制通过此注册名而不是类名引用此servlet。其次,可在 URL而不是类名中使用此名称。因此,利用刚才给出的定义,URL http://host/webAppPrefix/servlet/Test 可用于 http://host/webAppPrefix/servlet/moreservlets.TestServlet 的场所。
请 记住:XML元素不仅是大小写敏感的,而且定义它们的次序也很重要。例如,web-app元素内所有servlet元素必须位于所有servlet- mapping元素(下一小节介绍)之前,而且还要位于5.6节和5.11节讨论的与过滤器或文档相关的元素(如果有的话)之前。类似地,servlet 的servlet-name子元素也必须出现在servlet-class之前。5.2节"部署描述符文件内的元素次序"将详细介绍这种必需的次序。
例 如,程序清单5-1给出了一个名为TestServlet的简单servlet,它驻留在moreservlets程序包中。因为此servlet是扎根 在一个名为deployDemo的目录中的Web应用的组成部分,所以TestServlet.class放在deployDemo/WEB- INF/classes/moreservlets中。程序清单5-2给出将放置在deployDemo/WEB-INF/内的web.xml文件的一部 分。此web.xml文件使用servlet-name和servlet-class元素将名称Test与TestServlet.class相关联。图 5-1和图5-2分别显示利用缺省URL和注册名调用TestServlet时的结果。

程序清单5-1 TestServlet.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet used to illustrate servlet naming
* and custom URLs.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class TestServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle("Test Servlet") +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H2>URI: " + uri + "</H2>\n" +
"</BODY></HTML>");
}
}


程序清单5-2 web.xml(说明servlet名称的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- … -->
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>moreservlets.TestServlet</servlet-class>
</servlet>
<!-- … -->
</web-app>

3.2 定义定制的URL
大多数服务器具有一个缺省的serlvet URL:
http://host/webAppPrefix/servlet/packageName.ServletName。 虽然在开发中使用这个URL很方便,但是我们常常会希望另一个URL用于部署。例如,可能会需要一个出现在Web应用顶层的URL(如,http: //host/webAppPrefix/Anyname),并且在此URL中没有servlet项。位于顶层的URL简化了相对URL的使用。此外,对 许多开发人员来说,顶层URL看上去比更长更麻烦的缺省URL更简短。
事实上,有时需要使用定制的URL。比如,你可能想关闭缺省URL映射,以便更好地强制实施安全限制或防止用户意外地访问无初始化参数的servlet。如果你禁止了缺省的URL,那么你怎样访问servlet呢?这时只有使用定制的URL了。
为 了分配一个定制的URL,可使用servlet-mapping元素及其servlet-name和url-pattern子元素。Servlet- name元素提供了一个任意名称,可利用此名称引用相应的servlet;url-pattern描述了相对于Web应用的根目录的URL。url- pattern元素的值必须以斜杠(/)起始。
下面给出一个简单的web.xml摘录,它允许使用URL http://host/webAppPrefix/UrlTest而不是http://host/webAppPrefix/servlet/Test或
http: //host/webAppPrefix/servlet/moreservlets.TestServlet。请注意,仍然需要XML头、 DOCTYPE声明以及web-app封闭元素。此外,可回忆一下,XML元素出现地次序不是随意的。特别是,需要把所有servlet元素放在所有 servlet-mapping元素之前。
<servlet>
<servlet-name>Test</servlet-name>
<servlet-class>moreservlets.TestServlet</servlet-class>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name>Test</servlet-name>
<url-pattern>/UrlTest</url-pattern>
</servlet-mapping>
URL模式还可以包含通配符。例如,下面的小程序指示服务器发送所有以Web应用的URL前缀开始,以..asp结束的请求到名为BashMS的servlet。
<servlet>
<servlet-name>BashMS</servlet-name>
<servlet-class>msUtils.ASPTranslator</servlet-class>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name>BashMS</servlet-name>
<url-pattern>/*.asp</url-pattern>
</servlet-mapping>
3.3 命名JSP页面
因 为JSP页面要转换成sevlet,自然希望就像命名servlet一样命名JSP页面。毕竟,JSP页面可能会从初始化参数、安全设置或定制的URL中 受益,正如普通的serlvet那样。虽然JSP页面的后台实际上是servlet这句话是正确的,但存在一个关键的猜疑:即,你不知道JSP页面的实际 类名(因为系统自己挑选这个名字)。因此,为了命名JSP页面,可将jsp-file元素替换为servlet-calss元素,如下所示:
<servlet>
<servlet-name>Test</servlet-name>
<jsp-file>/TestPage.jsp</jsp-file>
</servlet>
命 名JSP页面的原因与命名servlet的原因完全相同:即为了提供一个与定制设置(如,初始化参数和安全设置)一起使用的名称,并且,以便能更改激活 JSP页面的URL(比方说,以便多个URL通过相同页面得以处理,或者从URL中去掉.jsp扩展名)。但是,在设置初始化参数时,应该注意,JSP页 面是利用jspInit方法,而不是init方法读取初始化参数的。
例如,程序清单5-3给出一个名为TestPage.jsp的简单JSP页面,它的工作只是打印出用来激活它的URL的本地部分。TestPage.jsp放置在deployDemo应用的顶层。程序清单5-4给出了用来分配一个注册名PageName,然后将此注册名与http://host/webAppPrefix/UrlTest2/anything 形式的URL相关联的web.xml文件(即,deployDemo/WEB-INF/web.xml)的一部分。

程序清单5-3 TestPage.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD>
<TITLE>
JSP Test Page
</TITLE>
</HEAD>
<BODY BGCOLOR="#FDF5E6">
<H2>URI: <%= request.getRequestURI() %></H2>
</BODY>
</HTML>


程序清单5-4 web.xml(说明JSP页命名的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<servlet>
<servlet-name>PageName</servlet-name>
<jsp-file>/TestPage.jsp</jsp-file>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name> PageName </servlet-name>
<url-pattern>/UrlTest2/*</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>


4 禁止激活器servlet

对servlet 或JSP页面建立定制URL的一个原因是,这样做可以注册从 init(servlet)或jspInit(JSP页面)方法中读取得初始化参数。但是,初始化参数只在是利用定制URL模式或注册名访问 servlet或JSP页面时可以使用,用缺省URL http://host/webAppPrefix/servlet/ServletName 访问时不能使用。因此,你可能会希望关闭缺省URL,这样就不会有人意外地调用初始化servlet了。这个过程有时称为禁止激活器servlet,因为 多数服务器具有一个用缺省的servlet URL注册的标准servlet,并激活缺省的URL应用的实际servlet。
有两种禁止此缺省URL的主要方法:
l 在每个Web应用中重新映射/servlet/模式。
l 全局关闭激活器servlet。
重 要的是应该注意到,虽然重新映射每个Web应用中的/servlet/模式比彻底禁止激活servlet所做的工作更多,但重新映射可以用一种完全可移植 的方式来完成。相反,全局禁止激活器servlet完全是针对具体机器的,事实上有的服务器(如ServletExec)没有这样的选择。下面的讨论对每 个Web应用重新映射/servlet/ URL模式的策略。后面提供在Tomcat中全局禁止激活器servlet的详细内容。
4.1 重新映射/servlet/URL模式
在一个特定的Web应用中禁止以http://host/webAppPrefix/servlet/ 开始的URL的处理非常简单。所需做的事情就是建立一个错误消息servlet,并使用前一节讨论的url-pattern元素将所有匹配请求转向该 servlet。只要简单地使用:
<url-pattern>/servlet/*</url-pattern>
作为servlet-mapping元素中的模式即可。
例如,程序清单5-5给出了将SorryServlet servlet(程序清单5-6)与所有以http://host/webAppPrefix/servlet/ 开头的URL相关联的部署描述符文件的一部分。

程序清单5-5 web.xml(说明JSP页命名的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<servlet>
<servlet-name>Sorry</servlet-name>
<servlet-class>moreservlets.SorryServlet</servlet-class>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name> Sorry </servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>


程序清单5-6 SorryServlet.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet used to give error messages to
* users who try to access default servlet URLs
* (i.e., http://host/webAppPrefix/servlet/ServletName)
* in Web applications that have disabled this
* behavior.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class SorryServlet extends HttpServlet {
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String title = "Invoker Servlet Disabled.";
out.println(ServletUtilities.headWithTitle(title) +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H2>" + title + "</H2>\n" +
"Sorry, access to servlets by means of\n" +
"URLs that begin with\n" +
"http://host/webAppPrefix/servlet/\n" +
"has been disabled.\n" + 
"</BODY></HTML>");
}

public void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}


4.2 全局禁止激活器:Tomcat
Tomcat 4中用来关闭缺省URL的方法与Tomcat 3中所用的很不相同。下面介绍这两种方法:
1.禁止激活器: Tomcat 4
Tomcat 4用与前面相同的方法关闭激活器servlet,即利用web.xml中的url-mapping元素进行关闭。不同之处在于Tomcat使用了放在 install_dir/conf中的一个服务器专用的全局web.xml文件,而前面使用的是存放在每个Web应用的WEB-INF目录中的标准 web.xml文件。
因此,为了在Tomcat 4中关闭激活器servlet,只需在install_dir/conf/web.xml中简单地注释出/servlet/* URL映射项即可,如下所示:
<!-- 
<servlet-mapping>
<servlet-name>invoker</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>
-->
再次提醒,应该注意这个项是位于存放在install_dir/conf的Tomcat专用的web.xml文件中的,此文件不是存放在每个Web应用的WEB-INF目录中的标准web.xml。
2.禁止激活器:Tomcat3
在Apache Tomcat的版本3中,通过在install_dir/conf/server.xml中注释出InvokerInterceptor项全局禁止缺省 servlet URL。例如,下面是禁止使用缺省servlet URL的server.xml文件的一部分。
<!-- 
<RequsetInterceptor 
className="org.apache.tomcat.request.InvokerInterceptor"
debug="0" prefix="/servlet/" />
-->

5 初始化和预装载servlet与JSP页面

这里讨论控制servlet和JSP页面的启动行为的方法。特别是,说明了怎样分配初始化参数以及怎样更改服务器生存期中装载servlet和JSP页面的时刻。
5.1 分配servlet初始化参数
利 用init-param元素向servlet提供初始化参数,init-param元素具有param-name和param-value子元素。例如, 在下面的例子中,如果initServlet servlet是利用它的注册名(InitTest)访问的,它将能够从其方法中调用getServletConfig(). getInitParameter("param1")获得"Value 1",调用getServletConfig().getInitParameter("param2")获得"2"。
<servlet>
<servlet-name>InitTest</servlet-name>
<servlet-class>moreservlets.InitServlet</servlet-class>
<init-param>
<param-name>param1</param-name>
<param-value>value1</param-value>
</init-param>
<init-param>
<param-name>param2</param-name>
<param-value>2</param-value>
</init-param>
</servlet>
在涉及初始化参数时,有几点需要注意:
l 返回值。GetInitParameter的返回值总是一个String。因此,在前一个例子中,可对param2使用Integer.parseInt获得一个int。
l JSP中的初始化。JSP页面使用jspInit而不是init。JSP页面还需要使用jsp-file元素代替servlet-class。
l 缺省URL。初始化参数只在通过它们的注册名或与它们注册名相关的定制URL模式访问Servlet时可以使用。因此,在这个例子中,param1和 param2初始化参数将能够在使用URL http://host/webAppPrefix/servlet/InitTest 时可用,但在使用URL http://host/webAppPrefix/servlet/myPackage.InitServlet 时不能使用。
例如,程序清单5-7给出一个名为InitServlet的简单servlet,它使用init方法设置firstName和emailAddress字段。程序清单5-8给出分配名称InitTest给servlet的web.xml文件。
程序清单5-7 InitServlet.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

/** Simple servlet used to illustrate servlet
* initialization parameters.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class InitServlet extends HttpServlet {
private String firstName, emailAddress;

public void init() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter("firstName");
emailAddress = config.getInitParameter("emailAddress");
}

public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
String uri = request.getRequestURI();
out.println(ServletUtilities.headWithTitle("Init Servlet") +
"<BODY BGCOLOR=\"#FDF5E6\">\n" +
"<H2>Init Parameters:</H2>\n" +
"<UL>\n" +
"<LI>First name: " + firstName + "\n" +
"<LI>Email address: " + emailAddress + "\n" +
"</UL>\n" + 
"</BODY></HTML>");
}
}


程序清单5-8 web.xml(说明初始化参数的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<servlet>
<servlet-name>InitTest</servlet-name>
<servlet-class>moreservlets.InitServlet</servlet-class>
<init-param>
<param-name>firstName</param-name>
<param-value>Larry</param-value>
</init-param>
<init-param>
<param-name>emailAddress</param-name>
<param-value>Ellison@Microsoft.com</param-value>
</init-param>
</servlet>
<!-- ... -->
</web-app>

5.2 分配JSP初始化参数
给JSP页面提供初始化参数在三个方面不同于给servlet提供初始化参数。
1)使用jsp-file而不是servlet-class。因此,WEB-INF/web.xml文件的servlet元素如下所示:
<servlet>
<servlet-name>PageName</servlet-name>
<jsp-file>/RealPage.jsp</jsp-file>
<init-param>
<param-name>...</param-name>
<param-value>...</param-value>
</init-param>
...
</servlet>
2) 几乎总是分配一个明确的URL模式。对servlet,一般相应地使用以http://host/webAppPrefix/servlet/ 开始的缺省URL。只需记住,使用注册名而不是原名称即可。这对于JSP页面在技术上也是合法的。例如,在上面给出的例子中,可用URL http://host/webAppPrefix/servlet/PageName 访问RealPage.jsp的对初始化参数具有访问权的版本。但在用于JSP页面时,许多用户似乎不喜欢应用常规的servlet的URL。此外,如果 JSP页面位于服务器为其提供了目录清单的目录中(如,一个既没有index.html也没有index.jsp文件的目录),则用户可能会连接到此 JSP页面,单击它,从而意外地激活未初始化的页面。因此,好的办法是使用url-pattern(5.3节)将JSP页面的原URL与注册的 servlet名相关联。这样,客户机可使用JSP页面的普通名称,但仍然激活定制的版本。例如,给定来自项目1的servlet定义,可使用下面的 servlet-mapping定义:
<servlet-mapping>
<servlet-name>PageName</servlet-name>
<url-pattern>/RealPage.jsp</url-pattern>
</servlet-mapping>
3)JSP页使用jspInit而不是init。自动从JSP页面建立的servlet或许已经使用了inti方法。因此,使用JSP声明提供一个init方法是不合法的,必须制定jspInit方法。
为了说明初始化JSP页面的过程,程序清单5-9给出了一个名为InitPage.jsp的JSP页面,它包含一个jspInit方法且放置于 deployDemo Web应用层次结构的顶层。一般,http://host/deployDemo/InitPage.jsp 形式的URL将激活此页面的不具有初始化参数访问权的版本,从而将对firstName和emailAddress变量显示null。但是, web.xml文件(程序清单5-10)分配了一个注册名,然后将该注册名与URL模式/InitPage.jsp相关联。

程序清单5-9 InitPage.jsp
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML>
<HEAD><TITLE>JSP Init Test</TITLE></HEAD>
<BODY BGCOLOR="#FDF5E6">
<H2>Init Parameters:</H2>
<UL>
<LI>First name: <%= firstName %>
<LI>Email address: <%= emailAddress %>
</UL>
</BODY></HTML>
<%!
private String firstName, emailAddress;

public void jspInit() {
ServletConfig config = getServletConfig();
firstName = config.getInitParameter("firstName");
emailAddress = config.getInitParameter("emailAddress");
}
%>


程序清单5-10 web.xml(说明JSP页面的init参数的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<servlet>
<servlet-name>InitPage</servlet-name>
<jsp-file>/InitPage.jsp</jsp-file>
<init-param>
<param-name>firstName</param-name>
<param-value>Bill</param-value>
</init-param>
<init-param>
<param-name>emailAddress</param-name>
<param-value>gates@oracle.com</param-value>
</init-param>
</servlet>
<!-- ... --> 
<servlet-mapping>
<servlet-name> InitPage</servlet-name>
<url-pattern>/InitPage.jsp</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>


5.3 提供应用范围内的初始化参数
一 般,对单个地servlet或JSP页面分配初始化参数。指定的servlet或JSP页面利用ServletConfig的 getInitParameter方法读取这些参数。但是,在某些情形下,希望提供可由任意servlet或JSP页面借助ServletContext 的getInitParameter方法读取的系统范围内的初始化参数。
可利用context-param元素声明这些系统范围内的初始化值。context-param元素应该包含param-name、param-value以及可选的description子元素,如下所示:
<context-param>
<param-name>support-email</param-name>
<param-value>blackhole@mycompany.com</param-value>
</context-param>
可 回忆一下,为了保证可移植性,web.xml内的元素必须以正确的次序声明。但这里应该注意,context-param元素必须出现任意与文档有关的元 素(icon、display-name或description)之后及filter、filter-mapping、listener或 servlet元素之前。
5.4 在服务器启动时装载servlet
假如servlet或JSP页面有一个要花很长时间执行的init (servlet)或jspInit(JSP)方法。例如,假如init或jspInit方法从某个数据库或ResourceBundle查找产量。这种 情况下,在第一个客户机请求时装载servlet的缺省行为将对第一个客户机产生较长时间的延迟。因此,可利用servlet的load-on- startup元素规定服务器在第一次启动时装载servlet。下面是一个例子。
<servlet>
<servlet-name> … </servlet-name>
<servlet-class> … </servlet-class> <!-- Or jsp-file -->
<load-on-startup/>
</servlet>
可 以为此元素体提供一个整数而不是使用一个空的load-on-startup。想法是服务器应该在装载较大数目的servlet或JSP页面之前装载较少 数目的servlet或JSP页面。例如,下面的servlet项(放置在Web应用的WEB-INF目录下的web.xml文件中的web-app元素 内)将指示服务器首先装载和初始化SearchServlet,然后装载和初始化由位于Web应用的result目录中的index.jsp文件产生的 servlet。
<servlet>
<servlet-name>Search</servlet-name>
<servlet-class>myPackage.SearchServlet</servlet-class> <!-- Or jsp-file -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>Results</servlet-name>
<servlet-class>/results/index.jsp</servlet-class> <!-- Or jsp-file -->
<load-on-startup>2</load-on-startup>
</servlet>

6 声明过滤器

servlet版本2.3引入了过滤器的概念。虽然所有支持servlet API版本2.3的服务器都支持过滤器,但为了使用与过滤器有关的元素,必须在web.xml中使用版本2.3的DTD。
过 滤器可截取和修改进入一个servlet或JSP页面的请求或从一个servlet或JSP页面发出的相应。在执行一个servlet或JSP页面之前, 必须执行第一个相关的过滤器的doFilter方法。在该过滤器对其FilterChain对象调用doFilter时,执行链中的下一个过滤器。如果没 有其他过滤器,servlet或JSP页面被执行。过滤器具有对到来的ServletRequest对象的全部访问权,因此,它们可以查看客户机名、查找 到来的cookie等。为了访问servlet或JSP页面的输出,过滤器可将响应对象包裹在一个替身对象(stand-in object)中,比方说把输出累加到一个缓冲区。在调用FilterChain对象的doFilter方法之后,过滤器可检查缓冲区,如有必要,就对它 进行修改,然后传送到客户机。
例如,程序清单5-11帝国难以了一个简单的过滤器,只要访问相关的servlet或JSP页面,它就截取请求并在标准输出上打印一个报告(开发过程中在桌面系统上运行时,大多数服务器都可以使用这个过滤器)。

程序清单5-11 ReportFilter.java
package moreservlets;

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;

/** Simple filter that prints a report on the standard output 
* whenever the associated servlet or JSP page is accessed.
* <P>
* Taken from More Servlets and JavaServer Pages
* from Prentice Hall and Sun Microsystems Press,
* http://www.moreservlets.com/.
* © 2002 Marty Hall; may be freely used or adapted.
*/

public class ReportFilter implements Filter {
public void doFilter(ServletRequest request,
ServletResponse response,
FilterChain chain)
throws ServletException, IOException {
HttpServletRequest req = (HttpServletRequest)request;
System.out.println(req.getRemoteHost() +
" tried to access " +
req.getRequestURL() +
" on " + new Date() + ".");
chain.doFilter(request,response);
}

public void init(FilterConfig config)
throws ServletException {
}

public void destroy() {}
}

一 旦建立了一个过滤器,可以在web.xml中利用filter元素以及filter-name(任意名称)、file-class(完全限定的类名)和 (可选的)init-params子元素声明它。请注意,元素在web.xml的web-app元素中出现的次序不是任意的;允许服务器(但不是必需的) 强制所需的次序,并且实际中有些服务器也是这样做的。但这里要注意,所有filter元素必须出现在任意filter-mapping元素之前, filter-mapping元素又必须出现在所有servlet或servlet-mapping元素之前。
例如,给定上述的ReportFilter类,可在web.xml中作出下面的filter声明。它把名称Reporter与实际的类ReportFilter(位于moreservlets程序包中)相关联。
<filter>
<filter-name>Reporter</filter-name>
<filter-class>moresevlets.ReportFilter</filter-class>
</filter>
一旦命名了一个过滤器,可利用filter-mapping元素把它与一个或多个servlet或JSP页面相关联。关于此项工作有两种选择。
首 先,可使用filter-name和servlet-name子元素把此过滤器与一个特定的servlet名(此servlet名必须稍后在相同的 web.xml文件中使用servlet元素声明)关联。例如,下面的程序片断指示系统只要利用一个定制的URL访问名为SomeServletName 的servlet或JSP页面,就运行名为Reporter的过滤器。
<filter-mapping>
<filter-name>Reporter</filter-name>
<servlet-name>SomeServletName</servlet-name>
</filter-mapping>
其次,可利用filter-name和url-pattern子元素将过滤器与一组servlet、JSP页面或静态内容相关联。例如,相面的程序片段指示系统只要访问Web应用中的任意URL,就运行名为Reporter的过滤器。
<filter-mapping>
<filter-name>Reporter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
例 如,程序清单5-12给出了将ReportFilter过滤器与名为PageName的servlet相关联的web.xml文件的一部分。名字 PageName依次又与一个名为TestPage.jsp的JSP页面以及以模式http: //host/webAppPrefix/UrlTest2/ 开头的URL相关联。TestPage.jsp的源代码已经JSP页面命名的谈论在前面的3节"分配名称和定制的URL"中给出。事实上,程序清单5- 12中的servlet和servlet-name项从该节原封不动地拿过来的。给定这些web.xml项,可看到下面的标准输出形式的调试报告(换行是 为了容易阅读)。
audit.irs.gov tried to access 
http://mycompany.com/deployDemo/UrlTest2/business/tax-plan.html
on Tue Dec 25 13:12:29 EDT 2001.

程序清单5-12 Web.xml(说明filter用法的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<filter>
<filter-name>Reporter</filter-name>
<filter-class>moresevlets.ReportFilter</filter-class>
</filter>
<!-- ... -->
<filter-mapping>
<filter-name>Reporter</filter-name>
<servlet-name>PageName</servlet-name>
</filter-mapping>
<!-- ... -->
<servlet>
<servlet-name>PageName</servlet-name>
<jsp-file>/RealPage.jsp</jsp-file>
</servlet>
<!-- ... -->
<servlet-mapping>
<servlet-name> PageName </servlet-name>
<url-pattern>/UrlTest2/*</url-pattern>
</servlet-mapping>
<!-- ... -->
</web-app>


7 指定欢迎页

假 如用户提供了一个像http: //host/webAppPrefix/directoryName/ 这样的包含一个目录名但没有包含文件名的URL,会发生什么事情呢?用户能得到一个目录表?一个错误?还是标准文件的内容?如果得到标准文件内容,是 index.html、index.jsp、default.html、default.htm或别的什么东西呢?
Welcome-file-list 元素及其辅助的welcome-file元素解决了这个模糊的问题。例如,下面的web.xml项指出,如果一个URL给出一个目录名但未给出文件名,服 务器应该首先试用index.jsp,然后再试用index.html。如果两者都没有找到,则结果有赖于所用的服务器(如一个目录列表)。
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
<welcome-file>index.html</welcome-file>
</welcome-file-list>
虽然许多服务器缺省遵循这种行为,但不一定必须这样。因此,明确地使用welcom-file-list保证可移植性是一种良好的习惯。

8 指定处理错误的页面

现 在我了解到,你在开发servlet和JSP页面时从不会犯错误,而且你的所有页面是那样的清晰,一般的程序员都不会被它们的搞糊涂。但是,是人总会犯错 误的,用户可能会提供不合规定的参数,使用不正确的URL或者不能提供必需的表单字段值。除此之外,其它开发人员可能不那么细心,他们应该有些工具来克服 自己的不足。
error-page元素就是用来克服这些问题的。它有两个可能的子元素,分别是:error-code和exception- type。第一个子元素error-code指出在给定的HTTP错误代码出现时使用的URL。第二个子元素excpetion-type指出在出现某个 给定的Java异常但未捕捉到时使用的URL。error-code和exception-type都利用location元素指出相应的URL。此 URL必须以/开始。location所指出的位置处的页面可通过查找HttpServletRequest对象的两个专门的属性来访问关于错误的信息, 这两个属性分别是:javax.servlet.error.status_code和javax.servlet.error.message。
可回忆一下,在web.xml内以正确的次序声明web-app的子元素很重要。这里只要记住,error-page出现在web.xml文件的末尾附近,servlet、servlet-name和welcome-file-list之后即可。

8.1 error-code元素
为了更好地了解error-code元素的值,可考虑一下如果不正确地输入文件名,大多数站点会作出什么反映。这样做一般会出现一个404错误信息,它表示不能找到该文件,但几乎没提供更多有用的信息。另一方面,可以试一下在www.microsoft.com、www.ibm.com 处或者特别是在www.bea.com 处输出未知的文件名。这是会得出有用的消息,这些消息提供可选择的位置,以便查找感兴趣的页面。提供这样有用的错误页面对于Web应用来说是很有价值得。 事实上rm-error-page子元素)。由form-login-page给出的HTML表单必须具有一个j_security_check的 ACTION属性、一个名为j_username的用户名文本字段以及一个名为j_password的口令字段。
例如,程序清单5-19指示服务器使用基于表单的验证。Web应用的顶层目录中的一个名为login.jsp的页面将收集用户名和口令,并且失败的登陆将由相同目录中名为login-error.jsp的页面报告。

程序清单5-19 web.xml(说明login-config的摘录)
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd">

<web-app>
<!-- ... -->
<security-constraint> ... </security-constraint>
<login-config>
<auth-method> FORM </auth-method>
<form-login-config>
<form-login-page>/login.jsp</form-login-page>
<form-error-page>/login-error.jsp</form-error-page>
</form-login-config>
</login-config>
<!-- ... -->
</web-app>


9.2 限制对Web资源的访问
现 在,可以指示服务器使用何种验证方法了。"了不起,"你说道,"除非我能指定一个来收到保护的 URL,否则没有多大用处。"没错。指出这些URL并说明他们应该得到何种保护正是security-constriaint元素的用途。此元素在 web.xml中应该出现在login-config的紧前面。它包含是个可能的子元素,分别是:web-resource-collection、 auth-constraint、user-data-constraint和display-name。下面各小节对它们进行介绍。
l web-resource-collection
此 元素确定应该保护的资源。所有security-constraint元素都必须包含至少一个web-resource-collection项。此元素 由一个给出任意标识名称的web-resource-name元素、一个确定应该保护的URL的url-pattern元素、一个指出此保护所适用的 HTTP命令(GET、POST等,缺省为所有方法)的http-method元素和一个提供资料的可选description元素组成。例如,下面的 Web-resource-collection项(在security-constratint元素内)指出Web应用的proprietary目录中 所有文档应该受到保护。
<security-constraint>
<web-resource-coolection>
<web-resource-name>Proprietary</web-resource-name>
<url-pattern>/propritary/*</url-pattern>
</web-resource-coolection>
<!-- ... -->
</security-constraint>
重 要的是应该注意到,url-pattern仅适用于直接访问这些资源的客户机。特别是,它不适合于通过MVC体系结构利用 RequestDispatcher来访问的页面,或者不适合于利用类似jsp:forward的手段来访问的页面。这种不匀称如果利用得当的话很有好 处。例如,servlet可利用MVC体系结构查找数据,把它放到bean中,发送请求到从bean中提取数据的JSP页面并显示它。我们希望保证决不直 接访问受保护的JSP页面,而只是通过建立该页面将使用的bean的servlet来访问它。url-pattern和auth-contraint元素 可通过声明不允许任何用户直接访问JSP页面来提供这种保证。但是,这种不匀称的行为可能让开发人员放松警惕,使他们偶然对应受保护的资源提供不受限制的 访问。 
l auth-constraint
尽管web-resource-collention元素质出了哪些URL应该受到保护, 但是auth-constraint元素却指出哪些用户应该具有受保护资源的访问权。此元素应该包含一个或多个标识具有访问权限的用户类别role- name元素,以及包含(可选)一个描述角色的description元素。例如,下面web.xml中的security-constraint元素部 门规定只有指定为Administrator或Big Kahuna(或两者)的用户具有指定资源的访问权。
<security-constraint>
<web-resource-coolection> ... </web-resource-coolection>
<auth-constraint>
<role-name>administrator</role-name>
<role-name>kahuna</role-name>
</auth-constraint>
</security-constraint>
重要的是认识到,到此为止,这个过程的可移植部分结束了。服务器怎样确定哪些用户处于任何角色以及它怎样存放用户的口令,完全有赖于具体的系统。
例如,Tomcat使用install_dir/conf/tomcat-users.xml将用户名与角色名和口令相关联,正如下面例子中所示,它指出用户joe(口令bigshot)和jane(口令enaj)属于administrator和kahuna角色。
<tomcat-users>
<user name="joe" password="bigshot" roles="administrator,kahuna" />
<user name="jane" password="enaj" roles="kahuna" />
</tomcat-users>
l user-data-constraint
这 个可选的元素指出在访问相关资源时使用任何传输层保护。它必须包含一个transport-guarantee子元素(合法值为NONE、 INTEGRAL或CONFIDENTIAL),并且可选地包含一个description元素。transport-guarantee为NONE值将 对所用的通讯协议不加限制。INTEGRAL值表示数据必须以一种防止截取它的人阅读它的方式传送。虽然原理上(并且在未来的HTTP版本中),在 INTEGRAL和CONFIDENTIAL之间可能会有差别,但在当前实践中,他们都只是简单地要求用SSL。例如,下面指示服务器只允许对相关资源做 HTTPS连接:
<security-constraint>
<!-- ... -->
<user-data-constraint>
<transport-guarantee>CONFIDENTIAL</transport-guarantee>
</user-data-constraint>
</security-constraint>
l display-name
security-constraint的这个很少使用的子元素给予可能由GUI工具使用的安全约束项一个名称。
9.3 分配角色名
迄今为止,讨论已经集中到完全由容器(服务器)处理的安全问题之上了。但servlet以及JSP页面也能够处理它们自己的安全问题。
例 如,容器可能允许用户从bigwig或bigcheese角色访问一个显示主管人员额外紧贴的页面,但只允许bigwig用户修改此页面的参数。完成这种 更细致的控制的一种常见方法是调用HttpServletRequset的isUserInRole方法,并据此修改访问。
Servlet的 security-role-ref子元素提供出现在服务器专用口令文件中的安全角色名的一个别名。例如,假如编写了一个调用 request.isUserInRole("boss")的servlet,但后来该servlet被用在了一个其口令文件调用角色manager而不 是boss的服务器中。下面的程序段使该servlet能够使用这两个名称中的任何一个。
<servlet>
<!-- ... -->
<security-role-ref>
<role-name>boss</role-name> <!-- New alias -->
<role-link>manager</role-link> <!-- Real name -->
</security-role-ref>
</servlet>
也可以在web-app内利用security-role元素提供将出现在role-name元素中的所有安全角色的一个全局列表。分别地生命角色使高级IDE容易处理安全信息。

10 控制会话超时

如 果某个会话在一定的时间内未被访问,服务器可把它扔掉以节约内存。可利用HttpSession的setMaxInactiveInterval方法直接 设置个别会话对象的超时值。如果不采用这种方法,则缺省的超时值由具体的服务器决定。但可利用session-config和session- timeout元素来给出一个适用于所有服务器的明确的超时值。超时值的单位为分钟,因此,下面的例子设置缺省会话超时值为三个小时(180分钟)。
<session-config>
<session-timeout>180</session-timeout>
</session-config>

11 Web应用的文档化

越 来越多的开发环境开始提供servlet和JSP的直接支持。例子有Borland Jbuilder Enterprise Edition、Macromedia UltraDev、Allaire JRun Studio(写此文时,已被Macromedia收购)以及IBM VisuaAge for Java等。
大量的web.xml元素不仅是为服务器设计的,而且还是为可视开发环境设计的。它们包括icon、display-name和discription等。
可回忆一下,在web.xml内以适当地次序声明web-app子元素很重要。不过,这里只要记住icon、display-name和description是web.xml的web-app元素内的前三个合法元素即可。
l icon
icon元素指出GUI工具可用来代表Web应用的一个和两个图像文件。可利用small-icon元素指定一幅16 x 16的GIF或JPEG图像,用large-icon元素指定一幅32 x 32的图像。下面举一个例子: 
<icon>
<small-icon>/images/small-book.gif</small-icon>
<large-icon>/images/tome.jpg</large-icon>
</icon>
l display-name
display-name元素提供GUI工具可能会用来标记此Web应用的一个名称。下面是个例子。
<display-name>Rare Books</display-name>
l description
description元素提供解释性文本,如下所示:
<description>
This Web application represents the store developed for
rare-books.com, an online bookstore specializing in rare
and limited-edition books.
</description>
posted @ 2007-07-02 13:05 和田雨 阅读(210) | 评论 (0)编辑 收藏

1. 如何让选中的一行记录高亮显示?
http://topic.csdn.net/t/20050105/09/3699823.html
2. 如何在下拉列表框里选择一个值后跳出新窗口?
http://topic.csdn.net/t/20050107/15/3707444.html
3. 如何在JSP中启动execl?
http://community.csdn.net/Expert/TopicView1.asp?id=4909154
http://community.csdn.net/Expert/TopicView1.asp?id=4685297
4. 两级联动菜单
http://topic.csdn.net/t/20040730/16/3227437.html
5. java中如何把一个目录下的文件移到另一个指定的目录?
http://topic.csdn.net/t/20050112/13/3719237.html
6. 如何制作表格线?
http://community.csdn.net/Expert/TopicView1.asp?id=5130862
7. jsp如判别一个字符在A到Z之间?
http://topic.csdn.net/t/20050113/08/3721286.html
8. 得到一浮点数小数点后4位,如何写以函数截取为两位
http://topic.csdn.net/t/20050117/14/3731712.html
9. 整型转字符?
http://topic.csdn.net/t/20030526/22/1837632.html
10. 显示数据库的记录,点击哪个标题栏,就按照哪个标题排序
http://topic.csdn.net/t/20041226/15/3677011.html
11. 制作两个按钮
??
12. 用什么sql语句将数据库中的一条记录,复制为两条?
http://topic.csdn.net/t/20050120/17/3741746.html
13. 有兩個按鈕A,B,其中A按下去,A就不可以再按,另外B按下去,A可以再按..請問如何用javascript控制.?
http://topic.csdn.net/t/20050120/17/3741568.html
14. 请问jsp中的数据库的连接方式
http://topic.csdn.net/t/20030107/11/1334377.html
15. 在jsp中,怎么实现按回车就可提交表单?
http://topic.csdn.net/t/20050121/11/3743133.html
16. 在JSP中如何传递数组?
http://topic.csdn.net/t/20030624/17/1952227.html
17. 如何按地址取图片?
http://community.csdn.net/Expert/TopicView1.asp?id=4894525
http://community.csdn.net/Expert/TopicView1.asp?id=4990451
18. JSP中如何上传图片到数据库字段?
http://community.csdn.net/Expert/TopicView1.asp?id=4923127
19. 页面自动刷新?
http://topic.csdn.net/t/20040430/16/3025697.html
20. 表单自动提交?
http://community.csdn.net/Expert/TopicView1.asp?id=4681334
21. 如何从JSP传数据的到JAVABEAN里?
这个不就是参数传递么??
22. weblogic 数据池连接? 数据源 JNDI名字 mysource
http://topic.csdn.net/t/20040915/22/3376617.html
23. 三级联动菜单?
http://blog.csdn.net/overmind/archive/2005/10/27/517856.aspx
24. 在JSP中如何调用浏览器中的"另存为"功能?
http://topic.csdn.net/t/20050509/09/3990931.html
25. 网页全屏显示
http://community.csdn.net/Expert/TopicView1.asp?id=5057245
26. 求两个日期相隔了多少天:输入时间格式为(yyyy-mm-dd)
http://topic.csdn.net/t/20050516/10/4009546.html
27. 上传文件对话框
http://community.csdn.net/Expert/TopicView1.asp?id=4961980
28. 分页测试
http://community.csdn.net/Expert/TopicView1.asp?id=4925123
29. 下载文件
http://community.csdn.net/Expert/TopicView1.asp?id=5019146
30. 简单汉字判断
http://blog.csdn.net/duanxd/archive/2005/05/19/376481.aspx
31. 可否将JSP的变量或者bean传递给JAVASCRIPT使用?
http://topic.csdn.net/t/20050520/14/4022667.html
32. 怎么把一种格式(如yyyy-mm-dd)的日期变量转换成另外一种格式(如dd-mm-yyyy)的日期变量!
http://topic.csdn.net/t/20050602/21/4055209.html
33. 如何实现在页面上添加一行输入对话框
??
34. 如何制作验证码图片?
http://community.csdn.net/Expert/TopicView1.asp?id=4857455
35. 图片缩小放大功能
http://topic.csdn.net/t/20041227/09/3678138.html
36. 如何点击输入框弹出日期选择?
http://community.csdn.net/Expert/TopicView1.asp?id=5020494
http://www.dynarch.com/projects/calendar/
37. 如何在图片上传前查看自己要上传的图片?
http://community.csdn.net/Expert/TopicView1.asp?id=4825282
38. 如何在图片排列的页面上选中一个图片,然后把它删掉?
http://community.csdn.net/Expert/TopicView1.asp?id=5162107
39. 显示某目录下的所有文件
http://community.csdn.net/Expert/TopicView1.asp?id=5056503
40. 如何把复选框中选中的值直接显示在输入文本框中?
http://community.csdn.net/Expert/TopicView1.asp?id=5004830
41. 如何选择路径下的文件直接显示文件内容?
http://community.csdn.net/Expert/TopicView1.asp?id=5194181
http://community.csdn.net/Expert/TopicView1.asp?id=5121413
42. 如何制作进度条?
http://topic.csdn.net/t/20051120/03/4405364.html
43. 如何对较长字符省略显示?
http://community.csdn.net/Expert/TopicView1.asp?id=5124078
44. 如何制作日历?
这个太多了
45. 点击文本弹出一个选择框,选中值后返回并显示
http://community.csdn.net/Expert/TopicView1.asp?id=4979197
http://community.csdn.net/Expert/TopicView1.asp?id=4988006
46. 如何关闭框架页面回到没框架的页面?
http://community.csdn.net/Expert/TopicView1.asp?id=4970057
47. 如何选中多条记录一次性删除?
http://community.csdn.net/Expert/TopicView1.asp?id=4762839
48. 如何自动获取页面文件名?
http://community.csdn.net/Expert/TopicView1.asp?id=4945500
49. 如何在JSP中调用ActiveX控件?
http://community.csdn.net/Expert/TopicView1.asp?id=4919689
50. 如何使用SmartUpload实现文件上传?
http://community.csdn.net/Expert/TopicView1.asp?id=4911791
51. 如何使用iReport和Jasperreport开发报表?
http://community.csdn.net/Expert/TopicView1.asp?id=5038328
52. 如何使用iText生成PDF?
http://www.lowagie.com/iText/tutorial/ch07.html#htmlparsing
http://java.ccidnet.com/art/3565/20050309/480327_1.html
53. 如何制作图片水印?
http://community.csdn.net/Expert/TopicView1.asp?id=5212770
http://community.csdn.net/Expert/TopicView1.asp?id=4885889
54. 如何在页面中屏蔽键盘功能键?
http://community.csdn.net/Expert/TopicView1.asp?id=4356360
55. 如何禁止用户复制网页内容?
http://community.csdn.net/Expert/TopicView1.asp?id=4356360
56. 如何实现不在地址拦中显示当前URL?
http://community.csdn.net/Expert/TopicView1.asp?id=4678351
http://community.csdn.net/Expert/TopicView1.asp?id=4863808
57. 如何获取用户的真实IP地址?
http://community.csdn.net/Expert/TopicView1.asp?id=4718686
58. 如何获取用户浏览器信息?
http://community.csdn.net/Expert/TopicView1.asp?id=4987045
http://community.csdn.net/Expert/TopicView1.asp?id=4898974
59. 如何获取当前绝对路径?
http://community.csdn.net/Expert/TopicView1.asp?id=5157921
http://community.csdn.net/Expert/TopicView1.asp?id=5178848
60. 如何将HTML文件转换成XML文件?
http://community.csdn.net/Expert/TopicView1.asp?id=4542984
61. 如何结合XML和XSL输出HTML页面?
http://community.csdn.net/Expert/TopicView1.asp?id=4355891
62. 如何制作动态树型菜单制作?
http://www.blogjava.net/rickhunter/articles/59742.html
http://community.csdn.net/Expert/TopicView1.asp?id=4966014
63. 如何制作类似QQ的短消息提示?
http://community.csdn.net/Expert/TopicView1.asp?id=4702037
http://community.csdn.net/Expert/TopicView1.asp?id=5087584
64. 如何用jfreechat制作拄、饼、曲线图型?
http://www.inspiresky.com/Article/ShowArticle.asp?ArticleID=731
65. 文本框怎么只容许输入数字?
http://community.csdn.net/Expert/TopicView1.asp?id=4927727
66. 如何打开Word和Execl文件?
http://community.csdn.net/Expert/TopicView1.asp?id=5194181
http://community.csdn.net/Expert/TopicView1.asp?id=4685297
67. 如何生成Word 和 Excel文档?
http://sourceforge.net/project/showfiles.php?group_id=15255&release_id=167948
68. JSP如何读取Word内容?
http://topic.csdn.net/t/20030217/13/1435281.html 
posted @ 2007-07-02 13:01 和田雨 阅读(549) | 评论 (0)编辑 收藏

 1、获取服务器端当前日期:
<%@ page import="java.util.Date"%>
<%
Date myDate = new Date();
%>
2、获取当前年、月、日:

<%@ page import="java.util.Date"%>

<%
Date myDate = new Date();
int thisYear = myDate.getYear() + 1900;//thisYear = 2003
int thisMonth = myDate.getMonth() + 1;//thisMonth = 5
int thisDate = myDate.getDate();//thisDate = 30
%>


3、按本地时区输出当前日期

<%@ page import="java.util.Date"%>
<%
Date myDate = new Date();
out.println(myDate.toLocaleString());
%>


输出结果为:
2003-5-30


4、获取数据库中字段名为"publish_time"、类型为Datetime的值


<%@ page import="java.util.Date"%>
<%
...连接数据库...
ResultSet rs = ...
Date sDate = rs.getDate("publish_time");
%>
[code]


5、按照指定格式打印日期


[code]
<%@ page import="java.util.Date"%>
<%@ page import="java.text.DateFormat"%>
<%
Date dNow = new Date();

SimpleDateFormat formatter = new SimpleDateFormat("E yyyy.MM.dd 'at' hh:mm:ss a zzz");
out.println("It is " + formatter.format(dNow));
%>

 

输出的结果为:
It is 星期五 2003.05.30 at 11:30:46 上午 CST
(更为详尽的格式符号请参看SimpleDateFormat类)


6、将字符串转换为日期

<%@ page import="java.util.Date"%>
<%@ page import="java.text.DateFormat"%>
<%
String input = "1222-11-11";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Date t = null;

try{

  t = formatter.parse(input);

  out.println(t);

  }catch(ParseException e){

  out.println("unparseable using" + formatter);

  }

%>

输出结果为:
Fri Nov 11 00:00:00 CST 1222


7、计算日期之间的间隔

 

<%@ page import="java.util.Date"%>
<%@ page import="java.text.DateFormat"%>
<%
String input = "2003-05-01";
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Date d1 = null;
trycatch(ParseException e)

Date d2 = new Date();

long diff = d2.getTime() - d1.getTime();
out.println("Difference is " + (diff/(1000*60*60*24)) + " days.");
%>


输出结果为:
Difference is 29 days.


8、日期的加减运算
方法:用Calendar类的add()方法


<%@ page import="java.util.*"%>
<%@ page import="java.text.*"%>
<%
Calendar now = Calendar.getInstance();
SimpleDateFormat formatter = new SimpleDateFormat("E yyyy.MM.dd 'at' hh:mm:ss a zzz");
out.println("It is now " + formatter.format(now.getTime()));
now.add(Calendar.DAY_OF_YEAR,-(365*2));
out.println("<br>");
out.println("Two years ago was " + formatter.format(now.getTime()));
%>

 

输出结果为:
It is now 星期五 2003.05.30 at 01:45:32 下午 CST
Two years ago was 星期三 2001.05.30 at 01:45:32 下午 CST


9、比较日期
方法:用equals()、before()、after()方法

 

<%@ page import="java.util.*"%>
<%@ page import="java.text.*"%>
<%
DateFormat df = new SimpleDateFormat("yyy-MM-dd");
Date d1 = df.parse("2000-01-01");
Date d2 = df.parse("1999-12-31");

String relation = null;
if(d1.equals(d2))
relation = "the same date as";
else if(d1.before(d2))
relation = "before";
else
relation = "after";
out.println(d1 +" is " + relation + ' ' + d2);
%>


输出结果为:
Sat Jan 01 00:00:00 CST 2000 is after Fri Dec 31 00:00:00 CST 1999


10、记录一件事所花费的时间
方法:调用两次System.getTimeMillis()方法,求差值

 

<%@ page import="java.text.*"%>
<%
long t0,t1;
t0 = System.currentTimeMillis();
out.println("Cyc starts at " + t0);
int k = 0;
for(int i =0;i<100000;i++)
t1 = System.currentTimeMillis();
out.println("<br>");
out.println("Cyc ends at " + t1);
out.println("<br>");
out.println("This run took " + (t1-t0) + "ms.");
%>


输出结果为:
Cyc starts at 1054275312432
Cyc ends at 1054275312442
This run took 10ms.

其它:如何格式化小数

 

<%@ page import="java.text.*"%>
<%
DecimalFormat df = new DecimalFormat(",###.00");
double aNumber = 33665448856.6568975;
String result = df.format(aNumber);
out.println(result);
%>


输出结果为:
33,665,448,856.66

posted @ 2007-07-02 13:01 和田雨 阅读(220) | 评论 (0)编辑 收藏

计数器是一般网站必备的东东,别小看它了,每当站长看着小小计数器上的数字飞速增长的时候,感觉实在是好极了。以前我们用cgi、asp来写计数器,这方面的文章很多了,在这里,我们将会采用目前比较流行的jsp技术演示如何做一个计数器。 6?P~(MPPm  
mVfGL 3P  
  其中我们用到了两个文件,test.jsp文件用于在浏览器中运行,counter.java是后台的一个小java bean程序,用来读计数器的值和写入计数器的值。而对于计数器的保存,我们采用了一个文本文件lyfcount.txt。
B2cYy,Whi  
6Nz</bHn  
转自:动态网制作指南
www.knowsky.com '$ 6Zv$S  
mgpeMc5R3  
下面是详细的程序代码(test.jsp放到web目录下,counter.java放到class目录):
+33qS}  
//test.jsp文件
@`3-X/Q  
<%@ page contentType="text/html;charset=gb2312"%>
LYL. eK  
<HTML>
 pW0  
<HEAD>
C|F1F &2U  
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
&wk.'0R9E  
<META NAME="GENERATOR" CONTENT="Oracle JDeveloper">
%K&gZIkX  
<TITLE>
87x7Rnj >=  
计数器演示程序
%fU wb  
</TITLE>
"I,z,/5Y  
</HEAD>
lJgBcZ{7  
<BODY>
IrtugFf  
<!--创建并调用bean(counter)-->
KPX)E!"v  
<jsp:useBean id="counter" class="counter" scope="request">
v2LW[I7]  
</jsp:useBean>
-5~k  
<%
~jKx  
//调用counter对象的ReadFile方法来读取文件lyfcount.txt中的计数
j(}8B=c{e6  
String cont=counter.ReadFile("/lyfcount.txt");
s Ki/puJ  
//调用counter对象的ReadFile方法来将计数器加一后写入到文件lyfcount.txt中
b#j^QB9w N  
counter.WriteFile("/lyfcount.txt",cont);%>
W*AWh`yDK  
您是第<font color="red"><%=cont%></font>位访问者
j {i6 TI  
</BODY>
e$]VJ-*Ni  
</HTML>
,x 61Tj6p  
{enOdTJQx  
//counter.java 读写文件的一个bean
}-0Rke6`>Y  
import java.io.*;
pcj}E(\ y  
mc?=kzE  
public class counter extends Object {
NHF1l}*  
private String currentRecord = null;//保存文本的变量
0&dB8+;Ge  
private BufferedReader file; //BufferedReader对象,用于读取文件数据
Pv: H87  
private String path;//文件完整路径名
 B4&% )2  
public counter() {
] RgX{;  
}
h4Hi&30  
//ReadFile方法用来读取文件filePath中的数据,并返回这个数据
ZF%0a,&%  
public String ReadFile(String filePath) throws FileNotFoundException
_v(ojt r=  
{
1HPnp /u  
path = filePath;
YicVk\(1  
//创建新的BufferedReader对象
2~ 0K7~  
file = new BufferedReader(new FileReader(path));
ct3vuSf?  
String returnStr =null;
&vt{]z=  
try
<ymk1tk8@  
{
p|~K$ +R  
//读取一行数据并保存到currentRecord变量中
~-g#'  
currentRecord = file.readLine();
C{s%WW3?  
}
! f/|Lm  
catch (IOException e)
DEC5"S'f#  
{//错误处理
j^ %9ji  
System.out.println("读取数据错误.");
uiro2 Fz8  
}
}H&/l?g'  
if (currentRecord == null)
DgO0/n  
//如果文件为空
Y:d2ra*[&  
returnStr = "没有任何记录";
`Au)H  
else
FN@p~ X  
{//文件不为空
="p{vnN"  
returnStr =currentRecord;
]v GW  
}
QDanl36  
//返回读取文件的数据
AR@urdG  
return returnStr;
V0 @M$op  
}
e i&:Z  
//ReadFile方法用来将数据counter+1后写入到文本文件filePath中
c$h=gQ  
//以实现计数增长的功能
~tnQw}x  
public void WriteFile(String filePath,String counter) throws FileNotFoundException
afTiP0 Qz  
{
dnx4bUW0  
path = filePath;
 *% Ga  
//将counter转换为int类型并加一
! Al9>  
int Writestr = Integer.parseInt(counter)+1;
^$%vQ/F,Z  
try {
C{A%JXV|y  
//创建PrintWriter对象,用于写入数据到文件中
oFhww4mY|  
PrintWriter pw = new PrintWriter(new FileOutputStream(filePath));
QyjT?K{j:  
//用文本格式打印整数Writestr
*R\U6A=J8  
pw.println(Writestr);
S$C5 c .  
//清除PrintWriter对象
>|=}0K  
pw.close();
AsBtn-X>^  
} catch(IOException e) {
(1XAc\Le  
//错误处理
e_Pdm8Lw)  
System.out.println("写入文件错误"+e.getMessage());
5._Wa[@  
}
25A-F<x  
}
BnUr~  
_f A:ynT]{  
}
#i'a`8Y6QT  
U_mAGr[  
  到这里,程序写完了,将counter.java编译为counter.class,同样放在对应的class目录下,在根目录下建立一个lyfcount.txt文件,文件内容就一个数字0,直接在浏览器中敲入地址就可以看到计数器了,刷新浏览器会看到不断变幻的数字。
mCzk^}5  
@(;G`0C  
  (如果运行时候提示找不到文件,请将上面test.jsp中的readfile那一句注释后运行一次则lyfcount.txt文件自动建立,然后就可以正常运行。)

posted @ 2007-07-02 12:59 和田雨 阅读(212) | 评论 (0)编辑 收藏

目前, 国内外信息化建设已经进入基于Web应用为核心的阶段, Java作为应用于网络的最好语言,前景无限看好。然而,就算用Java建造一个不是很烦琐的web应用,也不是件轻松的事情。概括一下,实施Java的WEB项目需要掌握的技术如下:

 
lJava语言
l面向对象分析设计思想
l设计模式和框架结构
lXML语言
l网页脚本语言
l数据库
l应用服务器
l集成开发环境
 
下面我们具体地看每个技术.
1、Java语言

Java
语言体系比较庞大,包括多个模块。从WEB项目应用角度讲有JSP、Servlet、JDBC、JavaBean(Application)四部分技术。
 
(1)、Java Database Connectivity (JDBC)技术
Java Web应用开发中,数据库管理系统(RDBMS)的使用是不可缺少的。JDBC(Java Database Connectivity) 是一种用于执行 SQL 语句的 Java API。它由一组用 Java 编程语言编写的类和接口组成。JDBC 为工具/数据库开发人员提供了一个标准的API,使他们能够用纯Java API 来编写数据库应用程序。
简单地说,JDBC 可做三件事:
l与数据库建立连接,
l发送 SQL 语句,
l处理结果。
 
 (2)、Servlet技术
Servlet是运行在服务器端的程序,可以被认为是服务器端的applet。servlet被Web服务器(例如Tomcat)加载和执行,就如同applet被浏览器加载和执行一样。servlet从客户端(通过Web服务器)接收请求,执行某种操作,然后返回结果。
 
Servlet的主要优点包括
lServlet是持久的。servlet只需Web服务器加载一次,而且可以在不同请求之间保持服务(例如一次数据库连接)。
lServlet是与平台无关的。如前所述,servlet是用Java编写的,它自然也继承了Java的平台无关性。
lServlet是可扩展的。由于servlet是用Java编写的,它就具备了Java所能带来的所有优点。Java是健壮的、面向对象的编程语言,它很容易扩展以适应你的需求。servlet自然也具备了这些特征。
lServlet是安全的。从外界调用一个servlet的惟一方法就是通过Web服务器。这提供了高水平的安全性保障,尤其是在你的Web服务器有防火墙保护的时候。
lServlet可以在多种多样的客户机上使用。由于servlet是用Java编写的,所以你可以很方便地在HTML中使用它们。
 
(3)、JavaServer Pages(JSP) 技术
JSP是从Servlet上分离出来的一小部分,简化了开发,加强了界面设计。JSP定位在交互网页的开发。运用Java语法,但功能较Servlet弱了很多,并且高级开发中只充当用户界面部分。JSP容器收到客户端发出的请求时,首先执行其中的程序片段,然后将执行结果以HTML格式响应给客户端。其中程序片段可以是:操作数据库、重新定向网页以及发送 E-Mail 等等,这些都是建立动态网站所需要的功能。所有程序操作都在服务器端执行,网络上传送给客户端的仅是得到的结果,与客户端的浏览器无关,因此,JSP 称为Server-Side Language。
 
JavaServer Pages的主要优点包括
●一次编写,各处执行(Write o­nce, Run Anywhere)特性
作为Java 平台的一部分,JavaServer Pages 技术拥有Java语言“一次编写,各处执行”的特点。随着越来越多的供货商将JavaServer Pages 技术添加到他们的产品中,您可以针对自己公司的需求,做出审慎评估后,选择符合公司成本及规模的服务器,假若未来的需求有所变更时,更换服务器平台并不影响之前所投下的成本、人力所开发的应用程序。
● 搭配可重复使用的组件
JavaServer Pages技术可依赖于重复使用跨平台的组件(如:JavaBean或Enterprise JavaBean组件)来执行更复杂的运算、数据处理。开发人员能够共享开发完成的组件,或者能够加强这些组件的功能,让更多用户或是客户团体使用。基于善加利用组件的方法,可以加快整体开发过程,也大大降低公司的开发成本和人力。
● 采用标签化页面开发
Web 网页开发人员不一定都是熟悉Java 语言的程序员。因此,JSP 技术能够将许多功能封装起来,成为一个自定义的标签,这些功能是完全根据XML 的标准来制订的,即JSP 技术中的标签库(Tag Library)。因此,Web 页面开发人员可以运用自定义好的标签来达成工作需求,而无须再写复杂的Java 语法,让Web 页面开发人员亦能快速开发出一动态内容网页。
今后,第三方开发人员和其他人员可以为常用功能建立自己的标签库,让Web 网页开发人员能够使用熟悉的开发工具,如同HTML 一样的标签语法来执行特定功能的工作。
N-tier 企业应用架构的支持
有鉴于网际网络的发展,为因应未来服务越来越繁杂的要求,且不再受地域的限制,因此,
必须放弃以往Client-Server的Two-tier 架构,进而转向更具威力、弹性的分散性对象系统。由于JavaServer Page 技术是Java 2 Platform Enterprise Edition (J2EE)集成中的一部分,它主要是负责前端显示经过复杂运算后之结果内容,而分散性的对象系统则是主要依赖EJB ( Enterprise JavaBean )和JNDI ( Java Naming and Directory Interface )构建而成。

(4)、JavaBean(Application)
应用组件技术
Application
是Java应用程序,在WEB项目和一些开发中主要应用JavaBean。它就是Application的一部分,逻辑运算能力很强,能极大的发挥Java语言的优点。JavaBean 被称为是Java 组件技术的核心。JavaBean 的结构必须满足一定的命名约定。JavaBean能提供常用功能并且可以重复使用,这使得开发人员可以把某些关键功能和核心算法提取出来封装成为一个组件对象,这样就增加了代码的重用率和系统的安全性。

高级的WEB项目会应用到以上所有技术,它们之间联合使用和协作开发会提高开发的效率和系统的性能。 

2、面向对象分析设计思想

Java
语言是完全面向对象的语言,所以在项目设计时会有很大的帮助,在设计时应尽量舍弃以往的面向过程的设计方式。

在分析项目业务关系的时候,应用一些UML(Unified Modeling Language)图,例如常用的用例图(use case diagram),类图(class diagram),时序图(sequence diagram)等等,会有很大的帮助,这样能尽快找出业务逻辑主要面对的对象,然后对每个对象进行行为划分,最后再实现对象之间的集成和通信。

3、设计模式和框架结构
Java从语言角度来讲不是很难,但是从整体设计角度来讲我们还需要了解一些高级应用框架。如果要设计一个良好的框架结构,单单只掌握Java语言远远不够。这就涉及到一个设计模式,还有和设计模式相关的一些知识。

设计模式在Java项目实施过程更是重中之重。主要在与两层的设计模式、三层的设计模式和N层的设计模式。它直接决定着项目的应用、部署和实际开发设计。

在普通的WEB项目中很多采用两层的开发结构。JSP+Servlet或JSP+JavaBean。当对开发要求高的项目中使用很多的还是MVC的三层开发结构,也就是JSP+Servlet+JavaBean。它能分有效的分离逻辑开发,使开发人员能专注于各自的开发。同时也能时整个开发结构流程更清晰,但是需要比较高的开发配合度。
在项目中,我们经常使用著名的Model-View-Controller(MVC)架构。
MVC架构是随着smalltalk language语言的发展提出的,它是一个著名的用户界面设计架构。经典的MVC架构把一个组件(可认为是整个应用程序的一个模块)划分成三部分组 Model管理这个模块中所用到的数据和业务逻辑。而View 管理模块如何显示给用户,Controller 决定如何处理用户和该模块交互式时候产生的事件 如用户点击一个按钮等。

4、XML
语言

在服务器和设计模式结构中会应用到自定义文件,而且在应用高级设计时也会定义自用的标签,现在流行的是用XML去定义配置,所以XML语言应该有一定掌握。
当前,Java 2平台企业版(J2EE)架构在厂商市场和开发者社区中倍受推崇。作为一种工具,可扩展标记语言(XML)简化了数据交换、进程间消息交换这一类的事情,因而对开发者逐渐变得有吸引力,并开始流行起来。自然,在J2EE架构中访问或集成XML解决方案的想法也很诱人。因为这将是强大系统架构同高度灵活的数据管理方案的结合。

XML的应用似乎是无穷无尽的,但它们大致上可以分为三大类:
1、简单数据的表示和交换(针对XML的简单API(SAX)和文档对象模型(DOM)语法解析,不同的文档类型定义(DTDs)和概要(schemas))
2、用户界面相关、表示相关的上下文(可扩展样式表语言(XSL),可扩展样式表语言转换(XSLT))
3、面向消息的计算(XML-RPC(远程过程调用),基于SOAP协议的Web 服务(Web Services),电子化业务XML(ebXML))

5、网页脚本语言
 
为了提高WEB项目的整体性能,提高人机交互的友好界面,网页的脚本语言是很有用处的,有的时候可以解决很大的难题或提高程序的性能和应用性。

网页脚本语言的执行都是在客户端执行的,速度很很快,并且大多的操作与服务器没有交互运算,所以在一些应用中非常理想。在设计WEB项目的应用中,网页的脚本语言起着不能忽视的作用,所以如果设计WEB项目的应用中,对JavaScript应有一定的了解。
 
JavaScript是一种基于对象(Object Based)和事件驱动(Event Driven)并具有安全性能(Secure)的脚本语言。使用它的目的是与HTML超文本标记语言、Java 脚本语言(Java小程序)一起实现在一个Web页面中链接多个对象,与Web客户交互作用。从而可以开发客户端的应用程序等。它是通过嵌入或调入在标准的HTML语言中实现的。它具有以下几个基本特点:
1.它是一种脚本编写语言
JavaScript是一种脚本语言,它采用小程序段的方式实现编程。像其它脚本语言一样,JavaScript同样已是一种解释性语言,它提供了一个易的开发过程。
它的基本结构形式与C、C++、VB十分类似。但它不像这些语言一样,需要先编译,而是在程序运行过程中被逐行地解释。它与HTML标识结合在一起,从而方便用户的使用操作。
2. 基于对象的语言。
 JavaScript是一种基于对象的语言,同时以可以看作一种面向对象的。这意味着它能运用自己已经创建的对象。因此,许多功能可以来自于脚本环境中对象的方法与脚本的相互作用。
3.简单性
 JavaScript的简单性主要体现在:首先它是一种基于Java基本语句和控制流之上的简单而紧凑的设计, 从而对于学习Java是一种非常好的过渡。其次它的变量类型是采用弱类型,并未使用严格的数据类型。
4.安全性
 JavaScript是一种安全性语言,它不允许访问本地的硬盘,并不能将数据存入到服务器上,不允许对网络文档进行修改和删除,只能通过浏览器实现信息浏览或动态交互。从而有效地防止数据的丢失。
5. 动态性
 JavaScript是动态的,它可以直接对用户或客户输入做出响应,无须经过Web服务程序。它对用户的响应,是采用以事件驱动的方式进行的。所谓事件驱动,就是指在主页(Home Page)中执行了某种操作所产生的动作,就称为“事件”(Event)。比如按下鼠标、移动窗口、选择菜单等都可以视为事件。当事件发生后,可能会引起相应的事件响应。

6、开发工具
(1)、数据库
在主要的应用中,数据库相关的环节应用很多,所以对数据库应该有一定了解。不能单单只了解一种数据库,因为在很多实际开发中会提出很多数据库解决方案,所以只有在了解多种数据库的情况下才能有一个比较方案。
对于数据库应该了解他的性能和一些基本的操作常识,还有该数据库的特点。而针对与Java语言WEB项目的数据库开发则主要是对JDBC的应用,还有数据库事务处理和连接池等高级概念的应用。

(2)、Web服务器
 
同数据库一样,应该了解该服务器的性能,特点和一些常识。
在应用方面,Web服务器主要是针对于配置和部署,对目录的配置,调试;对配置文件属性的修改;对访问权限和并发性的控制;Java类的部署等。

(3)、集成开发环境(IDE):
公欲善其事, 必先利其器”. 对于Web应用开发人员来讲,好的集成开发环境(IDE:Integrated Development Enviroment)是非常重要的。目前在市场上占主导位置的一个集成开发工具就是Eclipse.
 (转载文章请保留出处:Java家(www.javajia.com))
posted @ 2007-07-02 12:58 和田雨 阅读(311) | 评论 (0)编辑 收藏

一些网站采用了字母和数字的验证码,数字和字母加起来一共30多个,如果有心,还是能够通过方法识别出来。 O8<@d3  
我在网上看到一篇文章"jsp彩色验证码",我进行了加强,生成的验证码内容为汉字,可以方便应用在面向汉语网民的网站认证上。
oxcd:, Vc  
iXV6:&\9"r  
当然,我还看到别人考虑的几种方法:
{i867@H_e  
1.生成计算题,比如20+34*(23-12)=?,用户必须输入正确的答案才能通过验证,这个想法挺好,但是用户必须打开计算器进行计算,增加了用户的难度
dza^8ynu  
2.问一些常见的问题,比如“人”这个字由几笔组成。这也是不错的想法,关键是必须有上万条的题库,而且题库的答案必须是明确的,简单易比较的,这个难度也较大。
|||o<  
ejas+  
下面是彩色汉字验证码的代码。
,"-07[X  
]j&J&.:&,  
dVTACb`  
<%@ page contentType="image/jpeg" import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*" %>
YwpC}G;   
<%!
(.&vEwK  
//create by smallnest
z(S Lcx  
//email:
smallnest@gmail.com <p-Y a_&u  
//website:
www.kuaff.com hpC%no /s  
Du:x%[~]  
99Ztr4E  
Jk)|S2  
: pZ<K"Z-&  
//生成随机颜色
s*# ]mm.MD  
Color getRandColor(Random random,int fc,int bc)
M-C|5ahf8B  
{
!DF}` d  
if(fc>255) fc=255;
{+9C59]sA  
if(bc>255) bc=255;
^k41]4 RD  
int r=fc+random.nextInt(bc-fc);
')/u5Y{fp  
int g=fc+random.nextInt(bc-fc);
F\(TcR(  
int b=fc+random.nextInt(bc-fc);
qQ5> k=3  
return new Color(r,g,b);
2Es&*Kr+"  
}
 '0 )!e!=  
%>
=H NQZD`e  
<%
q=X0lJ<V  
//设置页面不缓存
Qsd{m_y#  
response.setHeader("Pragma","No-cache");
p[brf>x  
response.setHeader("Cache-Control","no-cache");
h-r<TW  
response.setDateHeader("Expires", 0);
hB~(UCtd  
{m@moH 3N  
">^0|,(e  
// 设置图片的长宽
G"KO  
int width=176, height=30;
rl> 1bV?8  
//设置备选汉字,剔除一些不雅的汉字
>22XQgGh  
String base = "\u7684\u4e00\u4e86\u662f\u6211\u4e0d\u5728\u4eba\u4eec\u6709\u6765\u4ed6\u8fd9\u4e0a\u7740\u4e2a\u5730\u5230\u5927\u91cc\u8bf4\u5c31\u53bb\u5b50\u5f97\u4e5f\u548c\u90a3\u8981\u4e0b\u770b\u5929\u65f6\u8fc7\u51fa\u5c0f\u4e48\u8d77\u4f60\u90fd\u628a\u597d\u8fd8\u591a\u6ca1\u4e3a\u53c8\u53ef\u5bb6\u5b66\u53ea\u4ee5\u4e3b\u4f1a\u6837\u5e74\u60f3\u751f\u540c\u8001\u4e2d\u5341\u4ece\u81ea\u9762\u524d\u5934\u9053\u5b83\u540e\u7136\u8d70\u5f88\u50cf\u89c1\u4e24\u7528\u5979\u56fd\u52a8\u8fdb\u6210\u56de\u4ec0\u8fb9\u4f5c\u5bf9\u5f00\u800c\u5df1\u4e9b\u73b0\u5c71\u6c11\u5019\u7ecf\u53d1\u5de5\u5411\u4e8b\u547d\u7ed9\u957f\u6c34\u51e0\u4e49\u4e09\u58f0\u4e8e\u9ad8\u624b\u77e5\u7406\u773c\u5fd7\u70b9\u5fc3\u6218\u4e8c\u95ee\u4f46\u8eab\u65b9\u5b9e\u5403\u505a\u53eb\u5f53\u4f4f\u542c\u9769\u6253\u5462\u771f\u5168\u624d\u56db\u5df2\u6240\u654c\u4e4b\u6700\u5149\u4ea7\u60c5\u8def\u5206\u603b\u6761\u767d\u8bdd\u4e1c\u5e2d\u6b21\u4eb2\u5982\u88ab\u82b1\u53e3\u653e\u513f\u5e38\u6c14\u4e94\u7b2c\u4f7f\u5199\u519b\u5427\u6587\u8fd0\u518d\u679c\u600e\u5b9a\u8bb8\u5feb\u660e\u884c\u56e0\u522b\u98de\u5916\u6811\u7269\u6d3b\u90e8\u95e8\u65e0\u5f80\u8239\u671b\u65b0\u5e26\u961f\u5148\u529b\u5b8c\u5374\u7ad9\u4ee3\u5458\u673a\u66f4\u4e5d\u60a8\u6bcf\u98ce\u7ea7\u8ddf\u7b11\u554a\u5b69\u4e07\u5c11\u76f4\u610f\u591c\u6bd4\u9636\u8fde\u8f66\u91cd\u4fbf\u6597\u9a6c\u54ea\u5316\u592a\u6307\u53d8\u793e\u4f3c\u58eb\u8005\u5e72\u77f3\u6ee1\u65e5\u51b3\u767e\u539f\u62ff\u7fa4\u7a76\u5404\u516d\u672c\u601d\u89e3\u7acb\u6cb3\u6751\u516b\u96be\u65e9\u8bba\u5417\u6839\u5171\u8ba9\u76f8\u7814\u4eca\u5176\u4e66\u5750\u63a5\u5e94\u5173\u4fe1\u89c9\u6b65\u53cd\u5904\u8bb0\u5c06\u5343\u627e\u4e89\u9886\u6216\u5e08\u7ed3\u5757\u8dd1\u8c01\u8349\u8d8a\u5b57\u52a0\u811a\u7d27\u7231\u7b49\u4e60\u9635\u6015\u6708\u9752\u534a\u706b\u6cd5\u9898\u5efa\u8d76\u4f4d\u5531\u6d77\u4e03\u5973\u4efb\u4ef6\u611f\u51c6\u5f20\u56e2\u5c4b\u79bb\u8272\u8138\u7247\u79d1\u5012\u775b\u5229\u4e16\u521a\u4e14\u7531\u9001\u5207\u661f\u5bfc\u665a\u8868\u591f\u6574\u8ba4\u54cd\u96ea\u6d41\u672a\u573a\u8be5\u5e76\u5e95\u6df1\u523b\u5e73\u4f1f\u5fd9\u63d0\u786e\u8fd1\u4eae\u8f7b\u8bb2\u519c\u53e4\u9ed1\u544a\u754c\u62c9\u540d\u5440\u571f\u6e05\u9633\u7167\u529e\u53f2\u6539\u5386\u8f6c\u753b\u9020\u5634\u6b64\u6cbb\u5317\u5fc5\u670d\u96e8\u7a7f\u5185\u8bc6\u9a8c\u4f20\u4e1a\u83dc\u722c\u7761\u5174\u5f62\u91cf\u54b1\u89c2\u82e6\u4f53\u4f17\u901a\u51b2\u5408\u7834\u53cb\u5ea6\u672f\u996d\u516c\u65c1\u623f\u6781\u5357\u67aa\u8bfb\u6c99\u5c81\u7ebf\u91ce\u575a\u7a7a\u6536\u7b97\u81f3\u653f\u57ce\u52b3\u843d\u94b1\u7279\u56f4\u5f1f\u80dc\u6559\u70ed\u5c55\u5305\u6b4c\u7c7b\u6e10\u5f3a\u6570\u4e61\u547c\u6027\u97f3\u7b54\u54e5\u9645\u65e7\u795e\u5ea7\u7ae0\u5e2e\u5566\u53d7\u7cfb\u4ee4\u8df3\u975e\u4f55\u725b\u53d6\u5165\u5cb8\u6562\u6389\u5ffd\u79cd\u88c5\u9876\u6025\u6797\u505c\u606f\u53e5\u533a\u8863\u822c\u62a5\u53f6\u538b\u6162\u53d4\u80cc\u7ec6";
=-/!18  
//备选汉字的长度
k63GRbd?  
int length = base.length();
n\+47N*ze  
,"jx!  
2b8~3d[L  
//创建内存图像
e/6VUo@>%  
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
X/$V%6)  
// 获取图形上下文
"g&Xk0  
Graphics g = image.getGraphics();
NEE4{?Q?  
k >@l  
Q{AR6CRAt  
//创建随机类的实例
ch6) "2  
Random random = new Random();
>>#>"<CMT  
K#]~p^Z}  
aF@:;!cVu  
// 设定图像背景色(因为是做背景,所以偏淡)
NAL2w3  
g.setColor(getRandColor(random,200,250));
{<=!bYh  
g.fillRect(0, 0, width, height);
7@mFmb] -#  
+.S``"'  
Eg^${%,d  
//备选字体
RE]p3t|O  
String[] fontTypes = {"\u5b8b\u4f53","\u65b0\u5b8b\u4f53","\u9ed1\u4f53","\u6977\u4f53","\u96b6\u4e66"};
b<psU?/% _  
int fontTypesLength = fontTypes.length;
YZ?KfO=r  
6< 1B;  
<;G d~(  
//在图片背景上增加噪点
Q`6 A{@5Y  
g.setColor(getRandColor(random,160,200));
8M`w  
g.setFont(new Font("Times New Roman",Font.PLAIN,14));
S h (%oy<+  
for (int i=0;i<6;i++)
[NFmqEEanb  
{
_R6 Lr9q  
g.drawString("*********************************************",0,5*(i+2));
goQ2Va+  
}
}):f;M*  
9L+)0)(  
+4vsFa6*  
Fj%N4$?(m  
+Sv2qb  
//取随机产生的认证码(6个汉字)
%[B|3.e  
=e?R(E^Y"  
XSp~5|I  
//保存生成的汉字字符串
p^ >Q(g  
String sRand="";
/0A5(P&q/  
for (int i=0;i<6;i++)
ypeul27H8  
{
h570L^  
int start = random.nextInt(length);
L[4Aolk:M  
String rand=base.substring(start,start+1);
&hA0:^  
sRand+=rand;
D/ ^Kl  
$N^odf(2   
//设置字体的颜色
2A<BW~3FPt  
g.setColor(getRandColor(random,10,150));
C hhWE@!  
//设置字体
6<\w'-  
g.setFont(new Font(fontTypes[random.nextInt(fontTypesLength)],Font.BOLD,18 + random.nextInt(6)));
ftVk!3e  
//将此汉字画到图片上
dMjL#H}  
g.drawString(rand,24*i+ 10 + random.nextInt(8),24);
I_i 7 %  
}
! [ $qI1  
X8H6Fki  
eP{42 C0=  
//将认证码存入session
kCl -?N{og  
session.setAttribute("rand",sRand);
<uT}CUy  
lfKXsvP  
+M'E6E%w  
g.dispose();
*$4SR?  
OIFYpaBXb  
<~[q5tC+  
//输出图象到页面
/z <4A^+  
ImageIO.write(image, "JPEG", response.getOutputStream());
L$ "5TP<  
%>
&+ Q :0Y  
~ oNku2,  
 |D6AG!J  
4f$X#:  
===========================
qozOA`,l  
/j|Au)P  
注:此方法没有在本站JSP虚拟主机测试过,不知道性能如何,如果要用验证请看:
~J|X4>.Vm  
posted @ 2007-07-02 12:57 和田雨 阅读(683) | 评论 (0)编辑 收藏

Sun Microsystems 在 1997 年下半年推出了 Servlet API,将它定位为 CGI 开发人员使用的一种功能强大的工具,这些开发人员正在寻找比 CGI(通用网关接口)编程更高效和轻便的优秀解决方案。但是,开发人员很快就发现 Servlet API 有其自身的缺点,从代码的可维护性和可扩展性方面来看,该解决方案难以实施。在某种程度上,这种缺点促使团队开发一种允许在 HTML 中嵌入 Java 代码的解决方案 — JavaServer Pages (JSP) 因此而出现。

不久以后,开发人员意识到将表达与商务逻辑混合在一起的复杂 JSP 页不易于理解和维护。不能编写 scriplet 的页面制作人员所面临的另一个问题是由于标准标记集而带来的 JSP 限制。这些限制使得难点变成利用 JSP 实施自定义标记的机制来创建 JSP 自定义标记。

JSP 标准标记库 (JSTL) 是自定义标记库的集合,它将许多 JSP 应用程序通用的核心功能封装为简单的标记。它不再需要使用 JSP scriptlet
和表达式,而使用表达式的更高级语法。它还实现了通用目的的功能,如迭代和条件化、数据管理格式化、XML 操作、数据库访问、国际化和对本地化信息敏感的格式化标记以及 SQL 标记。JSTL 1.0 推出了 EL 的概念,但只限于 JSTL 标记。在 JSP 2.0 中,您可以使用带模板文本的 EL,甚至可以通过 javax.servlet.jsp.el 获得编程方式的访问。

在我们了解 JSTL 如何适应环境以及与 JSTL 表达式语言相关的限制以后,我们来看 JSP 2.0 的重要优点之一 — JSP 表达式语言 (EL)。我们将特别涉及到以下内容:

JSP 表达式语言定义
在无脚本的 JSP 页面中支持 EL 的机制
表达式语言语法
JSP EL 中有效的表达式
使用 EL 表达式

表达式语言的灵感来自于 ECMAScript 和 XPath 表达式语言,它提供了在 JSP 中简化表达式的方法。它是一种简单的语言,基于可用的命名空间(PageContext 属性)、嵌套属性和对集合、操作符(算术型、关系型和逻辑型)的访问符、映射到 Java 类中静态方法的可扩展函数以及一组隐式对象。

EL 提供了在 JSP 脚本编制元素范围外使用运行时表达式的功能。脚本编制元素是指页面中能够用于在 JSP 文件中嵌入 Java 代码的元素。它们通常用于对象操作以及执行那些影响所生成内容的计算。JSP 2.0 将 EL 表达式添加为一种脚本编制元素。

脚本编制元素具有三个从属形式:

  • 声明
  • Scriptlet
  • 表达式。

    让我们来看代码中的这三种从属形式:

     

     

    table.getColumn( )

    在将 EL 添加到 JSP 工具包以后,可以使用更简单的语法来编写以上的代码,而获得与以上 JSP 元素相同的结果。EL 表达式的另一个优势是,可以在不允许使用上述任何脚本编制元素从属形式的无脚本的 JSP 页中使用它。但是必须注意,可以不使用三种脚本编制元素从属形式中的任一种来编写 JSP 页,而对 JSP 页是否应该为无脚本形式的选择则完全基于应用程序的需求。如果您希望明确分开表达与商务逻辑,则还可以选择将页面强制为无脚本形式。通过强制成为无脚本页面,必须通过其他元素(如 JavaBean、EL 表达式、自定义操作和标准标记库)提供 JSP 页的动态行为。

  • 有两种机制可以确保页面不包含任何脚本编制元素。每种机制还提供在无脚本的 JSP 页中支持 EL 的方法。

    • 使用页面指示:

      在使用页面指示时,您可以通过将 isELEnabled 指示的值相应地设为“true”或“false”,指定是否支持 EL,如下所示:


    • 使用部署描述符的元素:

      当使用部署描述符的元素时,您可以通过在 标记间包含布尔值“true”或“false”,指定是否支持 EL,如下所示:
      ...


      *.jsp
      true
      true


      ....

    JSP 表达式语言允许页面制作人员使用简单语法访问组件,如:

       
    ${expr}

    在以上的语法中,expr 代表有效的表达式。必须注意,该表达式可以与静态文本混合,还可以与其他表达式结合成更大的表达式。

    有效表达式可以包含文字、操作符、变量(对象引用)和函数调用。我们将分别了解这些有效表达式中的每一种:

    文字

    JSP 表达式语言定义可在表达式中使用的以下文字:

     

    文字 文字的值

    Boolean

    true 和 false

    Integer

    与 Java 类似。可以包含任何正数或负数,例如 24、-45、567

    Floating Point

    与 Java 类似。可以包含任何正的或负的浮点数,例如 -1.8E-45、4.567

    String

    任何由单引号或双引号限定的字符串。对于单引号、双引号和反斜杠,使用反斜杠字符作为转义序列。必须注意,如果在字符串两端使用双引号,则单引号不需要转义。

    Null null

    让我们来看一些使用文字作为有效表达式的示例:

    ${false} 
    ${3*8)

    操作符

    JSP 表达式语言提供以下操作符,其中大部分是 Java 中常用的操作符:

     

    术语 定义

    算术型

    +、-(二元)、*、/、div、%、mod、-(一元)

    逻辑型

    and、&&、or、||、!、not

    关系型

    ==、eq、!=、ne、、gt、<=、le、>=、ge。可以与其他值进行比较,或与布尔型、字符串型、整型或浮点型文字进行比较。

    空操作符是前缀操作,可用于确定值是否为空。

    条件型 A ?B :C。根据 A 赋值的结果来赋值 B 或 C。

    让我们来看一些使用操作符作为有效表达式的示例:

    ${ (6 * 5) + 5 } 
    ${empty name}

    隐式对象

    JSP 表达式语言定义了一组隐式对象,其中许多对象在 JSP scriplet 和表达式中可用:

    术语 定义

    pageContext

    JSP 页的上下文。它可以用于访问 JSP 隐式对象,如请求、响应、会话、输出、servletContext 等。例如,${pageContext.response} 为页面的响应对象赋值。

    此外,还提供几个隐式对象,允许对以下对象进行简易访问:

     

    术语 定义

    param

    将 请求参数名称映射到单个字符串参数值(通过调用 ServletRequest.getParameter (String name) 获得)。getParameter (String) 方法返回带有特定名称的参数。表达式 $(param.name) 相当于 request.getParameter (name)。

    paramValues

    将 请求参数名称映射到一个数值数组(通过调用 ServletRequest.getParameter (String name) 获得)。它与 param 隐式对象非常类似,但它检索一个字符串数组而不是单个值。表达式 ${paramvalues.name) 相当于 request.getParamterValues(name)。

    header

    将请求头名称映射到单个字符串头值(通过调用 ServletRequest.getHeader(String name) 获得)。表达式 ${header.name} 相当于 request.getHeader(name)。

    headerValues

    将 请求头名称映射到一个数值数组(通过调用 ServletRequest.getHeaders(String) 获得)。它与头隐式对象非常类似。表达式 ${headerValues.name} 相当于 request.getHeaderValues(name)。

    cookie 将 cookie 名称映射到单个 cookie 对象。向服务器发出的客户端请求可以获得一个或多个 cookie。表达式 ${cookie.name.value} 返回带有特定名称的第一个 cookie 值。如果请求包含多个同名的 cookie,则应该使用 ${headerValues.name} 表达式。
    initParam 将上下文初始化参数名称映射到单个值(通过调用 ServletContext.getInitparameter(String name) 获得)。

    除了上述两种类型的隐式对象之外,还有些对象允许访问多种范围的变量,如 Web 上下文、会话、请求、页面:

     

    术语 定义

    pageScope

    将页面范围的变量名称映射到其值。例如,EL 表达式可以使用 ${pageScope.objectName} 访问一个 JSP 中页面范围的对象,还可以使用 ${pageScope.objectName.attributeName} 访问对象的属性。

    requestScope

    将 请求范围的变量名称映射到其值。该对象允许访问请求对象的属性。例如,EL 表达式可以使用 ${requestScope.objectName} 访问一个 JSP 请求范围的对象,还可以使用 ${requestScope.objectName.attributeName} 访问对象的属性。

    sessionScope

    将会话范围的变量名称映射到其值。该对象允许访问会话对象的属性。例如:


    $sessionScope.name}

    applicationScope

    将应用程序范围的变量名称映射到其值。该隐式对象允许访问应用程序范围的对象。

    必须注意,当表达式根据名称引用这些对象之一时,返回的是相应的对象而不是相应的属性。例如:即使现有的 pageContext 属性包含某些其他值,${pageContext} 也返回 PageContext 对象。

     

    EL 表达式可用于两种情况:

    • 作为标准操作和自定义操作中的属性值

    • 在 JSP 文件的模板文本中,如 HTML 或非 JSP 元素 — 在这种情况下,模板文本中的表达式被赋值并插入到当前的输出中。但是,必须注意,如果标记的主体被声明为与标记相关,则不会对表达式赋值。

    posted @ 2007-07-02 12:54 和田雨 阅读(659) | 评论 (0)编辑 收藏

    ServletJSP技术是用Java开发服务器端应用的主要技术,是开发商务应用表示端的标准。Java开发者喜欢使用它有多种原因,其一是对于已经熟悉Java语言的开发者来说这个技术容易学习;其二是Java把“一次编写,到处运行”的理念带入到Web应用中,实现了“一次编写,到处实现”。而且更为重要的是,如果遵循一些良好的设计原则的话,就可以把表示和内容相分离,创造出高质量的、可以复用的、易于维护和修改的应用程序。比方说,在HTML文档中如果嵌入过多的Java代码(scriptlet),就会导致开发出来的应用非常复杂、难以阅读、不容易复用,而且对以后的维护和修改也会造成困难。事实上,在CSDN的JSP/Servlet论坛中,经常可以看到一些提问,代码很长,可以逻辑却不是很清晰,大量的HTML和Java代码混杂在一起,让人看得一头雾水。这就是随意开发的弊端。

      如果你已经基本了解JSPServlet的各项技术(最好也开发过一些Web应用),那么我们可以一起探讨一下如何开发“好”的应用的一些指导原则。我们首先对ServletJSP技术做一个浏览。

      ServletJSP概览

      早期的动态网页主要采用CGI(Common Gateway Interface,公共网关接口)技术,你可以使用不同的语言编写CGI程序,如VB、C/C++Delphi等。虽然CGI技术发展成熟且功能强大,但由于编程困难、效率低下、修改复杂等缺点,所以有逐渐被取代的趋势。在所有的新技术中,JSP/Servlet具备更高效、更容易编程、功能更强、更安全和具有良好的可移植性,因而被许多人认为是未来最有发展前途的动态网站技术。

      与CGI相似,Servlet支持请求/响应模型。当一个客户向服务器递交一个请求时,服务器把请求送给ServletServlet负责处理请求并生成响应,然后送给服务器,再由服务器发送给客户。与CGI不同的是,Servlet没有生成新的进程,而是与HTTP Server处于同一进程中。它通过使用线程技术,减小了服务器的开销。Servlet处理请求的过程是这样的:当收到来自客户端的请求后,调用service方法,该方法中Servlet先判断到来的请求是什么类型的(GET/POST/HEAD…),然后调用相应的处理方法(doGet/doPost/doHead…)并生成响应。

      别看这么复杂,其实简单说来Servlet就是一个Java类。与一般类的不同之处是,这个类运行在一个Servlet容器内,可以提供session管理和对象生命周期管理。因而当你使用Servlet的时候,你可以得到Java平台的所有好处,包括安全性管理、使用JDBC访问数据库以及跨平台的能力。而且,Servlet使用线程,因而可以开发出效率更高的Web应用。

      JavaServer Pages (JSP)

      JSP技术是J2EE的一个关键技术,它在更高一级的层次上抽象Servlet。它可以让常规静态HTML与动态产生的内容相结合,看起来像一个HTML网页,却作为Servlet来运行。现在有许多商业应用服务器支持JSP技术,比如BEA WebLogic、IBM WebSphere、 JRun等等。使用JSP比用Servlet更简单。如果你有一个支持JSP的Web服务器,并且有一个JSP文件,你可以把它放倒任何静态HTML文件可以放置的位置,不用编译,不用打包,也不用进行ClassPath的设置,就可以像访问普通网页那样访问它,服务器会自动帮你做好其他的工作。

      JSP工作原理

      JSP 文件看起来就像一个普通静态HTML文件,只不过里面包含了一些Java代码。它使用.jsp的后缀,用来告诉服务器这个文件需要特殊的处理。当我们访问一个JSP页面的时候,这个文件首先会被JSP引擎翻译为一个Java源文件,其实就是一个Servlet,并进行编译,然后像其他Servlet一样,由Servlet引擎来处理。Servlet引擎装载这个类,处理来自客户的请求,并把结果返回给客户,如下图所示:


    图1: 调用JSP页面的流程

      以后再有客户访问这个页面的时候,只要该文件没有发生过更改,JSP引擎就直接调用已经装载的Servlet。如果已经做过修改的话,那就会再次执行以上过程,翻译、编译并装载。其实这就是所谓的“第一人惩罚”。因为首次访问的时候要执行一系列以上的过程,所以会耗费一些时间;以后的访问就不会这样了。

    开发原则


      这一部分我们列出一些开发原则,重点是JSP页面。关于如何分离表现和内容的MVC因为要涉及到JSPServlet的整合,我们稍候再谈。

      不要在JSP页面中嵌入过量的Java代码:对于非常简单或是测试性的代码,把所有的Java 代码直接放入JSP页面内是没有问题的。但是这种方法不应该被过度使用,否则就会产生一大堆HTML和Java混合起来的代码,让人难以阅读和理解。解决方法是写一个单独的类,用来执行相关的计算。一旦这个类测试通过,就可以把它放在任何执行同样计算的场合中。这样可以促进代码的复用。

      选择合适的包含(include)机制: 如果一个应用中每个页面有一样的抬头和底部,或者还有导航条,那么就应该把它们放到一个单独的文件中,然后在每一个页面中使用包含机制把它们加入到这个页面中:

    Include 指令: <%@ include file="filename" %>或等效xml语法
    <jsp:directive.includefile=”filename” />

    Include 动作: <jsp:include page="page.jsp" flush="true" />

      Include指令是当JSP页面翻译为Servlet的时候包含另外一个文件,Include 动作是当请求时包含另外一个文件的输出。如果被包含的文件不是经常改动的时候,我建议使用Include 指令,这样速度更快。如果被包含的文件需要不时改动或者知道请求时才能决定需要包含的内容时,那么应该使用Include 动作。

      如果你使用JSP标准标记库(JavaServer pages Standard Tag Library即JSTL)的话,那么还有第三中包含机制<c:import>,可以用来包含本地或者远程的资源。例如:

    <c:import url="./copyright.html"/>
    <c:import url="http://www.somewhere.com/hello.xml"/>

      不要把业务逻辑和表示混合在一起: 复杂的应用涉及大量的代码,因而把业务逻辑和前端的表示相分离就显得格外重要,这种分离可以让任何一方的变化不会影响到另外一方。所以,所有的JSP代码都应该限制在表示层,可是如果这样的话,你如何实现你的业务逻辑呢?这就是JavaBean所做的事情。JavaBean技术是一个独立于平台的组件模型,它让开发者编写、测试通过一个组件后,可以随处使用,提高了复用性。在JSP技术中,JavaBean实现了业务逻辑部分,它把数据返回给JSP页面,由JSP页面负责格式化数据并输出到客户端的浏览器。在JSP页面中使用JavaBean组件的好处是:

      产生了可以复用的组件:任何应用都可以使用这些组件

      可以把业务逻辑和表示相分离:你可以修改数据的显示方式而不用考虑业务逻辑。这样做的结果也可以明确工作中开发人员的分工,网页开发人员可以把精力放到如何显示数据上,Java开发者则更为关注业务逻辑的实现。

      对于JavaBean你不用提供源代码,这样你的代码就不会被浏览器网页的人轻易获得,可以保护你的劳动成果。

      如果你的应用中使用了EJB组件,那么业务逻辑就应该放置在EJB中,因为EJB模型提供了生命周期管理、事务管理以及多客户访问域对象(Entity Beans)。你可以仔细看一下Enterprise BluePrints中的例子,它就是这么做的。

      使用定制的标记: 上面我们已经讨论过,把所有Java代码嵌入到JSP页面内并不合适,因为网页开发人员并不一定懂得Java语言,更难以理解Java语法。JavaBean可以封装很多Java代码,不过在JSP页面内使用JavaBean仍然要求页面开发人员了解一些Java语法。

      JSP技术中包含了定制标记库的功能。Java开发人员可以生成自己的标记库,这样网页设计人员就可以使用类似HTML的语法来使用这些标记。编写和使用自己定制的标记库可以在更大程度上促进业务逻辑和表示的分离。使用定制标记库主要有以下好处:

      可以消除在JSP页面中使用scriptlet 标记使用的任何参数都可以通过属性传入,从而不需要使用Java代码就可以达到希望的目的。

      可以简化使用 网页设计人员不需要学会使用Java语法,他们可以用类似HTML语法就可以使用标记。
    不懂Java的网页设计人员可以使用标记库来完成单独使用HTML不能完成的任务。

      提高了复用性 标记库完全可以复用,这可以节省开发和测试的时间。Scriptlet代码只能在“拷贝粘贴”级别上进行“复用”。

      简单说来,你可以像用HTML构建表示层一样使用标记库完成非常复杂的任务。下面是表页标记库的一些注意事项:

      1. 保持简洁性:如果一个标记需要好几个属性的话,那么尽可能把它分为几个标记。

      2. 保持复用性:同标记的使用人员(网页设计人员)多多交流,尽量开发出可以高度复用的标记库。

      3. 不要一切都从头开始:现在已经有一些可以免费使用的标记库,比如Jakarta Taglibs。如果你要用到一个标记,先看看是不是已经有现成的可以使用。

      不要“重新发明轮子”,不要一切从头开始: 通过定制组件可以提高复用性,不过定制组件仍然需要编写、测试和调试程序。问题是这个事情别人可能已经实现了,而且你的实现方式并不一定比人家做得更好。这就是JSP标准标记库(JavaServer Pages Standard Tag Library, JSTL)要做的事情(JSTL请参考JSTL官方网站)。JSTL提供了循环、读属性、遍历各种数据结构、条件表达式求值等各种标记。它也提供了一些复杂的标记,甚至像解析XML文档的标记它都有。所以如果你要用到一个标记的话,最好先看看有没有别人已经实现的可以使用,而不要次次从头开始,自己搞一套。

      使用JSTL表达使语言(JSTL Expression Language): 传递给JSP页面的数据一般通过JSP作用域属性或者请求参数来进行。专门为网页开发者设计的表达式语言(Expression Language, EL)把使用作用域属性传递信息作为从业务逻辑向JSP页面传递信息的标准方式。这里要注意的是,EL只是JSP技术中关键的一个方面,并不是一种通用的程序设计语言。相反,它只是一种数据访问语言,它可以简化应用程序的数据的访问,不用Scriptlet和请求时表达式求值就可以访问数据。

      在JSP中,网页设计师要使用表达式语法<%= name %>或JavaBean组件来取得某些变量或属性的值,例如:

    <tagLib:tag attribute="<%=

    pageContext.getAttribute("name") %>">

      或

    <%= aCustomerBean.getAddress().getCountry() %>

      表达使语言让网页设计师可以使用简化的语法来访问信息。如果你只是要访问一个简单的变量,你可以使用这样的语法:

    <tagLib:tag attribute="${name}">

      如果你要访问一个嵌套JavaBean的属性,你可以这样:

    <tagLib:tag attribute ="${

    aCustomerBean.address.country}">

      表达式语言(EL)借用了JavaScript 的语法,所以如果你对JavaScript 很熟悉的话,你就会觉得巨爽。

      使用过滤器(filter): 过滤器是一个对象,可以传输请求或修改响应。它可以在请求到达Servlet/JSP之前对其进行预处理,而且能够在响应离开Servlet/JSP之后对其进行后处理。所以如果你有几个Servlet/JSP需要执行同样的数据转换或页面处理的话,你就可以写一个过滤器类,然后在部署描述文件(web.xml)中把该过滤器与对应的Servlet/JSP联系起来。

      创建过滤器其实很容易,你只须实现javax.servlet.Filter接口及它的三个方法:

    public void init(FilterConfig config)

    public void doFilter(ServletRequest req, ServletResponse rep,

    FilterChain chain)

    public void destroy()

      这样,你就可以完成你的过滤器。

    使用可移植的安全模型:大部分的应用服务器都提供了安全模型,不过一般它们都是针对某一个服务器或某一个厂商专有的。如果你的应用需要移植的话,那么你的应用最好使用可以移植的安全模型。如果你的应用有一些预先定义的固定用户的话,那么你可以使用FROM验证和BASIC验证。可是如果你要动态生成客户的话(一般都是这种情况),你可能就需要使用服务器特定的API来创建和管理用户。这样当你的应用移植到另外一个服务器时,你可能就会碰到API不兼容的问题。这种情况下,最好的解决方法是使用适配器(Adapter)模式(如果你对设计模式不熟悉的话,请参看GoF的《设计模式》一书)。

      用数据库来保存持久性数据: Servlet/JSP中可以使用HttpSession对象也就是会话对象来保存用户的临时数据。不过如果你想保存持久性数据的时候,你应该使用数据库,数据保存数据会更安全,而且对客户所用的浏览器没有什么要求。这样即使你的应用服务器由于某种原因崩溃了,你的数据依然良好。

      高速缓存页面: 应用程序中总有一些东西是相对固定的,而另外一些东西是经常变化的。你应该使用静态的HTML文档来存储那些相对固定的内容,这样客户端就可以进行高速缓存,客户每次访问你的应用时,只需访问已经改动的部分。这样可以加快客户的访问速度。

      使用连接池: 如果你要自己写数据库访问代码的话,我觉得使用你应该学会如何使用数据库连接池技术。每一个服务器都有针对数据库连接池的配置文档,你要学习一下如何使用。数据库连接池可以加速你的应用的数据访问的速度,而且由于服务器替你管理了数据库连接,这可以节省你的很多工作。

      缓存数据库的访问结果: 如果你的应用要对数据库进行频繁访问的话,你可以使用一个对象来缓存你的数据,这样你就可以节省大量访问数据库的开销。在《J2EE核心模式》和《实用J2EE设计模式编程指南》两本书中都有关于值对象模式(Value Object Pattern)的详细探讨,你可以参考这两本书来获得相应的知识。

      使用数据访问对象模式:如果你的应用需要访问多个数据库系统或者可能会移植到其它的存储系统中,那么你针对特定厂商的优化代码就可能会失效。使用通用的代码存在执行效率的问题,而使用优化代码又存在移植的问题。所以就产生了数据访问对象模式(Data Access Object Pattern, DAO),该模式既提供了各数据库厂商的适应性,又能利用到他们提供的独特的好处。按照面向对象的分离任务的原则,该模式将与企业信息系统(Enterprise Information System, EIS)通讯需要的逻辑隔离到它自己的类中。这样,事物对象,如Servlet/JSP组件、JavaBean就可以利用数据访问对象(DAO)处理所有与EIS有关的事务。

      最好采用JSPXML语法: JSP技术中经常存在着两种完成同一个任务的语法,一种是常规的JSP语法,一种是对应的XML语法。虽然两种语法作用相同,你最好还是使用XML语法。存在两种语法的原因是,JSP语法可以与以前的代码兼容,而J2EE使用XML作为其交换数据的核心,所以同时提供了XML语法。随着J2EE的发展,XML的作用会越来越大,所以我建议你使用XML语法。

      研究Sun提供的J2EE BluePrints: Sun的Enterprise BluePrints 提供了大量指导原则、设计模式和很好的例子(宠物店,Pet Store)。你可以好好研究一下这些内容,这样可以提高你的设计和开发水平。

      整合ServletJSP

      JSP技术规范种给出了两种使用JSP开发Web应用的方式,这两种方式可以归纳为模型一和模型二,这两种模型的主要差别在于它们处理业务的流程不同。模型一,如下图所示,称之为JSP+JavaBeans模型。在这一模型中,JSP页面独自响应请求并将处理结果返回给客户,所有的数据通过JavaBean来处理,JSP实现页面的表现。


    图2: JSP模型一

      从上图可以看出,模型一也实现了页面表现和业务逻辑相分离。然而使用这种方式就要在JSP页面使用大量的Java代码,当需要处理的业务逻辑很复杂时,这种情况会变得非常糟糕。大量嵌入式代码使整个页面程序变得异常复杂。对于前端界面设计的网页开发人员来说,这简直是一场噩梦。所以,模型一不能满足大型应用的需要,但是对于小型应用,因为该模型简单,不用涉及诸多要素,从而可以很好地满足小型应用的需要,所以在简单应用中,可以考虑模型一。

      模型二,如下图所示,称之为JSP+Servlet+JavaBeans模型。这一模型结合了JSPServlet技术,充分利用了JSPServlet两种技术原有的优势。这个模型使用JSP技术来表现页面,使用Servlet技术完成大量的事务处理,使用Bean来存储数据。Servlet用来处理请求的事务,充当一个控制者的角色,并负责向客户发送请求。它创建JSP需要的Bean和对象,然后根据用户请求的行为,决定将哪个JSP页面发送给客户。


    图3: JSP模型二

      从开发的观点看,模型二具有更清晰的页面表现,清楚的开发角色的划分,可以充分利用开发团队中的网页设计人员和Java开发人员。这些优势在大型项目中表现得尤为突出,网页设计人员可以充分发挥自己的美术和设计才能来充分表现页面,程序编写人员可以充分发挥自己的业务逻辑处理思维,实现项目中的业务处理。

      另外,从设计结构来看,这种模型充分体现了模型视图控制器(MVC)的设计架构。事实上,现存的很多开发框架都是基于这种模型的,充分实现了MVC ,例如Apache Struts框架和JavaServer Faces框架
    posted @ 2007-07-02 12:51 和田雨 阅读(276) | 评论 (0)编辑 收藏

    最近整理以前写的东东,发现2004年底的时候对比各类Java反编译器时记下来的一篇心得,也不知道是不是有点儿过时了,仅供大家参考吧。

    =====================================================================

    JAVA语言是19955月由SUN公司发布的,由于其安全性高、代码优化、跨平台等特性,迅速取代了很多传统高级语言,占据了企业级网络应用开发等诸多领域的霸主地位。

    不过,JAVA最突出的跨平台优势使得它不能被编译成本地代码,而要以中间代码的形式运行在虚拟机环境中,这使得JAVA的反编译要比别的高级语言容易实现,并且反编译的代码经过优化后几乎可以与源代码相媲美。

    为了更好地保护知识产权,避免本公司的智力成果轻易被人窃取,开发者有必要对反编译工具深入了解,以便有针对性地采取保护措施。

    目前,比较流行的JAVA反编译工具超过30种,其中有三款堪称精品:

    一、         应用广泛的JAD

    在众多的JAVA反编译工具中,有几种非常著名的工具使用了相同的核心引擎——JAD,其中主要包括:Front End PlusmDeJavaDecafe ProCavaj Java DecompilerDJ Java DecompilerNMI’s Java Class Viewer和国产的JAVA源代码反编译专家等等。

    JAD本身是一个命令行工具,没有图形界面,上述的这些工具大多是在JAD内核的基础之上加了一个图形界面而已。这么多种产品的共同选择,足可证明JADJAVA反编译领域中的尊贵地位。

    笔者用来测试的JAD版本是1.5.8f。

    JAD是使用Microsoft Visual C++开发的,运行速度非常快,可以处理很复杂的JAVA编译文件。众多的参数使JAD可以灵活应付多种加密手段,令反编译的代码更加优化和易读。由于JAD参数太多,没必要一一解释,其中有几个最常用的如下:

             -d

    - 用于指定输出文件的目录

     

             -s - 输出文件扩展名(默认为: .jad),通常都会把输出文件扩展名直接指定为.java,以方便修改的重新编译。

             -8       - Unicode字符转换为ANSI字符串,如果输出字符串是中文的话一定要加上这个参数才能正确显示。

    最常用的反编译指令如下所示:

    Jad –d c:\javasource –s .java -8 javatest.class

    这条指令将当前目录下的javatest.class反编译为javatest.java并保存在c:\javasource目录里,其中的提示输出为中文,而不是Unicode代码。

    二、         源码开放的JODE

    JODE是全球最大的开源项目网站Sourceforge.net的成员,不要以为源码开放就小瞧它,在所有的JAVA反编译器中,JODE的反编译效果是最好的,尤其是对付一些常见的加密手段,例如混淆技术等,更是出类拔粹。

    JODE本身也是纯JAVA开发的,最近越来越多的JAVA反编译软件也选择JODE来做它们的核心引擎,例如JCavaj Java DecompilerBTJ (Back To Java)jEdit's JavaInsight plugin等。

    JODE是一个可运行的JAR文件,在windows环境下双击即可运行。

    需要特别说明的是,JODE不是通过常规的Open->File的方式来加载JAVA编译后的类文件(*.class)或是类包(*.jar)的,而是通过在Options菜单中的Set Classpath来实现的,单独的类文件可以将它的上一级目录作为Classpath输入,然后再选择Reload Classpath即可。

      

    新加入的类包或是类的名字会在左侧窗口出现,双击类包名可以展开目录树结构,双击需要反编译的类名则在右上角的窗口中直接显示反编译后的源代码。

     

    三、         独树一帜的DAVA

    DAVA不是一个独立的JAVA反编译器,而是JAVA代码优化工具Soot的一部分。SootJODE一样是纯JAVA开发的,也是一个独立的JAR包,但却不能通过双击直接运行,而是象JAD一样在命令行状态运行。

    Soot对环境变量的配置要求非常严格,通常情况下要对CLASSPATH做如下设置:

    Set CLASSPATH=%CLASSPATH%;c:\sootdir\sootclasses-2.1.0.jar;.;

    其中的c:\sootdir\是下载的soot类包放置的路径,CLASSPATH末尾的.;代表了当前目录,如果不加上这个的话Soot经常会报一个找不到类的错误。

          DAVA是作为Soot的一个参数使用的,通常的用法如下:

    Java soot.Main –f dava –d c:\javasource javatest

    注意最后的类名不用带.class后缀,因为它默认是处理class文件,这个操作与前述的JAD的参数效果相同。

    DAVA采取了流程优化的方式进行反编译,与传统反编译思路不尽相同,但却对改变流程类的加密方法有独特的反编译效果。

     

    上述的三种工具各有千秋,但效果都非常不错。经测试,它们基本上都可以把JDK自带的一些例程完全反编译,然后不加任何修改可再编译成功,并能正常运行!

    (文中工具均经过本人亲手测试,当时用的是jdk1.4.2_03,现在离写文章的时候过了一年多了,jdk都出到1.5了,怕是有些程序也不太好反编了)

    /*原创作品,转载请注明出处*/

    posted @ 2007-07-02 12:50 和田雨 阅读(2469) | 评论 (0)编辑 收藏

         摘要:   Apache Ant Project Ant 众所周知,Ant(蚂蚁)是一套基于java的程序...  阅读全文
    posted @ 2007-07-02 12:49 和田雨 阅读(252) | 评论 (0)编辑 收藏

     

    Java的线程调度操作在运行时是与平台无关的。一个多任务系统需要在任务之间实现QoS(Quality of Service)管理时,如果CPU资源的分配基于Java线程的优先级,那么它在不同平台上运行时的效果是很难预测的。本文利用协调式多任务模型,提出一个与平台无关、并且能在任务间动态分配CPU资源的方案。
      现在,由于计算机系统已经从人机交互逐步向机机交互转化,计算机和计算机之间的业务对于时间的要求非常高。软件系统对于业务的支持已经不仅表现为对不同业务的逻辑和数据(算法+数据结构)支持,而且还表现为对同时处理不同任务的时效性(任务响应速度)支持。一般,任务响应的速度可以通过算法优化及并行运算分担负载等手段来提高。但是,用户业务逻辑的复杂度决定了算法优化的发挥空间,硬件规模决定了所能够承担负载的大小。我们利用Java平台的特点,借鉴协调式多任务思想,使CPU资源能够在任务间动态分配,从而为时间要求强的任务分配更多的CPU运行资源。这也可以充分利用现有硬件,为用户业务提供最大的保障。

      用Java解决问题

      本着软件系统结构和现实系统结构一致的思想,开发复杂业务服务的程序一般按照计算机任务和现实业务对应的思路,最终形成一个大规模的多任务系统。由于其跨平台性,Java系统可以随着业务的扩大,平滑地升级到各种硬件平台上。由于Java自身的发展及其应用场合的不断扩大,用它实现多任务系统已经成为当前的应用方向。在J2EE(Java2 Enterprise Edition)推出以后,Sun公司已经将Java的重心放在了服务器端(Server Side)系统的构造上。由于客户/服务器模型固有的多对一的关系,服务器端程序也必然是一个多任务系统。

      在Java多任务应用中,动态地将CPU资源在任务间分配有很重要的意义。比如一个Inte.Net服务商的系统往往有多种任务同时运行,有HTTP、FTP、MAIL等协议的支持,也有商务、娱乐、生活、咨询等业务的服务。在白天,网站希望系统的CPU资源尽量保障网上用户的服务质量,提高电子商务等任务的响应速度;晚上则希望让自己的娱乐服务和资料下载尽可能满足下班后人们的需要。另外,在新兴的网管(比如TMN, Telecommunication Management.Network)等应用领域中,服务程序往往需要支持成千上万个并发响应事件的被管理对象(MO,Managed Object)。对于被管理对象执行的操作,不同用户在不同时刻往往有不同的时间要求。

      方案选择

      在考虑动态分配CPU资源的实施方案时,往往有以下两点要求:

      1. 须充分利用现有硬件资源,在系统空闲时,让低优先级任务也能够得到系统所能给予的最快响应。

      2.当硬件资源超负荷运行时,虽然系统中有大规模、多数量的任务不能处理,但它不应受影响,而能够顺利处理那些能够被处理的、最重要的高优先级任务。

      多任务系统要用多线程实现的最简单方法就是将线程和任务一一对应,动态调整线程的优先级,利用线程调度来完成CPU资源在不同任务间动态分配。这种思路在以前使用本地化代码(Native Code),充分利用特定硬件和操作系统技巧的基础上是基本可行的。但在跨平台的Java环境中,这个思路对仅有小规模任务数的简单系统才可行,原因有以下两点:

      1. Java的线程虽然在编程角度(API)是与平台无关的,但它的运行效果却和不同操作系统平台密切相关。为了利用更多的CPU资源,Java中的一个线程(Thread)就对应着不同操作系统下的一个真实线程。因为Java虚拟机没有实现线程的调度,所以这些Java的线程在不同操作系统调度下运行的差异性也就比较明显。例如在Windows系统中,不仅线程的优先级少于Java API参数规定的十个优先级,而且微软明确反对程序员动态调整线程优先级。即使在操作系统中有足够的优先权,让线程优先级的参数和真实线程的优先级对应,不同操作系统的调度方式也会有许多不同。这最终会造成代码在不同平台上的行为变得不可预测。这就很难满足复杂的、大规模并发任务的众多优先级需求,从而很难达到用户业务需要达到的效果。

      2. 由于在Java系统中,线程被包装在一个Java语言的对象类—Thread中,所以为了完成Java语言对象和操作系统线程的对应,Java线程的系统开销还是比较大的(在NT 4.0中,平均每个线程大致占用30KB内存)。因此如果让Thread对象个数和成千上万的任务数同比例增长,就显然是不合理的。

      综上所述,根据并发多任务的大规模需求和Java平台固有的特点,想要利用Java Thread对象的优先级调整CPU资源的分配是非常困难的,所以应该尽量避免让线程和任务直接对应,也尽量避免使用操作系统线程优先级的调度机制。

      解决方案

      根据以上分析,问题的症结在于:多任务系统中的任务在Java语言中的对应以及任务间的相互调度。

      从本质上看,一个任务就是一系列对象方法的调用序列,与Java的Thread对象或者别的类的对象没有必然联系。在避免使用不同操作系统线程调度且同时Java虚拟机又没有线程调度能力的情况下,要想构造一个协调式多任务系统,让各个任务相互配合就成了最直接的思路。协调式多任务系统一般有以下特点:

      1. 任务由消息驱动,消息的响应代码完成任务逻辑的处理;

      2. 消息队列完成消息的存储和管理,从而利用消息处理的次序体现任务优先级的不同;

      3. 任务中耗时的消息响应逻辑能够主动放弃CPU资源,让别的任务执行(像Windows 3.1中的Yield函数、Visual Basic中的DoEvents语句)。

      可能出于巧合,Java语言具有构造协调式多任务系统天然的条件。Java对象的方法不仅是一个函数调用,它还是一个Java.lang.reflect.Method类的对象。而所有对象的方法都可以通过Method类的invoke方法调用。如果能使每个任务所对应的一系列方法全部以对象形式包装成消息,放到消息队列中,然后再按照自己的优先级算法将队列中的消息取出,执行其Method对象的invoke调用,那么一个基本的协调式多任务系统就形成了。其中,任务的优先级和线程的优先级没有绑定关系。该系统的主体调度函数可以设置成一个“死循环”,按照需要的优先级算法处理消息队列。对于有多重循环、外设等待等耗时操作的消息响应函数,可以在响应函数内部递归调用主体调度函数,这一次调用把原来的“死循环”改成在消息队列长度减少到一定程度(或者为空)后退出。退出后,函数返回,执行刚才没有完成的消息响应逻辑,这样就非常自然地实现了协调式系统中任务主动放弃CPU资源的要求。

      如果仅仅做到这一步,完成一个像Windows 3.1中的多任务系统,实际只用了一个线程,没有利用Java多线程的特点。应该注意到,虽然Java系统中线程调度与平台相关,但是相同优先级的线程之间分时运行的特点基本上是不受特定平台影响的。各个相同优先级的线程共享CPU资源,而线程又被映射成了Java语言中的Thread对象。这些对象就可以被认为是CPU资源的代表。Thread与线程执行代码主体的接口—Runnable之间是多对一的关系。一个Runnable可以被多个Thread执行。只要将Runnable的执行代码设置成上述的消息调度函数,并和消息队列对应上,那么就可以通过控制为它服务的Thread个数来决定消息队列执行的快慢,并且在运行时可以动态地新增(new)和退出Thread对象。这样就能任意调整不同消息队列在执行时所占用CPU资源的多少。至此,任何一个Java调用都可以在Thread个数不同的消息队列中选择,并可以调整这些消息队列服务的Thread个数,从而实现在运行时调整任务所占用的CPU资源。

      纵观整个方案,由于仅仅基于Java语言固有的Method对象,不同任务间动态分配CPU资源并没有对任务的性质及其处理流程有任何限制,那么在消息队列中没有高优先级消息时,低优先级消息的处理函数自然会全部占用CPU资源。在不同消息队列处理速度任意设置时,并没有将特定的消息限制在快的或者慢的消息队列上。如果系统的负荷超出(比如消息队列长度超过一定限制),只要将队列中低优先级消息换出或者拒绝不能处理的消息进入,那么系统的运行就可以基本上不受负荷压力的影响,从而最大保障用户的关键业务需求。

      当然,协调式多任务的思想也有其局限性,主要就是它的调度粒度比较大。系统能够保证的粒度是一次消息处理过程。如果消息处理逻辑非常费时,那么编程人员就必须再处理函数内部,让系统主动让出CPU资源。这虽然需要在处理消息响应逻辑时增加一个考虑因素,但是,在Windows系统盛行的今天,这是一个已经被普遍接受的思路。由于方案中并没有局限为消息队列服务的线程数目,所以一个长时间的消息响应只会影响一个线程,而不会对整个系统产生致命的影响。除了调度粒度的问题以外,还有访问消息队列操作在各个线程间互斥的问题。取出消息的过程是串行化的,因此对于这一瓶颈的解决方案就是:假设取出一条消息的操作相对于处理消息的消耗可以忽略不计,那么对于多次调用且仅有两三行响应逻辑的消息,编程人员通过函数调用就可以直接执行。

      前面比较详细地阐述了多任务系统中任务的划分以及执行等内容。虽然这些是一个系统的核心,但是在一个实用的系统中,还需要任务间的同步、互斥等机制。在上述框架内,互斥可以简单地用Java的Synchronized机制实现。由于任务可以主动让出执行权限,要实现等待(Wait任务中止)和通知(Notify任务继续),从而实现任务同步也就比较容易了。

    posted @ 2007-07-02 12:48 和田雨 阅读(216) | 评论 (0)编辑 收藏

    SUN 的新版J2EE1.4提供了在J2EE中开发Web Service的基础,对开发工具做了一些重要的增强,在应用程序部署和服务器管理方面也提供了新的标准,在集成性和安全性方面有所增强,提升了J2EE开发Web应用程序的能力;在编程模型方面的重要变化包括JSP表达式语言、简化的标记库等;EJB 2.1中提供了新的timer服务,查询语言(QL)也有所增强;Jdbc3.0 API把通常的Jdbc API与扩充API结合起来;J2EE Connectors规范和独立于消息类型的EJB提供了对双向通信的支持。下面给大家重点介绍J2EE1.4中包含的JSP2.0、Servlet2.4、Jdbc3.0以及EJB2.1方面的新特性。

        JSP 2.0属于J2EE 1.4平台,它在JSP 1.2基础之上增加了新的功能。它保证了向下兼容,原先使用的JSP技术在JSP 2.0中都可以支持。JSP 2.0的新功能主要包括下面几部分:

    一)运行环境变化

    1、web.xml格式变化

    我们知道JSP 1.2可以在Java 2标准版1.3版本运行,而JSP 2.0要求使用Java 2标准版1.4或更新版本,JSP 2.0使用由Servlet 2.4规定的Web程序部署描述格式。

    在Web程序描述文件web.xml中需要使用xml schema打头的格式。在web.xml中主要的变化是所有有关JSP的设置信息要放在<jsp-config>标记中。下面程序例1显示了一个web.xml大致的样子。

        例1:

        <?xml version="1.0" encoding="IS0-8859-1"?>

        <web-app xmlns="http://java.sun.com/xml/ns/j2ee"

        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-app_2_4.xsd"

        version="2.4">

        .......

        <jsp-config>

        <taglib>   

        <taglib-uri>

        http://www.icconcept.com/ics/sample-taglib

        </taglib-uri>

        <taglib-location>

        /WEB-INF/jsp/sample-taglib.tld

        </taglib-location>

        </taglib>

        ........

        <jsp-property-group>

        <description>

        For config the ICW sample application

        </description>

        <display-name>JSPConfiguration</display-name>

        <url-pattern>/jsp/datareset.jsp</url-pattern>

        <el-ignored>true</el-ignored>

        <page-encoding>ISO-8859-1</page-encoding>

        <scripting-invalid>true</scripting-invalid>

        <include-prelude>/jsp/prelude.jspf</include-prelude>

        <include-coda>/jsp/coda.jspf</include-coda>

        </jsp-property-group>

        </jsp-config>

        </web-app>

    2、JSP设置
       在<jsp-config>标记提供了Web程序中JSP程序的设置信息。<jsp-config>包括<taglib>和<jsp-property-group>两类元素。<taglib>定义了Web程序使用的custom tag,它的用法和以前JSP 1.2中的用法相同。<jsp-property-group>定义了一组JSP的特性。这些特性实际上对应JSP的page directive定义的特性。通过<jsp-property-group>只不过可以方便地对多个具有相同属性的JSP统一定义。
        <jsp-property-group>定义一个或多个URL样式,在<jsp-property-group>中定义的属性会适用于匹配这些URL样式的所有JSP文件。在<jsp-property-group>中的属性可以定义以下设置:
        (1)允许或禁止使用表达式语言(EL)
        在<jsp-property-group>中可以设定是否允许对应<url-pattern>的JSP使用JSTL表达式语言(EL)。如果<el-ignored>属性标记被设定为false,JSP中的EL表达式会被处理;如果是true,Web容器会在转换JSP时忽略EL表达式。
        (2)允许或禁止使用scripting
        <scripting-invalid>属性可以允许或禁止使用JSP的脚本语言(scripting)。如果这个属性标记对应为true,即scripting元素被禁止,则JSP中不能使用scriptlet,scripting表达式和declaration,否则会有转换错误。当这个属性标记为false时,JSP可以像在1.2版本之前那样使用脚本语言。
        (3)声明JSP编码
        通过<page-encoding>标记可以设置对应<url-pattern>的JSP网页的编码。这个属性对应每个JSP中的pageEncoding属性,Web容器将根据这个属性对JSP内容进行编码。
        (4)对应隐含包括(Implicit Includes)
        在<jsp-property-group>中可以在对应JSP中加入抬头(preludes)和结尾(coda),使用<include-prelude>和<include-coda>属性可以设定在JSP网页中包括的preludes和coda的jspf文件。这些文件的位置相对于当前Web程序的context。当有超过一个preludes或coda元素在<jsp-property-group>中时,JSP会按照其顺序加入到内容中。
    二)引入表达式语言(EL)

    JSP 2.0的一个主要特点是它支持表达语言(expression language)。JSTL表达式语言可以使用标记格式方便地访问JSP的隐含对象和JavaBeans组件,JSTL的核心标记提供了流程和循环控制功能。自制标记也有自定义函数的功能,因此基本上所有seriptlet能实现的功能都可以由JSP替代。在JSP 2.0中,建议尽量使用EL而使JSP的格式更一致。

    在web.xml的<jsp-property-group>中可以控制一组JSP是否使用EL,在每个JSP中也可以指定是否该JSP使用EL。在page directive中的isELIgnored属性用来指定是否忽略。格式为:

      <%@ page isELIgnored="true|false"%>

    如果设定为真,那么JSP中的表达式被当成字符串处理。比如下面这个表达式<p>${2000 % 20}</p>在isELIgnored="true"时输出为${2000 % 20},而isELIgnored="false"时输出为100。Web容器默认isELIgnored="false"。

     虽然JSP 2.0可以使JSP中完全使用表达语言而避免scriptlet,在实际编程中,应该根据程序的功能要求和编程人员的自身条件选择合适的方式。使用表达语言的JSP比较方便规整,但是由于需要将标记进行转换,在第一次被调用时会比较慢;有些编程人员由于对Java比较了解,因而更习惯JSP 1.2之前的编程方式,因此,在使用中应因地制宜地选择适用的编程方法。   
     
    三)SimpleTag

    JSP 2.0中加入了新的创建自制标记的API,javax.servlet.jsp.tagext.SimpleTag定义了用来实现简单标记的接口。和JSP 1.2中的已有接口不同的是,SimpleTag接口不使用doStartTag()和doEndTag()方法,而提供了一个简单的doTag()方法。这个方法在调用该标记时只被使用一次。而需要在一个自制标记中实现的所有逻辑过程、循环和对标记体的评估等都在这个方法中实现。从这个方面来讲,SimpleTag和IterationTag可以达到同等的作用。但SimpleTag的方法和处理周期要简单得多。在SimpleTag中还有用来设置JSP内容的seUspBody()和getJspBody()方法。Web容器会使用setJspBody()方法定义一个代表JSP内容的JspFragment对象。实现SimpleTag标记的程序可以在doTag方法中根据需要多次调用getJspBody().invoke()方法以处理JSP内容。

     例如,程序例2 SimpleTag根据指定的次数(times)进行循环并输出当前序号(sequence)。程序的结构比较简单,所有逻辑都在doTag方法中实现。

    例2:

      packageICW.taglib;

      importjavax.servlet.jsp.JspException;

      importjavax.servlet.jsp.tagext.SimpleTagSupport;

      importjava.util.HashMap;

      importjava.io.IOException;

      public class IterationSimpleTag extends SimpleTagSupport{

      privateint times;

    public void setTimes(int_times){

        this.times=_times;

            }

        public void doTag() throws JspException,IOException{

        HashMapparams=new HashMap();

        for(inti=0; i<times;i++){

        params.put("sequence",String.valueOf(i+1));

        getJspBody().invoke(null,params);

          }

         }

        }
     
    这个标记的TLD文件内容如下,它使用了XML schcma定义标记的使用方法。

        程序例3如下:

        <?xml version="1.0" encoding="UTF-8" ?>

        <taglibxmlns="http://java.sun.com/xml/ns/i2ee"

        xmlns:xsi="http://WWW.w3.org/2001/XMLSchema-instance"

        xsl:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglihrary_2_0.xsd"

        version="2.0">

      <taglib>

        <tiib-version>1.0</tlib-version>

        <short-name>Jwad book simple tag</short-name>

        <uri>/JwadSimpleTag</uri>

        <description>Simple Tag Handler</description>

        <tag>

        <name>iteration</name>

        <tag-class>ICW.taglib.IterationSimpleTag</tag-class>

        <body-content>scriptless</body-content>

        <description>Iteration Tag</description>

        <variable>

        <description>Current iterationnumber</description>

        <name-given>sequence</name—given>

        </variable>

        <attribute>

        <name>times</name>

        <required>true</required>

        <rtexprvalue>true</rtexprvalue>

        </attribute>

        </tag>

       </taglib>
     

    程序例4中的JSP使用上面例3中定义的IterationSimpleTag,它根据Web请求参数中给定的“times”的值进行一定次数的循环。在每次循环中将输出"sequence"的值。

        例4:

        <%@ taglib prefix="ictag" uri="/WEB-INF/ics-jsp2.tld" %>

        <HTML><HEAD><TITLE>Simple Tag Sample</TITLE></HEAD>

        <BODY>

        <CENTER>

        <FONT COLOR='#009999' SIZE='4' face='Arial'>

        <STRONG>Interation Simple Tag</STRONG>

        </FONT>

        </CENTER>

        <HR>

        <c:setvar="time" value="$"/>

        <p><B>Reminder:</B></p><br>

        <ictag:iteration times="$">

        This is the $ Of $ times of reminder<br>

        </ictag:iteration>

        </body>

        </html>
     
    四)使用JSP fragment

     JSP 2.0中的一个主要功能是JSP fragment,它的基本特点是可以使处理JSP的容器推迟评估JSP标记属性。我们知道一般JSP是首先评估JSP标记的属性,然后在处理JSP标记时使用这些属性,而JSP fragment提供了动态的属性。也就是说,这些属性在JSP处理其标记体时是可以被改变的。JSP需要将这样的属性定义为javax.servlet.jsp.tagext.JspFragment类型。当JSP标记设置成这种形式时,这种标记属性实际上的处理方法类似于标记体。在实现标记的程序中,标记属性可以被反复评估多次。这种用法称为JSP fragment。JSP fragment还可以定义在一个SimpleTag处理程序中使用的自制标记动作。像前面例子说明的,getJspBody返回一个JspFragment对象并可以在doTag方法中多次使用。需要注意的是,使用JSP fragment的JSP只能有一般的文本和JSP action,不能有scriptlet和scriptlet表达式。

      我们可以简单地认为JSP fragment是一段可以重复使用的JSP。一段JSP fragment可以被传递到另一个JSP中并被使用。与JSP include概念不同的是,JSP fragment一般功能比较短小简单而且重复使用率高。

      JSP fragment一般在<jsp:attribute>标记体内或<jspcbody>标记体内定义。每次当含有JSP fragment的标记被使用时,Web容器生成一个JspFragment对象,这个对象和当前JSP的page scope联系起来。同时,这个JspFragment对象和包含它的父标记建立联系。JspFragment对象可以有两种调用方式:使用Java编写的标记处理程序,或者使用标记文件(tag file)。标记文件可以使用<jsp:invoke>,或者<jsp:doBody>动作使用JSP fragment。  JSP标记文件可以和JSP fragment共同工作。CustomTag都是以编程方式实现的。JSP标记文件是用文本文件格式(JSP语法)实现自制标记,这也是JSP 2.0的一个主要新功能。一个标记文件可以对应一个标记,可以不需tld文件定义该标记的方法。这样,即使编程人员不熟悉Java,也可以使用JSP语法定义自己的标记。标记文件一般使用.tag为后缀并放在Web程序的/WEB-INF目录下。

      程序例5中的taskstatus.jsp使用了两个JSP fragment。这个JSP的功能是显示一组Task的名称和完成日期,它通过<jsp:attribute name="...">定义了两段JSPfragment(名称为onSehedule和delayed)。在<jsp:attribute>标记内的JSP就是JSPfragment,而<jsp:attribute>标记被一个<ietag:listTasks>包围。这个标记是一个通过标记文件定义的自制标记,它的定义文件在/WEB-INF/tags目录下。标记文件的名称和标记名称一致为“listTasks.tag"。这个标记会使用到前面定义的两个JSP fragment。
     
     例5:

      <%@ taglib prefix="ictag" tagdir="/WEB-INF/tags" %>

      <HTML><HEAD><TITLE>JSP Fragment Sample</TITLE></HEAD>

      <BODY>

        <CENTER>

        <FONT COLOR='#009999' SIZE='4' face='Arial'>

        <STRONG>JSP Fragment Sample Using Tag Files</STRONG>

        </FONT>

        </CENTER>

        <HR>

        <h2>Tasks</h2>

        <ietag:listTasks>

        <jsp:attribute name="onSchedule">

        <td>

        Name:$<br/>

        </td><td>

        Date:$

        </td>

        </jsp:attribute>

        <jsp:attribute name="delayed">

        <td>

        Name:$<br/>

        </td><td>

        <font color="red">Plan:<strike> $</strike></font><br/>

        <b>Actural:$</b>

        </td>

        </jsp:attribute>

        </ictag:listTasks>

        </BODY>

        </HTML>
     
    五)其他特性

     JSP2.0还有一些其他特性变化,比如严格修正了I18N的语法规则,改进JSP对应XML语法从而允许使用namespaces等。这些并不是核心功能,大家可以参照java.sun.com的有关资料了解功能的细节,这里就不再阐述。

    posted @ 2007-07-02 12:32 和田雨 阅读(208) | 评论 (0)编辑 收藏

    标签库Taglib

    标签被定义和分布在一个称为标签库的结构中,一个标签库是由元信息和类组成的集合:

    1.标签处理器:实现定制标签功能的Java类。

    2.标签附加信息(TEI):向JSP容器提供边辑以确认标签属性和创建变量的类。

    3.标签库描述器(TLD):描述单个标签和整个标签库属性的XML文档。

    标签处理器和标签附加信息需要定位在JSP容器类载入器可以找到的地方。标签库描述器可在URL指定的符意位置。JSP1.1规范要求JSP容器接受一个打包成因定结构的JAR文件的标签库。TLD必须是/META-INF目录中名为taglib.tld的文件,JAR文件则复制到/WEB-INF/lib目录下。

    一、标签实现

    1.开发步骤

    a.定义标签的名字、属性、声明的变量和标签体的内容。

    b.编写标签库描述器TLD。

    c.编写标签处理器。

    d.在JSP页面中使用标签。

    2.JSP页面在JSP容器中的转换步骤:

    JSP页面存在三种形式:jsp文件、java文件和class文件。

    a.指令元素<%@page%>、<%@include%>和<%@taglib%>向JSP容器提供转换时信息。

    b.HTML行在_jspService()方法中依顺序转换到out.print()语名中。

    c.脚本元素的声明被原封不动地复制到_jspService()方法外的源码中。

    d.脚本元素的表达式在_jspService()方法中依顺序转换到out.print()语名中。

    e.脚本元素的Scriptlet被原封不动地复制到_jspService()方法中。

    f.行为元素被转换为执行其功能的运行时逻辑代码。

    g.定制标签被扩展到调用其相应标签处理器中方法的Java语句中。

    3.标签在JSP容器中的转换步骤:

    a.JSP容器使用taglib指令元素定位标签库描述器,将页面中用到的定制标签和TLD相匹配。

    b.读取标签库描述器的标签列表和每一标签相关的类名字。

    c.在页面中遇到一个标签时,查找与具有指定名字的标签前缀相关的一个标签库。

    d.容器使用在TLD中找到的标签结构信息生成一系列完成标签功能的Java语句。

    二、标签库描述器(TLD)

    标签库描述器是一个描述整个标签库标记信息和库中每个标签处理器及其属性的XML文档。

    标签库描述器的DTD由一个简单的元素组成,此元素包含下列一些子元素。

    整个标签库标记信息

    tlibversion标签库版本号。是一个点式十进制数,最多为4组小数点分隔的数字组成。

    jspversion标签库所需的JSP规范最低版本。例如JSP1.1

    shortname标签库的缩写名。JSP可以使用该名字作为库中标签的缺省前缀。

    uri标签库唯一URI的元素。典型URL位置来自可下载taglib的位置。

    info标签库描述信息。

    每个标签处理器及其属性

    tag在TLD中加入标签,描述组成库的每个标签。

    name与标签库的名字前缀一起使用的标签的名字,是JSP容器唯一的标签标识。

    tagclass实现标签的标签处理器类的全名。

    teiclass标签附加信息(TEI)类的全名。TEI类给出关于标签处理器创建变量及对标签司性执行的任意有效性验证的信息。

    bodycontent描述标签处理器如何使用标签体的内容。有三种取值:

    empty:表示标签体必须为空;

    JSP:表示脚本元素和模板及其它标签一样被评估。

    tagdependent:体内容被原封不动写入BodyContent,其它脚本元素以源码形式出现,而不被JSP容器解释。

    info标签的人工可读描述性信息。

    attribute使用标签时被编码的属性信息。用于定义标签的属性。

    属性名:属性的名字。

    true|false:属性在标签用到的位置是否要被编码。

    true|false:属性值能否用表达式指定。

    三、标签处理器

    标签处理器是通过实现JSP容器调用的一系列预定义方法执行定制标签行为的一个Java类。

    标签处理器实现了标签的行为,标签处理器是Java类。

    1.标签处理器的工作方式

    a.导入javax.servlet.jsp和javax.servlet.jsp.tagext包。

    b.实现javax.servlet.jsp.tagext包中的Tag接口或BodyTag接口。BodyTag是Tag的子接口。

    c.继承TagSupport类或BodyTagSuppoert类。它们是上述接口的缺省实现。

    d.重载publicintdoStartTag()throwsJspException方法。

    2.标签处理器的接口与实现

    javax.servlet.jsp.tagext.Tag是实现标签的最基本的接口。

    javax.servlet.jsp.tagext.TagSupport是实现Tag接口的具体类。

    通常情况下继承tagSupport类而不直接实现Tag接口通常是有益的。除了对所有必需方法提供了缺省实现外、还保存了pageContext对象及对嵌套标签的支持。

    Tag接口包含4个常量,表示doStartTag()和doEndTag()方法可能的返回码。

    EVAL_BODY_INCLUDE当doStartTag()返回时,指明servlet应对标签体进行评估。

    SKIP_BODY当doStartTag()返回时,指明servlet应忽视标签体。

    EVAL_PAGE当doEndTag()返回时,指明页面其余部分应被评估。

    SKIP_PAGE当doEndTag()返回时,指明页面其余部分就被跳过。

    Tag接口的方法

    publicvoidsetPageContext(PageContextctx)生成的servlet在请求处理器执行其它任务前首先调用此方法,实现类应保存上下文对象以便它可以在标签生命期中使用。从页面上下文中标签处理器可以访问所有JSP隐含对象。

    publicvoidsetParent(Tagp)使用一个标答可以找到操作栈中它上面的标签。在setPageContext后立即调用。

    publicTaggetParent()返回父标签。

    publicintdoStartTag()throwsJsp在设置了页面上下文、父标签和开始标记中编码的属性后调用。返回码表明JSP实现servlet是否就评估标签体。

    publicintdoEndTag()throwsJspException当遇到结否标记时调用。返回码表明JSP是否就继纽页面的其余部份。

    publicvoidrelease()确保在页面退出前被调用。释放资源并重置标签处理器状态。

    TagSupport类的方法

    publicstaticTagfinAncestorWithClass(TagthisTag,Classcls)为所需的父标签处理器查找运行时标签栈。一个标签处理器可以提供其范围内子标签调用的方法。

    publicvoidsetId(Stringid)保存和检索在id属性中指定的名字。

    publicvoidsetValue(Stringname,Objecto)在本地哈希表中设置指定名字的值。

    publicObjectgetValue(Stringname)从本地哈希表中获取指定名称的值。

    publicvoidremoveValue(Stringname)从本地哈希表中删除指定名称的值。

    publicEnumerationgetValues()返回哈希表中关键字的一个枚举。

    3.标签处理器的生命期

    a.生成servlet需要创建标签处理器类的一个实例。实现方式通常是调用JSP容器的工厂类的一个方法,工厂类包含一个标签处理器实例池以使其可重用不再处于激活状态的对象。

    b.初始化标签处理器,使servlet获知其存在性。servlet通过调用标签处理器的两个方法实现此过程:setPageContext(PageContextctx)和setParent(Tagparent)。

    c.如果标签具有属性,属性的取值通过处理器提供setter方法传入到对象。属性setter方法是一个标签支持属性所需的唯一方法。

    d.页面的上下文和父标签已被调置,并已具备属性。此时调用标签处理器的doStartTag()方法,该方法可以读取这些变量并执行实现标答功能所需的计算和操作。doStartTag()方法必须返回一个整型数。返回EVAL_BODY_INCLUDE则正常处理标签体,返回SKIP_BODY则从初始JSP页面中直到此标签结束标记处的内容均被忽略。

    e.标签体被评估或忽视后调用标签处理器的doEndTag()方法,返回EVAL_PAGE则页面的其余部分被评估,返回SKIP_PAGE则servlet代码立即从_jspService()中返回。

    4.体标签处理器的接口与实现

    javax.servlet.jsp.tagext.BodyTag是Tag的子接口。

    javax.servlet.jsp.tagext.BodyTagSupport是实现BodyTag类。

    BodyContent是javax.servlet.jsp.JspWriter的子类,但与其父类有所区别。

    BodyContent对象的内容不自动写了入servlet的输出流,而是积累在一字符串缓存中。当标签体完成后其对象仍可在doEndTag()方法中可以应用,由getString()或getReader()方法操作。并在必要时修改及写入恢复的JspWriter输出流。

    BodyContent类的方法

    publicvoidflush()throwsIOException复写JspWrite.flush()方法以便它总是产生溢出。刷新写入已失效,因为它没有连接到将被写入的实际输出流中。

    publicvoidclearBody()重置BodyContent缓存为空。

    publicReadergetReader()返回Reader读取体内容。

    publicStringgetString()返回包含体内容的一个字符串。

    publicvoidwriteOut(Writew)将体内容写入指定输出。

    publicJspWritegetEnclosingWrite()返回栈中下一个更高的写入者对象(可能是另一个BodyContent对象)。

    BodyTag接口定义了一个新的整型常量

    EVAL_BODY_TAG当doStartTag()返回时,使得新的BodyContent对象被创建并与此标签处理器相关联。当doAfterBody()返回时,使得JSPservlet在修改完此标签控制的任意变量后再次评估体。

    BodyTag接口的方法

    publicvoidsetBodyContern(BodyContentout)在当前JspWriter已被写入,一个新的BodyContent在被创建后由Jspservlet调用,它发生在doStartTag()之后。

    publicvoiddoInitBody()throwsJspExceptionsetBodyContent()之后,体被评估前调用的生命期方法。如果多次评估体,此方法只调用一次。

    publicinitdoAfterBody()throwsJspException体被评估后,BodyContent写入者仍处于激活状态时调用的生命期方法。此方法必须返回EVAL_BODY_TAG或SKIP_BODY,若返回EVAL_BODY_TAG时体再次被评估。

    BodyTagSupport类的方法

    publicintdoStartTag()throwsJspException复写TagSupport中的doStartTag()方法。

    publicintdoEndTag()throwsJspException调用TagSupport中的doEndTag()方法,返回结果。

    publicvoidsetBodyContent(BodyContentout)在一保护成员变量bodyContent中保存新的体内容对象,子类可直接访问此对象。

    publicvoiddoInitBody()throwsJspException缺省什么都不做。被需要执行初始化的子类所复写。

    publicintdoAfterBody()throwsJspException每次体被评估后由JSPservlet调用,体同容对象仍处于激活状态。返回SKEP_BODY或EVAL_BODY_TAG则体再次被评估

    publicvoidrelease()设置bodyContent对象为null,然后调用super.release()。

    publicBodyContentgetBodyContent()返回bodyContent变量。子类已经可以访问保护变量,但此方法允许无关的标签处理类对此体内容发送输出。

    publicJspWritergetPreviousOut()在bodyContent变量上调用getEnclosingWriter()并返回结果的简便方法。

    5.体标签处理器的生命期

    a.生成servlet需要创建标签处理器类的一个实例。实现方式通常是调用JSP容器的工厂类的一个方法,工厂类包含一个标签处理器实例池以使其可重用不再处于激活状态的对象。

    b.初始化标签处理器,使servlet获知其存在性。servlet通过调用标签处理器的两个方法实现此过程:setPageContext(PageContextctx)和setParent(Tagparent)。

    c.如果标签具有属性,属性的取值通过处理器提供setter方法传入到对象。属性setter方法是一个标签支持属性所需的唯一方法。

    d.页面的上下文和父标签已被调置,并已具备属性。调用标签处理器的doStartTag()方法,该方法可以读取这些变量并执行实现标答功能所需的计算和操作。

    doStartTag()方法必须返回一个整型数。

    返回EVAL_BODY_TAG则正常处理标签体(跳到e);

    返回SKIP_BODY则从初始JSP页面中直到此标签结束标记处的内容均被忽略。(跳到f)

    e.如果返回EVAL_BODY_TAG时,则正常处理标签体。

    e1.在栈中保存当前的JspWriter对象,创建新的BodyContent对象,并将其置为JSP页面的out对象保存在上下文范围内名为name的属性中。并调用它的setBodyContent()方法。

    e2.调用doInitBody()方法进行初始化。

    e3.处理标签体。将输出写入BodyContent对象中,此过程依赖于TLD的标签元素 ,有三种可能取值。

    e4.调用doAfterBody()方法,将体内体内容写入JspWriter,可如下实现:

    JspWriterout=bodyContent.getEnclosingWriter();

    out.println(bodyContent.getString());//bodyContent.writeOut(out);

    bodyContent.clear();

    e5.doAfterBody()方法返回两种可能:

    返回EVAL_BODY_TAG时,再对标签体进行评估,这是数组和枚举被循环处理的典型情况。

    返回SKIP_PAGE时,继续页面的其余部份。

    e6.体内容完成,因此创建它的过程被反向:

    调用pageContent.popBody()方法检索前面的JspWriter对象。

    将写入者设置回out隐含对象。

    f.标签体被评估或忽视后调用doEndTag()方法,允许标签处理器像输出流发回内容。

    返回EVAL_PAGE则页面的其余部分被评估;

    返回SKIP_PAGE则servlet代码立即从_jspService()中返回。

    g.此时体的内容在受保护的bodyContent对象中仍然可用。

    可以将它写入servlet输出流中:

    JspWriterout=pageContext.getOut();

    out.println(bodyContent.getString());

    或者

    bodyContent.WriteOut(pageContext.getOut());

    6.标签附加信息类

    四、标签指令

    taglib指令元素的目的是指定TLD的位置,设置在页面上与标签区分开来的一个短别名。

    语法:<%@taglibprefix=”tagprefix”uri=”tagliburi”%>

    属性:prefix:用于标识标签库的唯一标识。uri:标签库本身的URI。

    uri不必指向一个实际文件,它是JSP容器可以在web.xml中查找实际文件位置的唯一标识符。

    posted @ 2007-07-02 12:29 和田雨 阅读(191) | 评论 (0)编辑 收藏

    Servlet API 很久以前就已成为企业应用开发的基石,而 Servlet 过滤器则是对 J2EE 家族的相对较新的补充。本文将向您介绍 Servlet 过滤器体系结构,定义过滤器的许多应用,并指导您完成典型过滤器实现的三个步骤。同时本文还会透露bean 的一些激动人心的变化,预计刚发布的 Java Servlet 2.4规范会引入这些变化。

      Servlet 过滤器是可插入的 Web 组件,它允许我们实现 Web 应用程序中的预处理和后期处理逻辑。过滤器支持 servlet 和 JSP 页面的基本请求处理功能,比如日志记录、性能、安全、会话处理、XSLT 转换,等等。 过滤器最初是随 Java Servlet 2.3 规范发布的,最近定稿的 2.4 规范对它进行了重大升级。在此我将向您介绍 Servlet 过滤器的基础知识 ―― 比如总体的体系结构设计、实现细节,以及在 J2EE Web 应用程序中的典型应用,还会涉及一些预计最新的 Servlet 规范将会提供的扩展功能。

      Servlet 过滤器是什么?

      Servlet 过滤器是小型的 Web 组件,它们拦截请求和响应,以便查看、提取或以某种方式操作正在客户机和服务器之间交换的数据。过滤器是通常封装了一些功能的 Web 组件,这些功能虽然很重要,但是对于处理客户机请求或发送响应来说不是决定性的。典型的例子包括记录关于请求和响应的数据、处理安全协议、管理会话属性,等等。过滤器提供一种面向对象的模块化机制,用以将公共任务封装到可插入的组件中,这些组件通过一个配置文件来声明,并动态地处理。

      Servlet 过滤器中结合了许多元素,从而使得过滤器成为独特、强大和模块化的 Web 组件。也就是说,Servlet 过滤器是:

    • 声明式的:过滤器通过 Web 部署描述符(web.xml)中的 XML 标签来声明。这样允许添加和删除过滤器,而无需改动任何应用程序代码或 JSP 页面。
    • 动态的:过滤器在运行时由 Servlet 容器调用来拦截和处理请求和响应。
    • 灵活的:过滤器在 Web 处理环境中的应用很广泛,涵盖诸如日志记录和安全等许多最公共的辅助任务。过滤器还是灵活的,因为它们可用于对来自客户机的直接调用执行预处理和后期处理,以及处理在防火墙之后的 Web 组件之间调度的请求。最后,可以将过滤器链接起来以提供必需的功能。
    • 模块化的:通过把应用程序处理逻辑封装到单个类文件中,过滤器从而定义了可容易地从请求/响应链中添加或删除的模块化单元。
    • 可移植的:与 Java 平台的其他许多方面一样,Servlet 过滤器是跨平台和跨容器可移植的,从而进一步支持了 Servler 过滤器的模块化和可重用本质。
    • 可重用的:归功于过滤器实现类的模块化设计,以及声明式的过滤器配置方式,过滤器可以容易地跨越不同的项目和应用程序使用。
    • 透明的:在请求/响应链中包括过滤器,这种设计是为了补充(而不是以任何方式替代)servlet 或 JSP 页面提供的核心处理。因而,过滤器可以根据需要添加或删除,而不会破坏 servlet 或 JSP 页面。

      所以 Servlet 过滤器是通过一个配置文件来灵活声明的模块化可重用组件。过滤器动态地处理传入的请求和传出的响应,并且无需修改应用程序代码就可以透明地添加或删除它们。最后,过滤器独立于任何平台或者 Servlet 容器,从而允许将它们容易地部署到任何相容的 J2EE 环境中。

      在接下来的几小节中,我们将进一步考察 Servlet 过滤器机制的总体设计,以及实现、配置和部署过滤器所涉及的步骤。我们还将探讨 Servlet 过滤器的一些实际应用,最后简要考察一下模型-视图-控制器(MVC)体系结构中包含的 Servlet 过滤器,从而结束本文的讨论。

      Servlet 过滤器体系结构

      正如其名称所暗示的, Servlet 过滤器用于拦截传入的请求和/或传出的响应,并监视、修改或以某种方式处理正在通过的数据流。过滤器是自包含、模块化的组件,可以将它们添加到请求/响应链中,或者在无需影响应用程序中其他 Web 组件的情况下删除它们。过滤器仅只是改动请求和响应的运行时处理,因而不应该将它们直接嵌入 Web 应用程序框架,除非是通过 Servlet API 中良好定义的标准接口来实现。

      Web 资源可以配置为没有过滤器与之关联(这是默认情况)、与单个过滤器关联(这是典型情况),甚至是与一个过滤器链相关联。那么过滤器究竟做什么呢? 像 servlet 一样,它接受请求并响应对象。然后过滤器会检查请求对象,并决定将该请求转发给链中的下一个组件,或者中止该请求并直接向客户机发回一个响应。如果请求被转发了,它将被传递给链中的下一个资源(另一个过滤器、servlet 或 JSP 页面)。在这个请求设法通过过滤器链并被服务器处理之后,一个响应将以相反的顺序通过该链发送回去。这样就给每个过滤器都提供了根据需要处理响应对象的机会。

      当过滤器在 Servlet 2.3 规范中首次引入时,它们只能过滤 Web 客户机和客户机所访问的指定 Web 资源之间的内容。如果该资源然后将请求调度给其他 Web 资源,那就不能向幕后委托的任何请求应用过滤器。2.4 规范消除了这个限制。Servlet 过滤器现在可以应用于 J2EE Web 环境中存在请求和响应对象的任何地方。因此,Servlet 过滤器可以应用在客户机和 servlet 之间、servlet 和 servlet 或 JSP 页面之间,以及所包括的每个 JSP 页面之间。这才是我所称的强大能力和灵活性!

    posted @ 2007-07-02 12:28 和田雨 阅读(183) | 评论 (0)编辑 收藏

    常用的设定,详细请看:http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd
    XML声明,定义XML的版本、编码格式。还有最重要的schema的来源
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
      .......
    </web-app>
     
    <description>
    当servlet的URL定义为其他文件类型的扩展名,该文件类型将不能访问,而访问了servlet
    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd" version="2.4">
      <display-name>myweb</display-name><!--站点名称-->
      <description>这里是站点描述</description>
      <icon>
        <small-icon>/images/small.gif</small-icon><!--小图标的路径16*16,必须为gif jpge格式-->
        <large-icon>/images/large.gif</large-icon><!--大图标的路径32*32,必须为gif jpge格式-->
      </icon>
      <!--如果存在<distributable/>则代表该站点能在多个JSP Container之间分散执行(分布式)-->
      <distributable/>




      <context-param><!--环境参数,设置常量,取得常量  this.getInitParameter("context");-->
        <param-name>context</param-name>
        <param-value>10000</param-value>
      </context-param>
      <filter><!--声明filter-->

        <filter-name>filterName</filter-name>
        <filter-class>filter.filterName</filter-class>
        <init-param>
          <param-name>encoding</param-name>
          <param-value>UTF-8</param-value>
        </init-param>
      </filter>
      <filter-mapping><!--定义filter所对应的URL-->
      <filter-name>filterName</filter-name>
      <url-pattern>/filterName</url-pattern>
      <dispatcher>REQUEST|INCLUDE|FORWARD|ERROR</dispatcher><!--filter对应的请求方式,有4种方式,默认REQUEST-->
      </filter-mapping>
      <listener><!--设定Listener接口-->
        <listener-class>listener.ListenerClassName</listener-class>
      </listener>
      <servlet><!--声明servlet的数据-->
      <servlet-name>servletName</servlet-name>
      <servlet-class>servlet.servletName</servlet-class>
      <init-param><!--初始化参数,可以在init()中使用ServletConfig().getInitParamenter("name")取得-->
        <param-name>name</param-name>
        <param-value>123</param-value>
      </init-param>
      <load-on-startup>n</load-on-startup><!--站点启动时,此servlet会被自动加载执行,1表示最早,2,3...-->

      </servlet>
      <servlet-mapping><!--定义servlet所对应的URL-->

        <servlet-name>name</servlet-name>
        <url-pattern>/name</url-pattern>
      </servlet-mapping>
      <session-config><!--设置session超时时间(20分钟)默认按服务器配置-->
        <session-timeout>20</session-timeout>
      </session-config>
      <mime-mapping><!--定义某个扩展名和某个MIME Type做映射-->
        <extension>doc</extension>
        <mime-type>application/vnd.ms-word</mime-type>
      </mime-mapping>
      <welcome-file-list><!--设置首页列表-->
      <welcome-file>index.jsp</welcome-file>
      </welcome-file-list>
      <error-page><!--将错误代码对应到WEB站点的资源路径-->
      <error-code>错误代码</error-code>Exception
      <location>/路径/</location>

      </error-page>
      <error-page><!--将异常的种类对应到WEB站点的资源路径-->
      <exception-type>Exception</exception-type>
      <location>/路径/</location>

      </error-page>

      <jsp-config><!--JSP的相关配置-->
        <taglib><!--JSP所用到的Tag Library-->
          <taglib-uri>URI</taglib-uri>
          <taglib-location>/WEB-INF/lib/xxx.tld</taglib-location>
        </taglib>
        <jsp-property-group>
          <description>此设定的说明</description>
          <display-name>Name</display-name>
          <url-pattern>URL</url-pattern><!--此设定影响的范围-->
          <el-ignored>true|false</el-ignored><!--true表示不支持EL语法-->
          <scripting-invalid>encoding</scripting-invalid><!--jsp的编码-->
          <include-coda>...jspf</include-coda><!--JSP的结尾,扩展名.jspf-->
        </jsp-property-group>
      </jsp-config>
      <resource-ref><!--利用JNDI取得站点可利用的资源-->
        <description>资源说明</description>
        <res-ref-name>资源名称</res-ref-name>
        <res-type>资源种类</res-type>
        <res-auth>Application|Container</res-auth><!--资源经由什么许可-->
        <res-sharing-scope>Shareable|Unshareable</res-sharing-scope><!--资源是否可以共享,默认为Shareable-->
      </resource-ref>
    </web-app>
    posted @ 2007-07-02 12:22 和田雨 阅读(4941) | 评论 (1)编辑 收藏

    2005年9月26日,Sun公司和JSR154的专家组发布Servlet API的一个新的版本。在一般情况下,一个JSR的新版本仅仅包括对以前少数有名无实的规范进行去除更新。但这次,新版本中增加新的特征和变化,他们对Servlets的产生重要影响,使得Servlet的版本升到了 2.5。
    在这篇文章里,我主要谈谈Servlet2.5版本中的新特征。描述每一个变化,阐述那些必要变化产生的背景,并展示如何在基于Servlet的项目中利用这些变化。 
    事实上,这是我为JavaWorld提供的第六篇关于Servlet API更新资料的文章。这篇文章意在两个目的:从眼前来看,向你介绍 Servlet的新特征。从长远来看,是展现Servlet变化的历史概要,这样当你基于老的Servlet API版本进行编码的时候,你可以正确地决定哪些特征和功能你可以使用,而哪些特征和功能你不应该使用。你可以参考我先前写的关于Servlet的文章。
    注意:当你想实践这些Servlet的新特征和功能时,你要知道的是:并不是所有的Servlet容器和Java企业级应用服务器都能立即适用于最新版的Servlet API,写这篇文章时(2006年1月2日),Jetty 6 server和Sun公司的GlassFish server是公认最好的支持Servlet2.5的容器,而Apache Tomcat5.5和Jboss 4.0目前只支持Servlet2.4。
    Servlet2.5一些变化的介绍:
    1)    基于最新的J2SE 5.0开发的。
    2)    支持注释。
    3)    web.xml中的几处配置更加方便。
    4)    去除了少数的限制。
    5)    优化了一些实例
    J2SE 5.0的产物:
    从一开始,Servlet 2.5 规范就列出J2SE 5.0 (JDK 1.5) 作为它最小的平台要求。它使得Servlet2.5只能适用基于J2SE 5.0开发的平台,这个变动意味着所有J2SE5.0的新特性可以保证对Servlet2.5程序员有用。
    传统意义上,Servlet和JEE版本一直与JDK的版本保持同步发展,但是这次,Servlet的版本跳过1.4版本。专家组认为版本的加速增长是正当的,因为J2SE5.0提出一个引人注目的,Servlet和JEE规范都要利用的特征??注释。
    注释:
    注释是作为JSR175的一部分提出的(一种为Java语言设计提供便利的Metadata)一种新的语言特色。它是利用Metadata为Java 编码结构(类,方法,域等等)装饰的一种机制。它不能像代码那样执行,但是可以用于标记代码,这个过程是基于Metadata信息的代码处理机,通过更新他们的事件行为来实现的。

    我们可以凭借不同的技巧来注释类和方法,例如连续地标记接口或者是@deprecated Javadoc评论。这种新式的Metadata可以便利地提供了一种标准的机制来实现注释功能,以及通过库来创建用户自己的注释类型的变量。
    下面是一个简单的Web service 注释例子:
    import javax.jws.WebService;
    import javax.jws.WebMethod;
    @WebService
    public class HelloWorldService {
      @WebMethod
      public String helloWorld() {
        return "Hello World!";
      }
    }
    @WebService和@WebMethod这两个注释类型,在JSR181(为Java平台提供的Web ServicesMetadata)有详细说明,可以像类一样的引用,标记这个类作为一个Web service并且标记它的helloWorld()方法做为一个Web service方法。对于他们本身来说,注释只是写在那里并没有什么作用,好像在岗位上做记录一样,但是,一个容器一旦加载这个类并对那些注释进行二进制编码,就可以把这个类连到Web service上。
    注释可以接受属性/值这些参数。它保存着参数的信息并且可以利用这些参数来更改被请求的事件行为。例如下面更高级的注释例子:
    @WebService(
     name = "PingService",
      targetNamespace=http://acme.com/ping
    )
    @SOAPBinding(
      style=SOAPBinding.Style.RPC,
      use=SOAPBinding.Use.LITERAL
    )
    public class Ping {
      @WebMethod(operationName = "Foo")
      public void foo() { }
    }
    一旦加载了这个类,一个正确配置的容器就会识别出注释及其参数,并将这个做为一个PingService通过利用remote-procedure- call/literal的编码方式与一个Foo operation相连。实际上,注释便指明了类和类的容器之间的联系。

    Java本身的规范(JSR175)仅仅有少量的注释类型变量。而这些有趣的注释类型变量主要来自于其他的JSRs:
    "JSR 250: Java平台的公共注释
    "JSR 220: 企业级JavaBeans 3.0 
    "JSR 224: 基于XML的Java API Web Services (JAX-WS) 2.0 
    "JSR 181: Java平台的Web Services Metadata 
    Servlet2.5中的注释:
    回到Servlet2.5上来,一种新的规范描述了几种注释在Servlet环境中是如何工作的。功能弱的Servlet容器忽略了这些规范,然而JEE容器中的Servlet却严格遵守这些规范。

    有的注释提供了在XML注册的可选择性,否则就要注册在配置文件web.xml中。有的作为容器的请求来执行其任务,否则就由Servlet亲自来执行。还有的注释两者都具备。

    注释准确的定义不是完全固定的,因为Servlet本身并没有定义注释。它仅仅解释了它们如何影响Servlet环境,下面是注释的一个简要的概述,你可以看到在JEE5中它们的用途:

    "@Resource and @Resources:@Resource位于类或变量中以对Servlet容器进行“资源注入”。当容器识别出这个注释时,它会在获得服务地位之前,用适当的值实现带注释的变量的重新注入。通过使用这种注释,你不必利用JNDI来查找命令和在配置文件web.xml中手动声明资源。服务器通过Servlet的自我调整来执行它的任务。变量的名称和类型由映像机制自动确定,尽管你可以利用注释的参数来超越这一限制。一个注入的资源可以是数据源,Java信息服务目的文件或者是环境设置的标量。下面是一个例子:
    @Resource javax.sql.DataSource catalog;
    public getData() {
      Connection con = catalog.getConnection();
    }
    现在,在这段Servlet代码变成服务之前,容器会定位JNDI变量,并对于目录变量进行手动分配。
    为了效率,仅仅某些类支持资源注入,这些类有:Servlets,Servlet过滤器,Servlet事件监听器,JSP标签操作器,JSP库事件监听器,用于处理beans的JSF,以及一些与Serlvets无关的类。
    "@Resources注释与@Resource相似,但是它用于一组@Resource注释。它们都来自JSR250,是Java平台的公共注释。
    "@PostConstruct and @PreDestroy:可以使方法成为带有生命周期的方法。@PostConstruct方法用于资源注入初始化之后。@PreDestroy方法用于Servlet脱离服务并释放注入的资源的时候。回收的方法必须是事实的方法,返回void并且不可以抛出任何异常。这些注释本质上使得任何方法都成为init()和destroy()的子方法,这些特征也来自与JSR250。
    "@EJB:类似于@Resource,设计用于注入企业级的JavaBeans。比起@Resource,它略有不同,在于@EJB的参数特定设计用来定位EJB的参数。这个注释来自EJB3.0的规范。
    "@WebServiceRef:与@Resource 和 @EJB相似,设计用于注入Web service参数。来自于JAX-WS2.0规范。
    "@PersistenceContext, @PersistenceContexts, @PersistenceUnit, and @PersistenceUnits:这些注释来自EJB3.0规范来支持Java对象的持久化。
    "@DeclareRoles: 定义应用程序中安全角色的使用。当定义一个Servlet类时,在配置文件web.xml中<security-role>标签中对它进行设置,来自JSR250。
    " @RunAs:用于声明哪个类应该执行。当定义一个Servlet类时,在配置文件web.xml中<run-as>标签中对它进行设置。来自于JSR250。

    注释的执行:
    不论你使用注释与否??尤其在你不使用时??它对于理解服务器上程序的执行有着重要意义。为了让服务器识别类中的注释,它必须加载这些类,这就意味着服务器必须是启动着的,服务器通过WEB-INF/classes目录下和WEB-INF/lib目录下的所有类文件来查找注释。(每个规范下,服务器不必查找这两个目录以外的目录。)你可以通过下面的方法指明<web-app>根的属性而不必使用如何进行注释:
    <web-app xmlns="http://java.sun.com/xml/ns/javaee"
                 version="2.5" full="true">
    </web-app>
    web.xml的便利:
    Servlet2.5对于web.xml引入几个小的变动,使得它更加方便。
    Servlet名称的通配符化:
    首先,当你写<filter-mapping>,你现在可以在<Servlet-name>标签中使用*号来代表所有的Servlets。而以前,你必须一次把一个Servlet绑定到过滤器上,像这样:

    <filter-mapping>
      <filter-name>Image Filter</filter-name>
      <Servlet-name>ImageServlet</Servlet-name>
    </filter-mapping> 

    现在,你可以一次绑定所有的Servlets:

    <filter-mapping>
      <filter-name>Image Filter</filter-name>
      <Servlet-name>*</Servlet-name>  <!?新特征 -->
    </filter-mapping>

    这有着很大用途,例如:

    <filter-mapping>
      <filter-name>Dispatch Filter</filter-name>
      <Servlet-name>*</Servlet-name>
      <dispatcher>FORWARD</dispatcher>
    </filter-mapping>

    映射的复合模式:
    其次,当我们写<Servlet-mapping> 或者 <filter-mapping>时,你现在可以在同一的标签中采用复合匹配的标准。以前一个<Servlet-mapping>只支持一个<url-pattern>元素,现在它不只支持一个,例如:
    <Servlet-mapping>
      <Servlet-name>color</Servlet-name>
      <url-pattern>/color/*</url-pattern>
      <url-pattern>/colour/*</url-pattern>
    </Servlet-mapping>

    同样地,以前<filter-mapping>也是只支持一个<url-pattern> 或者一个 <Servlet-name>。现在它对于每个元素都可以支持任意多个:

    <filter-mapping>
      <filter-name>Multipe Mappings Filter</filter-name>
      <url-pattern>/foo/*</url-pattern>
      <Servlet-name>Servlet1</Servlet-name>
      <Servlet-name>Servlet2</Servlet-name>
      <url-pattern>/bar/*</url-pattern>
    </filter-mapping>

    HTTP方法名:

    最近,你可以将合法的HTTP/1.1方法名放进<http-method>元素中。当你使用这些方法时,<http- method>将指明<security-constraint>标记里的方法应该被应用。从以前来看,它仅限于HTTP/1.1的7 个标准方法:GET,POST,PUT,DELETE,HEAD,OPTIONS和TRACE。但是,HTTP/1.1允许对方法进行扩展,WebDAV 是用于这种扩展的普遍技术。在Servlet2.5中,你可以安全地约束任何可能的HTTP方法名,标准及扩展,包括WebDAV方法,例如:LOCK, UNLOCK,COPY及MOVE。
    如果你写一个WebDAV的Servlet,你不必使用doLock()和doCopy()方法。你必须写自己的service()方法及分派request.getMethod()方法。正由于这种变化,你不必管理系统的安全性。
    去除限制:

    Servlet2.5去除了关于错误处理和回话跟踪的一些限制。对于错误处理,Servlet2.5之前,配置在<error- page>中的错误处理页面不能通过调用setStatus()方法来修改触发他们的错误代码,而Servlet2.5减弱了这一规范。这样的规范的产生于这样的观点,就是错误页面的工作是指出每个错误而不是修改错误。但是,实际使用中,错误页面不只是用于指出错误,而是还能做更多的事情,或许可以代替在线帮助来帮助用户解决问题。这个规范将不再限制错误页面所产生的反馈信息。

    对于会话跟踪,Servlet2.5之前,调用RequestDispatcher.include()的Servlet不能设置响应的标题头,而 Servlet2.5减弱了这一规范。原规范的目的是使内部的Servlets限制在自己的页面空间中,不可以影响外部的页面。现在这个规范已经减弱,允许在内部的Servlet中使用request.getSession()命令,这个命令可以悄悄地创建一个会话跟踪cookie的标题头。逻辑上要求限制内部的资源,但逻辑上也要求这些限制不应该取消其启动session的这一功能。这个变动对于Portlet规范来说显得尤其重要。作用是:如果响应已经有效,则getSession()命令就会抛出一个IllegalStateException(异常),而在此之前,就没有这个功能。

    优化:
    最近,新的规范优化了一些实例,使得Servlets更加方便而且保证了更好地按要求工作。

    终止响应:
    第一处优化细小又深奥,但做为规范中的一个例子还有蛮有趣的。Servlet2.4规范规定响应在这几种情况下应该是有效的,包括:在响应的 setContentLength方法中内容已经明确说明,以及内容已经写进了响应中。这种情况只有你的代码像下面这样才可以使响应重新定向:

    response.setHeader("Host", "localhost");
    response.setHeader("Pragma", "no-cache");
    response.setHeader("Content-Length", "0");
    response.setHeader("Location", "http://www.apache.org");

    Servlet技术忽略特定区域的标题头,因为内容满足0字节长度,响应就会立即生效。而在它开始之前,响应就已失效了!Servlet容器通常拒绝执行这种行为,而Servlet2.5版本增加了“长度必须大于0”这个原则。
    实例编码:

    Servlet2.4规范规定必须在调用request.getReader()方法之前调用 request.setCharacterEncoding()方法。但是,如果你忽略这个原则而在其之后去调用 request.setCharacterEncoding()方法,那么会产生什么后果,这个问题规范里并没有说。为了简便,现在消除这种情况!
    Cross-context sessions(不同上下文目录间的会话):
    最近,关于Cross-context会话处理的规则已经明确说明。当Servlets指派从一个上下文到其他上下文的请求时,这个规则就发挥了作用??在目标调用过程中,包括哪些会话。这个版本的出现使得一个上下文目录的主页里的portlets可以通过几种内部的命令来对别的上下文目录里的 portlets起作用。Servlet2.5明确指出一个上下文目录里的资源可以访问其他上下文目录的session(会话),而不用考虑这个请求从哪里开始的。这意味着portlets可以脱离主页的范围而在自己的范围里运行,而且这个规范还会应用在不兼容的Serlvet容器中。
    期待:

    由于Servlet2.5版本要保持一些旧的性质,几个大的概念不得不延后到下一个阶段。它们包括:
    "新的输入/输出(NIO)支持:使NIO通道更有利于Servlets进行客户端通信成为可能。
    "过滤器wrap-under或wrap-over语义:有时用过滤器包装请求,和/或者响应对象去修改方法行为或者启用新的方法。当把这种包装和服务器对请求和响应的包装结合起来时,又应该怎么包装在一起?
    "用于欢迎的Servlets文件:做为索引应该充当欢迎作用的文件吗?在此之前,这个回答是肯定的。但是规范没有明确说明如何使用这个功能,尤其在没有索引的情况下。
    "用于欢迎的文件的分派规则:如何分派欢迎文件,这个细节并没有完全说明,而是遗留了一些开放的缺口来应对不兼容问题。
    "登陆后选择默认页面:如果用户通过他们的书签访问Servlet的登陆页面,那么在成功登陆后页面应该转向哪里呢?这个问题至今尚未明确说明。
    "用户的主题日志:在通过网站正确地注册之后,不通过传统地登陆方式没有办法使Servlet信任用户。
    结束语:
    如果抛开注释来看Servlet2.5的变化,可见在配置文件web.xml中去除了一些限制,是有利的,同时又优化了实例行为使其更适合更便于开发Web系统(网页)。
    Servlet2.5中注释的作用更加戏剧化。Servlets本身不能声明注释类型的变量,甚至性能弱的Servlet容器都不支持注释。然而在 JEE5环境下的Servlets编写者可以看到,通过公共的注释及EJB3.0和JAX-WS2.0规范而引入的注释类型会对代码产生很大变化,并且这也将对Servlet如何管理外部资源,对象的持久化及EJB的构成产生重大影响

    J2EE的两种重要的表现层技术JSP和JSF发布了新技术规范的预览版本,其中最重要的一点是两者将表达式语言(Expression Language,EL)部分合二为一。在不久的将来,这两种技术有可能更进一步地彼此融合,成为一种统一的表现层技术。然而在J2EE社群的普遍观点中,如果单单作为一种视图技术,JSP并不是最佳的选择,Velocity和XSLT等基于模板的视图技术通常比JSP更方便;而基于组件的JSF也面临广泛的信任危机。两者的组合是否能得到业界的认可,还需要时间的检验。

    jsp 2.1


        我们很高兴向大家宣告,JavaServer Pages、JSR-245下开发的Faces.JavaServer Pages(JSP)2.1和JSR-252下开发的JavaServer Faces(Faces)1.2的新版规范的Early Draft Review发布。

        JSP 2.1把Expression Language(EL)输出到它自己各自分离的文档中,在技术上,这些文档是JSP规范的子文档。这些统一的EL规范定义了一个更高层的java 包,javax.el。这个包与使用它的技术之间完全独立,并且允许此技术将自身插入EL处理过程。更改的JSP规范遵从使用标准化EL的规范。

        对于前面提到的JSR-252,这个规范并没什么新特性。Faces 1.2支持新的标准化EL,还包含一些bug修复的相关规范。

        Faces和JSP在JSRs下的结盟带来了一些新功能,也为将来的发展打下了坚实的基础。例如,在同时使用Faces和JSP的web应用中,网页仅使用JSP(不包含任何faces内容)来访问Managed Beans成为可能。在JSP规范的附录E中和Faces规范的前言中都可以看到更改内容的细节。

    posted @ 2007-07-02 12:20 和田雨 阅读(2282) | 评论 (0)编辑 收藏

    看到很多文章在问和答关于java网页汉字乱码的情况,有些甚至认为这是servlet包容器实现上的错误。
    可是如果读过servlet规格说明书,关于java网页编码的问题就应该迎刃而解了。

    编写java网页(servlet\jsp),需要关心response(反馈给客户端的页面)和request(来自客户端的数据)的编码问题。

    response的编码可以直接用代码实现。而request的编码怎么办呢?
    以下是servlet规范书上的文字(我粗略翻译一下吧,不怕见笑):

    -- 整理自<Java Servlet Specification Version 2.3> SRV.4.9 --

    当前,很多浏览器并不发送带有“Content-Type”头信息的字符编码限定符,而由读取HTTP请求的代码来决定字符的编码方式。如果客户端请求未定义编码限定符,则包容器(如tomcat)用于创建request reader和分析POST数据的request的缺省编码方式必须是“ISO-8859-1”。然而,为了向开发者指明客户端没有发送字符编码信息的情况,包容器对getCharacterEncoding方法返回null。

    如果客户端不设置字符编码方式,并且request不是按照以上缺省编码方式(iso-8859-1)来编码,则会发生问题。为了解决这个问题,在接口ServletRequest中加入了一个新的方法setCharacterEncoding(String enc)。开发者可以调用这个方法来替换包容器提供的缺省字符编码方式。但是,必须在从request中分析任何POST数据或者读取任何输入之前,调用这个方法。一旦数据已被读取,则调用这个方法将不会影响编码方式。
    posted @ 2007-07-02 12:19 和田雨 阅读(963) | 评论 (1)编辑 收藏

    一、Servlet过滤器的概念:
    ***************************************************************************************
    Servlet过滤器是在Java Servlet规范2.3中定义的,它能够对Servlet容器的请求和响应对象进行检查和修改。   

    Servlet过滤器本身并不产生请求和响应对象,它只能提供过滤作用。Servlet过期能够在Servlet被调用之前检查Request对象,修改Request Header和Request内容;在Servlet被调用之后检查Response对象,修改Response Header和Response内容。

    Servlet过期负责过滤的Web组件可以是Servlet、JSP或者HTML文件。 
    ***************************************************************************************


    二、Servlet过滤器的特点:
    ***************************************************************************************
    A.Servlet过滤器可以检查和修改ServletRequest和ServletResponse对象
    B.Servlet过滤器可以被指定和特定的URL关联,只有当客户请求访问该URL时,才会触发过滤器
    C.Servlet过滤器可以被串联在一起,形成管道效应,协同修改请求和响应对象
    ***************************************************************************************


    三、Servlet过滤器的作用:
    ***************************************************************************************
    A.查询请求并作出相应的行动。
    B.阻塞请求-响应对,使其不能进一步传递。
    C.修改请求的头部和数据。用户可以提供自定义的请求。
    D.修改响应的头部和数据。用户可以通过提供定制的响应版本实现。
    E.与外部资源进行交互。
    ***************************************************************************************


    四、Servlet过滤器的适用场合:
    ***************************************************************************************
    A.认证过滤
    B.登录和审核过滤
    C.图像转换过滤 
    D.数据压缩过滤 
    E.加密过滤 
    F.令牌过滤 
    G.资源访问触发事件过滤 
    H.XSL/T过滤 
    I.Mime-type过滤
    ***************************************************************************************


    五、Servlet过滤器接口的构成:
    ***************************************************************************************
    所有的Servlet过滤器类都必须实现javax.servlet.Filter接口。这个接口含有3个过滤器类必须实现的方法:

    A.init(FilterConfig):
    这是Servlet过滤器的初始化方法,Servlet容器创建Servlet过滤器实例后将调用这个方法。在这个方法中可以读取web.xml文件中Servlet过滤器的初始化参数

    B.doFilter(ServletRequest,ServletResponse,FilterChain):
    这个方法完成实际的过滤操作,当客户请求访问于过滤器关联的URL时,Servlet容器将先调用过滤器的doFilter方法。FilterChain参数用于访问后续过滤器

    B.destroy():
    Servlet容器在销毁过滤器实例前调用该方法,这个方法中可以释放Servlet过滤器占用的资源
    ***************************************************************************************


    六、Servlet过滤器的创建步骤:
    ***************************************************************************************
    A.实现javax.servlet.Filter接口
    B.实现init方法,读取过滤器的初始化函数
    C.实现doFilter方法,完成对请求或过滤的响应
    D.调用FilterChain接口对象的doFilter方法,向后续的过滤器传递请求或响应
    E.销毁过滤器
    ***************************************************************************************


    七、Servlet过滤器对请求的过滤:
    ***************************************************************************************
    A.Servlet容器创建一个过滤器实例
    B.过滤器实例调用init方法,读取过滤器的初始化参数
    C.过滤器实例调用doFilter方法,根据初始化参数的值判断该请求是否合法
    D.如果该请求不合法则阻塞该请求
    E.如果该请求合法则调用chain.doFilter方法将该请求向后续传递
    ***************************************************************************************


    八、Servlet过滤器对响应的过滤:
    ***************************************************************************************
    A.过滤器截获客户端的请求
    B.重新封装ServletResponse,在封装后的ServletResponse中提供用户自定义的输出流
    C.将请求向后续传递
    D.Web组件产生响应
    E.从封装后的ServletResponse中获取用户自定义的输出流
    F.将响应内容通过用户自定义的输出流写入到缓冲流中
    G.在缓冲流中修改响应的内容后清空缓冲流,输出响应内容
    ***************************************************************************************

    九、Servlet过滤器的发布:
    ***************************************************************************************
    A.发布Servlet过滤器时,必须在web.xml文件中加入<filter>元素和<filter-mapping>元素。

    B.<filter>元素用来定义一个过滤器:
    属性                   含义
    filter-name    指定过滤器的名字
    filter-class    指定过滤器的类名
    init-param    为过滤器实例提供初始化参数,可以有多个

    C.<filter-mapping>元素用于将过滤器和URL关联:
    属性                     含义
    filter-name    指定过滤器的名字
    url-pattern    指定和过滤器关联的URL,为”/*”表示所有URL
    ***************************************************************************************


    十一、Servlet过滤器使用的注意事项
    ***************************************************************************************
    A.由于Filter、FilterConfig、FilterChain都是位于javax.servlet包下,并非HTTP包所特有的,所以其中所用到的请求、响应对象ServletRequest、ServletResponse在使用前都必须先转换成HttpServletRequest、HttpServletResponse再进行下一步操作。

    B.在web.xml中配置Servlet和Servlet过滤器,应该先声明过滤器元素,再声明Servlet元素


    C.如果要在Servlet中观察过滤器生成的日志,应该确保在server.xml的localhost对应的<host>元素中配置如下<logger>元素:
    <Logger className = “org.apache.catalina.logger.FileLogger”
    directory = “logs”prefix = “localhost_log.”suffix=”.txt”
    timestamp = “true”/>
    ***************************************************************************************
    posted @ 2007-07-02 12:13 和田雨 阅读(263) | 评论 (0)编辑 收藏