byterat

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

2007年1月19日 #

Eclipse及其插件介绍和下载

0.Eclipse下载
EMF,GEF - Graphical Editor Framework,UML2,VE - Visual Editor都在这里下载
http://www.eclipse.org/downloads/index.php

0.5.lomboz J2EE插件,开发JSP,EJB
http://forge.objectweb.org/projects/lomboz
1.MyEclipse J2EE开发插件,支持SERVLET/JSP/EJB/数据库操纵等
http://www.myeclipseide.com

2.Properties Editor 编辑java的属性文件,并可以自动存盘为Unicode格式
http://propedit.sourceforge.jp/index_en.html

3.Colorer Take 为上百种类型的文件按语法着色
http://colorer.sourceforge.net/

4.XMLBuddy 编辑xml文件
http://www.xmlbuddy.com

5.Code Folding 加入多种代码折叠功能(比eclipse自带的更多)
http://www.coffee-bytes.com/servlet/PlatformSupport

6.Easy Explorer 从eclipse中访问选定文件、目录所在的文件夹
http://easystruts.sourceforge.net/

7.Fat Jar 打包插件,可以方便的完成各种打包任务,可以包含外部的包等
http://fjep.sourceforge.net/

8.RegEx Test 测试正则表达式
http://brosinski.com/stephan/archives/000028.php

9.JasperAssistant 报表插件(强,要钱的)
http://www.jasperassistant.com/

10.Jigloo GUI Builder JAVA的GUI编辑插件
http://cloudgarden.com/jigloo/

11.Profiler 性能跟踪、测量工具,能跟踪、测量BS程序
http://sourceforge.net/projects/eclipsecolorer/

12.AdvanQas 提供对if/else等条件语句的提示和快捷帮助(自动更改结构等)
http://eclipsecolorer.sourceforge.net/advanqas/index.html

13.Log4E Log4j插件,提供各种和Log4j相关的任务,如为方法、类添加一个logger等
http://log4e.jayefem.de/index.php/Main_Page

14.VSSPlugin VSS插件
http://sourceforge.net/projects/vssplugin

15.Implementors 提供跳转到一个方法的实现类,而不是接中的功能(实用!)
http://eclipse-tools.sourceforge.net/implementors/
16.Call Hierarchy 显示一个方法的调用层次(被哪些方法调,调了哪些方法)
http://eclipse-tools.sourceforge.net/call-hierarchy/index.html

17.EclipseTidy 检查和格式化HTML/XML文件
http://eclipsetidy.sourceforge.net/

18.Checkclipse 检查代码的风格、写法是否符合规范
http://www.mvmsoft.de/content/plugins/checkclipse/checkclipse.htm

19.Hibernate Synchronizer Hibernate插件,自动映射等
http://www.binamics.com/hibernatesync/

20.VeloEclipse Velocity插件
http://propsorter.sourceforge.net/

21.EditorList 方便的列出所有打开的Editor
http://editorlist.sourceforge.net/

22.MemoryManager 内存占用率的监视
http://cloudgarden.com/memorymanager/

23.swt-designer java的GUI插件
http://www.swt-designer.com/

24.TomcatPlugin 支持Tomcat插件
http://www.eclipsetotale.com/tomcatPlugin.html

25.XML Viewer
http://tabaquismo.freehosting.net/ignacio/eclipse/xmlview/index.html

26.quantum 数据库插件
http://quantum.sourceforge.net/

27.Dbedit 数据库插件
http://sourceforge.net/projects/dbedit

28.clay.core 可视化的数据库插件
http://www.azzurri.jp/en/software/index.jsp
http://www.azzurri.jp/eclipse/plugins

29.hiberclipse hibernate插件
http://hiberclipse.sourceforge.net
http://www.binamics.com/hibernatesync

30.struts-console Struts插件
http://www.jamesholmes.com/struts/console/

31.easystruts Struts插件
http://easystruts.sourceforge.net

32.veloedit Velocity插件
http://veloedit.sourceforge.net/

33.jalopy 代码整理插件
http://jalopy.sourceforge.net/

34.JDepend 包关系分析
http://andrei.gmxhome.de/jdepend4eclipse/links.html

35.Spring IDE Spring插件
http://springide-eclip.sourceforge.net/updatesite/

36.doclipse 可以产生xdoclet 的代码提示
http://beust.com/doclipse/
posted @ 2008-06-05 15:44 比特鼠| 编辑 收藏

     摘要: 有这样一个函数, 它接受一个函数(或者说闭包)作为参数  阅读全文
posted @ 2008-05-30 15:19 比特鼠| 编辑 收藏

当谈到表格数据的设计时,没有太多的网页设计师会有太大的兴趣。今天我们已经收集了20多个功能超大且看上去挺漂亮的Ajax/CSS表格设计,并且教你一些表格设计中所运用的技巧,例如表格数据的排序和过滤等。

OK,让我们来看一下这些表格:

1. Tablecloth

Tablecloth 由CSS Globe 开发,是一个轻巧易于使用的表格,简洁的将表格样式添加到你的HTML 表格元素中。

21个新奇漂亮的Ajax/CSS表格设计-Tablecloth

2. Ask the CSS Guy Table

Ask the CSS Guy Table教给我们要如何去创建能够清晰显出去资料之间的相关联系的表格,例如:点击一个表格元素时,将突了显示这个元素,并且在纵列和横列都显示出相关的类别关系。

21个新奇漂亮的Ajax/CSS表格设计-Ask the CSS Guy Table

#3. A CSS styled table version 2

Veerle Duoh 为我们展示了一个漂亮的表格设计,并教我们如何使用CSS来吸引用户的眼球。

21个新奇漂亮的Ajax/CSS表格设计-A CSS styled table version 2

#4. Sortable Table

Sortable Table 演示了如何按升序或降序排列以及如何过滤表格中的数据。

21个新奇漂亮的Ajax/CSS表格设计-Sortable Table

5. Row Locking with CSS and JavaScript

Css Guy再次对表格使用了聚焦高亮的效果,除非用户再次点击,否则表单数据将一直保持亮高。

21个新奇漂亮的Ajax/CSS表格设计-Row Locking with CSS and JavaScript

他还给了我们另一个示例:another example to Lock rows with radios .

#6. Vertical scrolling tables

如果您有大量的表格数据,但却没有太大的空间来展示它,这可能是个比较好的方法:一个纯CSS的表格与固定的标题和页脚,以及滚动显示的内容。

21个新奇漂亮的Ajax/CSS表格设计-Vertical scrolling tables

7. Replicating a Tree table

这是一个使用HTML 和CSS 设计的树形状表格。

21个新奇漂亮的Ajax/CSS表格设计-Replicating a Tree table

8 ) Paginate, sort and search a table with Ajax and Rails

这个表格提供了一个动态的界面,而不需要重新刷新整个页面。

21个新奇漂亮的Ajax/CSS表格设计-ajax tables

9. Collapsible tables with DOM and CSS

此表格加上箭头形象的脚本提示,用来控制表格的伸展和收缩。

21个新奇漂亮的Ajax/CSS表格设计-Collapsible tables with DOM and CSS

10. TableSorter plug-in for jQuery

它的主要特性包括多列排序,支持<TH>的rowspan和colspan属性以及许多其他功能。

21个新奇漂亮的Ajax/CSS表格设计-TableSorter plug-in for jQuery

11. Stripe your tables the OO way

使用了Javascript 为表格中的行进行颜色交替,并且添加了onmouseoveronmouseout 事件,当鼠标点击时,切换背景颜色。

21个新奇漂亮的Ajax/CSS表格设计-Stripe your tables the OO way

12. MooTools Table Row & Column highlighting

基于MooTools 框架,高亮显示鼠标悬停时的单元格所在的行和列。

21个新奇漂亮的Ajax/CSS表格设计-MooTools Table Row & Column highlighting

13. CSS Table Gallery

93 styled tables是一个专门收集表格样式的站点,下面是来自一个表格样式的截图:

21个新奇漂亮的Ajax/CSS表格设计-CSS Table Gallery

14. jQuery Table Filter

可以对数据进行各种不同的排序、过滤。

21个新奇漂亮的Ajax/CSS表格设计-jQuery Table Filter

15. Sortable/Resizable/Editable TableKit

TableKit基于Prototype框架,专门收集各种HTML表格,可以利用Ajax实时的进行表格栏目大小、排序等编辑。

21个新奇漂亮的Ajax/CSS表格设计-sortable, resizable, editable

16. Make all your tables sortable

21个新奇漂亮的Ajax/CSS表格设计-sortable table

17. Zebra Tables

alistapart为我们提供了一个极好的例子,如何使用JavaScript和DOM的改变背景色风格,以突出显示单元格。

21个新奇漂亮的Ajax/CSS表格设计-Zebra Tables

18. Standardista Table Sorting

Standardista Table Sorting 是一个Javascript模块,让您可以对HTML数据表的任何栏目进行排序。

21个新奇漂亮的Ajax/CSS表格设计-Standardista Table Sorting

19. GridView3 Example

21个新奇漂亮的Ajax/CSS表格设计-GridView3 Example

20. Mootable

21个新奇漂亮的Ajax/CSS表格设计-Mootable

21. Drag & Drop Sortable Lists with JavaScript and CSS

21个新奇漂亮的Ajax/CSS表格设计-Drag & Drop Sortable Lists with JavaScript and CSS

可能还会有一些你更想寻找的详细资料,下面是一些相关的资源链接:

如果你知道其它更强大的Ajax/CSS表格,欢迎在此留言。

posted @ 2008-01-23 17:46 比特鼠 阅读(3187) | 评论 (0)编辑 收藏

一个在线调色工具
posted @ 2008-01-23 17:44 比特鼠 阅读(380) | 评论 (1)编辑 收藏

/**
 * 加码解码工具
 * @author lwm
 *
 */

public class Encode {
 
 /*
  * 对应javascript的escape()函数, 加码后的串可直接使用javascript的unescape()进行解码
  */
 public static String escape(String src) {
  int i;
  char j;
  StringBuffer tmp = new StringBuffer();
  tmp.ensureCapacity(src.length() * 6);
  for (i = 0; i < src.length(); i++) {
   j = src.charAt(i);
   if (Character.isDigit(j) || Character.isLowerCase(j)
     || Character.isUpperCase(j))
    tmp.append(j);
   else if (j < 256) {
    tmp.append("%");
    if (j < 16)
     tmp.append("0");
    tmp.append(Integer.toString(j, 16));
   } else {
    tmp.append("%u");
    tmp.append(Integer.toString(j, 16));
   }
  }
  return tmp.toString();
 }

 /*
  * 对应javascript的unescape()函数, 可对javascript的escape()进行解码
  */
 public static String unescape(String src) {
  StringBuffer tmp = new StringBuffer();
  tmp.ensureCapacity(src.length());
  int lastPos = 0, pos = 0;
  char ch;
  while (lastPos < src.length()) {
   pos = src.indexOf("%", lastPos);
   if (pos == lastPos) {
    if (src.charAt(pos + 1) == 'u') {
     ch = (char) Integer.parseInt(src
       .substring(pos + 2, pos + 6), 16);
     tmp.append(ch);
     lastPos = pos + 6;
    } else {
     ch = (char) Integer.parseInt(src
       .substring(pos + 1, pos + 3), 16);
     tmp.append(ch);
     lastPos = pos + 3;
    }
   } else {
    if (pos == -1) {
     tmp.append(src.substring(lastPos));
     lastPos = src.length();
    } else {
     tmp.append(src.substring(lastPos, pos));
     lastPos = pos;
    }
   }
  }
  return tmp.toString();
 }

}

posted @ 2008-01-11 17:08 比特鼠 阅读(1818) | 评论 (0)编辑 收藏

读 YUI ,EXT等源码的时候看JS天旋地转,那可不是51JS上那种挪挪位置就能理解的,此刻如果没有JavaScrip的基础,更是像没有星光的黑夜…….

自以为觉得Js对象是很好理解的东东,然而真实践起来却一片糊涂。
通过查阅经典书籍《Professional JavaScript For Web Developers》稍微有些理解了

JavaScript的基本类型
原始类型如: Undefined Null Boolean Number String 等 用 typeof方法能辨别之
引用类型如: Object Function Array Boolean Number String Date等,用insanceof方法辨别之

严格来讲,JavaScript没有对象(Object),但是由于和OO术语对应,所以也称之为对象。所以Array,Function,基本类型,引用类型,函数,以及函数的属性 等等这些都是对象。

而对象分类,则可以分为内置对象(Built-in Object) 和宿主对象(host object)。
内置对象如 Math,Data啊。
宿主对象则如 BOM,DOM之类.

重新回顾了下这些基本概念之后,在做简单实践就有些理解了。
因此对象的使用,创建方式不尽相同,最简单的归类如下:

1 基本创建方式

function Class() {
window.alert("Hello Class!");
}
var clz= new Class();

2 访问对象成员

function Class(){
this.x = " this is x";
this.y = "this is y";
this.z = viewXY;
function viewXY(){
alert("x+","+y);
}
}
var clz= new Class();
clz.viewXY();

3 对象继承

function Parent() {
this.type= "human!";
}
function Child(){
this.age = "26";
this.sex ="male";
this.say= myInfo;
function myInfo(msg){
alert(msg+this.type+ ","+this.age+","+this.sex);
}
}
Child.prototype = new Parent();
var clild = new Child();
clild.say("I'm ");

4.重用原对象 (书上的例子太好了,搬来了)

Funcion.prototype.toString() = function(){
return "Function code hidden";
}
function sayHi(){
alert("hi");
}
alert(sayHi.toString());
posted @ 2008-01-02 11:06 比特鼠 阅读(255) | 评论 (0)编辑 收藏

希望能做到以下几点:

1. 在Java服务端架构的设计, 选型, 方案等方面有所突破! -- 这是最主要的!
2. 也想玩一玩Web前端的AJAX编程, RIA(富互联网应用)等等
3. 熟悉Linux/Unix系统的命令行操作
4. 在Java中跑脚本语言Python, JRuby等等
5. 项目管理

暂时就这么多吧!

posted @ 2007-12-28 09:41 比特鼠 阅读(197) | 评论 (0)编辑 收藏

为 Ajax 安全性所提出的经验法则:
  1. 如果你使用身份验证, 确定你在请求页上检查!
  2. 为 SQL 注入检查。
  3. 为 JavaScript 注入检查。
  4. 保留商务逻辑在服务器上!
  5. 不要假设每个请求是真正的!
  6. 确认检查数据!
  7. 审查请求的数据而且确定它是正确的。
posted @ 2007-12-19 17:10 比特鼠 阅读(262) | 评论 (0)编辑 收藏

1. jvm内部分为主工作区和线程工作区。主工作区是实例的所有线程共有,线程工作区是实例的每个线程专有的工作区,其中包括一些主工作区的一些实例字段数据的拷贝。

2. 服务器一般都有线程池,线程资源是可以重复利用的。你2000个用户在线,不见得能又200个用户同时(或者说并发)访问。再说,只要对象不是太大,我宁愿用200个拷贝,也不想让用户在这个200个任务的队列里等待。

3. 两个DB之间的复制数据,每个DB各自使用自己的Sequane来生成id。复制数据时,如果DB中的外键是由DB维护的,则不会产生id冲突,如果外键是由外部程序维护的,则可能会产生错误!

4. 对于非static的类的数据成员来说,在该类产生的实例中都有一份,并且相互独立(修改后并不影响其他实例), 但static的数据成员则变成了每个类只有一份,即在该类产生的所有实例共享这一个数据成员, 该数据成员的改变会影响到其他的实例. 而static的方法则是让你不用创建对象及能调用这个方法.

5. ThreadLocal的作用就是将经常要用到的对象的引用放到属于线程自己的一个存储空间中,在该线程的执行过程中,可以通过类的静态的ThreadLocal来方便的获取到这个对象,而不用通过参数的形式传来传去。
posted @ 2007-12-19 14:54 比特鼠 阅读(244) | 评论 (0)编辑 收藏

很多高分辨率的图像真的能够扮靓一个Web网站。但是它们也可能会降低网站的(响应)速度——图像都是文件,文件就要占用带宽,而带宽与等待时间直接相关。现在是你进行自我学习,了解如何利用一种叫做图像预加载的小技巧给网站提速的时候了。

图像的预加载

       浏览器通常的工作方式是:只有当要求加载图像的HTTP请求被发送的时候,图像才会被加载,而不论它是被动地通过<img>标记加载,还是主动地通过方法调用加载。所以,如果你有一段JavaScript,需要在鼠标悬停的时候切换图像,或者在超时之后自动地更换图像,那么你就可能会在从服务器取回图像的时候随时碰到等待,时间会从数秒钟到几分钟不等。当你以较慢的速度连接到Internet上的时候,或者被取回的图像非常巨大的时候,这种状况尤其显著,而这种数据延迟通常都会毁掉你所期望的效果。

        有些浏览器会试图转嫁这一问题,比如把图像保存在本地缓冲区里,这样以后对它的调用就能够很快进行了,但是需要第一次调用图像的时候仍然会产生延迟。预加载是一项在需要图像之前就把它下载到缓冲区里的技术。通过这种方式,当真的需要图像的时候,它可以被从缓冲区里取出来,并立即显示出来。

Image()对象
        预加载图像最简单的方法用JavaScript将一个新的Image()对象实例化,并把你想要预加载的图像的URL传递给它。假设我们有一个叫做
http://www.host01.com/Get/jsp/00040004/heavyimagefile.jpg的图像,我们希望,当用户把鼠标放在一个已经显示过的图像上的时,系统能够显示出这个图像。为了预加载这个图像,以便实现更快的响应时间,我们只用创建一个新的Image()对象,将其命名为heavyImage,并使用onLoad()事件处理程序把它同时加载到页面上。

1 < html >< head >< script  language  = "JavaScript" > function  preloader()  {heavyImage  =   new  Image(); heavyImage.src = " http://www.host01.com/Get/jsp/00040004/heavyimagefile.jpg " ;} </ script ></ head >< body  onLoad ="javascript:preloader()" >< href ="#"  onMouseOver ="javascript:document.img01.src='http://www.host01.com/Get/jsp/00040004/heavyimagefile.jpg'" >< img  name ="img01"  src =http://www.host01.com/Get/jsp/00040004/"justanotherfile.jpg" ></ a ></ body ></ html >
2

 

          要注意的是,图像标记自身并不会处理onMouseOver()和onMouseOut()事件,这就是为什么上面例子里的<img>标记被放在一个<a>标记里,后者的确加入了对这些事件类型的支持。
用数组加载多个图像


           在实际操作中,你可能需要预加载一幅以上的图像;例如,在包含有多个图像翻滚(rollover)的菜单条里,或者如果你正在尝试创建平滑的动态效果。这并不困难;你所需要做的就是使用JavaScript的数组,就像下面例子里的一样:

 

1 < script language = " JavaScript " > function  preloader()  //  counter var i = 0; // create object imageObj = new Image(); // set image list images = new Array(); images[0]="image1.jpg" images[1]="image2.jpg" images[2]="image3.jpg" images[3]="image4.jpg" // start preloading for(i=0; i<=3; i++) { imageObj.src=images[i]; }
2 }
  </ script >


         在上面的例子里,你先定义变量i和叫做imageObj的Image()对象。然后定义一个叫做images[]的新数组,在这个数组里,每个数组元素都保存着需要预加载的图像来源。最后,创建一个for()循环,让它在数组里循环,并将它们中的每一个都指派给Image()对象,这样就能够把它预加载到缓冲区里。
onLoad()事件处理程序
        就和JavaScript里的其它很多对象一样,Image()对象也带有多个事件处理程序。这其中最有用的毫无疑问的就是onLoad()处理程序了,它会在完成图像加载的时候被调用。这个处理程序可以与自定义的函数一起使用,以便在完成图像加载之后进行特定的任务。下面的例子通过在图像加载的时候显示“请等待(please wait)”提示信息来说明这个问题,然后在图像完成加载之后就向浏览器发送一个新的URL。

 

< html >< head >< script  language ="JavaScript" > //  create an image objectobjImage = new Image(); // set what happens once the image has loaded objImage.onLoad=imagesLoaded(); // preload the image fileobjImage.src='http://www.host01.com/Get/jsp/00040004/images/image1n.gif';// function invoked on image loadfunction imagesLoaded(){ document.location.href='index2.html';}</script></head><body>Please wait, loading images</body></html>

 


       当然,你还可以创建一个图像数组,对它进行循环,预加载每个图像,并在每个阶段对已加载图像的数量保持跟踪。一旦加载了所有的图像,事件处理程序就能够按照设定把浏览器带到下一个页面(或者进行其他的任务)。

预加载与多状态菜单

          现在,把你刚刚学到的理论付诸真正的实践怎么样?下面一部分内容就是我碰巧编写的一段代码——一个由多个按钮(图像链接)组成的菜单条——其中每个按钮都可能处于三种状态中的一种:正常(normal)、hover(悬停)和点击(click)。由于所有的按钮都有多个状态,所以就有必要使用图像预加载来确保菜单能够根据其切换到的状态进行快速的响应。列表A里的代码就说了这一点。

           列表A里的HTML代码会建立一个由四个按钮组成的菜单条,每个按钮都有三种状态:正常、悬停和点击。其要求如下:

          但鼠标移动到处于正常状态的按钮上时,按钮会变为悬停状态。当鼠标移开的时候,按钮又会恢复到正常状态。当鼠标点击按钮的时候,按钮就会变为点击状态。它会一直保持这个状态,直到另外一个按钮被点击。如果有一个按钮被点击,那么其他的按钮就都不能处于点击状态。其他的按钮只能够处于悬停或者正常状态。一次只能有一个按钮可以被点击。一次只能有一个按钮处于悬停状态。
        第一项任务是建立保存有菜单每个状态的图像的数组。与这些数组元素相对应的<img>元素也都在HTML文档的主体里被创建,并按顺序命名。要注意的是,对数组值的索引是从0开始的,而相应的<img>元素是从1开始命名的——这就需要在脚本后面的一段里进行某种计算上的调整。

        PreloadImages()函数会负责把所有的图像都加载到缓冲区里,这样的话对鼠标移动的响应时间会被减到最小。一个for()循环被用在第一步里创建的图像里进行迭代,并预加载每一个图像。

            ResetAll()函数是把所有图像恢复都到它们正常状态的方便方法。这是有必要的,因为当菜单的项目被点击的时候,菜单里其他所有的项目都必须在被点击项目能够切换到点击状态之前恢复到正常状态。

        SetNormal()、setHover()和setClick()函数负责把特定图像(图像的编号被作为函数的自变量进行传递)的来源分别改为正常、悬停或者点击状态。由于被点击的图像必须一直保持点击状态,直到另外一个图像被点击(见第二项要求),所以它们暂时不会对鼠标移动作出反应;这样的话,如果按钮还不是处在点击状态,那么setNormal()和setHover()函数所包括的代码就只能用来改变按钮的状态。

         上面所提到的预加载只是提高你JavaScript效果响应时间的多种方法之一。就在你的网站上使用上面列出的技巧,并根据你的要求在需要的地方更改它们吧。祝你好运!

posted @ 2007-12-19 10:40 比特鼠 阅读(240) | 评论 (0)编辑 收藏

这些东西都是Java Script大部分都是由老外写的,并且封装得很好,在运用上也很方便,而且也都兼容FF与OPERA,档案中少部分是由中国的高手写的。

 

  一、多样化摺叠菜单:下载

  一个由老外写的折叠式垂直菜单,多样化,多功能,可自订,使用容易,支持FF。

国内外 Java Script 经典封装
图1

  二、CSS圆角边框:下载

  以CSS为主要,用Java Script封装的相当完整,也是老外写的,支持多浏览器,可以自订样式,目前有十多种可以运用。

国内外 Java Script 经典封装
图2

国内外 Java Script 经典封装
图3

  三、模拟视窗:下载

  用层模拟的视窗,是一个中国高手写的,Java Script封装的相当好,使用上也很容易

国内外 Java Script 经典封装
图4
  

  四、支持FF的省略符:下载

  说到省略符,那非CSS莫属,有个老外用Java Script来实现,并且是批量处理的,重点是支持FF。

国内外 Java Script 经典封装
图5

  五、TAB选项卡:下载

  用Java Script模仿各种作业系统的选项卡,老外就是牛,不仅支援多样式的即时切换,同时也支援每个选项卡是否附带图示的切换选项,选项卡也可以上下切换。

国内外 Java Script 经典封装
图6
  

  六、最佳化多样式Windows:下载

  用层模拟视窗的最佳代表作,这是我看过功能最多的模拟式窗,内附多达74项功能与样式,你完完全全可以把它当成是一个真正的视窗来应用,可以根据你的需求来应用,快丢掉你那认为好用的层视窗,这套封装非常完整的视窗绝对可以满足你的各种需求。

国内外 Java Script 经典封装
图7

国内外 Java Script 经典封装
图8

  七、多样化的垂直菜单:附件

  别具风格的方块式垂直折叠菜单,目前有8种风格可以运用,如果你已经厌烦WEB上平凡的菜单,这套在国外颇受欢迎的菜单肯定是你的最佳首选。

国内外 Java Script 经典封装
图9
  

  八、多样化的连结提示效果:下载

  这个连结提示样式允许你直接写入css与html,共有14项功能可以让你自订。

国内外 Java Script 经典封装
图10

  九、侧栏式折叠菜单:下载

  这是一个侧栏式的折叠菜单,它允许你设置它是否有过渡效果、侧栏菜单是否自动伸缩、菜单项切换是否允许动画过渡、是否轮替切换等多项设置,并且也有多种样式可以运用。

  这个脚本有个很好玩的东东,下载并且解压後,请进入samples的目录并打show.html看看效果,我不知道这效果容不容易实现,但是这效果很牛,菜单全自动运行的~

国内外 Java Script 经典封装
图11
  

  十、图形滚动条:下载

  老外写的图形滚动条,有多种样式,在ie里头还支持滚轮滚动。

国内外 Java Script 经典封装
图12

  十一、图片倒影效果:下载
  说到图片倒影,不外乎就是直接作成图片跟css滤镜来实现,但是这个是用Java Script实现的,值得借镜。

国内外 Java Script 经典封装
图13

  十二、代码自动高亮:下载

  虽说这不是什麽新东西,但总是会有人需要吧,而且想学正则表达的人,这肯定是最佳借镜的作品。

国内外 Java Script 经典封装
图14
  
  

  十三、酷似flash效果的图片展示:下载

  这个老外牛到有点变态,这图片展示效果已经跟FLASH没什麽两样,用Java Script写的耶。

国内外 Java Script 经典封装
图15

  十四、让ie6支援png图档:下载

国内外 Java Script 经典封装
图16

  这个问题之前被很多人讨论过,我就不多说什麽了,有需要下吧。

posted @ 2007-12-13 17:29 比特鼠 阅读(5319) | 评论 (5)编辑 收藏

在一个老外的Blog上看到了这个网站,发现原来是一个以C语言为基准的性能比较网站!

Java还算不错,Ruby就不怎么样了, 在脚本语言中居然排在了最后!

看来,解析性的语言玩起来是简单方便了,可是却是以损失性能为代价的!
posted @ 2007-12-13 16:34 比特鼠 阅读(453) | 评论 (0)编辑 收藏

BIG-ENDIAN(大字节序、高字节序)
LITTLE-ENDIAN(小字节序、低字节序)
主机字节序
网络字节顺序
JAVA字节序

1.BIG-ENDIAN、LITTLE-ENDIAN跟多字节类型的数据有关的比如int,short,long型,而对单字节数据byte却没有影响。BIG-ENDIAN就是低位字节排放在内存的低端,高位字节排放在内存的高端。而LITTLE-ENDIAN正好相反。
比如 int a = 0x05060708
在BIG-ENDIAN的情况下存放为:
字节号 0 1 2 3
数据 05 06 07 08
在LITTLE-ENDIAN的情况下存放为:
字节号 0 1 2 3
数据 08 07 06 05

2.BIG-ENDIAN、LITTLE-ENDIAN、跟CPU有关的,每一种CPU不是BIG-ENDIAN就是LITTLE-ENDIAN、。IA架构的CPU中是Little-Endian,而PowerPC 、SPARC和Motorola处理器。这其实就是所谓的主机字节序。而网络字节序是指数据在网络上传输时是大头还是小头的,在Internet的网络字节序是BIG-ENDIAN。所谓的JAVA字节序指的是在JAVA虚拟机中多字节类型数据的存放顺序,JAVA字节序也是BIG-ENDIAN。

3.所以在用C/C++写通信程序时,在发送数据前务必用htonl和htons去把整型和短整型的数据进行从主机字节序到网络字节序的转换,而接收数据后对于整型和短整型数据则必须调用ntohl和ntohs实现从网络字节序到主机字节序的转换。如果通信的一方是JAVA程序、一方是C/C++程序时,则需要在C/C++一侧使用以上几个方法进行字节序的转换,而JAVA一侧,则不需要做任何处理,因为JAVA字节序与网络字节序都是BIG-ENDIAN,只要C/C++一侧能正确进行转换即可(发送前从主机序到网络序,接收时反变换)。如果通信的双方都是JAVA,则根本不用考虑字节序的问题了。

4.如果网络上全部是PowerPC,SPARC和Motorola CPU的主机那么不会出现任何问题,但由于实际存在大量的IA架构的CPU,所以经常出现数据传输错误。

5.文章开头所提出的问题,就是因为程序运行在X86架构的PC SERVER上,发送数据的一端用C实现的,接收一端是用JAVA实现的,而发送端在发送数据前未进行从主机字节序到网络字节序的转换,这样接收端接收到的是LITTLE-ENDIAN的数据,数据解释自然出错。
具体数据如下,实际发送的数据为23578
发送端发送数据: 1A 5C
接收端接收到数据后,按BIG-ENDIAN进行解释具体数据是多少?你们自己去计算并比较吧!


===============================================================================================

Big Endian and Little Endian

    谈到字节序的问题,必然牵涉到两大CPU派系。那就是Motorola的PowerPC系列CPU和Intel的x86系列CPU。PowerPC系列采用big endian方式存储数据,而x86系列则采用little endian方式存储数据。那么究竟什么是big endian,什么又是little endian呢?

    其实big endian是指低地址存放最高有效字节(MSB),而little endian则是低地址存放最低有效字节(LSB),即常说的低位在先,高位在后。
    用文字说明可能比较抽象,下面用图像加以说明。比如数字0x12345678在两种不同字节序CPU中的存储顺序如下所示:

Big Endian

  低地址                           高地址
  ----------------------------------------->
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |     12     |      34    |     56      |     78    |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

Little Endian

  低地址                           高地址
  ----------------------------------------->
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |     78     |      56    |     34      |     12    |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

    从上面两图可以看出,采用big endian方式存储数据是符合我们人类的思维习惯的。而little endian,!@#$%^&*,见鬼去吧 -_-|||

    为什么要注意字节序的问题呢?你可能这么问。当然,如果你写的程序只在单机环境下面运行,并且不和别人的程序打交道,那么你完全可以忽略字节序的存在。但是,如果你的程序要跟别人的程序产生交互呢?尤其是当你把你在微机上运算的结果运用到计算机群上去的话。在这里我想说说两种语言。C/C++语言编写的程序里数据存储顺序是跟编译平台所在的CPU相关的,而JAVA编写的程序则唯一采用big endian方式来存储数据。试想,如果你用C/C++语言在x86平台下编写的程序跟别人的JAVA程序互通时会产生什么结果?就拿上面的 0x12345678来说,你的程序传递给别人的一个数据,将指向0x12345678的指针传给了JAVA程序,由于JAVA采取big endian方式存储数据,很自然的它会将你的数据翻译为0x78563412。什么?竟然变成另外一个数字了?是的,就是这种后果。因此,在你的C程序传给JAVA程序之前有必要进行字节序的转换工作。

    无独有偶,所有网络协议也都是采用big endian的方式来传输数据的。所以有时我们也会把big endian方式称之为网络字节序。当两台采用不同字节序的主机通信时,在发送数据之前都必须经过字节序的转换成为网络字节序后再进行传输。ANSI C中提供了四个转换字节序的宏。


========================================================================================================

/**
* 通信格式转换
*
* Java和一些windows编程语言如c、c++、delphi所写的网络程序进行通讯时,需要进行相应的转换
* 高、低字节之间的转换
* windows的字节序为低字节开头
* linux,unix的字节序为高字节开头
* java则无论平台变化,都是高字节开头
*/

public class FormatTransfer {
/**
  * 将int转为低字节在前,高字节在后的byte数组
  * @param n int
  * @return byte[]
  */
public static byte[] toLH(int n) {
  byte[] b = new byte[4];
  b[0] = (byte) (n & 0xff);
  b[1] = (byte) (n >> 8 & 0xff);
  b[2] = (byte) (n >> 16 & 0xff);
  b[3] = (byte) (n >> 24 & 0xff);
  return b;
}

/**
  * 将int转为高字节在前,低字节在后的byte数组
  * @param n int
  * @return byte[]
  */
public static byte[] toHH(int n) {
  byte[] b = new byte[4];
  b[3] = (byte) (n & 0xff);
  b[2] = (byte) (n >> 8 & 0xff);
  b[1] = (byte) (n >> 16 & 0xff);
  b[0] = (byte) (n >> 24 & 0xff);
  return b;
}

/**
  * 将short转为低字节在前,高字节在后的byte数组
  * @param n short
  * @return byte[]
  */
public static byte[] toLH(short n) {
  byte[] b = new byte[2];
  b[0] = (byte) (n & 0xff);
  b[1] = (byte) (n >> 8 & 0xff);
  return b;
}

/**
  * 将short转为高字节在前,低字节在后的byte数组
  * @param n short
  * @return byte[]
  */
public static byte[] toHH(short n) {
  byte[] b = new byte[2];
  b[1] = (byte) (n & 0xff);
  b[0] = (byte) (n >> 8 & 0xff);
  return b;
}

 

/**
  * 将将int转为高字节在前,低字节在后的byte数组

public static byte[] toHH(int number) {
  int temp = number;
  byte[] b = new byte[4];
  for (int i = b.length - 1; i > -1; i--) {
    b = new Integer(temp & 0xff).byteValue();
    temp = temp >> 8;
  }
  return b;
}

public static byte[] IntToByteArray(int i) {
    byte[] abyte0 = new byte[4];
    abyte0[3] = (byte) (0xff & i);
    abyte0[2] = (byte) ((0xff00 & i) >> 8);
    abyte0[1] = (byte) ((0xff0000 & i) >> 16);
    abyte0[0] = (byte) ((0xff000000 & i) >> 24);
    return abyte0;
}


*/

/**
  * 将float转为低字节在前,高字节在后的byte数组
  */
public static byte[] toLH(float f) {
  return toLH(Float.floatToRawIntBits(f));
}

/**
  * 将float转为高字节在前,低字节在后的byte数组
  */
public static byte[] toHH(float f) {
  return toHH(Float.floatToRawIntBits(f));
}

/**
  * 将String转为byte数组
  */
public static byte[] stringToBytes(String s, int length) {
  while (s.getBytes().length < length) {
    s += " ";
  }
  return s.getBytes();
}


/**
  * 将字节数组转换为String
  * @param b byte[]
  * @return String
  */
public static String bytesToString(byte[] b) {
  StringBuffer result = new StringBuffer("");
  int length = b.length;
  for (int i=0; i<length; i++) {
    result.append((char)(b & 0xff));
  }
  return result.toString();
}

/**
  * 将字符串转换为byte数组
  * @param s String
  * @return byte[]
  */
public static byte[] stringToBytes(String s) {
  return s.getBytes();
}

/**
  * 将高字节数组转换为int
  * @param b byte[]
  * @return int
  */
public static int hBytesToInt(byte[] b) {
  int s = 0;
  for (int i = 0; i < 3; i++) {
    if (b >= 0) {
    s = s + b;
    } else {
    s = s + 256 + b;
    }
    s = s * 256;
  }
  if (b[3] >= 0) {
    s = s + b[3];
  } else {
    s = s + 256 + b[3];
  }
  return s;
}

/**
  * 将低字节数组转换为int
  * @param b byte[]
  * @return int
  */
public static int lBytesToInt(byte[] b) {
  int s = 0;
  for (int i = 0; i < 3; i++) {
    if (b[3-i] >= 0) {
    s = s + b[3-i];
    } else {
    s = s + 256 + b[3-i];
    }
    s = s * 256;
  }
  if (b[0] >= 0) {
    s = s + b[0];
  } else {
    s = s + 256 + b[0];
  }
  return s;
}


/**
  * 高字节数组到short的转换
  * @param b byte[]
  * @return short
  */
public static short hBytesToShort(byte[] b) {
  int s = 0;
  if (b[0] >= 0) {
    s = s + b[0];
    } else {
    s = s + 256 + b[0];
    }
    s = s * 256;
  if (b[1] >= 0) {
    s = s + b[1];
  } else {
    s = s + 256 + b[1];
  }
  short result = (short)s;
  return result;
}

/**
  * 低字节数组到short的转换
  * @param b byte[]
  * @return short
  */
public static short lBytesToShort(byte[] b) {
  int s = 0;
  if (b[1] >= 0) {
    s = s + b[1];
    } else {
    s = s + 256 + b[1];
    }
    s = s * 256;
  if (b[0] >= 0) {
    s = s + b[0];
  } else {
    s = s + 256 + b[0];
  }
  short result = (short)s;
  return result;
}

/**
  * 高字节数组转换为float
  * @param b byte[]
  * @return float
  */
public static float hBytesToFloat(byte[] b) {
  int i = 0;
  Float F = new Float(0.0);
  i = ((((b[0]&0xff)<<8 | (b[1]&0xff))<<8) | (b[2]&0xff))<<8 | (b[3]&0xff);
  return F.intBitsToFloat(i);
}

/**
  * 低字节数组转换为float
  * @param b byte[]
  * @return float
  */
public static float lBytesToFloat(byte[] b) {
  int i = 0;
  Float F = new Float(0.0);
  i = ((((b[3]&0xff)<<8 | (b[2]&0xff))<<8) | (b[1]&0xff))<<8 | (b[0]&0xff);
  return F.intBitsToFloat(i);
}

/**
  * 将byte数组中的元素倒序排列
  */
public static byte[] bytesReverseOrder(byte[] b) {
  int length = b.length;
  byte[] result = new byte[length];
  for(int i=0; i<length; i++) {
    result[length-i-1] = b;
  }
  return result;
}

/**
  * 打印byte数组
  */
public static void printBytes(byte[] bb) {
  int length = bb.length;
  for (int i=0; i<length; i++) {
    System.out.print(bb + " ");
  }
  System.out.println("");
}

public static void logBytes(byte[] bb) {
  int length = bb.length;
  String ut = "";
  for (int i=0; i<length; i++) {
    ut = out + bb + " ";
  }

}

/**
  * 将int类型的值转换为字节序颠倒过来对应的int值
  * @param i int
  * @return int
  */
public static int reverseInt(int i) {
  int result = FormatTransfer.hBytesToInt(FormatTransfer.toLH(i));
  return result;
}

/**
  * 将short类型的值转换为字节序颠倒过来对应的short值
  * @param s short
  * @return short
  */
public static short reverseShort(short s) {
  short result = FormatTransfer.hBytesToShort(FormatTransfer.toLH(s));
  return result;
}

/**
  * 将float类型的值转换为字节序颠倒过来对应的float值
  * @param f float
  * @return float
  */
public static float reverseFloat(float f) {
  float result = FormatTransfer.hBytesToFloat(FormatTransfer.toLH(f));
  return result;
}

}

posted @ 2007-10-24 09:57 比特鼠 阅读(3392) | 评论 (0)编辑 收藏

实现Leader/Fellows模式的项目--CAJ, 地址是:http://caj.cosylab.com/
posted @ 2007-10-23 14:57 比特鼠 阅读(194) | 评论 (0)编辑 收藏

网址:http://httpd.apache.org/docs/2.0/programs/ab.html
posted @ 2007-10-23 14:53 比特鼠 阅读(263) | 评论 (0)编辑 收藏

缓冲区基础

抽象类Buffer是java.nio包支持缓冲区的基础。 Buffer 的工作方式就象内存中用于读写基本数据类型的 RandomAccessFile 。象 RandomAccessFile 一样,使用 Buffer ,所执行的下一个操作(读/写)在当前某个位置发生。执行读/写操作中的任一个都会改变那个位置,所以在写操作之后进行读操作不会读到刚才所写的内容,而会读到刚才所写内容之后的数据。 Buffer 提供了四个指示方法,用于访问线性结构(从最高值到最低值):

capacity() :表明缓冲区的容量大小, 一旦确定了大小, 将不能再改变;
limit() :告诉您到目前为止已经往缓冲区填了多少字节,或者让您用 :limit(int newLimit) 来改变这个限制
position() :告诉您当前的位置,以执行下一个读/写操作
mark() :为了稍后用 reset() 进行重新设置而记住某个位置
flip() :交换限制指针和位置指针,然后将位置置为 0,并废弃已经做的mark标记

缓冲区的基本操作是读 get() 和写 put() ;然而,这些方法在子类中都是针对每种数据类型的特定方法。为了说明这一情况,让我们研究一个简单示例,该示例演示了从同一个缓冲区读和写一个字符。在清单 1 中, flip() 方法交换限制和位置,然后将位置置为 0,并废弃标记,让您读刚才所写的数据:


清单 1. 读/写示例
import java.nio.*;
...
CharBuffer buff = ...;
buff.put('A');
buff.flip();
char c = buff.get();
System.out.println("An A: " + c);
 


现在让我们研究一些具体的 Buffer 子类。

 

缓冲区类型

Merlin 具有 7 种特定的 Buffer 类型,每种类型对应着一个基本数据类型(不包括 boolean):

ByteBuffer       //存放任何除boolean类型外的其他基本类型
CharBuffer       //存放char
DoubleBuffer     //存放double
FloatBuffer      //存放float
IntBuffer        //存放int
LongBuffer       //存放long
ShortBuffer      //存放short

在本文后面,我将讨论第 8 种类型 MappedByteBuffer ,它用于内存映射文件。如果您必须使用的类型不是这些基本类型,则可以先从 ByteBuffer 获得字节类型,然后将其转换成 Object 或其它任何类型。


创建缓冲区
一共有两种类型的缓冲区,直接缓冲区和非直接缓冲区。

在创建缓冲区时,可以要求创建直接缓冲区,创建直接缓冲区的成本要比创建间接缓冲区高,但这可以使运行时环境直接在该缓冲区上进行较快的本机 I/O 操作。因为创建直接缓冲区所增加的成本,所以直接缓冲区只用于长生存期的缓冲区,而不用于短生存期、一次性且用完就丢弃的缓冲区。而且,只能在 ByteBuffer 这个级别上创建直接缓冲区,如果希望使用其它类型,则必须将 Buffer 转换成更具体的类型。

判断一个缓冲区是否是直接缓冲区,可以调用isDirect()方法。

有三种方式来获取一个缓冲区的对象:
a. 调用allocate()或者allocateDirect()方法直接分配,其中allocateDirect()返回的是直接缓冲区。
b. 包装一个数组,如:
      byte[] b = new byte[1024];
      ByteBuffer bb = ByteBuffer.wrap(b);
c. 内存映射,即调用FileChannel的map()方法。

缓冲区基本属性
这几个属性是每个缓冲区都有的并且是常用的操作。
a. 容量(capacity),缓冲区大小
b. 限制(limit),第一个不应被读取或写入的字节的索引,总是小于容量。
c. 位置(position),下一个被读取或写入的字节的索引,总是小于限制。
d. clear()方法:设置limit为capacity,position为0。
e. filp()方法:设置limit为当前position,然后设置position为0。
f. rewind()方法:保持limit不变,设置position为0。

缓冲区数据操作
操作包括了读取和写入数据两种。
读取数据使用get()及其系列方法,除boolean外,每一种类型包括了对应的get()方法,如getInt(),getChar()等,get()方法用来读取字节,支持相对和绝对索引两种方式。
写入数据使用put()及其系列方法,和get()方法是对应的。

package nio;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class BufferDemo ...{

    
    public static void main(String[] args) throws Exception...{
        //分配一个非直接缓冲区
        ByteBuffer bb = ByteBuffer.allocate(100);
        //向缓冲区写入0到100的字节制
        for(int i = 0; i <100; i++)...{
            byte b = (byte) (Math.random() * 100);
            bb.put(b);
        }
        
        System.out.println("写入文件前的缓冲区数据");
        bb.flip();
        while(bb.hasRemaining())
            System.out.print(bb.get() + " ");
        System.out.println();
        
        //获取一个关联到文件buffer.txt的信道
        FileChannel fc = new FileOutputStream("buffer.txt").getChannel();
        //将缓冲区数据写到文件中
        bb.flip();
        fc.write(bb);
        //防止缓存
        fc.force(true);
        //关闭信道
        fc.close();
        bb = null;
        fc = null;
        
        //下面从文件中读取数据
        fc = new FileInputStream("buffer.txt").getChannel();
        ByteBuffer bb2 = ByteBuffer.allocate((int) fc.size());
        fc.read(bb2);
        System.out.println("从文件读取的缓冲区数据");
        bb2.flip();
        while(bb2.hasRemaining())
            System.out.print(bb2.get() + " ");
        System.out.println();
        fc.close();
        bb2 = null;
        fc = null;
        

    }

}

内存映射文件

第 8 种 Buffer 类型 MappedByteBuffer 只是一种特殊的 ByteBuffer 。 MappedByteBuffer 将文件所在区域直接映射到内存。通常,该区域包含整个文件,但也可以只映射部分文件。所以,必须指定要映射文件的哪部分。而且,与其它 Buffer 对象一样,这里没有构造函数;必须让 java.nio.channels.FileChannel 的 map() 方法来获取 MappedByteBuffer 。此外,无需过多涉及通道就可以用 getChannel() 方法从 FileInputStream 或 FileOutputStream 获取 FileChannel 。通过从命令行传入文件名来读取文本文件的内容,清单 4 显示了 MappedByteBuffer :


清单 4. 读取内存映射文本文件
import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.nio.charset.*;
public class ReadFileBuff {
  public static void main(String args[]) throws IOException {
     if (args.length != 0) {
      String filename = args[0];
      FileInputStream fis = new FileInputStream(filename);
      FileChannel channel = fis.getChannel();
      int length = (int)channel.size();
      MappedByteBuffer byteBuffer =
        channel.map(FileChannel.MapMode.READ_ONLY, 0, length);
      Charset charset = Charset.forName("ISO-8859-1");
      CharsetDecoder decoder = charset.newDecoder();
      CharBuffer charBuffer = decoder.decode(byteBuffer);
      for (int i=0, n=charBuffer.length(); i<n; i++) {
        System.out.print(charBuffer.get());
      }
    }
  }
}

posted @ 2007-08-01 11:13 比特鼠 阅读(3813) | 评论 (0)编辑 收藏

  1. 左移操作: x << n
    x可以是byte, short, char, int, long基本类型, n(位移量)只能是int型

    编译器的执行步骤:
    1) 如果x是byte, short, char类型, 则将x提升为int;
    2) 如果x是byte, short, char, int类型, 则n被重新赋值(过程是:取n的补码的低5位再转成十进制的int值,相当对n取32模: n=n%32);
       如果x是long型, 则n被重新赋值(过程是:取n的补码的低6位再转成十进制的int值,相当对n取64模: n=n%64);
       (因为int类型为4个字节,即32位,移动32位将没有任何意义.对于long则是模64)
    3) 对x左移n个位数, 整个表达式产生一个新值(x的值不变);
  2. <<是左移符号,列x<<1,就是x的内容左移一位(x的内容并不改变)
  3. >>是带符号位的右移符号,x>>1就是x的内容右移一位,如果开头是1则补1,是0责补0,(x的内容并不改变).
  4. >>>是不带符号位的右移,x>>>1就是x的内容右移一位,开头补0(x的内容并不改变)
posted @ 2007-08-01 10:12 比特鼠 阅读(2795) | 评论 (0)编辑 收藏

原文地址 http://www.programbbs.com/doc/2433.htm
为什么会排队等待?

下面的这个简单的 Java 程序完成四项不相关的任务。这样的程序有单个控制线程,控制在这四个任务之间线性地移动。此外,因为所需的资源 — 打印机、磁盘、数据库和显示屏 -- 由于硬件和软件的限制都有内在的潜伏时间,所以每项任务都包含明显的等待时间。因此,程序在访问数据库之前必须等待打印机完成打印文件的任务,等等。如果您正在等待程序的完成,则这是对计算资源和您的时间的一种拙劣使用。改进此程序的一种方法是使它成为多线程的。
 
四项不相关的任务
 

class myclass {
    static public void main(String args[]) {
        print_a_file();
        manipulate_another_file();
        access_database();
        draw_picture_on_screen();
    }
}

在本例中,每项任务在开始之前必须等待前一项任务完成,即使所涉及的任务毫不相关也是这样。但是,在现实生活中,我们经常使用多线程模型。我们在处理某些任务的同时也可以让孩子、配偶和父母完成别的任务。例如,我在写信的同时可能打发我的儿子去邮局买邮票。用软件术语来说,这称为多个控制(或执行)线程。

可以用两种不同的方法来获得多个控制线程:

多个进程
在大多数操作系统中都可以创建多个进程。当一个程序启动时,它可以为即将开始的每项任务创建一个进程,并允许它们同时运行。当一个程序因等待网络访问或用户输入而被阻塞时,另一个程序还可以运行,这样就增加了资源利用率。但是,按照这种方式创建每个进程要付出一定的代价:设置一个进程要占用相当一部分处理器时间和内存资源。而且,大多数操作系统不允许进程访问其他进程的内存空间。因此,进程间的通信很不方便,并且也不会将它自己提供给容易的编程模型。


线程
线程也称为轻型进程 (LWP)。因为线程只能在单个进程的作用域内活动,所以创建线程比创建进程要廉价得多。这样,因为线程允许协作和数据交换,并且在计算资源方面非常廉价,所以线程比进程更可取。线程需要操作系统的支持,因此不是所有的机器都提供线程。Java 编程语言,作为相当新的一种语言,已将线程支持与语言本身合为一体,这样就对线程提供了强健的支持。


使用 Java 编程语言实现线程
Java 编程语言使多线程如此简单有效,以致于某些程序员说它实际上是自然的。尽管在 Java 中使用线程比在其他语言中要容易得多,仍然有一些概念需要掌握。要记住的一件重要的事情是 main() 函数也是一个线程,并可用来做有用的工作。程序员只有在需要多个线程时才需要创建新的线程。

Thread 类
Thread 类是一个具体的类,即不是抽象类,该类封装了线程的行为。要创建一个线程,程序员必须创建一个从 Thread 类导出的新类。程序员必须覆盖 Thread 的 run() 函数来完成有用的工作。用户并不直接调用此函数;而是必须调用 Thread 的 start() 函数,该函数再调用 run()。下面的代码说明了它的用法:

创建两个新线程

import java.util.*;

class TimePrinter extends Thread {
    int pauseTime;

    String name;

    public TimePrinter(int x, String n) {
        pauseTime = x;
        name = n;
    }

    public void run() {
        while (true) {
            try {
                System.out.println(name + ":"
                        + new Date(System.currentTimeMillis()));
                Thread.sleep(pauseTime);
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }

    static public void main(String args[]) {
        TimePrinter tp1 = new TimePrinter(1000, "Fast Guy");
        tp1.start();
        TimePrinter tp2 = new TimePrinter(3000, "Slow Guy");
        tp2.start();

    }
}

在本例中,我们可以看到一个简单的程序,它按两个不同的时间间隔(1 秒和 3 秒)在屏幕上显示当前时间。这是通过创建两个新线程来完成的,包括 main() 共三个线程。但是,因为有时要作为线程运行的类可能已经是某个类层次的一部分,所以就不能再按这种机制创建线程。虽然在同一个类中可以实现任意数量的接口,但 Java 编程语言只允许一个类有一个父类。同时,某些程序员避免从 Thread 类导出,因为它强加了类层次。对于这种情况,就要 runnable 接口。

Runnable 接口
此接口只有一个函数,run(),此函数必须由实现了此接口的类实现。但是,就运行这个类而论,其语义与前一个示例稍有不同。我们可以用 runnable 接口改写前一个示例。(不同的部分用黑体表示。)

创建两个新线程而不强加类层次

import java.util.*;

class TimePrinter implements Runnable {
    int pauseTime;

    String name;

    public TimePrinter(int x, String n) {
        pauseTime = x;
        name = n;
    }

    public void run() {
        while (true) {
            try {
                System.out.println(name + ":"
                        + new Date(System.currentTimeMillis()));
                Thread.sleep(pauseTime);
            } catch (Exception e) {
                System.out.println(e);
            }
        }
    }

    static public void main(String args[]) {
        Thread t1 = new Thread(new TimePrinter(1000, "Fast Guy"));
        t1.start();
        Thread t2 = new Thread(new TimePrinter(3000, "Slow Guy"));
        t2.start();

    }
}

请注意,当使用 runnable 接口时,您不能直接创建所需类的对象并运行它;必须从 Thread 类的一个实例内部运行它。许多程序员更喜欢 runnable 接口,因为从 Thread 类继承会强加类层次。

synchronized 关键字
到目前为止,我们看到的示例都只是以非常简单的方式来利用线程。只有最小的数据流,而且不会出现两个线程访问同一个对象的情况。但是,在大多数有用的程序中,线程之间通常有信息流。试考虑一个金融应用程序,它有一个 Account 对象,如下例中所示:

一个银行中的多项活动

public class Account {
    String holderName;

    float amount;

    public Account(String name, float amt) {
        holderName = name;
        amount = amt;
    }

    public void deposit(float amt) {
        amount += amt;
    }

    public void withdraw(float amt) {
        amount -= amt;
    }

    public float checkBalance() {
        return amount;
    }
}

在此代码样例中潜伏着一个错误。如果此类用于单线程应用程序,不会有任何问题。但是,在多线程应用程序的情况中,不同的线程就有可能同时访问同一个 Account 对象,比如说一个联合帐户的所有者在不同的 ATM 上同时进行访问。在这种情况下,存入和支出就可能以这样的方式发生:一个事务被另一个事务覆盖。这种情况将是灾难性的。但是,Java 编程语言提供了一种简单的机制来防止发生这种覆盖。每个对象在运行时都有一个关联的锁。这个锁可通过为方法添加关键字 synchronized 来获得。这样,修订过的 Account 对象(如下所示)将不会遭受像数据损坏这样的错误:

对一个银行中的多项活动进行同步处理

public class Account {
    String holderName;

    float amount;

    public Account(String name, float amt) {
        holderName = name;
        amount = amt;
    }

    public synchronized void deposit(float amt) {
        amount += amt;
    }

    public synchronized void withdraw(float amt) {
        amount -= amt;
    }

    public float checkBalance() {
        return amount;
    }
}

deposit() 和 withdraw() 函数都需要这个锁来进行操作,所以当一个函数运行时,另一个函数就被阻塞。请注意,checkBalance() 未作更改,它严格是一个读函数。因为 checkBalance() 未作同步处理,所以任何其他方法都不会阻塞它,它也不会阻塞任何其他方法,不管那些方法是否进行了同步处理。

Java 编程语言中的高级多线程支持

线程组
线程是被个别创建的,但可以将它们归类到线程组中,以便于调试和监视。只能在创建线程的同时将它与一个线程组相关联。在使用大量线程的程序中,使用线程组组织线程可能很有帮助。可以将它们看作是计算机上的目录和文件结构。

线程间发信
当线程在继续执行前需要等待一个条件时,仅有 synchronized 关键字是不够的。虽然 synchronized 关键字阻止并发更新一个对象,但它没有实现线程间发信。Object 类为此提供了三个函数:wait()、notify() 和 notifyAll()。以全球气候预测程序为例。这些程序通过将地球分为许多单元,在每个循环中,每个单元的计算都是隔离进行的,直到这些值趋于稳定,然后相邻单元之间就会交换一些数据。所以,从本质上讲,在每个循环中各个线程都必须等待所有线程完成各自的任务以后才能进入下一个循环。这个模型称为屏蔽同步,下例说明了这个模型:

屏蔽同步

public class BSync {
    int totalThreads;

    int currentThreads;

    public BSync(int x) {
        totalThreads = x;
        currentThreads = 0;
    }

    public synchronized void waitForAll() {
        currentThreads++;
        if (currentThreads < totalThreads) {
            try {
                wait();
            } catch (Exception e) {
            }
        } else {
            currentThreads = 0;
            notifyAll();
        }
    }
}

当对一个线程调用 wait() 时,该线程就被有效阻塞,只到另一个线程对同一个对象调用 notify() 或 notifyAll() 为止。因此,在前一个示例中,不同的线程在完成它们的工作以后将调用 waitForAll() 函数,最后一个线程将触发 notifyAll() 函数,该函数将释放所有的线程。第三个函数 notify() 只通知一个正在等待的线程,当对每次只能由一个线程使用的资源进行访问限制时,这个函数很有用。但是,不可能预知哪个线程会获得这个通知,因为这取决于 Java 虚拟机 (JVM) 调度算法。

将 CPU 让给另一个线程
当线程放弃某个稀有的资源(如数据库连接或网络端口)时,它可能调用 yield() 函数临时降低自己的优先级,以便某个其他线程能够运行。

守护线程
有两类线程:用户线程和守护线程。用户线程是那些完成有用工作的线程。守护线程是那些仅提供辅助功能的线程。Thread 类提供了 setDaemon() 函数。Java 程序将运行到所有用户线程终止,然后它将破坏所有的守护线程。在 Java 虚拟机 (JVM) 中,即使在 main 结束以后,如果另一个用户线程仍在运行,则程序仍然可以继续运行。

避免不提倡使用的方法
不提倡使用的方法是为支持向后兼容性而保留的那些方法,它们在以后的版本中可能出现,也可能不出现。Java 多线程支持在版本 1.1 和版本 1.2 中做了重大修订,stop()、suspend() 和 resume() 函数已不提倡使用。这些函数在 JVM 中可能引入微妙的错误。虽然函数名可能听起来很诱人,但请抵制诱惑不要使用它们。

调试线程化的程序
在线程化的程序中,可能发生的某些常见而讨厌的情况是死锁、活锁、内存损坏和资源耗尽。

死锁
死锁可能是多线程程序最常见的问题。当一个线程需要一个资源而另一个线程持有该资源的锁时,就会发生死锁。这种情况通常很难检测。但是,解决方案却相当好:在所有的线程中按相同的次序获取所有资源锁。例如,如果有四个资源 —A、B、C 和 D — 并且一个线程可能要获取四个资源中任何一个资源的锁,则请确保在获取对 B 的锁之前首先获取对 A 的锁,依此类推。如果“线程 1”希望获取对 B 和 C 的锁,而“线程 2”获取了 A、C 和 D 的锁,则这一技术可能导致阻塞,但它永远不会在这四个锁上造成死锁。

活锁
当一个线程忙于接受新任务以致它永远没有机会完成任何任务时,就会发生活锁。这个线程最终将超出缓冲区并导致程序崩溃。试想一个秘书需要录入一封信,但她一直在忙于接电话,所以这封信永远不会被录入。

内存损坏
如果明智地使用 synchronized 关键字,则完全可以避免内存错误这种气死人的问题。

资源耗尽
某些系统资源是有限的,如文件描述符。多线程程序可能耗尽资源,因为每个线程都可能希望有一个这样的资源。如果线程数相当大,或者某个资源的侯选线程数远远超过了可用的资源数,则最好使用资源池。一个最好的示例是数据库连接池。只要线程需要使用一个数据库连接,它就从池中取出一个,使用以后再将它返回池中。资源池也称为资源库。

调试大量的线程
有时一个程序因为有大量的线程在运行而极难调试。在这种情况下,下面的这个类可能会派上用场:

public class Probe extends Thread {
    public Probe() {
    }

    public void run() {

        while (true) {
            Thread[] x = new Thread[100];
            Thread.enumerate(x);

            for (int i = 0; i < 100; i++) {
                Thread t = x[i];
                if (t == null)
                    break;
                else
                    System.out.println(t.getName() + "\t" + t.getPriority()
                            + "\t" + t.isAlive() + "\t" + t.isDaemon());
            }
        }
    }
}

限制线程优先级和调度
Java 线程模型涉及可以动态更改的线程优先级。本质上,线程的优先级是从 1 到 10 之间的一个数字,数字越大表明任务越紧急。JVM 标准首先调用优先级较高的线程,然后才调用优先级较低的线程。但是,该标准对具有相同优先级的线程的处理是随机的。如何处理这些线程取决于基层的操作系统策略。在某些情况下,优先级相同的线程分时运行;在另一些情况下,线程将一直运行到结束。请记住,Java 支持 10 个优先级,基层操作系统支持的优先级可能要少得多,这样会造成一些混乱。因此,只能将优先级作为一种很粗略的工具使用。最后的控制可以通过明智地使用 yield() 函数来完成。通常情况下,请不要依靠线程优先级来控制线程的状态。

小结
本文说明了在 Java 程序中如何使用线程。像是否应该使用线程这样的更重要的问题在很大程序上取决于手头的应用程序。决定是否在应用程序中使用多线程的一种方法是,估计可以并行运行的代码量。并记住以下几点:

使用多线程不会增加 CPU 的能力。但是如果使用 JVM 的本地线程实现,则不同的线程可以在不同的处理器上同时运行(在多 CPU 的机器中),从而使多 CPU 机器得到充分利用。
如果应用程序是计算密集型的,并受 CPU 功能的制约,则只有多 CPU 机器能够从更多的线程中受益。
当应用程序必须等待缓慢的资源(如网络连接或数据库连接)时,或者当应用程序是非交互式的时,多线程通常是有利的。
基于 Internet 的软件有必要是多线程的;否则,用户将感觉应用程序反映迟钝。例如,当开发要支持大量客户机的服务器时,多线程可以使编程较为容易。在这种情况下,每个线程可以为不同的客户或客户组服务,从而缩短了响应时间。

某些程序员可能在 C 和其他语言中使用过线程,在那些语言中对线程没有语言支持。这些程序员可能通常都被搞得对线程失去了信心。

posted @ 2007-07-30 09:36 比特鼠 阅读(199) | 评论 (0)编辑 收藏

众所周知, Java在从XML文件中装载内容到内存过程中,不论用何种方式,IO操作的开销都无可避免。本文尝试比较dom4j中的XPP3和SAX两种方式装载XML文件的性能,以便将IO操作的开销降到最小!

package gz.lwm;

import java.io.File;
import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XPP3Reader;

public class TestDom4j {
 private static final Logger log = Logger.getLogger(TestDom4j.class);
 private static long bt; 
 
 public static void main(String[] args) {
  Document doc = DocumentHelper.createDocument();   
  //先运行getXmlSAX()
  bt = System.currentTimeMillis();
  String strXml = getXmlSAX("xml/test.xml");
  if(log.isDebugEnabled()){
   log.debug("\ngetXmlSAX() use time: " + (System.currentTimeMillis() - bt) + " millis\n");
  }

  //再运行getXmlXPP3()
  bt = System.currentTimeMillis();
  String s1 =getXmlXPP3("xml/test.xml");
  if(log.isDebugEnabled()){
   log.debug("\ngetXmlXPP3() use time: " + (System.currentTimeMillis() - bt) + " millis\n");
  }
  
  
 }
 
 public static String getXmlSAX(String xmlFile){
  String result = "";
  try {
   SAXReader reader = new SAXReader();
   Document document = reader.read(new File(xmlFile));
   result = document.asXML();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return result;
 }
 
 public static String getXmlXPP3(String xmlFile){
  String result = "";
  try {
   XPP3Reader reader = new XPP3Reader();
   Document document = reader.read(new File(xmlFile));
   result = document.asXML();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return result;
 }


 
}

有没有这一句"Document doc = DocumentHelper.createDocument()",对性能的影响很大,特别是对大xml文件(尽管并没有使用doc)

另外, getXmlXSAX()和getXmlXPP3()运行的先后次序对性能的影响也很大!

测试:
    在我的机器上,对一个100k左右的XML文件进行多次测试后的均值结果为:

    getXmlXPP3() use time: 265 millis
    ...
    getXmlXSAX() use time: 359 millis
    ...

结论:
    通过比较,在读取XML文件上,XPP3略为优于SAX!


注意:

要运行例子,classpath需包含:
dom4j-1.6.1.jar
jaxen-1.1-beta-10.jar
log4j-1.2.9.jar
pull-parser-2.1.10.jar
xpp3-1.1.4c.jar


参考:
dom4j :  http://www.dom4j.org/
XPP   :  http://www.extreme.indiana.edu/xgws/xsoap/xpp/

posted @ 2007-05-19 00:39 比特鼠 阅读(2498) | 评论 (0)编辑 收藏

   Namespace namespace ...

   //第一种方法
   Document doc = DocumentHelper.createDocument();
   Element root = doc.addElement("Root", namespace.getURI());
   Element eResultMessage = root.addElement("ResultMessage");

   结果为:
   <Root xmlns="http://aaaaaa"><ResultMessage>...</ResultMessage></Root>



   //第二种方法
   Document doc = DocumentHelper.createDocument();
   Element root = doc.addElement(("Root");
   root.add(namespace);
   Element eResultMessage = root.addElement("ResultMessage");
   
   结果为:
   <Root xmlns="
http://aaaaaa"><ResultMessage xmlns="">...</ResultMessage></Root>

posted @ 2007-05-17 21:08 比特鼠 阅读(2642) | 评论 (0)编辑 收藏

正则表达式语法 
 

正则表达式是一种文本模式,包括普通字符(例如,a 到 z 之间的字母)和特殊字符(称为“元字符”)。模式描述在搜索文本时要匹配的一个或多个字符串。

正则表达式示例
 
表达式  匹配 
/^\s*$/
 匹配空行。
 
/\d{2}-\d{5}/
 验证由两位数字、一个连字符再加 5 位数字组成的 ID 号。
 
/<\s*(\S+)(\s[^>]*)?>[\s\S]*<\s*\/\1\s*>/
 匹配 HTML 标记。
 

下表包含了元字符的完整列表以及它们在正则表达式上下文中的行为:

 
字符  说明 
\
 将下一字符标记为特殊字符、文本、反向引用或八进制转义符。例如,“n”匹配字符“n”。“\n”匹配换行符。序列“\\”匹配“\”,“\(”匹配“(”。
 
^
 匹配输入字符串开始的位置。如果设置了 RegExp 对象的 Multiline 属性,^ 还会与“\n”或“\r”之后的位置匹配。
 
$
 匹配输入字符串结尾的位置。如果设置了 RegExp 对象的 Multiline 属性,$ 还会与“\n”或“\r”之前的位置匹配。
 
*
 零次或多次匹配前面的字符或子表达式。例如,zo* 匹配“z”和“zoo”。* 等效于 {0,}。
 
+
 一次或多次匹配前面的字符或子表达式。例如,“zo+”与“zo”和“zoo”匹配,但与“z”不匹配。+ 等效于 {1,}。
 
?
 零次或一次匹配前面的字符或子表达式。例如,“do(es)?”匹配“do”或“does”中的“do”。? 等效于 {0,1}。
 
{n}
 n 是非负整数。正好匹配 n 次。例如,“o{2}”与“Bob”中的“o”不匹配,但与“food”中的两个“o”匹配。
 
{n,}
 n 是非负整数。至少匹配 n 次。例如,“o{2,}”不匹配“Bob”中的“o”,而匹配“foooood”中的所有 o。“o{1,}”等效于“o+”。“o{0,}”等效于“o*”。
 
{n,m}
 M 和 n 是非负整数,其中 n <= m。匹配至少 n 次,至多 m 次。例如,“o{1,3}”匹配“fooooood”中的头三个 o。'o{0,1}' 等效于 'o?'。注意:您不能将空格插入逗号和数字之间。
 
?
 当此字符紧随任何其他限定符(*、+、?、{n}、{n,}、{n,m})之后时,匹配模式是“非贪心的”。“非贪心的”模式匹配搜索到的、尽可能短的字符串,而默认的“贪心的”模式匹配搜索到的、尽可能长的字符串。例如,在字符串“oooo”中,“o+?”只匹配单个“o”,而“o+”匹配所有“o”。
 
.
 匹配除“\n”之外的任何单个字符。若要匹配包括“\n”在内的任意字符,请使用诸如“[\s\S]”之类的模式。
 
(pattern)
 匹配 pattern 并捕获该匹配的子表达式。可以使用 $0…$9 属性从结果“匹配”集合中检索捕获的匹配。若要匹配括号字符 ( ),请使用“\(”或者“\)”。
 
(?:pattern)
 匹配 pattern 但不捕获该匹配的子表达式,即它是一个非捕获匹配,不存储供以后使用的匹配。这对于用“or”字符 (|) 组合模式部件的情况很有用。例如,'industr(?:y|ies) 是比 'industry|industries' 更经济的表达式。
 
(?=pattern)
 执行正向预测先行搜索的子表达式,该表达式匹配处于匹配 pattern 的字符串的起始点的字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?=95|98|NT|2000)' 匹配“Windows 2000”中的“Windows”,但不匹配“Windows 3.1”中的“Windows”。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。
 
(?!pattern)
 执行反向预测先行搜索的子表达式,该表达式匹配不处于匹配 pattern 的字符串的起始点的搜索字符串。它是一个非捕获匹配,即不能捕获供以后使用的匹配。例如,'Windows (?!95|98|NT|2000)' 匹配“Windows 3.1”中的 “Windows”,但不匹配“Windows 2000”中的“Windows”。预测先行不占用字符,即发生匹配后,下一匹配的搜索紧随上一匹配之后,而不是在组成预测先行的字符后。
 
x|y
 匹配 x 或 y。例如,'z|food' 匹配“z”或“food”。'(z|f)ood' 匹配“zood”或“food”。
 
[xyz]
 字符集。匹配包含的任一字符。例如,“[abc]”匹配“plain”中的“a”。
 
[^xyz]
 反向字符集。匹配未包含的任何字符。例如,“[^abc]”匹配“plain”中的“p”。
 
[a-z]
 字符范围。匹配指定范围内的任何字符。例如,“[a-z]”匹配“a”到“z”范围内的任何小写字母。
 
[^a-z]
 反向范围字符。匹配不在指定的范围内的任何字符。例如,“[^a-z]”匹配任何不在“a”到“z”范围内的任何字符。
 
\b
 匹配一个字边界,即字与空格间的位置。例如,“er\b”匹配“never”中的“er”,但不匹配“verb”中的“er”。
 
\B
 非字边界匹配。“er\B”匹配“verb”中的“er”,但不匹配“never”中的“er”。
 
\cx
 匹配 x 指示的控制字符。例如,\cM 匹配 Control-M 或回车符。x 的值必须在 A-Z 或 a-z 之间。如果不是这样,则假定 c 就是“c”字符本身。
 
\d
 数字字符匹配。等效于 [0-9]。
 
\D
 非数字字符匹配。等效于 [^0-9]。
 
\f
 换页符匹配。等效于 \x0c 和 \cL。
 
\n
 换行符匹配。等效于 \x0a 和 \cJ。
 
\r
 匹配一个回车符。等效于 \x0d 和 \cM。
 
\s
 匹配任何空白字符,包括空格、制表符、换页符等。与 [ \f\n\r\t\v] 等效。
 
\S
 匹配任何非空白字符。与 [^ \f\n\r\t\v] 等效。
 
\t
 制表符匹配。与 \x09 和 \cI 等效。
 
\v
 垂直制表符匹配。与 \x0b 和 \cK 等效。
 
\w
 匹配任何字类字符,包括下划线。与“[A-Za-z0-9_]”等效。
 
\W
 与任何非单词字符匹配。与“[^A-Za-z0-9_]”等效。
 
\xn
 匹配 n,此处的 n 是一个十六进制转义码。十六进制转义码必须正好是两位数长。例如,“\x41”匹配“A”。“\x041”与“\x04”&“1”等效。允许在正则表达式中使用 ASCII 代码。
 
\num
 匹配 num,此处的 num 是一个正整数。到捕获匹配的反向引用。例如,“(.)\1”匹配两个连续的相同字符。
 
\n
 标识一个八进制转义码或反向引用。如果 \n 前面至少有 n 个捕获子表达式,那么 n 是反向引用。否则,如果 n 是八进制数 (0-7),那么 n 是八进制转义码。
 
\nm
 标识一个八进制转义码或反向引用。如果 \nm 前面至少有 nm 个捕获子表达式,那么 nm 是反向引用。如果 \nm 前面至少有 n 个捕获,则 n 是反向引用,后面跟有字符 m。如果两种前面的情况都不存在,则 \nm 匹配八进制值 nm,其中 n 和 m 是八进制数字 (0-7)。
 
\nml
 当 n 是八进制数 (0-3),m 和 l 是八进制数 (0-7) 时,匹配八进制转义码 nml。
 
\un
 匹配 n,其中 n 是以四位十六进制数表示的 Unicode 字符。例如,\u00A9 匹配版权符号 (?)。
 

posted @ 2007-05-17 10:48 比特鼠 阅读(7515) | 评论 (0)编辑 收藏

package com.sunrise.ocs.webservice.unicom.test;

import java.io.File;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.Iterator;

import javax.xml.parsers.DocumentBuilderFactory;

import org.apache.log4j.Logger;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.XPath;
import org.dom4j.io.SAXReader;

import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

public class TestDom4j {
 private static final Logger log = Logger.getLogger(TestDom4j.class);

 private static long bt;

 public static void main(String[] args) {
  String strXml = "";
  int b = 0;
  String file1 = "xml/CreateUserRequest.xml";
  String file2 = "xml/CancelUserRequest.xml";
  if(b==0){
   bt = System.currentTimeMillis();
   strXml = xmlFile2String(file1);
   if (log.isDebugEnabled()) {
    log.debug("\nxmlFile2String() use time: "
      + (System.currentTimeMillis() - bt) + " millis\n");
   }
  }else{
   bt = System.currentTimeMillis();
   strXml = xmlFile2String2(file1);
   if (log.isDebugEnabled()) {
    log.debug("\nxmlFile2String2() use time: "
      + (System.currentTimeMillis() - bt) + " millis\n");
   }
  }

  if(b==0){
   bt = System.currentTimeMillis();
   findElement4XPath1(strXml);
   if (log.isDebugEnabled()) {
    log.debug("\nfindElement4XPath1() use time: "
      + (System.currentTimeMillis() - bt) + " millis\n");
   }
  }else{
   bt = System.currentTimeMillis();
   findElement4XPath2(strXml);
   if (log.isDebugEnabled()) {
    log.debug("\nfindElement4XPath2() use time: "
      + (System.currentTimeMillis() - bt) + " millis\n");
   } 
   
  }
 }

 public static void findElement4XPath1(String xml) {
  try {
   String str = delNamespace4Pattern(xml);
   Document doc = DocumentHelper.parseText(str);
   Element e = (Element) doc.selectSingleNode("//CreateUserRequest/RequestMessage/MessageHeader");
   if (e != null) {
    Iterator iter = e.elementIterator();
    while (iter.hasNext()) {
     Element sub = (Element) iter.next();
     log.debug("\n" + sub.getText() + "\n");
    }
   }
   
   /* 读取属性的例子
   List childNodes = doc.selectNodes("//Config/Child/ChildNode");
         for(Object obj:childNodes) {
             Node childNode = (Node)obj;
             String name = childNode.valueOf("@name"); //读取属性
             String text = childNode.getText();
         }
         */

   
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
 public static void findElement4XPath2(String xml) {
  try {
   Document doc = DocumentHelper.parseText(xml);
   Element root = doc.getRootElement();
   
   HashMap map = new HashMap();
   map.put("tns", "http://bme.sunrise.com/unicom/gd");
   XPath x = doc.createXPath("//tns:CreateUserRequest/tns:RequestMessage/tns:MessageHeader");
   x.setNamespaceURIs(map);
   
   Element e = (Element) x.selectSingleNode(doc);
   if (e != null) {
    Iterator iter = e.elementIterator();
    while (iter.hasNext()) {
     Element sub = (Element) iter.next();
     if (log.isDebugEnabled()) {
      log.debug("\n" + sub.getText() + "\n");
     }
    }
   }
  } catch (Exception e) {
   e.printStackTrace();
  }
 }

 public static Document xml2Document(String xml) {
  try {
   return DocumentHelper.parseText(xml);
  } catch (Exception e) {
   e.printStackTrace();
  }
  return null;
 }

 public static String xmlFile2String(String xmlFile) {
  try {
   return new SAXReader().read(new File(xmlFile)).asXML();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return null;
 }
 
 //读取xml文件为xml串
 public static String xmlFile2String2(String xmlFile) {
  try {
   org.w3c.dom.Document document = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xmlFile); 
   OutputFormat format = new OutputFormat(document);
   //format.setEncoding("UTF-8");
   StringWriter stringOut = new StringWriter();
   XMLSerializer serial = new XMLSerializer(stringOut, format);
   serial.asDOMSerializer();
   serial.serialize(document.getDocumentElement());
   return stringOut.toString();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return "";
 }
 
 
 public static String delNamespace4Pattern(String xml){
  String result = "";
  try {
   result = xml.replaceFirst("xmlns([^ ]*)=([^ ]*)http([^>^\"]*)\"", "");
  } catch (Exception e) {
   e.printStackTrace();
  }
  return result;
  
 }

 

}

posted @ 2007-05-17 10:46 比特鼠 阅读(198) | 评论 (0)编辑 收藏

package com.sunrise.ocs.webservice.unicom.test;

import java.io.File;
import java.io.StringReader;
import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;

import com.sun.org.apache.xml.internal.serialize.OutputFormat;
import com.sun.org.apache.xml.internal.serialize.XMLSerializer;

public class TestDom {
 
 //将xml串转换为document
 public static Document xml2Document(String xml) {
  Document doc = null;
  try {
   DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
   doc = builder.parse(new InputSource(new StringReader(xml)));
  } catch (Exception e) {
   e.printStackTrace();
  }
  return doc;
 }
 
 //将xml文件串转换为document
 public static Document xmlFile2Document(String xmlFile) {
  Document doc = null;
  try {
   DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
   doc = builder.parse(new File(xmlFile));
  } catch (Exception e) {
   e.printStackTrace();
  }
  return doc;
 }
 
 //删除命名空间: xmlns="..."
 public static String delNamespace(String xml) {
  String result = xml;
  try {
   Document doc = xml2Document(xml);
   Element root = doc.getDocumentElement();
   root.removeAttribute("xmlns");
   result = asXml(doc);
  } catch (Exception e) {
   e.printStackTrace();
  }
  return result;
 }
 
 //将doc转换为xml串
 public static String asXml(Document doc) {
  String strxml = "";
  try {
   OutputFormat format = new OutputFormat(doc);
   // format.setEncoding("UTF-8");
   StringWriter stringOut = new StringWriter();
   XMLSerializer serial = new XMLSerializer(stringOut, format);
   serial.asDOMSerializer();
   serial.serialize(doc.getDocumentElement());
   strxml = stringOut.toString();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return strxml;
 }
 
 //将node转换为xml串
 public static String asXml(Node node, Document doc) {
  String strxml = "";
  try {
   OutputFormat format = new OutputFormat(doc);
   // format.setEncoding("UTF-8");
   StringWriter stringOut = new StringWriter();
   XMLSerializer serial = new XMLSerializer(stringOut, format);
   serial.asDOMSerializer();
   serial.serialize((Element)node);
   strxml = stringOut.toString();
  } catch (Exception e) {
   e.printStackTrace();
  }
  return strxml;
 }
}

posted @ 2007-05-17 10:10 比特鼠 阅读(271) | 评论 (0)编辑 收藏

vi 是visual edit 的缩写
文本编辑器是所有计算机系统中最常用的一种工具。UNIX下的编辑器有ex,sed和vi等,其中,使用最为广泛的是vi,而vi命令繁多,论坛里好像这方面的总结不多,以下稍做总结,以资共享!渴望更正和补充!

进入vi的命令
vi filename :打开或新建文件,并将光标置于第一行首
vi +n filename :打开文件,并将光标置于第n行首
vi + filename :打开文件,并将光标置于最后一行首
vi +/pattern filename:打开文件,并将光标置于第一个与pattern匹配的串处
vi -r filename :在上次正用vi编辑时发生系统崩溃,恢复filename
vi filename....filename :打开多个文件,依次进行编辑

移动光标类命令
h :光标左移一个字符
l :光标右移一个字符
space:光标右移一个字符
Backspace:光标左移一个字符
k或Ctrl+p:光标上移一行
j或Ctrl+n :光标下移一行
Enter :光标下移一行
w或W :光标右移一个字至字首
b或B :光标左移一个字至字首
e或E :光标右移一个字至字尾
) :光标移至句尾
( :光标移至句首
}:光标移至段落开头
{ :光标移至段落结尾
nG:光标移至第n行首
n+:光标下移n行
n-:光标上移n行
n$:光标移至第n行尾
H :光标移至屏幕顶行
M :光标移至屏幕中间行
L :光标移至屏幕最后行
0:(注意是数字零)光标移至当前行首
$:光标移至当前行尾

屏幕翻滚类命令
Ctrl+u:向文件首翻半屏
Ctrl+d:向文件尾翻半屏
Ctrl+f:向文件尾翻一屏
Ctrl+b;向文件首翻一屏
nz:将第n行滚至屏幕顶部,不指定n时将当前行滚至屏幕顶部。

插入文本类命令
i :在光标前
I :在当前行首
a:光标后
A:在当前行尾
o:在当前行之下新开一行
O:在当前行之上新开一行
r:替换当前字符
R:替换当前字符及其后的字符,直至按ESC键
s:从当前光标位置处开始,以输入的文本替代指定数目的字符
S:删除指定数目的行,并以所输入文本代替之
ncw或nCW:修改指定数目的字
nCC:修改指定数目的行

删除命令
ndw或ndW:删除光标处开始及其后的n-1个字
do:删至行首
d$:删至行尾
ndd:删除当前行及其后n-1行
x或X:删除一个字符,x删除光标后的,而X删除光标前的
Ctrl+u:删除输入方式下所输入的文本

搜索及替换命令
/pattern:从光标开始处向文件尾搜索pattern
?pattern:从光标开始处向文件首搜索pattern
n:在同一方向重复上一次搜索命令
N:在反方向上重复上一次搜索命令
:s/p1/p2/g:将当前行中所有p1均用p2替代
:n1,n2s/p1/p2/g:将第n1至n2行中所有p1均用p2替代
:g/p1/s//p2/g:将文件中所有p1均用p2替换

选项设置
all:列出所有选项设置情况
term:设置终端类型
ignorance:在搜索中忽略大小写
list:显示制表位(Ctrl+I)和行尾标志($)
number:显示行号
report:显示由面向行的命令修改过的数目
terse:显示简短的警告信息
warn:在转到别的文件时若没保存当前文件则显示NO write信息
nomagic:允许在搜索模式中,使用前面不带“\”的特殊字符
nowrapscan:禁止vi在搜索到达文件两端时,又从另一端开始
mesg:允许vi显示其他用户用write写到自己终端上的信息

最后行方式命令
:n1,n2 co n3:将n1行到n2行之间的内容拷贝到第n3行下
:n1,n2 m n3:将n1行到n2行之间的内容移至到第n3行下
:n1,n2 d :将n1行到n2行之间的内容删除
:w :保存当前文件
:e filename:打开文件filename进行编辑
:x:保存当前文件并退出
:q:退出vi
:q!:不保存文件并退出vi
:!command:执行shell命令command
:n1,n2 w!command:将文件中n1行至n2行的内容作为command的输入并执行之,若不指定n1,n2,则表示将整个文件内容作为command的输入
:r!command:将命令command的输出结果放到当前行

寄存器操作
"?nyy:将当前行及其下n行的内容保存到寄存器?中,其中?为一个字母,n为一个数字
"?nyw:将当前行及其下n个字保存到寄存器?中,其中?为一个字母,n为一个数字
"?nyl:将当前行及其下n个字符保存到寄存器?中,其中?为一个字母,n为一个数字
"?p:取出寄存器?中的内容并将其放到光标位置处。这里?可以是一个字母,也可以是一个数字
ndd:将当前行及其下共n行文本删除,并将所删内容放到1号删除寄存器中。


进入vi
vi test
离开vi
:q! 离开vi,并放弃刚在缓冲区内编辑的内容。
   :wq 将缓冲区内的资料写入磁盘中,并离开vi。
   :ZZ 同wq
同wq
:w 将缓冲区内的资料写入磁盘中,但并不离开vi。
  :q 离开vi,若文件被修改过,则会被要求确认是否放弃修改的内容,此指令可与: w 配合使用。
Vi 的操作模式
Vi 提供两种操作模式:
输入模式(insert mode)
指令模式(command mode)
当使用者进入vi后,既处于指令模式下,此刻键入任何字元皆被视为指令。

输入模式:a(append) 游标之后加入资料。
A 该行之末加入资料
i (insert) 游标之前加入资料
I 该行之首加入资料
o (open) 新增一行与该行之下供输入资料
O 新增一行与该行之上供输入资料

指令模式:B      移至该行第一个字符,若光标在该行第一字符则光标移至上一行第一字符。
   b    由游标所在位置之前一个字串的第一个字元
     cc 删除整行,修改整行的内容。
     D      以行为单位,删除游标在内后面的所有字符。
db 删除该行光标前字符
     dd 删除该行
     de 删除自光标开始后面的字符
     d加字符   删除光标所在位置至字符之间的单
     E      移至该行最后字符,若光标在该行最后字符则光标移至下一行最后字符
 e      由游标所在位置至该字串的最后一个字元
     G 移至该档案的最后一行 
     h 向前移一个字元
j 向下移一个字元
k 向上移一个字元
0 移至该行之首
M 移至视窗的中间那行
L 移至视窗的最后一行
     l 向后移一个字符
0 由游标所在位置该行的第一个字元
nG 移至该档案的第n行
n+ 自游标所在位置向后移n行至该行的第一字符
n- 自游标所在位置向前移n行至该行的第一字符
R 进入取代状态,直到《ESC》为止
s 删除游标所在字元,并进入取代模式直到《ESC》
S 删除游标所在之该行资料,并进入输入模式直到《ESC》
w 由游标所在位置之下一个字串的第一个字元
x 删除游标所在该字元。
X 删除游标所在之前一字元。
r 用接于此指令之后的字元取代(replace)游标所在字元
yy yank整行,使游标所在该行复制到记忆体缓冲区
显示该行之行号、档案名称、档案中最末之行号、游标所在行号占
总行号之百分比
$ 由游标所在位置至该行的最后一个字元。
) 由游标所在位置至下一个句子的第一个字元。
( 由游标所在位置至该句子的第一个字元。
{  由游标所在位置至该段落的最后一个字元。
} 由游标所在位置至该段落的第一个字元

yank和delete可将指定的资料复制到记忆体缓冲区,而藉有put指令可将缓冲区内的资料复制到荧幕上
例如:搬移一行 :在该行执行dd
游标移至目的地
执行p
复制一行 :在该行执行yy
游标移至目的地
执行p
视窗移动:
视窗往下卷一页
视窗往上卷一页
视窗往下卷半页
视窗往上卷半页
视窗往下卷一行
视窗往上卷一行
删除、复制及修改指令介绍:
d(delete)、c(change)和y(yank)这一类的指令在vi 中的指令格式为:
operation+scope=command
(运算子)(范围)
运算子:
d 删除指令。删除资料,但会将删除资料复制到记忆体缓冲区。
y 将资料(字组、行列、句子或段落)复制到缓冲区。
p 放置(put)指令,与d和y配合使用。可将最后delete或yank的资料放置于游标所在位置之行列下。
c 修改(change)指令,类似delete于insert的组合。删除一个字组、句子等资料,并插入新键入的




posted @ 2007-05-15 11:33 比特鼠 阅读(229) | 评论 (0)编辑 收藏

1、使用JdbcTemplate的execute()方法执行SQL语句

代码

2、如果是UPDATE或INSERT,可以用update()方法。
代码

3、带参数的更新
代码

代码

4、使用JdbcTemplate进行查询时,使用queryForXXX()等方法
代码

代码

代码

代码

JdbcTemplate将我们使用的JDBC的流程封装起来,包括了异常的捕捉、SQL的执行、查询结果的转换等等。spring大量使用Template Method模式来封装固定流程的动作,XXXTemplate等类别都是基于这种方式的实现。
除了大量使用Template Method来封装一些底层的操作细节,spring也大量使用callback方式类回调相关类别的方法以提供JDBC相关类别的功能,使传统的JDBC的使用者也能清楚了解spring所提供的相关封装类别方法的使用。

JDBC的PreparedStatement

代码

代码

代码

在getUser(id)里面使用UserRowMapper

代码

网上收集
org.springframework.jdbc.core.PreparedStatementCreator 返回预编译SQL 不能于Object[]一起用

代码

1.增删改
org.springframework.jdbc.core.JdbcTemplate 类(必须指定数据源dataSource)
代码


代码

org.springframework.jdbc.core.PreparedStatementSetter 接口 处理预编译SQL
代码

2.查询JdbcTemplate.query(String,[Object[]/PreparedStatementSetter],RowMapper/RowCallbackHandler)
org.springframework.jdbc.core.RowMapper 记录映射接口 处理结果集
代码

org.springframework.jdbc.core.RowCallbackHandler 记录回调管理器接口 处理结果集
代码

posted @ 2007-04-11 18:48 比特鼠 阅读(502) | 评论 (0)编辑 收藏

制作可执行的JAR文件包及jar命令详解


常常在网上看到有人询问:如何把 java 程序编译成 .exe 文件。通常回答只有两种,一种是制作一个可执行的 JAR 文件包,然后就可以像.chm 文档一样双击运行了;而另一种是使用 JET 来进行 编译。但是 JET 是要用钱买的,而且据说 JET 也不是能把所有的 Java 程序都编译成执行文件,性能也要打些折扣。所以,使用制作可执行 JAR 文件包的方法就是最佳选择了,何况它还能保持 Java 的跨平台特性。 

下面就来看看什么是 JAR 文件包吧: 

1. JAR 文件包 

JAR 文件就是 Java Archive File,顾名思意,它的应用是与 Java 息息相关的,是 Java 的一种文档格式。JAR 文件非常类似 ZIP 文件——准确的说,它就是 ZIP 文件,所以叫它文件包。JAR 文件与 ZIP 文件唯一的区别就是在 JAR 文件的内容中,包含了一个 META-INF/MANIFEST.MF 文件,这个文件是在生成 JAR 文件的时候自动创建的。举个例子,如果我们具有如下目录结构的一些文件: 

  == 

  `-- test 

    `-- Test.class 

把它压缩成 ZIP 文件 test.zip,则这个 ZIP 文件的内部目录结构为: 

  test.zip 

  `-- test 

    `-- Test.class 

如果我们使用 JDK 的 jar 命令把它打成 JAR 文件包 test.jar,则这个 JAR 文件的内部目录结构为: 

  test.jar 

  |-- META-INF 

  |  `-- MANIFEST.MF 

  `-- test 

    `--Test.class 

2. 创建可执行的 JAR 文件包 

制作一个可执行的 JAR 文件包来发布你的程序是 JAR 文件包最典型的用法。 

Java 程序是由若干个 .class 文件组成的。这些 .class 文件必须根据它们所属的包不同而分级分目录存放;运行前需要把所有用到的包的根目录指定给 CLASSPATH 环境变量或者 java 命令的 -cp 参数;运行时还要到控制台下去使用 java 命令来运行,如果需要直接双击运行必须写 Windows 的批处理文件 (.bat) 或者 Linux 的 Shell 程序。因此,许多人说,Java 是一种方便开发者苦了用户的程序设计语言。 

其实不然,如果开发者能够制作一个可执行的 JAR 文件包交给用户,那么用户使用起来就方便了。在 Windows 下安装 JRE (Java Runtime Environment) 的时候,安装文件会将 .jar 文件映射给 javaw.exe 打开。那么,对于一个可执行的 JAR 文件包,用户只需要双击它就可以运行程序了,和阅读 .chm 文档一样方便 (.chm 文档默认是由 hh.exe 打开的)。那么,现在的关键,就是如何来创建这个可执行的 JAR 文件包。 

创建可执行的 JAR 文件包,需要使用带 cvfm 参数的 jar 命令,同样以上述 test 目录为例,命令如下: 

jar cvfm test.jar manifest.mf test 

这里 test.jar 和 manifest.mf 两个文件,分别是对应的参数 f 和 m,其重头戏在 manifest.mf。因为要创建可执行的 JAR 文件包,光靠指定一个 manifest.mf 文件是不够的,因为 MANIFEST 是 JAR 文件包的特征,可执行的 JAR 文件包和不可执行的 JAR 文件包都包含 MANIFEST。关键在于可执行 JAR 文件包的 MANIFEST,其内容包含了 Main-Class 一项。这在 MANIFEST 中书写格式如下: 

Main-Class: 可执行主类全名(包含包名) 

例如,假设上例中的 Test.class 是属于 test 包的,而且是可执行的类 (定义了 public static void main(String[]) 方法),那么这个 manifest.mf 可以编辑如下: 

Main-Class: test.Test <回车>; 

这个 manifest.mf 可以放在任何位置,也可以是其它的文件名,只需要有 Main-Class: test.Test 一行,且该行以一个回车符结束即可。创建了 manifest.mf 文件之后,我们的目录结构变为: 

  == 

  |-- test 

  |  `-- Test.class 

  `-- manifest.mf 

这时候,需要到 test 目录的上级目录中去使用 jar 命令来创建 JAR 文件包。也就是在目录树中使用“==”表示的那个目录中,使用如下命令: 

jar cvfm test.jar manifest.mf test 

之后在“==”目录中创建了 test.jar,这个 test.jar 就是执行的 JAR 文件包。运行时只需要使用 java -jar test.jar 命令即可。 

需要注意的是,创建的 JAR 文件包中需要包含完整的、与 Java 程序的包结构对应的目录结构,就像上例一样。而 Main-Class 指定的类,也必须是完整的、包含包路径的类名,如上例的 test.Test;而且在没有打成 JAR 文件包之前可以使用 java <类名>; 来运行这个类,即在上例中 java test.Test 是可以正确运行的 (当然要在 CLASSPATH 正确的情况下)。 

3. jar 命令详解 

jar 是随 JDK 安装的,在 JDK 安装目录下的 bin 目录中,Windows 下文件名为 jar.exe,Linux 下文件名为 jar。它的运行需要用到 JDK 安装目录下 lib 目录中的 tools.jar 文件。不过我们除了安装 JDK 什么也不需要做,因为 SUN 已经帮我们做好了。我们甚至不需要将 tools.jar 放到 CLASSPATH 中。 

使用不带任何的 jar 命令我们可以看到 jar 命令的用法如下: 

jar {ctxu}[vfm0M] [jar-文件] [manifest-文件] [-C 目录] 文件名 ... 

其中 {ctxu} 是 jar 命令的子命令,每次 jar 命令只能包含 ctxu 中的一个,它们分别表示: 

-c 创建新的 JAR 文件包 

-t 列出 JAR 文件包的内容列表 

-x 展开 JAR 文件包的指定文件或者所有文件 

-u 更新已存在的 JAR 文件包 (添加文件到 JAR 文件包中) 

[vfm0M] 中的选项可以任选,也可以不选,它们是 jar 命令的选项参数 

-v 生成详细报告并打印到标准输出 

-f 指定 JAR 文件名,通常这个参数是必须的 

-m 指定需要包含的 MANIFEST 清单文件 

-0 只存储,不压缩,这样产生的 JAR 文件包会比不用该参数产生的体积大,但速度更快 

-M 不产生所有项的清单(MANIFEST〕文件,此参数会忽略 -m 参数 

[jar-文件] 即需要生成、查看、更新或者解开的 JAR 文件包,它是 -f 参数的附属参数 

[manifest-文件] 即 MANIFEST 清单文件,它是 -m 参数的附属参数 

[-C 目录] 表示转到指定目录下去执行这个 jar 命令的操作。它相当于先使用 cd 命令转该目录下再执行不带 -C 参数的 jar 命令,它只能在创建和更新 JAR 文件包的时候可用。   

文件名 ... 指定一个文件/目录列表,这些文件/目录就是要添加到 JAR 文件包中的文件/目录。如果指定了目录,那么 jar 命令打包的时候会自动把该目录中的所有文件和子目录打入包中。 

下面举一些例子来说明 jar 命令的用法: 

1) jar cf test.jar test 

该命令没有执行过程的显示,执行结果是在当前目录生成了 test.jar 文件。如果当前目录已经存在 test.jar,那么该文件将被覆盖。 

2) jar cvf test.jar test 

该命令与上例中的结果相同,但是由于 v 参数的作用,显示出了打包过程,如下: 

标明清单(manifest) 

增加:test/(读入= 0) (写出= 0)(存储了 0%) 

增加:test/Test.class(读入= 7) (写出= 6)(压缩了 14%) 

3) jar cvfM test.jar test 

该命令与 2) 结果类似,但在生成的 test.jar 中没有包含 META-INF/MANIFEST 文件,打包过程的信息也略有差别: 

增加:test/(读入= 0) (写出= 0)(存储了 0%) 

增加:test/Test.class(读入= 7) (写出= 6)(压缩了 14%) 

4) jar cvfm test.jar manifest.mf test 

运行结果与 2) 相似,显示信息也相同,只是生成 JAR 包中的 META-INF/MANIFEST 内容不同,是包含了 manifest.mf 的内容 

5) jar tf test.jar 

在 test.jar 已经存在的情况下,可以查看 test.jar 中的内容,如对于 2) 和 3) 生成的 test.jar 分别应该此命令,结果如下; 

对于 2) 

META-INF/ 

META-INF/MANIFEST.MF 

test/ 

test/Test.class 

对于 3) 

test/ 

test/Test.class 

6) jar tvf test.jar 

除显示 5) 中显示的内容外,还包括包内文件的详细信息,如: 

0 Wed Jun 19 15:39:06 GMT 2002 META-INF/ 

86 Wed Jun 19 15:39:06 GMT 2002 META-INF/MANIFEST.MF 

0 Wed Jun 19 15:33:04 GMT 2002 test/ 

7 Wed Jun 19 15:33:04 GMT 2002 test/Test.class 

7) jar xf test.jar 

解开 test.jar 到当前目录,不显示任何信息,对于 2) 生成的 test.jar,解开后的目录结构如下: 

  == 

  |-- META-INF 

  |  `-- MANIFEST 

  `-- test 

    `--Test.class 

8) jar xvf test.jar 

运行结果与 7) 相同,对于解压过程有详细信息显示,如: 

创建:META-INF/ 

展开:META-INF/MANIFEST.MF 

创建:test/ 

展开:test/Test.class 

9) jar uf test.jar manifest.mf 

在 test.jar 中添加了文件 manifest.mf,此使用 jar tf 来查看 test.jar 可以发现 test.jar 中比原来多了一个 manifest。这里顺便提一下,如果使用 -m 参数并指定 manifest.mf 文件,那么 manifest.mf 是作为清单文件 MANIFEST 来使用的,它的内容会被添加到 MANIFEST 中;但是,如果作为一般文件添加到 JAR 文件包中,它跟一般文件无异。 

10) jar uvf test.jar manifest.mf 

与 9) 结果相同,同时有详细信息显示,如: 

增加:manifest.mf(读入= 17) (写出= 19)(压缩了 -11%) 

4. 关于 JAR 文件包的一些技巧 

1) 使用 unzip 来解压 JAR 文件 

在介绍 JAR 文件的时候就已经说过了,JAR 文件实际上就是 ZIP 文件,所以可以使用常见的一些解压 ZIP 文件的工具来解压 JAR 文件,如 Windows 下的 WinZip、WinRAR 等和 Linux 下的 unzip 等。使用 WinZip 和 WinRAR 等来解压是因为它们解压比较直观,方便。而使用 unzip,则是因为它解压时可以使用 -d 参数指定目标目录。 

在解压一个 JAR 文件的时候是不能使用 jar 的 -C 参数来指定解压的目标的,因为 -C 参数只在创建或者更新包的时候可用。那么需要将文件解压到某个指定目录下的时候就需要先将这具 JAR 文件拷贝到目标目录下,再进行解压,比较麻烦。如果使用 unzip,就不需要这么麻烦了,只需要指定一个 -d 参数即可。如: 

unzip test.jar -d dest/ 

2) 使用 WinZip 或者 WinRAR 等工具创建 JAR 文件 

上面提到 JAR 文件就是包含了 META-INF/MANIFEST 的 ZIP 文件,所以,只需要使用 WinZip、WinRAR 等工具创建所需要 ZIP 压缩包,再往这个 ZIP 压缩包中添加一个包含 MANIFEST 文件的 META-INF 目录即可。对于使用 jar 命令的 -m 参数指定清单文件的情况,只需要将这个 MANIFEST 按需要修改即可。 

3) 使用 jar 命令创建 ZIP 文件 

有些 Linux 下提供了 unzip 命令,但没有 zip 命令,所以需要可以对 ZIP 文件进行解压,即不能创建 ZIP 文件。如要创建一个 ZIP 文件,使用带 -M 参数的 jar 命令即可,因为 -M 参数表示制作 JAR 包的时候不添加 MANIFEST 清单,那么只需要在指定目标 JAR 文件的地方将 .jar 扩展名改为 .zip 扩展名,创建的就是一个不折不扣的 ZIP 文件了,如将上一节的第 3) 个例子略作改动: 

jar cvfM test.zip test
posted @ 2007-04-11 18:19 比特鼠 阅读(314) | 评论 (1)编辑 收藏

 

import java.io.File;

public class Test {
    
public static void main(String[] args) {
         System.out.println(
"1:"+Thread.currentThread().getContextClassLoader().getResource(""));     

          System.out.println(
"2:"+Test.class.getClassLoader().getResource(""));        

          System.out.println(
"3:"+ClassLoader.getSystemResource(""));        
          System.out.println(
"4:"+Test.class.getResource(""));        
          System.out.println(
"5:"+Test.class.getResource("/")); //Class文件所在路径  
          System.out.println("6:"+new File("").getAbsolutePath());        
          System.out.println(
"7:"+System.getProperty("user.dir"));  
          
          String s 
= ClassLoader.getSystemResource("").getPath();
          System.out.println(s.substring(
1));
          System.out.println(s.substring(
1).substring(0, s.lastIndexOf("/classes")));

    }

}
posted @ 2007-04-04 16:49 比特鼠 阅读(160) | 评论 (0)编辑 收藏

dom4j 直接往Element中加入符合格式的xml串!

下面的代码直接往root元素插入符合格式的xml串:

String strXml = "<aaa><bbb></bbb><ccc></ccc></aaa>";
Document doc = DocumentHelper.createDocument();
Element  root = doc.getRootElement();
root.add(DocumentHelper.parseText(strXml).getRootElement());
posted @ 2007-03-27 16:20 比特鼠 阅读(469) | 评论 (1)编辑 收藏

 public long algo(){
    int a = 0;
    String b = null;
    long st = System.currentTimeMillis();
    for (int i = 0; i < 2000000; i++){
      b = Integer.toString(a);
    }
    long et = System.currentTimeMillis();
    return (et - st);
  }
posted @ 2007-03-27 15:11 比特鼠 阅读(1406) | 评论 (1)编辑 收藏

玩转 XPath 和缺省命名空间(Default Namespaces)

原文出自:http://www.edankert.com/defaultnamespaces.html
翻译文出自:http://wakan.blog.51cto.com/blog/59583/7220



诸如“为什么用 XPath 的表达式进行查询,却没有返回所期望的结果?”的问题通常都与命名空间(NameSpace)有关,而且绝大多数是与缺省命名空间(Default Namespace)有关。本文试图解释这个问题并针对三种流行的 XPath 实现给出解决方法:Jaxen、JAXP XPPathFactory 以及 XSLT。
内容列表
问题描述
“前缀-命名空间”映射
Jaxen 和 Dom4J
Jaxen 和 XOM
Jaxen 和 JDOM
JAXP XPathFactory
XSLT
结束语
资源


问题描述
看下述 XML:
<catalog>
  <cd>
    <artist>Sufjan Stevens</artist>
    <title>Illinois</title>
    <src>http://www.sufjan.com/</src>
  </cd>
  <cd>
    <artist>Stoat</artist>
    <title>Future come and get me</title>
    <src>http://www.stoatmusic.com/</src>
  </cd>
  <cd>
    <artist>The White Stripes</artist>
    <title>Get behind me satan</title>
    <src>http://www.whitestripes.com/</src>
  </cd>
</catalog>

    你可以使用“//cd”来得到没有在任何命名空间中定义的“cd”节点。 


    现在让我们来改造这个 XML,让它的所有元素都属于 'http://www.edankert.com/examples/' 命名空间中。
 
    为了避免在每个不同的元素前都要加个前缀,我们在根元素上定义通常所说的缺省命名空间。改造后的 XML 如下:
 
<catalog xmlns="
http://www.edankert.com/examples/ ">
  <cd>
    <artist>Sufjan Stevens</artist>
    <title>Illinois</title>
    <src>http://www.sufjan.com/</src>
  </cd>
  <cd>
    <artist>Stoat</artist>
    <title>Future come and get me</title>
    <src>http://www.stoatmusic.com/</src>
  </cd>
  <cd>
    <artist>The White Stripes</artist>
    <title>Get behind me satan</title>
    <src>http://www.whitestripes.com/</src>
  </cd>
</catalog>

    当我们使用与上文相同的 XPath “//cd”,将得不到任何元素。这是因为指定的 XPath 返回的是所有不属于任何命名空间的“cd”节点,而本例中,所有的“cd”元素都属于缺省的命名空间“http://www.edankert.com/examples/”。


“前缀-命名空间”映射
    为了取出命名空间“http://www.edankert.com/examples/”中的所有“cd”元素,我们需要对 XPath 表达式做一些额外的工作。
 
    为了解决这个问题,XPath 规范允许我们使用 QName 来指定元素或者属性。QName 可以是元素的直接名称(形如“element”),或者包含一个前缀(形如“pre:element”)。这个前缀需要映射到一个命名空间的 URI 上。例如,如果把“pre”前缀映射到“http://www.edankert.com/test”上,则通过“pre:element”可以查找出属于命名空间“http://www.edankert.com/test”的所有 “element”元素。
 
    在本例中,我们把“edx”映射到“'http://www.edankert.com/examples/”命名空间上。通过 XPath“//edx:cd”就可以查找出属于“'http://www.edankert.com/examples/”命名空间的所有“cd”元素。 
 
    XPath 处理器允许设置“前缀-命名空间”的映射,但是,如何去映射,却要依赖于具体的实现。下文举例说明 Jaxen (JDOM/dom4j/XOM)、JAXP 以及 XSLT 中是如何进行“前缀-命名空间”的映射的。

Jaxen 和 Dom4J
    下述代码从文件系统读入一个 XML 文件到 org.dom4j.Document 对象中,并且在 Document 中查找属于“http://www.edankert.com/examples/”命名空间的所有“cd”元素。
 
try {
  SAXReader reader = new SAXReader();
  Document document = reader.read( "file:catalog.xml");
 
  HashMap map = new HashMap();
  map.put( "edx", "
http://www.edankert.com/examples/ ");
 
  XPath xpath = new Dom4jXPath( "//edx:cd");
  xpath.setNamespaceContext( new SimpleNamespaceContext( map));
 
  List nodes = xpath.selectNodes( document);
 
  ...
 
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( DocumentException e) {
  // the document is not well-formed.
  ...
}
    第一步,创建一个 SAXReader,用来从文件系统中读取“catalog.xml”并创建一个特定于 Dom4j 的 Document 对象。

    第二步,对于所有 Jaxen 实现都一样,就是创建一个 HashMap 对象,用于保存“前缀-命名空间的 URI”的映射。
 
    为了能通过 Dom4j 使用 Jaxen 的 XPath 功能,需要创建一个与 Dom4j 相关的 XPath 对象:Dom4jXPath。创建方法是把 XPath 的表达式(即“//edx:cd”)传给 Dom4jXPath 的构造方法。
 
    现在,我们已经创建了 XPath 对象,接下来可以把“前缀-命名空间”的映射表传递给 XPath 引擎:把这个 HashMap 映射表用 SimpleNamespaceContext 包装起来。SimpleNamespaceContext 是 Jaxen 的 NamespaceContext 接口的默认实现类。
 
    最后一步就是调用 XPath 对象的 selectNodes() 方法进行查找。并把完整的 Dom4j  Document 对象作为参数传递进去。实际上,Document 中的任何结点都可以作为参数。

Jaxen 和 XOM
    XOM 是基于简单的 Java DOM APIs 之上的最新工具,它的设计初衷是提供简单和易学易用的接口。
 
try {
  Builder builder = new Builder();
  Document document = builder.build( "file:catalog.xml");
 
  HashMap map = new HashMap();
  map.put( "edx", "
http://www.edankert.com/examples/ ");
 
  XPath xpath = new XOMXPath( "//edx:cd");
  xpath.setNamespaceContext( new SimpleNamespaceContext( map));
 
  List nodes = xpath.selectNodes( document);
 
  ...
 
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( IOException e) {
  // An error occurred opening the document
  ...
} catch ( ParsingException e) {
  // An error occurred parsing the document
  ...
}

 
    我们需要创建一个 Builder 对象,从文件系统中读取“catalog.xml”文件,并创建出与 XOM 相关的 Document 对象。
 
    下一步创建出包含了“前缀-命名空间”映射关系的 HashMap 对象。
 
    我们需要创建一个特定于 XOM 的 XPath 对象:XOMXPath。创建方法是把 XPath 表达式传递给构造方法,然后就可以通过 XOM 使用 Jaxen 的 XPath 功能了。
 
    创建完 XPath 对象后,同样,我们把“前缀-命名空间”的映射表用 SimpleNamespaceContext 对象封装后,传递给 XPath 引擎。
 
    最后调用 XPath 对象的“selectNodes()”方法进行查找,把 XOM Document 对象作为本方法的参数。
Jaxen 和 JDOM
    JDOM 是第一个提供简单的 XML 访问 API 的工具。
 
try {
  SAXBuilder builder = new SAXBuilder();
  Document document = builder.build( "file:catalog.xml");
 
  HashMap map = new HashMap();
  map.put( "edx", "
http://www.edankert.com/examples/ ");
 
  XPath xpath = new JDOMXPath( "//edx:cd");
  xpath.setNamespaceContext( new SimpleNamespaceContext( map));
 
  List nodes = xpath.selectNodes( document);
 
  ...
 
} catch ( JaxenException e) { // An error occurred parsing or executing the XPath ... } catch ( IOException e) {
  // An error occurred opening the document
  ...
} catch ( JDOMException e) {
  // An error occurred parsing the document
  ...
}

 
    首先,通过 SAXBuilder 创建了一个特定于 JDom 的 Document 对象。
 
    接着创建一个特定于 JDOM 的 XPath 对象:JDOMXPath。
 
    然后,把“前缀-命名空间”的映射表(HashMap)用 SimpleNamespaceContext 对象封装起来,传递给 XPath 引擎。
 
    最后调用 XPath 对象的“selectNodes()”方法来进行查找,并把 JDOM 的 Document 对象作为本方法的输入参数。
JAXP XPathFactory
    从 1.3 版起, JAXP 还提供了一种在 XML Object Models 上进行查询的通用机制。
 
try {
 DocumentBuilderFactory domFactory =DocumentBuilderFactory.newInstance();
  domFactory.setNamespaceAware( true);
 
 DocumentBuilder builder = domFactory.newDocumentBuilder();Document document = builder.parse( new InputSource( "file:catalog.xml"));
 
 XPathFactory factory =XPathFactory.newInstance();
 XPath xpath = factory.newXPath();
  xpath.setNamespaceContext( new NamespaceContext() {
    public String getNamespaceURI(String prefix) {
      if ( prefix.equals( "edx")) {
        return "
http://www.edankert.com/examples/ ";
      } else if ...
        ...
      }
     
      return XPathConstants.NULL_NS_URI;
    }
 
    public String getPrefix(String namespaceURI) {
      if ( namespaceURI.equals( "
http://www.edankert.com/examples/ ")) {
        return "edx";
      } else if ...
        ...
      } 
   
      return null;
    }
 
    public Iterator getPrefixes(String namespaceURI) {
     ArrayList list = new ArrayList();
   
      if ( namespaceURI.equals( "
http://www.edankert.com/examples/ ")) {
        list.add( "edx");
      } else if ...
        ...
      }
   
      return list.iterator();
    }
  });
 
 Object nodes = xpath.evaluate( "//edx:cd", document.getDocumentElement(),
                                XPathConstants.NODESET);
 
  ...
 
} catch (ParserConfigurationException e) {
  ...
} catch (XPathExpressionException e) {
  ...
} catch (SAXException e) {
  ...
} catch (IOException e) {
  ...
}

    首先用 JAXP 的 DocumentBuilderFactory 创建一个org.w3c.dom.Document 对象,确保启用了 namespace 处理功能。
 
    现在可以通过 XPathFactory 来创建 XPath 对象,并通过 XPath 对象对文档进行查询。
 
    为了创建“前缀-命名空间”映射并传递给 XPath 引擎,我们需要实现 NamespaceContext 接口,该接口目前还没有默认实现类。这就意味着要实现 getNamespaceURI、getPrefix 和getPrefixes 方法,并确保这些方法能返回正确的值,包括“xmlns”和“xml”前缀所对应的命名空间的 URI 值。
 
    把我们自己实现的 NamespaceContext 对象传递给 XPath 引擎后,就可以通过 evaluate 方法来查询 XPath 表达式所对应的元素:使用上文中提到的 XPath 表达式,并使用 Document 的根节点作为输入入参数,并接收一个 NodeList 对象作为返回结果。
XSLT
    XPath 设计的初衷是用于 XSLT。这也许能解释“为什么在 XSLT 中定义命名空间的前缀是一件很平常的事”(也许因为 XSLT 也是一个 XML 名词的缘故吧)。
 
<xsl:stylesheet version="1.1" xmlns:xsl="
http://www.w3.org/1999/XSL/Transform ">
  <xsl:template match="//edx:cd" xmlns:edx="
http://www.edankert.com/examples/ ">
    <xsl:apply-templates/>
  </xsl:template>
</xsl:stylesheet>
 
    只需要使用 XML 本身的机制,简单地为 edx 前缀赋予一个命名空间的 URI 值。
 
    通过与我们的 XPath 表达式“//edx:cd”相匹配的 xsl:template,能得到与上文其他例子相同的输出结果。

结束语
    为了在(缺省)命名空间上使用 XPath 表达式,我们需要指定一个“前缀-命名空间”映射。正如我们所看到的,具体使用什么样的前缀名称,是无关紧要的。
 
    同样的方法,也可以用于查询那些用其他前缀修饰的元素。这意味着上面的例子对下述 XML 也有效。下述 XML 没有使用缺省命名空间,而是使用了 examples 作命名空间的前缀:
 
<examples:catalog xmlns:examples="
http://www.edankert.com/examples/ ">
  <examples:cd>
    <examples:artist>Sufjan Stevens</examples:artist>
    <examples:title>Illinois</examples:title>
    <examples:src>http://www.sufjan.com/</examples:src>
  </examples:cd>
  <examples:cd>
    <examples:artist>Stoat</examples:artist>
    <examples:title>Future come and get me</examples:title>
    <examples:src>http://www.stoatmusic.com/</examples:src>
  </examples:cd>
  <examples:cd>
    <examples:artist>The White Stripes</examples:artist>
    <examples:title>Get behind me satan</examples:title>
    <examples:src>http://www.whitestripes.com/</examples:src>
  </examples:cd>
</examples:catalog>

 
    使用“//edx:cd”作为 XPath 表达式,使用与前文例子相同的“前缀-命名空间”映射,在这个 XML 上同样能查询出属于“http://www.edankert.com/examples/”命名空间的所有“cd”元素。
资源
Extensible Markup Language (XML) 1.0 (Third Edition)
http://www.w3.org/TR/REC-xml/
Namespaces in XML
http://www.w3.org/TR/REC-xml-names/
XML Path Language (XPath) Version 1.0
http://www.w3.org/TR/xpath
XSL Transformations (XSLT) Version 1.0
http://www.w3.org/TR/xslt
dom4j
http://www.dom4j.org/
XOM
http://www.xom.nu/
JDOM
http://www.jdom.org/
Jaxen
http://www.jaxen.org/
Java 5.0
http://java.sun.com/j2se/1.5.0/

posted @ 2007-03-21 10:13 比特鼠 阅读(1707) | 评论 (1)编辑 收藏

spring事务探索

原文出处:
http://www.javaeye.com/topic/11190

spring自建事务管理模块。而且这个事务管理是一个抽象设计,可以应用到很多场合,包括普通的DataSource,jta,jms和hibernate上。

要正确使用spring的事务,首先需要了解spring在事务设计上的一些概念
统观spring事务,围绕着两个核心PlatformTransactionManager和TransactionStatus

PlatformTransactionManager直译过来就是平台相关事务,这里的平台指的是“事务源”,包括刚才我说的DataSource,jta等等。这些无一不是一个事务源。广义的说,凡是可以完成事务性操作的对象,都可以设计出相对应的PlatformTransactionManager,只要这个事务源支持commit,rollback和getTransaction语意。

查看spring代码,可以发现这些manager实现事务,就是调用事务源的事务操作方法

比如

HibernateTransactionManager

代码

 

  1. protected   void  doCommit(DefaultTransactionStatus status) {   
  2.         HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();   
  3.          if  (status.isDebug()) {   
  4.             logger.debug( "Committing Hibernate transaction on session ["  +   
  5.                     txObject.getSessionHolder().getSession() +  "]" );   
  6.         }   
  7.          try  {   
  8.             txObject.getSessionHolder().getTransaction().commit();   
  9.         }   
  10. ...   
  11.   
  12.     }  

jdbc 的DataSourceTransactionManager

代码

 

  1. protected   void  doCommit(DefaultTransactionStatus status) {   
  2.         DataSourceTransactionObject txObject = (DataSourceTransactionObject) status.getTransaction();   
  3.         Connection con = txObject.getConnectionHolder().getConnection();   
  4.          if  (status.isDebug()) {   
  5.             logger.debug( "Committing JDBC transaction on connection ["  + con +  "]" );   
  6.         }   
  7.          try  {   
  8.             con.commit();   
  9.         }   
  10.         ...   
  11.     }  

那么PlatformTransactionManager以什么依据处理事务呢?
是TransactionStatus
查看api发现这个接口有三个方法
isNewTransaction() ,isRollbackOnly(),setRollbackOnly()
PlatformTransactionManager就是根据前两个方法决定是否要创建一个新事务,是要递交还是回滚。至于第三个方法是改变事务当前状态的,很多地方都要用到,偏偏PlatformTransactionManager自身好像不怎么用,毕竟事务状态的改变是由程序员代码决定的,不需要一个manager多管闲事。

总结上面所说的,spring的事务由PlatformTransactionManager管理,manager最后调用事务源的方法来实现一个事务过程。而manager通过TransactionStatus 来决定如何实现。

接下去说spring事务中的TransactionTemplate和TransactionInterceptor

TransactionTemplate其实和spring中其他的template的作用类似,起到化简代码的作用,不要被它那么长的名字吓倒了,事实上这个template并不是什么非常核心的对象。如果比较学究派的,可以去看看template设计模式,在此就不再对此赘述了。
为什么要有TransactionTemplate?先来看看如果没有TransactionTemplate,我们的代码该怎么写

先来看看spring reference中的一段代码

代码

 

  1. DefaultTransactionDefinition def =  new  DefaultTransactionDefinition()   
  2. def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);   
  3.   
  4. TransactionStatus status = transactionManager.getTransaction(def);   
  5.   
  6. try  {   
  7.      // execute your business logic here   
  8. catch  (MyException ex) {   
  9.     transactionManager.rollback(status);   
  10.      throw  ex;   
  11. }   
  12. transactionManager.commit(status);  


这是直接使用transactionManager的例子,可以看到真正执行business logic 的地方是在try当中那段,前后的代码都是为了完成事务管理的。如果每个business logic都要写上那么一段,我肯定是疯了。我们翻出TransactionTemplate的代码看看他怎么化简了我们的代码

代码

 

  1. public  Object execute(TransactionCallback action)  throws  TransactionException {   
  2.         TransactionStatus status =  this .transactionManager.getTransaction( this );   
  3.         Object result =  null ;   
  4.          try  {   
  5.             result = action.doInTransaction(status);   
  6.         }   
  7.          catch  (RuntimeException ex) {   
  8.              // transactional code threw application exception -> rollback   
  9.             rollbackOnException(status, ex);   
  10.              throw  ex;   
  11.         }   
  12.          catch  (Error err) {   
  13.              // transactional code threw error -> rollback   
  14.             rollbackOnException(status, err);   
  15.              throw  err;   
  16.         }   
  17.          this .transactionManager.commit(status);   
  18.          return  result;   
  19.     }  

同上面的代码如出一辙,前后是事务处理代码,当中那段result = action.doInTransaction(status);是我们的应用代码。至于action是什么,全看各位的需要了。但是有一点要主要,如果利用TransactionTemplate,那么他不管你扔出什么异常都会回滚事务,但是回滚的是哪个事务呢?继续挖代码

代码

 

  1. private   void  rollbackOnException(TransactionStatus status, Throwable ex)  throws  TransactionException {   
  2.          if  (logger.isDebugEnabled()) {   
  3.             logger.debug( "Initiating transaction rollback on application exception" , ex);   
  4.         }   
  5.          try  {   
  6.              this .transactionManager.rollback(status);   
  7.         }   
  8.          catch  (RuntimeException ex2) {   
  9.             logger.error( "Application exception overridden by rollback exception" , ex);   
  10.              throw  ex2;   
  11.         }   
  12.          catch  (Error err) {   
  13.             logger.error( "Application exception overridden by rollback error" , ex);   
  14.              throw  err;   
  15.         }   
  16.     }  


真相大白,是对template所持有的某个transactionManager进行回滚。所以如果你的应用代码用的是事务源a的一些资源,比如到服务器a的一个datasource,但是你的transactionManager管理的是另一些资源,比如服务器b的一个datasource,代码铁定不会正常运行

特别是在一些多事务源的程序里,这点千万不能搞错。如果多个事务源之间要完成全局事务,还是老老实实用分布式事务管理服务吧(jta)

那么TransactionInterceptor是干什么的?这个是spring 的声明式事务的支持方式。因为用TransactionTemplate要硬编码,而且调整事务策略很麻烦(不是说不能调。举个例子原来程序抛出异常A需要回滚,现在不需要要,我就可以把a catch吃掉。这时候template就不会回滚了。但是每次调整都要重写编码。)而用TransactionInterceptor就可以将这些调整写在配置中。我们再来挖TransactionInterceptor的代码

代码

 

  1. public  Object invoke(MethodInvocation invocation)  throws  Throwable {   
  2.          // Work out the target class: may be null.   
  3.          // The TransactionAttributeSource should be passed the target class   
  4.          // as well as the method, which may be from an interface   
  5.         Class targetClass = (invocation.getThis() !=  null ) ? invocation.getThis().getClass() :  null ;   
  6.            
  7.          // Create transaction if necessary   
  8.         TransactionInfo txInfo = createTransactionIfNecessary(invocation.getMethod(), targetClass);   
  9.   
  10.         Object retVal =  null ;   
  11.          try  {   
  12.              // This is an around advice.   
  13.              // Invoke the next interceptor in the chain.   
  14.              // This will normally result in a target object being invoked.   
  15.             retVal = invocation.proceed();   
  16.         }   
  17.          catch  (Throwable ex) {   
  18.              // target invocation exception   
  19.             doCloseTransactionAfterThrowing(txInfo, ex);   
  20.              throw  ex;   
  21.         }   
  22.          finally  {   
  23.             doFinally(txInfo);   
  24.         }   
  25.         doCommitTransactionAfterReturning(txInfo);   
  26.   
  27.          return  retVal;   
  28.     }  


万变不离其宗。

所以使用spring的事务管理需要作这些事
1,设置好事务源,比如DataSource,hibernate的session。如果有多个事务源要考虑他们之间是否有全局事务,如果有,老老实实用jta,否则就需要自己写一个manager了
2,设置manager,根据你的事务源选择对应的PlatformTransactionManager
3,选择实现事物的方式,用template还是interceptor。用template代码直观点,但是template所管辖的manager和你应用代码所用的事务源要一致。如果用interceptor千万注意,一定要调用interceptor那个bean,而不是原始的那个target。在坛子上我已经看到至少有两个朋友说spring事物不起作用,从配置和代码上看都正确,这时要好好查查,调用的bean是哪一个。
4,这个是设计问题了,推荐事务处于一个较高层次,比如service上的某个函数,而底层的dao可以不考虑事务,否则可能会出现事务嵌套,增加程序复杂度。

posted @ 2007-03-05 14:45 比特鼠 阅读(234) | 评论 (0)编辑 收藏

2007年已经过去一个月了, 还没有制定今年的规划, 现在是时候了!

争取掌握以下技术:

1. Ruby On Rails 
进一步学习ruby的语法和语义,最好能深入到解释器一层看看有些特性是怎么实现的。

2、Spring AOP 及 AspectJ
个人觉得Spring 2.0和AspectJ的结合,有很多潜力,值得深入挖掘。

3. Web Service

4. Java JPA

5. Role-Based Access Control , Acegi
buaawhl推荐的,
http://csrc.nist.gov/rbac/
http://www.amazon.com/Role-Based-Access-Control-David-Ferraiolo/dp/1580533701/sr=1-1/qid=1161828835/ref=pd_bbs_1/002-1138304-7372032?ie=UTF8&s=books

下一个项目的权限管理会比较复杂,以前没学过这方面的知识。从Acegi开始吧,如果够用最好。

6. JBoss JBoss的Cache,AppServer的cluster方面是我比较感兴趣的地方,想尝试一下JBoss应用服务器

7. 满足一定条件(比如: 并发达1000个请求)框架Web Application群集部署

posted @ 2007-02-02 15:28 比特鼠 阅读(227) | 评论 (0)编辑 收藏

关于Spring属性编辑器详解

原文出处:
http://stamen.javaeye.com/blog/24660

最近刚在研究Spring的编辑器,发现很有意思,刚好galaxystar起了一个这样贴,我想对PropertyEditor作一个详细的整理会对大家有益,特定启了这个新帖。

所谓的PropertyEditor,顾名思义,就是属性编辑器。由于Bean属性通过配置文档以字符串了方式为属性赋值,所以必须有一个“东东”负责将这个字符串转换为属性的直接对象,如属性的类型为int,那么编辑器要做的工作就是int i = Integer.parseInt("1");
Spring为一般的属性类型提供了默认的编辑器,BeanWrapperImpl是Spring框架中重要的类,它负责对注入的Bean进行包装化的管理,常见属性类型对应的编辑器即在该类中通过以下代码定义:

代码

但是,并非Bean的属性都是这些常见的类型,如果你的Bean需要注入一个自定义类型的属性,而又想享受IoC的好处,那么就只得自己开干,提供一个自定义的PropertyEditor了。
下面,分几个步骤来说明,定义一个自定义PropertyEditor的过程。
1)首先,碰到的问题即是,要如何编辑自己的PropertyEditor,其实需要了解一点java.beans包的知识,在该包中,有一个java.beans.PropertyEditor的接口,它定义了一套接口方法(12个),即通过这些方法如何将一个String变成内部的一个对象,这两个方法是比较重要的:
a)setValue(Object value) 直接设置一个对象,一般不直接用该方法设置属性对象
b)setAsText(String text) 通过一个字符串来构造对象,一般在此方法中解析字符串,将构造一个
类对象,调用setValue(Object)来完成属性对象设置操作。

2)实现所有的接口方法是麻烦的,java.beans.PropertyEditorSupport 适时登场,一般情况下,我们通过扩展这个方便类即可。

3)编写完后,就是在Spring配置文件中注册该属性类型编辑器的问题,Spring提供了专门的注册工具类
org.springframework.beans.factory.config.CustomEditorConfigurer,它负责将属性类型和
属性编辑器关联起来。到时BeanFactory注入Bean的属性时,即会在注册表中查找属性类型对应的编辑器。

下面给出一个小例子,例子先作一个简单描述:
1)Person 需要进行属性注入的Bean,有两个属性 一个是name,一个是address Address是一个类
2)Address Person的属性类型,本身有3个属性。
3)AddressPropertyEditor Address类型对应的属性编辑器。

开工:
1.Person.java

代码

2.Address.java
代码


AddressPropertyEditor.java
代码

打开Spring配置文件,添上这两个配置项:

代码

 

下面是我自己写的日期转换类:

我的配置:
posted @ 2007-01-20 21:21 比特鼠 阅读(395) | 评论 (0)编辑 收藏

Error convertoring HTML to XHTML: System.ArgumentException: Cannot have ']]>' inside an XML CDATA block. at System.Xml.XmlTextWriter.WriteCData(String text) at System.Xml.XmlWriter.WriteNode(XmlReader reader, Boolean defattr) at FreeTextBoxControls.Support.Formatter.HtmlToXhtml(String input)
posted @ 2007-01-19 16:51 比特鼠 阅读(203) | 评论 (0)编辑 收藏

原文出自: http://www.blogjava.net/waterye/archive/2005/08/23/10836.aspx

OpenSessionInView模式

Spring+Hibernate中,  集合映射如果使用lazy="true", 当PO传到View层时, 出现未初始化session已关闭的错误,只能在dao先初始化
parent.getChilds().size();

Spring提供Open Session In View来解决这个问题, 有两种方式
1. Interceptor
    <!--  =========== OpenSession In View pattern ============== -->
    
< bean  id ="openSessionInViewInterceptor"
          class
="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor" >
        
< property  name ="sessionFactory"  ref ="sessionFactory" />
    
</ bean >

    
< bean  id ="urlMapping"  class ="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping" >
        
< property  name ="interceptors"  ref ="openSessionInViewInterceptor" />
        
< property  name ="mappings" >
            
< props >
            ......
            
</ props >

        
</ property >
    
</ bean >
2. Filter
< web-app >
 
< filter >
< filter-name > hibernateFilter </ filter-name >
< filter-class >
org.springframework.orm.hibernate.support.OpenSessionInViewFilter
</ filter-class >
</ filter >
 
< filter-mapping >
< filter-name > hibernateFilter </ filter-name >
< url-pattern > *.do </ url-pattern >
</ filter-mapping >

</ web-app >

说明: 个人更喜欢用Interceptor方式, filter是在web.xml中定义
有了OpenSessionInView,并不是一切就ok了。简单的crud可以,但对于复杂业务逻辑就要想点法子。

OSIV默认是request per session的, 所以即使没有显式update(po),Hibernate也会帮你保存的,dao的业务判断就无效,还好有evict()可用。
update code
// Controller
public
 ModelAndView update() {
    p 
=
 dao.getVo(id);
    bindObject(request);
    dao.update(p);
}

// Dao
public
 Object getVo(id) {
    p 
=
 getHibernateTemplate().get(clazz, id);
    p.getChilds().size();
    getHibernateTemplate().evict(p);
    
return
 p;
}

public   void
 update(p) {
    oldP 
=
 getVo(id);
    
//  ++--(oldP);

    getHibernateTemplate().update(p);
    
//  ++--(p);

}
posted @ 2007-01-19 15:33 比特鼠 阅读(296) | 评论 (0)编辑 收藏