Java Tools

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

2007年7月3日 #

     摘要:   阅读全文
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 和田雨 阅读(278) | 评论 (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 和田雨 阅读(613) | 评论 (0)编辑 收藏

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

     摘要:   阅读全文
posted @ 2007-08-09 19:22 和田雨 阅读(280) | 评论 (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 和田雨 阅读(406) | 评论 (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 和田雨 阅读(13001) | 评论 (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 和田雨 阅读(248) | 评论 (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 和田雨 阅读(2326) | 评论 (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 和田雨 阅读(241) | 评论 (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 和田雨 阅读(1072) | 评论 (1)编辑 收藏

     摘要:   阅读全文
posted @ 2007-07-21 13:50 和田雨 阅读(242) | 评论 (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 和田雨 阅读(248) | 评论 (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 和田雨 阅读(5008) | 评论 (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 和田雨 阅读(2254) | 评论 (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 和田雨 阅读(464) | 评论 (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 和田雨 阅读(1799) | 评论 (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 和田雨 阅读(389) | 评论 (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 和田雨 阅读(542) | 评论 (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 和田雨 阅读(390) | 评论 (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 和田雨 阅读(448) | 评论 (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 和田雨 阅读(275) | 评论 (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 和田雨 阅读(254) | 评论 (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 和田雨 阅读(219) | 评论 (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 和田雨 阅读(190) | 评论 (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 和田雨 阅读(468) | 评论 (0)编辑 收藏

     摘要: 版权所有:(xiaodaoxiaodao)蓝小刀    xiaodaoxiaodao@gmail.com http://www.blogjava.net/xiaodaoxiaodao/articles/103469.html        转载请注明来源/作者 Java Web Start 入门基础教程 &nbs...  阅读全文
posted @ 2007-07-06 11:34 和田雨 阅读(305) | 评论 (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 和田雨 阅读(272) | 评论 (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 和田雨 阅读(4277) | 评论 (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 和田雨 阅读(554) | 评论 (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 和田雨 阅读(500) | 评论 (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 和田雨 阅读(333) | 评论 (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 和田雨 阅读(394) | 评论 (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 和田雨 阅读(236) | 评论 (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 和田雨 阅读(436) | 评论 (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 和田雨 阅读(245) | 评论 (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 和田雨 阅读(207) | 评论 (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 和田雨 阅读(260) | 评论 (0)编辑 收藏