﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Java Votary-随笔分类-Java</title><link>http://www.blogjava.net/ericwang/category/5187.html</link><description /><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 13:11:44 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 13:11:44 GMT</pubDate><ttl>60</ttl><item><title>始终会用上的Common BeanUtils</title><link>http://www.blogjava.net/ericwang/archive/2006/01/15/28118.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Sun, 15 Jan 2006 12:20:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2006/01/15/28118.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/28118.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2006/01/15/28118.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/28118.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/28118.html</trackback:ping><description><![CDATA[<div class="postTitle">
		<a href="http://blog.csdn.net/calvinxiu/archive/2005/02/02/277748.aspx">始终会用上的Common BeanUtils</a>
<script language="javascript">document.title="始终会用上的Common BeanUtils - "+document.title</script>
	</div>	
	
	
	
		<p>Beanutils用了魔术般的反射技术，实现了很多夸张有用的功能，都是C/C++时代不敢想的。无论谁的项目，始终一天都会用得上它。我算是后知后觉了，第一回看到它的时候居然错过。</p><p><font size="3"><strong>1.属性的动态getter、setter </strong></font></p><div>在这框架满天飞的年代，不能事事都保证执行getter,setter函数了，有时候属性是要根据名字动态取得的，就像这样：　　</div><pre>BeanUtils.getProperty(myBean,"code");</pre><div>而Common BeanUtils的更强功能在于可以直接访问内嵌对象的属性，只要使用点号分隔。</div><pre>BeanUtils.getProperty(orderBean, "address.city");</pre><div>相比之下其他类库的BeanUtils通常都很简单，不能访问内嵌的对象，所以有时要用Commons BeanUtils来替换它们。<br><br>BeanUtils还支持List和Map类型的属性，如下面的语法即可取得Order的顾客列表中第一个顾客的名字</div><pre>BeanUtils.getProperty(orderBean, "customers[1].name");</pre><div>其中BeanUtils会使用ConvertUtils类把字符串转为Bean属性的真正类型，方便从HttpServletRequest等对象中提取bean，或者把bean输出到页面。</div><div>而PropertyUtils就会原色的保留Bean原来的类型。</div><p><font size="3"><strong>2.BeanCompartor 动态排序 </strong></font></p><div>还是通过反射，动态设定Bean按照哪个属性来排序，而不再需要在实现bean的Compare接口进行复杂的条件判断。 </div><pre>List peoples = ...; // Person对象的列表<br>Collections.sort(peoples, new BeanComparator("age"));<br></pre><p>如果要支持多个属性的复合排序，如"Order By lastName,firstName" </p><pre>ArrayList sortFields = new ArrayList();<br>sortFields.add(new BeanComparator("lastName"));<br>sortFields.add(new BeanComparator("firstName"));<br>ComparatorChain multiSort = new ComparatorChain(sortFields);<br>Collections.sort(rows,multiSort);<br></pre><p>其中ComparatorChain属于jakata commons-collections包。<br>如果age属性不是普通类型，构造函数需要再传入一个comparator对象为age变量排序。<br>另外, BeanCompartor本身的ComparebleComparator, 遇到属性为null就会抛出异常, 也不能设定升序还是降序。这个时候又要借助commons-collections包的ComparatorUtils.</p><pre>&nbsp;&nbsp; Comparator mycmp = ComparableComparator.getInstance();<br>&nbsp;&nbsp;&nbsp;mycmp = ComparatorUtils.nullLowComparator(mycmp);&nbsp; //允许null<br>&nbsp;&nbsp; mycmp = ComparatorUtils.reversedComparator(mycmp); //逆序<br>&nbsp;&nbsp; Comparator cmp = new BeanComparator(sortColumn, mycmp);</pre><font size="3"><strong>3.Converter 把Request或ResultSet中的字符串绑定到对象的属性 </strong></font><p>&nbsp;&nbsp; 经常要从request,resultSet等对象取出值来赋入bean中，如果不用MVC框架的绑定功能的话，下面的代码谁都写腻了。</p><pre>   String a = request.getParameter("a");<br>   bean.setA(a);<br>   String b = ....<br>   bean.setB(b);<br>   ......<br></pre><p>不妨写一个Binder自动绑定所有属性:</p><pre>    MyBean bean = ...;<br>    HashMap map = new HashMap();<br>    Enumeration names = request.getParameterNames();<br>    while (names.hasMoreElements())<br>    {<br>      String name = (String) names.nextElement();<br>      map.put(name, request.getParameterValues(name));<br>    }<br>    BeanUtils.populate(bean, map);</pre><p>&nbsp;&nbsp;&nbsp; 其中BeanUtils的populate方法或者getProperty,setProperty方法其实都会调用convert进行转换。<br>&nbsp;&nbsp;&nbsp;&nbsp;
但Converter只支持一些基本的类型，甚至连java.util.Date类型也不支持。而且它比较笨的一个地方是当遇到不认识的类型时，居然会抛
出异常来。&nbsp;对于Date类型，我参考它的sqldate类型实现了一个Converter，而且添加了一个设置日期格式的函数。<br>要把这个Converter注册，需要如下语句：</p><pre>    ConvertUtilsBean convertUtils = new ConvertUtilsBean();<br>&nbsp;&nbsp; DateConverter dateConverter = new DateConverter();<br>&nbsp;&nbsp; convertUtils.register(dateConverter,Date.class);<br> <br><br><br>    //因为要注册converter,所以不能再使用BeanUtils的静态方法了，必须创建BeanUtilsBean实例<br>    BeanUtilsBean beanUtils = new BeanUtilsBean(convertUtils,new PropertyUtilsBean());<br>   beanUtils.setProperty(bean, name, value);</pre><font size="3"><strong>4 其他功能</strong></font> <div><strong>4.1 ConstructorUtils，动态创建对象</strong></div><div><pre>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static Object invokeConstructor(Class klass, Object arg)</pre><strong>4.2 MethodUtils，动态调用方法</strong> <pre>    MethodUtils.invokeMethod(bean, methodName, parameter);</pre></div><br><strong>4.3 PropertyUtils，当属性为Collection,Map时的动态读取：<br></strong>Collection: 提供index<br><div><pre>&nbsp;&nbsp; BeanUtils.getIndexedProperty(orderBean,"items",1);</pre>或者<pre>&nbsp; BeanUtils.getIndexedProperty(orderBean,"items[1]");</pre>Map: 提供Key Value<pre>&nbsp; BeanUtils.getMappedProperty(orderBean, "items","111");//key-value goods_no=111 </pre>或者<pre>&nbsp; BeanUtils.getMappedProperty(orderBean, "items(111)") </pre></div><div><strong><br>4.4 PropertyUtils，直接获取属性的Class类型</strong></div><pre>&nbsp;&nbsp;&nbsp;&nbsp; public static Class getPropertyType(Object bean, String name)</pre><strong>4.5 动态Bean </strong>见<a href="http://blog.csdn.net/calvinxiu/archive/2005/02/02/277765.aspx" target="__blank">用DynaBean减除不必要的VO和FormBean&nbsp;</a><img src ="http://www.blogjava.net/ericwang/aggbug/28118.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2006-01-15 20:20 <a href="http://www.blogjava.net/ericwang/archive/2006/01/15/28118.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[转]我的Java学习之路</title><link>http://www.blogjava.net/ericwang/archive/2005/12/26/25498.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Mon, 26 Dec 2005 13:43:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/12/26/25498.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/25498.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/12/26/25498.html#Feedback</comments><slash:comments>5</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/25498.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/25498.html</trackback:ping><description><![CDATA[<p>这里说说我的经历吧。大学前以及大学前面三年的经历就不说了，因为大学前的高中就是好好学习，大学前三年就是混过来的。</p><p>&nbsp;&nbsp;&nbsp;
我上的学校还算可以，虽然不是北大清华这样第一流名牌大学，但至少也算中国的第二流名牌大学了。大学中前面三年都陪伴着游戏过去，所学到的只是些计算机基
础知识。到大四后我突然发现就业的问题就在眼前，而自己似乎什么也不会，于是开始看书。最一开始重点看的是C++，可是后来自从看了一本J2ME的书以后
被Java所吸引。当时虽然学校上过Java课程，但是自己也只是学了很少的皮毛，也就只会写写Hello
World和什么加减法之类很简单的程序，连API都知道没有几个，比如说字符串长度的API我都不知道。所以刚开始自己学J2ME的时候屡屡受挫，自己
也明白自己的缺点，决定从J2SE开始好好补上。</p><p>&nbsp;&nbsp;&nbsp;
刚开始为了熟悉Java开发环境，买了本JBuilder开发的教程，并且在自己的本本上安装了JBuilder进行演练。当时的我连JavaDoc都不
知道，每次究竟什么API能做什么事情一点头绪都没有，还不知道哪里去查，后来同学告诉我有个JavaDoc这个东西，我还兴奋不已，觉得自己被从黑暗中
拉回来了。一开始使用JBuilder的时候，马上为之所吸引，有两个原因，第一是因为它自动标出语法错误，边写代码边提示你什么地方语法出错，记得以前
使用VC++的时候，每次程序写好后先编译，然后再Build，再运行，这其中每个步骤都会出不少错误。特别是在编译的时候，写个200多行的程序一次编
译下来就有100多个错误，结果每次花在这上面的工夫都要好长时间。而JBuilder使用了即时语法分析，所以基本上程序写完，就可以省略调试语法错误
的步骤了。第二个原因是可以自动提示代码，这个功能可以让你迅速熟悉API，免得每次去查帮助文档那么麻烦，我就是这么很快掌握了许多API的。</p><p>可能大家会问我为什么一开始不学习《Java编程思想》，的确这本书我们宿舍就有好几本，不过大家普遍反映效果不好，到最后都不知道说的是什么，所以我也没敢看。</p><p>&nbsp;&nbsp;&nbsp;
经过20天左右的学习，对Java有了更进一步的了解，熟悉了不少API函数，由于在那本书上写开发SWING占了不少篇幅，所以也对Swing的开发了
解了不少。看完以后因为同学说Java的灵魂就是多线程编程，所以开始看Oreilly的《Java线程》。记得在大学中操作系统这门课我们就提到过线程
的知识。并且课本上就是用Java实现的，当时有了一点点概念，但这次看这本专门说线程的书后才发现我原来了解的那些根本是什么都不算（当然，现在回想起
来，我那时看书学到的也只是很简单的皮毛而已）。看完这本书后我自己学会在我的JBuilder下开发很简单的多线程程序，并且模拟线程冲突，等待等情
况。当时看着自己写的一两百行程序可以顺利执行，那种兴奋劲就别提了。这本书我看得也很快，大概就花了3个星期看完。</p><p>&nbsp;&nbsp;&nbsp; 经过上面的学习，自己相比以前来说提升了不少，这时候自己也找到了工作，是做J2EE对日外包的，所以更加坚定了努力学习Java的信心。</p><p>&nbsp;&nbsp;&nbsp;
在上面写的程序中，我自己写程序没有规范性，在代码编写的时候自己的盲点特别多，还容易犯低级失误。同学有一个《Effective
Java》中文版，可是我看了几页发现自己根本看不懂，里面什么静态工厂啊，什么单例模式什么的根本不知道什么东东。我知道自己目前的水平还不够，所以决
定放下这本书，去寻找别的适合我的书看。这个时候我看到了候捷先生翻译的《Practical
Java》一书，当时是刚刚上的书架。这本书我在书店翻了下目录后就感觉如获至宝，马上买回家，在回家的公车上就贪婪地读起来。这本书不算很厚，但是自己
看得却很认真很仔细，也明白了不少东西，比如Java中等号和equals()方法的区别，究竟什么时候用什么。还有Exception处理机制，以前不
知道什么叫Exception，只是JBuilder提示我要我抛出Exception我再抛出Exception，自己觉得这东西基本没什么用呢。但是
看了这本书后我改变了看法，我发现Exception是个很好的东西，可以迅速把程序从正常状态和异常状态区分开来，即使而准确地在指定位置得到处理。那
时自己也有了以后写程序的时候注意编写异常处理部分的想法。《Practical
Java》这本书虽然不厚，但是我却非常仔细地去看了，大概花了1个月时间，我把这本书完全消化了下去。</p><p>&nbsp;&nbsp;&nbsp;
当时听说Java在网络上的应用非常广，我也不知道究竟是什么应用，我于是买了Oreilly的《Java网络编程》这本书。这本书虽然很厚，其实前半部
分内容不是很复杂，后半部分写什么RMI的东西我也看不大懂，只能理解个概念。通过这本书，我了解了HTTP协议究竟是什么一个东西，在它上面利用
Java传输数据该如何做，知道了什么是Request，什么是Response。这也为以后开始我的J2EE之旅打下了很好的基础。当时自己依然是边看
书边自己写代码来验证，自己写了个服务器端Socket和客户端Socket，成功进行了通信，又在上面加上了安全Socket内容，实现了SSL通信。
当时我把写的这个又套上了Swing的外壳，还和同学拿这个传文件呢。不过当时也没有考虑过什么校验码之类的东西，所以传传小文件还是可以的，文件稍微一
大一点，传过去的文件总是不对头，和我原来的文件经常会出一些差异，导致文件打不开。</p><p>&nbsp;&nbsp;&nbsp;
《Java网络编程》这本书看了不少时间，因为书比较厚，东西也比较多，不过除了后面的一些知识以外，其他的还是容易理解的。大概花了2个月左右的时间看
完。看完后，时间也到了2004年的3月。我也轮到开始我毕业设计的时候了。我们的毕业设计导师都还不错，给你自己选个课题，我选的是一个B/S结构的在
线简历处理系统，正好和我所学和下面所工作的东西是一条路上的了。这时我觉得我应该往B/S结构上转了，当时在选择先看Servlet还是先看JSP上犹
豫不决。最终决定先看Servlet，后来也证明了我的决定是对的，我在熟悉了Servlet后再学JSP是非常容易的，基本上根本没有遇到什么难点。</p><p>可
能有人会觉得我看了好多Oreilly的书，虽然我不能说Oreilly本本都是好书，不过相对来说，好书的概率总超过许多其他的出版社，而且体系比较齐
全。我看得几本书我都觉得还不错。现说说下面这本我学Servlet时候看的《Java
Servlet编程》来说吧，很不错的一本书，让我迅速知道了什么是Servlet，然后通过最简单的实例，让你知道了Servlet如何运行的，跟
HTTP协议是如何配合的，如何返回HTML形式的文本，XML配置符该如何写，究竟每个元素是什么意思等等。由于我原来有一定的XML基础（知道XML
语法各种格式的含义而已），所以掌握起来还算比较快。通过这本书，我知道了如何动态生成HTML文档，知道如何把一个Servlet映射到一个虚拟的地
址。在后半部分写到了数据库操作部分，我对数据库的了解其实也仅限于原来大学课本上的《数据库系统原理》，如何从程序和数据库交互是一窍不通。通过数据库
操作这章，我知道了如何使用JDBC语句如何编写，大家不要笑，对于当初一个新手来说，这个真是一个全新的领域，做什么事情都需要Sample来对照，跟
着依葫芦画瓢吧，其实现在的软件开发也是这样，我想现在大家谁能直接手写Struts或者Hibernate的配置文件都很难吧。闲话少说，大概这个时
候，我对毕业设计的雏形有了点思想上的概念。看完了《Java
Servlet编程》后紧接着就又看Oreilly的《JSP设计》，由于有了Servlet的基础，学起JSP特别快。当时没有着重看Tag的自定义设
计，光看了JSP的其他东西，终于在五一节后把毕业设计都写完了，当时总代码量是2000多行，第一次写这么多代码的程序觉得很有成就感。现在看起来那时
做的是标准垃圾，但是当时觉得还是很不错。用了Servlet +
JSP。其实Servlet也不是用来当控制器的，而是和JSP做的差不多功能，都是作view的功能的。很快，毕业设计交差过去了，写写毕业论文，准备
答辩。在这个过程中，我又一次考虑自己下面该看什么书。</p><p>这次我又看中了侯捷翻译的一本巨著，也就是鼎鼎大名的Martin
Fowler写的《重构——改善既有代码的设计》这本书。刚开始听见重构这个名字，总觉得非常高深，加上都评论说重构是和设计模式齐名的东东，感觉更加高
深恐怖了。大概在6月初我开始看了重构，刚开始看的时候虽然抱着试试看的心态，不过还是非常认真的。但是，让我颇感意外的是重构并不是很难，至少这本书中
说的非常通俗易懂，通过大量的实例让你觉得重构是种很简单很基本的技术。虽然我看完了重构以后在真实的代码编写中很少直接按照上面代码所说的方法进行重构
代码，基本上都是通过IDE来重构代码，但是却大大提升了自己编程思维，从此以后写代码就很少瞻前顾后了，因为我拥有了重构这个工具。这本书有点厚，再加
上中间有答辩，拍毕业照，以及毕业手续等等，这本书我花了一个半月看完。我看书的速度也不算快，不过我看书比较有恒心，不像有部分人看几天就不想看了，我
能坚持天天看，所以总的来说还是不慢的。我计算过，如果我每天看10页书，坚持下去，那一年就是3650页书，平均一本书365页来算，1年就是10本。
如果这10本书中有8本不属于垃圾书籍，那么你这年就能有非常大的提高了。</p><p>看重构这本书中间我也抽了一段时间看了两本其他的书，第一本是
《Java夜未眠》，挺不错的一本书，虽然是散文，但是还是能让你明白不少道理，受益匪浅。另外一本就是李维的《Borland传奇》，由于自己当时最喜
欢用的工具就是JBuilder，所以也对Borland公司非常敬仰，特别对安德森，简直就顶礼膜拜啊，哈哈。这本书写得很精彩，写了Borland公
司二十年来的血泪史，写了如何跟微软斗争，如何在微软和IBM的夹缝中生存。当然，也有很多的对于技术方面作者李维自己的见解，看了会有不少同感的。就这
样，磨磨蹭蹭地把重构看完了。</p><p>&nbsp;&nbsp;&nbsp;
当看完了《重构》这本书之后，我也开始去公司报到上班了。可以看出来，我当时工作的时候水平也很有限，但总比一年前要好不少，至少很多东西都已经知道了。
那时外面极限编程听的比较多，自己也去书店买了本《Java极限编程》回来看，现在想想算是我看得第一本垃圾书籍了。不过也是有收获的，这本书极限编程也
就说了点概念，然后就写了不少工具的使用方法。在看《重构》中对JUnit有了点认识，不过只是皮毛中的皮毛。看了这本《Java极限编程》后对
JUnit的使用又了解了不少皮毛，对于Cactus有了点了解，对Ant了解了不少，至少可以自己写出自己需要的配置文件了，并且可以结合JUnit生
成测试Report。由于我去的是日企，做对日外包的，所以公司开始培训日本语，用的是《标准日本语》这套教材。我于是边学日语边看技术，大概2个星期左
右我把那本《Java极限编程》初步看完后就扔在了家里。这时的我已经开始会用Ant了，觉得是步入J2EE的重要一步。</p><p>&nbsp;&nbsp;&nbsp;
很快啃掉那本垃圾书以后又看了本和Java不是非常有关的书：《程序员修炼之道——从小工到专家》，原因其实很简单，大学同学都说这本书是经典书，看书这
东西，别人的评价还是能起不少作用的。这本书字数不是很多，不过排版的时候比较分散，导致书本有点厚，呵呵，可能也算出版社赚钱的一种方法吧。不过总的来
说，我觉得出版社纸张质量最好的是电子工业出版社，其次是中国电力出版社，最烂的恐怕就是机械工业出版社了，机械工业出版社有少量书纸张还能说过去，但有
不少简直让人不得不有脾气啊，纸张薄得感觉和写毛笔字的宣纸都差不多了。这本电子工业出版社的书纸张质量的确不错，不过也许是因为我功力尚浅，所以这本书
虽然都看懂了，但是深有感触并且铭记于心的没有几个，现在再回想，也只记得软件模块设计时要正交等等少数几点了。这本书由于内容不是非常多，所以我就看了
半个月不到搞定。这时的我开发IDE已经转移到了Eclipse上，毕竟商业开发用D版有点说不过去，而且公司也怕查，所以不允许用JBuilder，鼓
励大家用Eclipse。我用了一段时间的Eclipse后，从一开始的不适应到后来觉得Eclipse很方便使用，JBuilder比Eclipse多
的就是一些根据不同类型开发的模版而已，而这些可以由Eclipse的插件来弥补。到了这时，我觉得我的Java基础应该算还可以的了，API也熟悉了非
常多。我觉得看《Effective Java》的时机成熟了。</p><p>&nbsp;&nbsp;&nbsp; 由于大学已经毕业了，所以也不会有同学的《Effective
Java》放在边上让我看这样的好事出现，老老实实地去了书店买了本《Effective
Java》中文版回来研读。呵呵，大家也许会问我为什么不买本E文的看，虽然我大学早早也把英语4级过了，而且大学中不少计算机专业课程教材也是E文的，
当时为了考试也认真读了。但是毕竟英语不是我们的母语，看起来速度上明显比中文版的慢一截。当然，如果是那种垃圾翻译者用机器翻译出来的中文版，看那些垃
圾中文版速度肯定比不上直接看英文原版的。这时的我看《Effective
Java》已经不再是当初的那么感觉很陌生了，觉得上面说的那些要点自己想想还都是可以理解的。我个人觉得提高自身编程习惯以及水平最多的还是看类似于
《Practical Java》和《Effective
Java》的这种按照条目来进行说明的书，能把你自己平时容易忽略的地方按照重点一个个揪出来进行修正。比如《Effective
Java》中的第一条，使用静态工厂来代替构造函数，自己原来在进行开发的时候，从来不怎么会主动想到建立一个静态工厂，总觉得使用构造函数来新建一个对
象是天经地义的事情。但看完这个条目后，我的看法也随之改变，发现静态工厂还是非常好的，当然，也不是什么地方用静态工厂都很好。上面也写到了静态工厂的
优缺点，比如在什么地方适合使用，什么场合最好不要使用等等。这本书我觉得翻译的也不错，绝对值，强烈向有一定开发经验的人推荐。我大概看了3周半的样子
把这本书看完，这时的时间也到了2004年的9月初，新员工入司培训也不再是第一个月纯粹的日语培训，而是技术培训和日语培训一起开展，技术上培训
Java，Web开发，数据库开发这三门课程，日语则开始进行日本语国际三级的培训。公司的日语培训和技术培训都还不错，技术培训纯粹把大家当作什么都不
懂的人，在Java上从最原始的Hello
World开始培训，Web开发上从HTML页面开始培训，数据库开发则从Oracle的安装，SQL语句的编写开始培训。当然，在培训的过程中我也不会
闲着，而是又开始寻找自己要啃的书本，这次，我选中了Oreilly新出版不久的《Java与XML》第二版。</p><p>&nbsp;&nbsp;&nbsp;
由于XML表达数据的自由性以及强大型，所以XML特别适合于做配置文件以及数据信息文件，在Java中XML的使用可谓是多如牛毛。在J2EE中，从
Web
Application的web.xml开始就是XML文件，到下面的Framework配置等等，没有一个没有XML的身影，而且XML都起到了举足轻
重的作用。虽然我原来也懂一点XML，不过也仅限于XML的语法以及结构等等，那些深入下去的东西基本还是盲点，关于Java中如何处理XML更是一窍不
通。为了更好的学习J2EE，XML是必须征服得一座山峰。这次，我依然又再一次信任了Oreilly出版社，买了本当时出版不久的《Java与XML》
中文第二版。这本书刚开始并没有过多介绍XML本身过多的东西，只是为了怕某些读者并不了解XML而对XML语法结构等做了非常简要的介绍，不过也非常到
位的介绍。介绍完了这些XML基础知识后就开始了SAX——〉DOM——〉JDOM——〉JAXP——〉Web
Service的历程。不过我现在觉得如果能介绍DOM4J就更好了，因为我现在觉得DOM4J是Java中最好用而且性能也不错的XML处理工具。刚开
始的我其实什么是SAX，什么是DOM都不知道，对JAXP更是一无所知。这本书英文版据说很受好评，中文版我只能说一般，因为有些地方估计译者并不擅长
这一块，所以翻译得很生硬，以至于部分段落难于理解。总体来说，书的绝大多数内容还是可以看懂，由于没有具体实际操作的经验，所以很多也就是把概念理解
了，直到几个月后做正式项目开始应用这些XML处理工具进行开发的时候才达到了熟练运用的能力。在这本书中学会了JDOM的使用方法，JDOM也还是比较
好用的，学会了JDOM，以后操纵XML也方便了许多。这本书我的建议就是，可以一口气读到第十章JAXP部分，后面的Cocoon以及SOAP等等部分
那本书介绍的并不是很好。Cocoon我是看了官方专门的帮助文档以后才感觉入了门。而SOAP是经过别的书籍加上项目中的实际运用才真正学会的。</p><p>这
时到我刚进公司已经两个月过去了，时间已经到了9月中旬的样子，还有一个月我们公司新员工入司培训就要结束，也意味着还有一个多月我们就要开始接触正式项
目。这时的我写B/S程序仅仅是JSP +
JavaBean的水平，连JSP中的TAG都不会自定义，看见别人网上的程序自己还自己定义Tag很是羡慕，于是决定把那本《JSP设计》认真看完，把
自定义Tag的功能实现。后来看了以后发现原来那本《JSP设计》的精华都在最后的150页内，最后那部分先是介绍了自定义Tag的定义方法以及Tag定
义所带来的一些好处。自从学会了如何自定义Tag，在后来公司的项目中自己也根据项目的特点定义了一些共通的Tag，大大方便了不少项目中的开发人员，提
高了生产力。这本书而且也说了一下B/S开发的两种Web
Module。在这里，我第一次知道了Web开发可以用一个Servlet作为控制器，用JSP仅仅作用于表现层，这也为以后掌握MVC打下了很好的基
础。</p><p>9月中下旬扫完了《JSP设计》的尾巴后，有一次跟公司给我们培训的老师在闲聊时谈到了项目开发，我询问他项目是不是用JSP和
JavaBean来开发，他笑着和我说不是这样的，而是基于Framework来进行开发。比如Struts就是公司的常用Framework。
Struts这东西以前也好像听说过，不过从来也只是听说而已，并没有看过。得到这个信息的我，为了能尽快熟悉实际项目的开发环境，便决心尽快学会
Struts。当时的市场上讲解Struts的书只有一本，也就是Oreilly的《Jakarta Struts编程》，不像现在连《Struts
in
Action》的中文版也有了。我去了书店买来开始回家看，刚开始看的时候觉得如同云里雾里一般，因为这本书归纳总结性的东西很多，比较适合当参考手册，
而真正带领新手入门这一块做的并不好。所以当我把这本书都看完了以后，还是不会用Struts编写一个程序，只是感觉自己朦朦胧胧懂了一些概念，比如
MVC什么的。在公司我们的培训也结束了，通知在国庆节过来以后的第一个星期——大概是10月10日左右进行考试，最后根据培训考核情况来调整薪水。当时
跟我一起培训的新员工基本上没有人会Struts，其实这个时候连会用JSP +
JavaBean写一个最简单的登录画面的人也没有多少个，大部分人还是模模糊糊懂一点，但是具体做东西还是做不来的那种水平。国庆节大概10月5号的我
去了趟书店，突然发现书架上新上了一本书，就是孙卫琴编写的《精通Struts》这本书。孙卫琴的书我倒是听说过，就是在这之前出的一本关于Tomcat
以及Web
App开发的书，据说挺容易上手的。我翻看了这本书的目录结构，觉得可以值得一读，于是虽然价格不菲，仍然买回家当天就研读起来。凭我的读后感觉来说，这
本书也许学术价值并不高，说得深入的东西基本没有，但是特别适合初学者，通过Hello
World这种例子迅速让你手把手编写出第一个Struts程序。就这样，在这本书买回来的第二天，我自己就用Struts写了一个很简单的登录画面程
序，当时的感觉别提多兴奋了，就感觉自己入了门，以后的道路一片光明。在这里，我要由衷地感谢孙卫琴女士，写了这么一本适合初学者的书，同时也建议没有学
过Struts但又想掌握Struts的Java程序员们买这本书回来看（不知道我是不是有书托之嫌，我只是说我自己的心里想法）。</p><p>&nbsp;&nbsp;&nbsp;
国庆的假期放完了，我也回到了公司准备考核，上午是笔试，下午是上机考试。笔试分为了4块，分别是Java，Web开发，Oracle数据库，以及
CMMI规约。这四门除了Oracle数据库我一向不是很擅长，只考了个中等分数以外，其他三门分数都名列前茅。不过CMMI规约老实说我也不怎么会，不
过碰巧考的很多都是我知道的东西。下午是上机考试，题目给出来了，我一看题目，原来是一个最简易的成绩查询系统，也就是数据库里面已经有一些学生成绩，我
们写一个检索页面，可以输入或者选择检索条件，把符合我们检索条件的数据输出并显示在画面中。我于是拿刚学会不久的Struts进行编写，在3个小时内把
整个页面都写好了，并且还自定义了一个Tag来显示数据信息。考完以后我才知道总共也就五六个人程序可以运行，而且只有我一个人用的是Struts，其他
人基本都是最简单的JSP +
JavaBean，有的人连JavaBean都没有，数据库操作全部写在了JSP页面中。毫无疑问，这次上机考试我得到了好评，给了最高分。在全部的培训
成绩中我也居前两名，我们部门新员工我排第一名。带着这个成绩，我们的入司培训基本结束，开始进入部门做实习项目。</p><p>&nbsp;&nbsp;&nbsp;
虽然说我们正式进了部门，不过试用期还没有结束，我们试用期最后一个月的任务就是做一个实习项目，当然，每天还是要进行日语培训，因为要参加12月份的国
际日语三级考试。公司也象征性得给大家培训了三节课的技术，第一节是Struts培训，第二节是Web
App的MVC结构的培训，第三节是Log4j培训，这几次培训下来，大部分人感觉好象云里雾里一样，基本什么都没听懂，不过我由于有了点Struts的
基本知识，所以感觉收获比较大，特别是MVC的培训中我真正明白了视图——控制器——模型这三层每层应该怎么处理，知道了一个Web
App中如何分Java
Package比较好，明白了专门有一个DAO层来处理数据库所带来的便捷，明白了Log在Web开发中的重要地位，这为以后的开发带来了很大的好处。实
习项目的课题很快就下来了，要我们做一个电子相册的B/S系统，要求有图片上传，图片检索，图片显示以及要用Struts来构建，这些是基本的要求，其他
功能可以自由扩张。我们部门的新员工分为两个小Group，都是一样的课题，互相促进和学习，每个Group还配备了一个老员工，作为督促我们的进度，防
止我们有过大的偏差等等，不过具体做东西上原则上要求是不会给我们什么帮助。首先每个小Group要选出一个Leader，结果我被大家一致选为我们
Group的Leader。在小组讨论中我们先进行需求分析，大家的讨论很是热烈，主意也很多，不过基本上组员们也都是点子多，具体实现上面还都没有想
过。对于他们的那些建议，绝大多数我决定都作为我们要实现的目标，但也有少部分我觉得目前以我们的水平还无法实现的，就先否决了。会议开完后，当天回家以
后我就开始排开发计划和个人的进度，第二天写画面的基本设计，第三天把组员拉过来开始Review基本设计，我们组的速度还算比较快。从星期二公布课题，
到星期五就和几个组员一起把DEMO画面设计出来了。原来的计划是第二个星期一开始Coding，大概花一个星期完成。不过其余组员似乎还是不怎么会
Struts，于是我回家星期六星期天基本全天都在看书写代码学习，花了两天把项目基本Coding完毕。其中Web页面部分也不再使用一开始使用
Frame的做法，而是采用了Tiles框架。Tiles的使用过后我感觉是非常好的东西，经过简单的配置可以完成大批网页中类似部分的构建，而且生成的
属于一个页面，这样就省去了以前写Frame时提交页面总是要考虑设置Target以及在引用对象的时候大批Parent或者top对象使用的麻烦事了。
在开发过程中我使用了Log4j，这为我的调试程序带来了极大的方便，呵呵，可以想象，没有Log来调试一个Web程序真是不可想象的。</p><p>这
段时间我是边开发边翻查那本《精通Struts》的，这样，迅速把Struts中提供的许多Tag弄熟练了，为以后具体的项目开发带来了便捷。也许是一向
以来公司的实习项目完成效果都不是很理想吧，这次我们的迅速完成比较出乎Leader的意料，综合三个月的试用培训，由于我在日语和技术以及实习项目中表
现都还不错，所以定工资级别时也是同一批进公司的新员工中最高的，随着转正会议的结束，我也在10月底成为了公司的正式员工。大概刚刚进入11月份，我们
Group便开动一个项目，项目不是很大。当时老员工们许多都在做项目的详细设计，我便跟着公司一位技术专家（也是当初给我们入司培训的其中一位老师）做
起项目的Framework构建工作。当时的我才进公司，第一资历尚浅，第二我的确也并不是很会什么东西，所以给我的任务很多都是一些模块的
Utility的设计。比如通用的Check方法啊，CSV以及定长文件的读取解析什么的啊，还有某些在IE中可以实现的效果如何在Netscape中也
能实现同样的效果等等。虽然这些东西在现在看来并不是很复杂，但是当时自己的确随着做这些东西而学到了很多很多。比如使用JDOM对XML文件的解析啊，
很多Check方法的小技巧啊，IE和Netscape究竟有什么地方不一致，该如何解决等等，这些都在这几天内了解了很多。在这几天中，我通过网上查找
资料，临场迅速学习了Java反射的使用方法，并且自己边学边写实例，实现了各种情况下的反射案例。我个人觉得掌握Java反射技术是非常重要的，这让你
可以写一些通用的工具。如果不会反射技术的话，也许你永远只能写一些针对特定情况下的解决方法。而会使用反射以后，你可以写一些代码，这些代码可以用在许
多地方，达到自己扩展甚至构建Framework的效果。在这个项目中，我使用了自定义Tag和Java反射技术，定义了些项目中比较需要的通用的
Tag，方便了大家。</p><p>&nbsp;&nbsp;&nbsp;
后来听老员工说新员工进公司就开始做Framework是以前从来都没有过的事情，因为我们Leader对我希望比较大，所以想尽可能培养我，让我早点挑
起项目大梁，所以给我的成长提供了一次又一次的机遇。11月中旬以后，项目开始进入编码阶段，我也第一次看到了正式的项目设计书。第一次看到设计书的时候
我都觉得自己脑子有点懵，一大堆日语什么含义自己不是很清楚，而且感觉根本无从下手，不知道从哪里开始看比较好。项目担当耐心得和我说了设计书的格式以及
究竟什么地方是什么一个含义，以及Coding的时候按照什么个思路来看设计书。再加上项目中有老员工先写了个Sample，让大家看了标准的一个流程，
所以我们就依葫芦画瓢，慢慢得把一个画面一个画面Coding完毕。当然了，后来也有测试员来测试我们的画面，发现bug后就发Bug
Report给我，那一个月就是在Coding，修正Bug中渡过的，这个项目是用Struts做的，因为不大。所以也没有再用其他的
Framework，数据库操作那里只有个非常简单的单表操作DAO层，其余的DB操作都是自己通过JDBC操作语句来完成的。在这第一个自己接触的真正
项目中，我自己学到了很多B/S设计的技巧，感觉很充实。不过书本学习方面我也没有闲着，我为了能够深入了解Java，大概在11月中旬左右开始看《深入
Java虚拟机》这本书，由于内容比较深入，所以看得也有点吃力。书翻译得和写得都还不错，值得一看，我一直看了前八章，看到Java程序运行细节后就没
再看了，大概看到了12月底的样子吧，呵呵，有时间的话决定把后面的部分也看完。这本书看完后收获就是了解了Class文件的实质，Java的安全模型，
虚拟机是如何工作的。这些知识对后来调试程序Bug或者Exception的时候非常有好处，可以把以前自己觉得莫名其妙的错误的原因找出来，不像以前遇
到很古怪的Exception的时候怎么死的都不知道，从读完这本书以后，在以后的调试异常中很少再有不知所以然的感觉了。</p><p>&nbsp;&nbsp;&nbsp;
2004年12月底的时候，我的第一个项目也做完了，由于我空闲着，Leader便在星期三的时候把一个公司内部开发的Source统计的小工具让我进行
修改，使得添加一个比较有用的功能。东西给我的时候基本没有任何文档，在我手上的就是一堆源代码而已，界面是用Swing制作的，因为没有专门在UI上进
行精心设计，所以说不上好看，典型的Java编写的图形界面的程序的样子。软件不是非常大，估计在1万行源代码以内，不过对于只有一个人修改来说，也比较
够呛了。还好我在刚学Java的时候用JBuilder写了一些Swing的程序，现在还是对Swing有概念的，所以拿到手上以后经过仔细分析，逐渐理
清了头绪。经过修改和自己测试完毕后，觉得还比较满意，达到了预期的目标，于是在星期五的时候提交给了Leader。通过这次，对Swing的开发又加深
了印象，自然，在有的细节技巧方面受益匪浅。</p><p>元旦很快来临了，在年底以前，公司觉得有必要学习下Hibernate，虽然我们目前的项目中
还没有用过Hibernate，而是用另外一个公司内部开发的ORM工具，不过几名技术专家初步对Hibernate感觉后觉得Hibernate的功能
要强大的多，而且是开源的，不断有人在推动，升级，所以有必要我们要学Hibernate。这次的学习采用学习小组的形式，也就是公司内部先抽几名员工
（主要是技术部门的，当然，开发部门如果有兴趣的话也可以考虑）来进行深入学习，然后定期开会交流互相学习，达到短时间内先行的几名成员迅速深入掌握
Hibernate的形式。由于我第一处于空闲状态，第二也比较有兴趣，而且跟技术部门的专家们也比较谈得来，所以我也加入了其中，成为几名学习小组中成
员的一部分。我们学习资料主要就是《Hibernate in
Action》英文版一书以及官方的帮助手册。我负责其中对象操作，Transaction和Cache，还有高级Mapping关系的设置几个部分的学
习。由于资料都是全英文的，所以看书速度并不是很快，不过还是初步得到掌握了。大概学习了半个多月的样子，我们各自基本学习完毕，互相交流后并且写下了读
书笔记，用来后面具体项目开发时候参考用。通过这大半个月的学习，我个人觉得提高了非常多，在这之前，我只知道有ORM这种东西，但是从来没有使用过，也
从来没有看过。自从学过了以后，我不仅对Hibernate在代码编写时的使用比较熟悉了，而且对Hibernate的配置以及许多底层的知识有了很清楚
的认识，让自己在数据持久化方面的认识提高了大大的一步。</p><p>&nbsp;&nbsp;&nbsp;
元旦过后，虽然一边在学习Hibernate，不过由于下面项目的需要，Leader跟我说要我学一下Unix下的Shell编程，因为
项目中许多批处理会用Shell来启动。UNIX命令在学校时候学过的，不过这个时候已经忘记了很多，只是翻阅资料的时候还能回想起来不少命令。
Shell并不难，如果有了编程基础，学习Shell编程也很快的，总体感觉就是编程语言大同小异，从基本语法来说，不外乎赋值、条件、循环这几种类型。
只要迅速掌握这几种类型在这种编程语言中的编码格式，那么你就可以迅速掌握这门语言最基本的编程能力。Shell经过一周的学习后觉得感觉不错，不仅可以
顺利看懂别人写的Shell程序，而且自己可以在Linux下顺利写出符合自己需求的Shell程序并能顺利执行。但是突发事件总是有的，那个项目突然决
定延后两个月，所以前一个星期的学得Shell等于暂时派不上用场了。不过嘛，多学一样技能总是没有害处的，而且又复习了那么多Unix命令啦，感觉还是
很不错的。于是我又进入了不在项目中的真空期了。</p><p>&nbsp;&nbsp;&nbsp;
但是好景不长啊，好日子还没有过上两个星期，公司去年做的一个比较大的项目开始了2期开发，我也被一下拖入了项目中。说起那个项目，公司好多人还心有余
悸，据说去年那个项目开发的时候，大概50多号人干了好几个月，每天都是11点以后才有可能回家，周六是铁定加班，周日是看情况，晚上就算加班加到凌晨3
点也不是什么奇怪的事情。当时大家都说多来几个这种项目大家就要死了，这次这个项目的2期过来了，大家精神又一次紧张起来咯。一开始照例是项目会议，听完
项目经理描述以后，大家也放心了不少，这次虽然说是二期，不过规模不大，只需要15个人左右干一个月就能搞定。主要是把项目一期里面一些地方进行改修，当
然也有需要新的画面的开发，不过相对于去年的那块不是很多而已。对我来说这次是个很大的考验，因为项目是二期，项目组内除了我，其他的人都做过1期开发，
所以对项目结构都很清楚。这次项目开始并没有什么培训，所以我只能单独看1期的源代码来熟悉项目结构什么的。这个时候项目经理把我叫去要我办理护照，准备
这个项目派遣我去东京现场维护。</p><p>这个项目是个比较全面比较大的项目，服务器采取了集群的方式，数据量也是千万乃至上亿级别的，所以性能要求
特别高。在技术方面用到了很多，使用EJB来控制Transaction，使用了ORM工具来操纵DB数据等等等等。而且由于比较庞大，所以服务器初始化
的那块为了Load上去大批配置信息，代码量极其庞大，在权限控制的那块地方，代码非常难以读懂。这也给我一开始的学习代码带来了很大的一块麻烦。不过总
算静下心来后把整个项目框架以及实现手法基本摸清楚了，这个时候觉得非常开心，而且对Web应用程序的构造心里面也非常充实，觉得自己已经具备写
Framework的初步能力了。</p><p>项目是紧张的，基本上每天晚上都要加班到11点，然后打车回家，哈哈，公司报销。而且临近过年，这么加班
也一点都感觉不到过年的气息。不过我也不能因此放松了自己的学习。我觉得自己的基础应该算比较好了，便开始啃起另外一本大部头——《Java与模式》。一
直以来我对设计模式的感觉就是一些已经成型的，久经考验的代码框架，具有非常好的可扩展能力以及非常好的代码健壮性。不过初学者最好不要看设计模式，因为
你接触的代码不多，如果贸然看设计模式的话，会造成不明白为什么这种设计模式好，究竟好在什么地方的情况下就在代码中乱套设计模式，对自己的以后编码发展
带来不利的影响。每一种设计模式都有每一种设计模式的特点，自然也有他们自身的适用范围，比如拿最基本的单例模式（Singleton）来说，适合于做配
置信息读取器，主键产生器等全局唯一的东西。如果初学者不明白这些，拿单例模式乱套，什么都用单例模式，比如把普通传递数据用的JavaBean也做成了
单例模式，带来的恶果就严重了。这本《Java与模式》我是从头到尾认认真真看了，每看完一个模式都会仔细回想以前看的代码哪里用到过这个模式，总会自己
想想这些模式适用于哪些地方。因为这个时候我自己编写的代码行数也已经很多了，所以看见这些模式就会特别有感觉。经过50多天的认真研读，所有模式都被我
消化了下去，并且使得我的对程序开发上面的认识提升了非常大的一步。顺路说一句，这本书写得非常好，例子很多，但是不复杂，有一定代码编写经验的人就可以
看懂了。不过看懂并不是最重要的，重要的是消化下去，用来理解以前看过的代码的精华，这样自己才能得到提高。</p><p>&nbsp;&nbsp;&nbsp;
这个项目虽然很紧张很忙，不过我还是适应了下来，而且对整个项目结构什么的都有了比较好的整体的把握。项目横跨了整个过年期间，所以在过年的那几天都必须
开着手机，怕有什么突发事件要求去加班。签证在2月4日左右送过去签，Leader跟我说因为在过年期间，所以签证可能会比较缓慢，比较难签，不过一般情
况下1个月应该足够了。原计划我是跟另外两个同事3月6日去东京，这样算算也差不多。不过中国有句话叫好事多磨，呵呵，用在我身上的确不过分，我的签证3
月3日日本领事馆才签，三月四日送到南京。3月5日和3月6日是双休日，所以3月7日签证才送到我手上。由于计划是3月6日派人去东京，所以只好派另外一
个身上有签证还没有过期的人代替我过去，这次的机会就算泡汤咯。不过我并不是很在意，因为公司这里去东京出差的机会狠多，特别对于开发人员，据说工作几年
后一听到去日本出差就不乐意，毕竟也背井离乡么。</p><p>&nbsp;&nbsp;&nbsp;
在这个项目的途中，大概在2005年1月底2月初的时候公司也开始进行了制作详细设计的培训，我虽然在项目中，不过也成为了其中一员。这次培训总共大概6
次课，每次2个多小时，虽然时间不长，不过把详细设计的要点以及思路和容易出错的地方都说了出来，感觉很是不错，这几次课的培训后，虽然可能要我立即进行
详细设计编写还有点困难，不过心里面已经有了不少底，我觉得经过一段时间后的锻炼，我应该可以有进行详细设计的能力了。</p><p>&nbsp;&nbsp;&nbsp;
3月初这个大项目结束后，本以为可以休整下，不过很快通知我3月7日加入另外一个项目，其实也不算一个正式的项目，属于公司知识库的一个信息查询模块。由
公司的一个技术专家负责，那人也就是我进公司时候第一个项目中带着我的那个技术专家，说起来我和他还真有缘，现在我这个项目还是跟着他，而且公司里面唯一
一个和我同月同日生的人，真是很巧的巧合呢。他人挺好，很热心，所以我也从他那学到了很多东西。这次由于不是正式项目，所以并没有什么基本设计书，而是他
给我们开会议的时候大致说了下项目的内容，每个画面的具体功能以及数据库表格的设计。由于这次项目规模很小，总共就12个画面的量，所以不采取
Struts等Framework，而是采用比较原始的JSP +
JavaBeans的构造。我们每个人根据他所跟我们讲解得功能写每个人自己分配到的画面的详细设计，其实也不算真正的详细设计，就是每个人把自己操作的
那块的具体逻辑设计写出来，然后和他一起review一次，再开始编写代码。详细设计这里我做的很快，当天下午就把自己分配到的两个画面业务逻辑什么的都
写好了，星期一布置得任务，我星期三的时候全部编码自测完毕提交，所以我的感觉就好像这个小项目一瞬间就结束了。</p><p>日本每年财务结算是在3月
份，所以我们历来的习惯就是每年1月和2月很忙，3月开始清闲，一直可以到5月左右会接到个大项目昨。所以接下来就真正到了我的空闲时期，没有项目的压
力，我可以自由学我自己喜欢的东西。很久以前买了本《精通EJB》第二版，可是一直以来我觉得自己功力尚浅，所以没有看，这次我想认真学学EJB。虽然大
家公认EJB并不是很好，不过历来受到批评的都是EJB中的Entity
Bean部分，这部分我觉得可以借助Hibernate来弥补，而会话Bean和消息驱动Bean则还是挺不错的。这次也当学一门技术，学习其好的东西，
不是很好的东西就当作以后开发时候的借鉴。《精通EJB》这本书我的感觉是书质量比较好，不过翻译的水平稍微差了点，特别是有不少错误，而且很低级的错误
居然校对的时候都没有发现，不能不说是个比较大的瑕疵。但是它不失为一本EJB的好教材。从一开始的JNDI开始，然后讲解了对象序列化，RMI-
IIOP等等。这些以前都模模糊糊，或者是看过了但是还不知道究竟有什么用。但是经过这次的学习以后，对这些分布式系统比较需要的东西有了进一步的了解，
感觉头脑中比较清晰，究竟RMI是什么样的工作原理，怎样实现一个远程方法调用等等。接下来的EJB学习，自己用Eclipse +
Weblogic边看书边动手，写了一个个自己的学习小程序。我个人感觉看书最好就是边看边自己动手写小学习程序，这样比光看不练能学到多得多的东西。学
了EJB后觉得脑子又清晰了很多，看见一个案例后头脑中就会有好几种如何解决的方法，几种方法互相在头脑中自己比较，经过这样，大大提高了自己的思维活跃
性。</p><p>&nbsp;&nbsp;&nbsp;
3月中旬开始由于公司比较清闲，大部分人处于没有项目的状态，所以公司举办了第一届全公司范围的编程竞赛。公司只指定了题目为一个日历系统，要求具有日程
记事等功能，其余功能自由发挥。这次不再采用团队形式了，而是采取各自为战的策略。自从培训过详细设计以后，我头脑一直有如何写详细设计的思路，这次我自
己首先指定了开发计划，保证自己控制自己的进度。接着进行了需求分析，确定了我有哪些功能。然后在自己的基本设计中开始进行数据库结构设计。这次我决定采
用Hibernate+Struts的结构进行编写，这样我的数据持久层操作大大简化，而且功能上也增强了许多。DB设计好以后我开始DEMO画面的制
作。说实话，我美工水平实在不怎么样，可以说虽然一般网页的效果我都会自己做出来，不过具体网页设计成什么样我还真是一窍不通。还好
Dreamweaver我还算算是比较熟练，自己捣鼓捣鼓也想摸象样把DEMO画面给设计出来了，不过美观不美观我就觉得不怎么样了，只是我能力有限，也
没办法设计的更好看，这个时候我感受到了一个项目中美工是多么重要啊。下面的详细设计自己写得很开心，把需要的功能都用文字反映了出来，这也算我写成详细
设计样子的第一份详细设计了，做完挺有成就感的。接下来首先构筑自己这个小项目的Framework，经过公司两个正式项目的洗礼后，那两个项目的
Framework我都认真研读过源代码的，所以我自己有了自己心里一套Framework的构造方法，特别是如何把Struts和Hibernate结
合起来的结构，自己有自己的一些想法。在这次Framework构造中，我没有复制任何公司以前的代码段，都是凭着自己对以前看的代码理解后写出来的。这
次项目我觉得对自己的提高也很大，首先锻炼了自己详细设计的能力。其次，自己虽然学习过Hibernate，不过从来没有这么样应用过
Hibernate，这次让自己大大提升了实践运用的经验。公司由于知道这时也没有一个真正的项目使用Hibernate，所以这时的我也算公司内部
Hibernate使用经验最丰富的人了，这也为了后来我协助别的使用了Hibernate的项目解决问题的原因。再次，我这次自己写了
Framework，特别在批处理方面，运用了许多刚学会理解掉的设计模式，这些模式让我的程序更具有健壮性和可扩展性，让我在设计方面的能力大大提升
了。</p><p>这次的编程竞赛我写得比较认真，代码量的确也很大，总代码行数超过了3万行，有效代码行数也在1万行以上。经过公司专家们的评定后，我
得到了第一名，虽然没有什么奖品，不过肯定了我这段时间以来的努力，我还是很开心的。而且这次的编程竞赛让我大大增加了编码的熟练度，而且也在其中演练了
许多自己想出来的编程技巧，为以后的发展带来很大的好处。</p><p>&nbsp;&nbsp;&nbsp;
从4月份开始，公司由于比较清闲，所以部门内部开始进行各种培训。我们部门开展了3项培训，第一项就是编程能力培训，第二项是Oracle数据库技术培
训，第三项是测试技巧培训。在编程能力培训中，主要就是把原来没有注意的细节采取大家讨论，轮流讲课的方式进行的，虽然其中很多东西我原来都是知道的，不
过也有原来不清楚的地方。而且经过了这次互相讨论，更加加深了印象。在Oracle培训中我觉得收获很大，这个Oracle培训采取了传统的上课的模式，
由我们开发小组中一个取得OCM的老员工给我们讲解。对于Oracle，我原来基本上就只会写写SQL语句，具体Oracle有什么特别的功能，可以做什
么我也不是很清楚。但是这次上课从Oracle的启动原理开始，让我知道Oracle中究竟有什么，Oracle数据库各部分在磁盘上是如何存放的，
Control File，Redo
File究竟是什么意思，在数据库中起什么作用，数据库是怎么依赖他们运行的，还有如何对Oracle进行系统管理员级别的管理，如何在不停止数据库运行
的情况下进行数据库的更新、升级、备份等等。这些东西虽然非常有用，但在平时的开发是学不到的，这次趁着这个机会大大提升了自己Oracle的水平，感觉
非常开心。数据库一向是我的弱项，在上大学的时候我SQL语句能力只是一般，数据库管理配置什么基本一点都不懂，通过这次集中的培训，我觉得自己的能力又
进一步增强了，弱项也在慢慢退却。在三项培训中最后进行的测试培训我承认我没有怎么认真去学，所以学会的也就是些测试概念，具体的测试技巧什么的还是不怎
么会。现在开发和测试的结合性越来越高，看来要下下功夫，以免给淘汰咯。</p><p>&nbsp;&nbsp;&nbsp;
提了这段时间在公司的进展，还没说自己的学习呢，这段时间正好看见中文版的《JUnit in
Action》出版了，在书的背后写着“如果没有看过这本书，就不要对J2EE进行单元测试”这句话。我早在去年就了解了JUnit的强大功能，再加上
Ant的话对于回归测试是非常便利的。趁有时间，我便于3月底4月初的时候开始看这本书。当时的我看《精通EJB》第二版看了一半，发现其中错误越来越
多，而且文字也有些地方不知所云了，所以扔下不再浪费时间看那本书，专心攻读《JUnit In Action》。凭良心说，Manning的这套In
Action丛书的确很不错，从我先前看的《Hibernate In
Action》英文版就能看出来，其中对代码的编排非常方便读者，感觉可以很顺利的看到你所想看到的代码片断。这套《JUnit In
Action》也是一样，博文视点的纸张还是很好的，排版使用了Manning的风格，阅读起来很舒服，所以我读得很快，大概就两个多星期就读完了这本
400多页的书。感觉的确收获不浅，首先，原来的自动化配置工具中只会使用一个Ant，其他的基本没听说过，在这本书上详细介绍了Maven。听过书中的
讲解以及自己的试验，的确觉得Maven功能很强大，不过感觉起来配置比Ant要麻烦，所以我自己的感觉是Ant在项目中还是会广泛应用，不过Maven
在大型项目，特别是整个Site中有很大的用武之地，对于我们来说，使用的方法都是很简单的，掌握如何编写配置文件才是我们的关键。</p><p>书对
JUnit与Cactus在J2EE的测试手法上给了大量的事例，给人的感觉非常好，In
Action这套丛书最大的优点就在这里，用实例代码片断让你迅速了解一样东西。在实际工作中其实JUnit应用也是比较广泛的，特别如果采取测试驱动开
发的话，JUnit是必不可少的一部分。在TagLib测试，JSP单体测试，数据库测试和EJB测试都是我以前根本没有看过的东西。其实这次虽然学是学
会了，不过真正做的时候还是要有个代码例子依葫芦画瓢。我想大家肯定也都有这种感觉，写程序的时候先找一段有点相似的代码片断Copy过来，然后看看要修
改什么地方，真正从头到尾自己用手写的代码片断是不多的，除非你已经烂熟于心。不过这本书快看完的时候，项目又来了。</p><p>&nbsp;&nbsp;&nbsp;
这次做一个企业的MIS系统，与以往不同的是，这次客户给了一个比较庞大的基盘，封装了近100个Tag，基本上把各种各样有可能遇到的操作都封装到
Tag里面了。而且所有的画面显示等信息都是放在数据库的Table中，所以这次要求不写任何程序代码，只是学会使用好这些Tag，然后利用这些Tag写
出Jsp页面。一开始的时候还真是头疼，这些Tag一个都不明白，而且文档不是非常齐全，Tag的Source中注释也比较少，学习起来不是很方便。我们
一共3个人投入到这个项目的前期准备中，在第一个星期的学习中大家互相分配好个人学习的模块，随时互相交流。在后来的深入中发现这个项目的业务逻辑操作会
使用PL/SQL以及存储过程来进行，对于我来说，PL/SQL是从来没有做过的东西，就叫做一窍不通，于是我需要从头开始学习PL/SQL，以及如何编
写存储过程。我从网上下了一个PL/SQL的电子书籍，然后在公司花了一天时间进行学习，个人用的是Toad来调试PL/SQL的，虽然别人喜欢用
PL/SQL
Developer来进行开发，不过我还是比较钟爱Toad，而且Toad的确功能也很强大，使用起来也很方便就是了。经过第一天的PL/SQL的学习，
基本掌握了全部语法以及存储过程的书写格式等等，开始能够写写非常简单的PL/SQL。接下来的两三天不断巩固熟练，客户那里也发过来几本详细设计让我们
练习着做一下。有了实际的详细设计，再加上我们之间互相交流，我们提高的都很快，大概过了三四天，大家就把基本详细设计代码编写完毕了，而且经过实际锻
炼，我的PL/SQL编写存储过程的水平也大大提升，已经可以满足开发中的需要了。</p><p>这个项目因为如果我们一开始做的能让客户满意的话，后续
的项目将会比较庞大，所以Leader决定把我们Group比较空闲的其他人也先培训一下，让他们有点感觉，到以后正式开发的时候也能迅速进入状态，负责
给他们培训的任务也就交给了我。说起来是培训，其实也就是把大概流程以及方法通过一次会议的形式告诉他们，然后把我前面已经作好的那个画面作为他们的作
业，要他们看着设计书自己把画面制作出来。这个时候也要放劳动节了，黄金周可以休息一个星期，想想就觉得很Happy。劳动节的时候基本没有怎么学习，只
是先把XML-RPC仔细看了下，学会了如何去写一个XML-RPC的应用，接着稍微看了点SOAP，看得也不错，只是些简单的SOAP的例子而已，那些
SOAP的复杂东西都没有看。</p><p>&nbsp;&nbsp;&nbsp;
很快就五一黄金周七天放假放完，八号开始上班，上班后就开始正式做节前就定好的那个项目，这次性质属于试做，也就是人家先发一批设计书过来，我们然后开始
Coding，大概做了一周后，我自己害了急性结膜炎，只能回家休息，这次可真的是只能休息了，眼睛觉得特别涨，不要说电脑了，连书都不能看，看了眼睛就
疼。所以在家就只能睡大觉，过了一周眼睛大概才复原，可以去公司上班了。回到公司以后，Leader通知我说我不用去做上次那个项目了，要我加入我们
Group的一个新的项目，这个项目比较大，当时还处于东京刚刚做好基本设计，我们从东京把任务接下来，准备发回来做详细设计。我进去的时候项目才开始三
四天，基本上还没有做什么，这次我进入了详细设计制作小组，开始进行这个项目的详细设计的制作。</p><p>&nbsp;&nbsp;&nbsp;
由于我属于第一次在正式的项目中参与详细设计，所以很多东西都不明白，特别是业务上面的东西，许多日语中的业务术语我根本不明白，比如什么卖切，切替，仕
入什么的。看着基本设计书，感觉跟以前看详细设计书有很大的不同。具体的东西写的少了，业务流程逻辑框架什么的比较多，所以需要首先把业务内容都熟悉了，
才可能写出详细设计来。这次的详细设计我也不是孤军奋战，而是有一个进公司4年的老员工带着我一起做，我的任务很轻，不过重点是学会如何去写详细设计，也
许下次再有一个比较大的项目，就没有别人再带着我，而是我自己一个人去完成详细设计了。大概详细设计写了20天左右，我被通知当天把手上的一份详细设计写
完，第二天进入方式设计小组进行方式的设计。</p><p>进入方式小组以后，接到的任务就是好几个编写DB操作方面的代码自动化生成工具。由于这次DB
方面并没有非常强制性的那种规约，所以SQL语句的编写可以说比较随意，这就给我工具的编写带来了很大的难度和挑战。这次负责管理方式小组的人仍然是进公
司以后经常带着我的那位技术专家，所以也真算很巧呢。写工具其实很对自身代码编写的提高也很有好处，因为首先客户那里资料会不断修改，这些工具你为了以后
客户更新资料后你能顺利更新工具，你需要设计一个优良的Framework，不一定需要多么复杂的Framework，不过一定要尽量把程序各方面的耦合
度尽量降低，这样才有利于自己对工具进行扩展。紧接着很快，项目代码编写开始了，我的任务算中等偏上，有2个画面和一个批处理需要编写，复杂度还算比较繁
一点。这次项目需要编写JUnit程序，每天都要进行回归测试，保证代码Method的正确性。JUnit虽然自己会用，但是从来没有在真正的项目中使
用，所以在真正用的时候感觉有点手足无措。以前做JUnit从来都是觉得给个参数，检测一个返回值就好了，其实不是那么回事，业务逻辑复杂了，自己需要做
大量的Stub来模拟真实的Class的返回值。设计一个好的Stub是比较困难的，特别在数据库内容比较丰富的时候，一张数据库Table就有上百个
域，工作量可见一斑了。项目要到05年9月中旬才会结束，所以现在还在紧张的开发阶段。我写了JUnit的感觉就是难点不在如何去写JUnit程序，而是
如何去设计测试用例。对于我们这样不是以测试出身的程序员来说，设计测试用例是很痛苦而且很艰难的事情，估计有过相似经验的人肯定会表示赞同。</p><p>&nbsp;&nbsp;&nbsp;
当然我一边在紧张的做项目，对于书本的学习也没有闲着。这段时间抓紧把侯捷的Word排版艺术扫了一遍，看完觉得收获颇丰。虽然我以前觉得我在Word上
用得挺不错，日常的一些操作什么的我都会，不过看这本书的中间我发现我还是有很多地方不会的，也学到了不少东西，在以后的Word排版中会很受到好处。由
于项目用到了Spring知识，所以我也看了网络上那个流传广泛的Spring开发指南的PDF看了一遍，感觉长了见识，对IOC以及DI有了进一步的了
解，也理解了为什么需要采用IOC以及DI。不过这个也没有深入下去仔细看，以后等项目稍微空闲一点的时候一定再把Hibernate和Spring好好
看一下，学习人家的设计理念，提高自己能力。对了，也许最重要的是我最近在看一本书，就是《J2EE核心模式》的第二版，我当时原来准备看电子版的这本
《Core J2EE
Patterns》的，不过突然在书店发现这本书的中文版出来了，而且译者有熊节的名字，也就是跟侯捷一起翻译《重构——改善既有代码的设计》的那个译
者，我比较相信他翻译的水平，于是买回来看，虽然项目非常紧张，我一个月算上周末需要加班在100个小时左右的样子，但是我相信时间是海绵里的水，只要去
挤，肯定会有的。所以我到现在大概看了2周的样子，已经看了300多页，而且感觉自己的设计视野也开阔了许多，这本书的确很好，把J2EE中常用的一些模
块原理都说了出来，说明了为什么这么做好，这么做如何减少了耦合性，提高了可维护性等等，总之，有1年以上J2EE开发经验而且觉得自己对J2EE有了比
较好的了解的开发人员我强烈推荐看这本书。看了这本书以后我都在回想以前设计的一些框架，一些模块，觉得自己有很多地方当时设计的时候觉得很精巧，不过却
属于弄巧成拙，加大了模块的耦合性，所以在修改的时候比较难于下手。<br></p><img src ="http://www.blogjava.net/ericwang/aggbug/25498.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-12-26 21:43 <a href="http://www.blogjava.net/ericwang/archive/2005/12/26/25498.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转载】关于Java文件路径问题</title><link>http://www.blogjava.net/ericwang/archive/2005/12/16/24301.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Fri, 16 Dec 2005 14:53:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/12/16/24301.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/24301.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/12/16/24301.html#Feedback</comments><slash:comments>2</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/24301.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/24301.html</trackback:ping><description><![CDATA[
<script language="javascript">document.title="关于Java文件路径问题(原创＋转贴) - "+document.title</script>
		
	
	
	<div class="postText">
		<p><font size="3">1.如何获得当前文件路径</font></p><p><font size="3">常用：</font></p><p><font size="3">字符串类型：System.getProperty("user.dir");</font></p><p><font size="3">综合：</font></p><p><font size="3">package com.zcjl.test.base;<br>import java.io.File;<br>public class Test {<br>&nbsp;&nbsp;&nbsp; public static void main(String[] args) throws Exception {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(<br></font><font size="3"><font color="#ff0000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Thread.currentThread().getContextClassLoader().getResource(""));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(Test.class.getClassLoader().getResource(""));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(ClassLoader.getSystemResource(""));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(Test.class.getResource(""));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(Test.class.getResource("/"));<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(new File("").getAbsolutePath());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println(System.getProperty("user.dir"));</font><br>&nbsp;&nbsp;&nbsp; }<br>}</font></p><p><font size="3">2.Web服务中</font></p><p><font size="3">(1).Weblogic</font></p><p><font size="3">WebApplication的系统文件根目录是你的weblogic安装所在根目录。<br>例如：如果你的weblogic安装在c:\bea\weblogic700.....<br>那么，你的文件根路径就是c:\.<br>所以，有两种方式能够让你访问你的服务器端的文件：<br>a.使用绝对路径：<br>比如将你的参数文件放在c:\yourconfig\yourconf.properties，<br>直接使用 new FileInputStream("yourconfig/yourconf.properties");<br>b.使用相对路径：<br>相对路径的根目录就是你的webapplication的根路径，即WEB-INF的上一级目录，将你的参数文件放在yourwebapp\yourconfig\yourconf.properties，<br>这样使用：<br>new FileInputStream("./yourconfig/yourconf.properties");<br>这两种方式均可，自己选择。</font></p><p><font size="3">(2).Tomcat</font></p><p><font size="3">在类中输出System.getProperty("user.dir");显示的是%Tomcat_Home%/bin</font></p><p><font size="3">(3).Resin</font></p><p><font size="3">不是你的JSP放的相对路径,是JSP引擎执行这个JSP编译成SERVLET<br>的路径为根.比如用新建文件法测试File f = new File("a.htm");<br>这个a.htm在resin的安装目录下 </font></p><p><font color="#ff0000" size="3">(4).如何读相对路径哪？</font></p><p><font color="#ff0000" size="3">在Java文件中getResource或getResourceAsStream均可</font></p><p><font color="#ff0000" size="3">例：getClass().getResourceAsStream(filePath);//filePath可以是"/filename",这里的/代表web发布根路径下WEB-INF/classes</font></p><p><font size="3">(5).获得文件真实路径</font></p><p><font size="3">string&nbsp; file_real_path=request.getRealPath("mypath/filename");&nbsp; </font></p><p><font size="3">通常使用request.getRealPath("/");&nbsp; </font></p><p><font size="3">3.文件操作的类</font></p><p><font size="3">import java.io.*;<br>import java.net.*;<br>import java.util.*;<br>//import javax.swing.filechooser.*;<br>//import org.jr.swing.filter.*;</font></p><p><font size="3">/**<br>* 此类中封装一些常用的文件操作。<br>* 所有方法都是静态方法，不需要生成此类的实例，<br>* 为避免生成此类的实例，构造方法被申明为private类型的。<br>* @since&nbsp; 0.1<br>*/</font></p><p><font size="3">public class FileUtil {<br>&nbsp; /**<br>&nbsp;&nbsp; * 私有构造方法，防止类的实例化，因为工具类不需要实例化。<br>&nbsp;&nbsp; */<br>&nbsp; private FileUtil() {</font></p><p><font size="3">&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 修改文件的最后访问时间。<br>&nbsp;&nbsp; * 如果文件不存在则创建该文件。<br>&nbsp;&nbsp; * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考</font></p><p><font size="3">虑中。&lt;/b&gt;<br>&nbsp;&nbsp; * @param file 需要修改最后访问时间的文件。<br>&nbsp;&nbsp; * @since&nbsp; 0.1<br>&nbsp;&nbsp; */<br>&nbsp; public static void touch(File file) {<br>&nbsp;&nbsp;&nbsp; long currentTime = System.currentTimeMillis();<br>&nbsp;&nbsp;&nbsp; if (!file.exists()) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.err.println("file not found:" + file.getName());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.err.println("Create a new file:" + file.getName());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (file.createNewFile()) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; System.out.println("Succeeded!");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; System.err.println("Create file failed!");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (IOException e) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //&nbsp; System.err.println("Create file failed!");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; e.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; boolean result = file.setLastModified(currentTime);<br>&nbsp;&nbsp;&nbsp; if (!result) {<br>&nbsp;&nbsp;&nbsp; //&nbsp; System.err.println("touch failed: " + file.getName());<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 修改文件的最后访问时间。<br>&nbsp;&nbsp; * 如果文件不存在则创建该文件。<br>&nbsp;&nbsp; * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考</font></p><p><font size="3">虑中。&lt;/b&gt;<br>&nbsp;&nbsp; * @param fileName 需要修改最后访问时间的文件的文件名。<br>&nbsp;&nbsp; * @since&nbsp; 0.1<br>&nbsp;&nbsp; */<br>&nbsp; public static void touch(String fileName) {<br>&nbsp;&nbsp;&nbsp; File file = new File(fileName);<br>&nbsp;&nbsp;&nbsp; touch(file);<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 修改文件的最后访问时间。<br>&nbsp;&nbsp; * 如果文件不存在则创建该文件。<br>&nbsp;&nbsp; * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考</font></p><p><font size="3">虑中。&lt;/b&gt;<br>&nbsp;&nbsp; * @param files 需要修改最后访问时间的文件数组。<br>&nbsp;&nbsp; * @since&nbsp; 0.1<br>&nbsp;&nbsp; */<br>&nbsp; public static void touch(File[] files) {<br>&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; files.length; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; touch(files);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 修改文件的最后访问时间。<br>&nbsp;&nbsp; * 如果文件不存在则创建该文件。<br>&nbsp;&nbsp; * &lt;b&gt;目前这个方法的行为方式还不稳定，主要是方法有些信息输出，这些信息输出是否保留还在考</font></p><p><font size="3">虑中。&lt;/b&gt;<br>&nbsp;&nbsp; * @param fileNames 需要修改最后访问时间的文件名数组。<br>&nbsp;&nbsp; * @since&nbsp; 0.1<br>&nbsp;&nbsp; */<br>&nbsp; public static void touch(String[] fileNames) {<br>&nbsp;&nbsp;&nbsp; File[] files = new File[fileNames.length];<br>&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; fileNames.length; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; files = new File(fileNames);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; touch(files);<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 判断指定的文件是否存在。<br>&nbsp;&nbsp; * @param fileName 要判断的文件的文件名<br>&nbsp;&nbsp; * @return 存在时返回true，否则返回false。<br>&nbsp;&nbsp; * @since&nbsp; 0.1<br>&nbsp;&nbsp; */<br>&nbsp; public static boolean isFileExist(String fileName) {<br>&nbsp;&nbsp;&nbsp; return new File(fileName).isFile();<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 创建指定的目录。<br>&nbsp;&nbsp; * 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。<br>&nbsp;&nbsp; * &lt;b&gt;注意：可能会在返回false的时候创建部分父目录。&lt;/b&gt;<br>&nbsp;&nbsp; * @param file 要创建的目录<br>&nbsp;&nbsp; * @return 完全创建成功时返回true，否则返回false。<br>&nbsp;&nbsp; * @since&nbsp; 0.1<br>&nbsp;&nbsp; */<br>&nbsp; public static boolean makeDirectory(File file) {<br>&nbsp;&nbsp;&nbsp; File parent = file.getParentFile();<br>&nbsp;&nbsp;&nbsp; if (parent != null) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return parent.mkdirs();<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; return false;<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 创建指定的目录。<br>&nbsp;&nbsp; * 如果指定的目录的父目录不存在则创建其目录书上所有需要的父目录。<br>&nbsp;&nbsp; * &lt;b&gt;注意：可能会在返回false的时候创建部分父目录。&lt;/b&gt;<br>&nbsp;&nbsp; * @param fileName 要创建的目录的目录名<br>&nbsp;&nbsp; * @return 完全创建成功时返回true，否则返回false。<br>&nbsp;&nbsp; * @since&nbsp; 0.1<br>&nbsp;&nbsp; */<br>&nbsp; public static boolean makeDirectory(String fileName) {<br>&nbsp;&nbsp;&nbsp; File file = new File(fileName);<br>&nbsp;&nbsp;&nbsp; return makeDirectory(file);<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 清空指定目录中的文件。<br>&nbsp;&nbsp; * 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。<br>&nbsp;&nbsp; * 另外这个方法不会迭代删除，即不会删除子目录及其内容。<br>&nbsp;&nbsp; * @param directory 要清空的目录<br>&nbsp;&nbsp; * @return 目录下的所有文件都被成功删除时返回true，否则返回false.<br>&nbsp;&nbsp; * @since&nbsp; 0.1<br>&nbsp;&nbsp; */<br>&nbsp; public static boolean emptyDirectory(File directory) {<br>&nbsp;&nbsp;&nbsp; boolean result = false;<br>&nbsp;&nbsp;&nbsp; File[] entries = directory.listFiles();<br>&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; entries.length; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!entries.delete()) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; result = false;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; return true;<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 清空指定目录中的文件。<br>&nbsp;&nbsp; * 这个方法将尽可能删除所有的文件，但是只要有一个文件没有被删除都会返回false。<br>&nbsp;&nbsp; * 另外这个方法不会迭代删除，即不会删除子目录及其内容。<br>&nbsp;&nbsp; * @param directoryName 要清空的目录的目录名<br>&nbsp;&nbsp; * @return 目录下的所有文件都被成功删除时返回true，否则返回false。<br>&nbsp;&nbsp; * @since&nbsp; 0.1<br>&nbsp;&nbsp; */<br>&nbsp; public static boolean emptyDirectory(String directoryName) {<br>&nbsp;&nbsp;&nbsp; File dir = new File(directoryName);<br>&nbsp;&nbsp;&nbsp; return emptyDirectory(dir);<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 删除指定目录及其中的所有内容。<br>&nbsp;&nbsp; * @param dirName 要删除的目录的目录名<br>&nbsp;&nbsp; * @return 删除成功时返回true，否则返回false。<br>&nbsp;&nbsp; * @since&nbsp; 0.1<br>&nbsp;&nbsp; */<br>&nbsp; public static boolean deleteDirectory(String dirName) {<br>&nbsp;&nbsp;&nbsp; return deleteDirectory(new File(dirName));<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 删除指定目录及其中的所有内容。<br>&nbsp;&nbsp; * @param dir 要删除的目录<br>&nbsp;&nbsp; * @return 删除成功时返回true，否则返回false。<br>&nbsp;&nbsp; * @since&nbsp; 0.1<br>&nbsp;&nbsp; */<br>&nbsp; public static boolean deleteDirectory(File dir) {<br>&nbsp;&nbsp;&nbsp; if ( (dir == null) || !dir.isDirectory()) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw new IllegalArgumentException("Argument " + dir +<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; " is not a directory. ");<br>&nbsp;&nbsp;&nbsp; }</font></p><p><font size="3">&nbsp;&nbsp;&nbsp; File[] entries = dir.listFiles();<br>&nbsp;&nbsp;&nbsp; int sz = entries.length;</font></p><p><font size="3">&nbsp;&nbsp;&nbsp; for (int i = 0; i &lt; sz; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (entries.isDirectory()) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!deleteDirectory(entries)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (!entries.delete()) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }</font></p><p><font size="3">&nbsp;&nbsp;&nbsp; if (!dir.delete()) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return false;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; return true;<br>&nbsp; }</font></p><p><br><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 返回文件的URL地址。<br>&nbsp;&nbsp; * @param file 文件<br>&nbsp;&nbsp; * @return 文件对应的的URL地址<br>&nbsp;&nbsp; * @throws MalformedURLException<br>&nbsp;&nbsp; * @since&nbsp; 0.4<br>&nbsp;&nbsp; * @deprecated 在实现的时候没有注意到File类本身带一个toURL方法将文件路径转换为URL。<br>&nbsp;&nbsp; *&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 请使用File.toURL方法。<br>&nbsp;&nbsp; */<br>&nbsp; public static URL getURL(File file) throws MalformedURLException {<br>&nbsp;&nbsp;&nbsp; String fileURL = "file:/" + file.getAbsolutePath();<br>&nbsp;&nbsp;&nbsp; URL url = new URL(fileURL);<br>&nbsp;&nbsp;&nbsp; return url;<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 从文件路径得到文件名。<br>&nbsp;&nbsp; * @param filePath 文件的路径，可以是相对路径也可以是绝对路径<br>&nbsp;&nbsp; * @return 对应的文件名<br>&nbsp;&nbsp; * @since&nbsp; 0.4<br>&nbsp;&nbsp; */<br>&nbsp; public static String getFileName(String filePath) {<br>&nbsp;&nbsp;&nbsp; File file = new File(filePath);<br>&nbsp;&nbsp;&nbsp; return file.getName();<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 从文件名得到文件绝对路径。<br>&nbsp;&nbsp; * @param fileName 文件名<br>&nbsp;&nbsp; * @return 对应的文件路径<br>&nbsp;&nbsp; * @since&nbsp; 0.4<br>&nbsp;&nbsp; */<br>&nbsp; public static String getFilePath(String fileName) {<br>&nbsp;&nbsp;&nbsp; File file = new File(fileName);<br>&nbsp;&nbsp;&nbsp; return file.getAbsolutePath();<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 将DOS/Windows格式的路径转换为UNIX/Linux格式的路径。<br>&nbsp;&nbsp; * 其实就是将路径中的"\"全部换为"/"，因为在某些情况下我们转换为这种方式比较方便，<br>&nbsp;&nbsp; * 某中程度上说"/"比"\"更适合作为路径分隔符，而且DOS/Windows也将它当作路径分隔符。<br>&nbsp;&nbsp; * @param filePath 转换前的路径<br>&nbsp;&nbsp; * @return 转换后的路径<br>&nbsp;&nbsp; * @since&nbsp; 0.4<br>&nbsp;&nbsp; */<br>&nbsp; public static String toUNIXpath(String filePath) {<br>&nbsp;&nbsp;&nbsp; return filePath.replace('\\', '/');<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 从文件名得到UNIX风格的文件绝对路径。<br>&nbsp;&nbsp; * @param fileName 文件名<br>&nbsp;&nbsp; * @return 对应的UNIX风格的文件路径<br>&nbsp;&nbsp; * @since&nbsp; 0.4<br>&nbsp;&nbsp; * @see #toUNIXpath(String filePath) toUNIXpath<br>&nbsp;&nbsp; */<br>&nbsp; public static String getUNIXfilePath(String fileName) {<br>&nbsp;&nbsp;&nbsp; File file = new File(fileName);<br>&nbsp;&nbsp;&nbsp; return toUNIXpath(file.getAbsolutePath());<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 得到文件的类型。<br>&nbsp;&nbsp; * 实际上就是得到文件名中最后一个“.”后面的部分。<br>&nbsp;&nbsp; * @param fileName 文件名<br>&nbsp;&nbsp; * @return 文件名中的类型部分<br>&nbsp;&nbsp; * @since&nbsp; 0.5<br>&nbsp;&nbsp; */<br>&nbsp; public static String getTypePart(String fileName) {<br>&nbsp;&nbsp;&nbsp; int point = fileName.lastIndexOf('.');<br>&nbsp;&nbsp;&nbsp; int length = fileName.length();<br>&nbsp;&nbsp;&nbsp; if (point == -1 || point == length - 1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "";<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return fileName.substring(point + 1, length);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 得到文件的类型。<br>&nbsp;&nbsp; * 实际上就是得到文件名中最后一个“.”后面的部分。<br>&nbsp;&nbsp; * @param file 文件<br>&nbsp;&nbsp; * @return 文件名中的类型部分<br>&nbsp;&nbsp; * @since&nbsp; 0.5<br>&nbsp;&nbsp; */<br>&nbsp; public static String getFileType(File file) {<br>&nbsp;&nbsp;&nbsp; return getTypePart(file.getName());<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 得到文件的名字部分。<br>&nbsp;&nbsp; * 实际上就是路径中的最后一个路径分隔符后的部分。<br>&nbsp;&nbsp; * @param fileName 文件名<br>&nbsp;&nbsp; * @return 文件名中的名字部分<br>&nbsp;&nbsp; * @since&nbsp; 0.5<br>&nbsp;&nbsp; */<br>&nbsp; public static String getNamePart(String fileName) {<br>&nbsp;&nbsp;&nbsp; int point = getPathLsatIndex(fileName);<br>&nbsp;&nbsp;&nbsp; int length = fileName.length();<br>&nbsp;&nbsp;&nbsp; if (point == -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return fileName;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; else if (point == length - 1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int secondPoint = getPathLsatIndex(fileName, point - 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (secondPoint == -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (length == 1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return fileName;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return fileName.substring(0, point);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return fileName.substring(secondPoint + 1, point);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return fileName.substring(point + 1);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 得到文件名中的父路径部分。<br>&nbsp;&nbsp; * 对两种路径分隔符都有效。<br>&nbsp;&nbsp; * 不存在时返回""。<br>&nbsp;&nbsp; * 如果文件名是以路径分隔符结尾的则不考虑该分隔符，例如"/path/"返回""。<br>&nbsp;&nbsp; * @param fileName 文件名<br>&nbsp;&nbsp; * @return 父路径，不存在或者已经是父目录时返回""<br>&nbsp;&nbsp; * @since&nbsp; 0.5<br>&nbsp;&nbsp; */<br>&nbsp; public static String getPathPart(String fileName) {<br>&nbsp;&nbsp;&nbsp; int point = getPathLsatIndex(fileName);<br>&nbsp;&nbsp;&nbsp; int length = fileName.length();<br>&nbsp;&nbsp;&nbsp; if (point == -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "";<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; else if (point == length - 1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; int secondPoint = getPathLsatIndex(fileName, point - 1);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (secondPoint == -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return "";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return fileName.substring(0, secondPoint);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return fileName.substring(0, point);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 得到路径分隔符在文件路径中首次出现的位置。<br>&nbsp;&nbsp; * 对于DOS或者UNIX风格的分隔符都可以。<br>&nbsp;&nbsp; * @param fileName 文件路径<br>&nbsp;&nbsp; * @return 路径分隔符在路径中首次出现的位置，没有出现时返回-1。<br>&nbsp;&nbsp; * @since&nbsp; 0.5<br>&nbsp;&nbsp; */<br>&nbsp; public static int getPathIndex(String fileName) {<br>&nbsp;&nbsp;&nbsp; int point = fileName.indexOf('/');<br>&nbsp;&nbsp;&nbsp; if (point == -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; point = fileName.indexOf('\\');<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; return point;<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 得到路径分隔符在文件路径中指定位置后首次出现的位置。<br>&nbsp;&nbsp; * 对于DOS或者UNIX风格的分隔符都可以。<br>&nbsp;&nbsp; * @param fileName 文件路径<br>&nbsp;&nbsp; * @param fromIndex 开始查找的位置<br>&nbsp;&nbsp; * @return 路径分隔符在路径中指定位置后首次出现的位置，没有出现时返回-1。<br>&nbsp;&nbsp; * @since&nbsp; 0.5<br>&nbsp;&nbsp; */<br>&nbsp; public static int getPathIndex(String fileName, int fromIndex) {<br>&nbsp;&nbsp;&nbsp; int point = fileName.indexOf('/', fromIndex);<br>&nbsp;&nbsp;&nbsp; if (point == -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; point = fileName.indexOf('\\', fromIndex);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; return point;<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 得到路径分隔符在文件路径中最后出现的位置。<br>&nbsp;&nbsp; * 对于DOS或者UNIX风格的分隔符都可以。<br>&nbsp;&nbsp; * @param fileName 文件路径<br>&nbsp;&nbsp; * @return 路径分隔符在路径中最后出现的位置，没有出现时返回-1。<br>&nbsp;&nbsp; * @since&nbsp; 0.5<br>&nbsp;&nbsp; */<br>&nbsp; public static int getPathLsatIndex(String fileName) {<br>&nbsp;&nbsp;&nbsp; int point = fileName.lastIndexOf('/');<br>&nbsp;&nbsp;&nbsp; if (point == -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; point = fileName.lastIndexOf('\\');<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; return point;<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 得到路径分隔符在文件路径中指定位置前最后出现的位置。<br>&nbsp;&nbsp; * 对于DOS或者UNIX风格的分隔符都可以。<br>&nbsp;&nbsp; * @param fileName 文件路径<br>&nbsp;&nbsp; * @param fromIndex 开始查找的位置<br>&nbsp;&nbsp; * @return 路径分隔符在路径中指定位置前最后出现的位置，没有出现时返回-1。<br>&nbsp;&nbsp; * @since&nbsp; 0.5<br>&nbsp;&nbsp; */<br>&nbsp; public static int getPathLsatIndex(String fileName, int fromIndex) {<br>&nbsp;&nbsp;&nbsp; int point = fileName.lastIndexOf('/', fromIndex);<br>&nbsp;&nbsp;&nbsp; if (point == -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; point = fileName.lastIndexOf('\\', fromIndex);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; return point;<br>&nbsp; }</font></p><p><font size="3">&nbsp; /**<br>&nbsp;&nbsp; * 将文件名中的类型部分去掉。<br>&nbsp;&nbsp; * @param filename 文件名<br>&nbsp;&nbsp; * @return 去掉类型部分的结果<br>&nbsp;&nbsp; * @since&nbsp; 0.5<br>&nbsp;&nbsp; */<br>&nbsp; public static String trimType(String filename) {<br>&nbsp;&nbsp;&nbsp; int index = filename.lastIndexOf(".");<br>&nbsp;&nbsp;&nbsp; if (index != -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return filename.substring(0, index);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return filename;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; }<br>&nbsp; /**<br>&nbsp;&nbsp; * 得到相对路径。<br>&nbsp;&nbsp; * 文件名不是目录名的子节点时返回文件名。<br>&nbsp;&nbsp; * @param pathName 目录名<br>&nbsp;&nbsp; * @param fileName 文件名<br>&nbsp;&nbsp; * @return 得到文件名相对于目录名的相对路径，目录下不存在该文件时返回文件名<br>&nbsp;&nbsp; * @since&nbsp; 0.5<br>&nbsp;&nbsp; */<br>&nbsp; public static String getSubpath(String pathName,String fileName) {<br>&nbsp;&nbsp;&nbsp; int index = fileName.indexOf(pathName);<br>&nbsp;&nbsp;&nbsp; if (index != -1) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return fileName.substring(index + pathName.length() + 1);<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp; else {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return fileName;<br>&nbsp;&nbsp;&nbsp; }<br>&nbsp; }</font></p><p><font size="3">}<br>&nbsp;4.遗留问题</font></p><p><font size="3">目前new FileInputStream()只会使用绝对路径，相对没用过，因为要相对于web服务器地址，比较麻烦</font></p><p><font size="3">还不如写个配置文件来的快哪</font></p><p><font size="3">5.按Java文件类型分类读取配置文件</font></p><div class="storycontent"><p><font size="3">配
置文件是应用系统中不可缺少的，可以增加程序的灵活性。java.util.Properties是从jdk1.2就有的类，一直到现在都支持load
()方法，jdk1.4以后save(output,string)
-&gt;store(output,string)。如果只是单纯的读，根本不存在烦恼的问题。web层可以通过
Thread.currentThread().getContextClassLoader().<br>getResourceAsStream("xx.properties")
获取；Application可以通过new
FileInputStream("xx.properties");直接在classes一级获取。关键是有时我们需要通过web修改配置文件，我们不
能将路径写死了。经过测试觉得有以下心得：</font></p><p><font size="3">1.servlet中读写。如果运用Struts
或者Servlet可以直接在初始化参数中配置，调用时根据servlet的getRealPath("/")获取真实路径，再根据String
file = this.servlet.getInitParameter("abc");获取相对的WEB-INF的相对路径。<br>例：<br>InputStream input = Thread.currentThread().getContextClassLoader().<br>getResourceAsStream("abc.properties");<br>Properties prop = new Properties();<br>prop.load(input);<br>input.close();<br>OutputStream out = new FileOutputStream(path);<br>prop.setProperty("abc", “test");<br>prop.store(out, “–test–");<br>out.close();</font></p><p><font size="3">2.直接在jsp中操作，通过jsp内置对象获取可操作的绝对地址。<br>例：<br>// jsp页面<br>String path = pageContext.getServletContext().getRealPath("/");<br>String realPath = path+"/WEB-INF/classes/abc.properties";</font></p><p><font size="3">//java 程序<br>InputStream
in = getClass().getClassLoader().getResourceAsStream("abc.properties");
// abc.properties放在webroot/WEB-INF/classes/目录下<br>prop.load(in);<br>in.close();</font></p><p><font size="3">OutputStream out = new FileOutputStream(path); // path为通过页面传入的路径<br>prop.setProperty("abc", “abcccccc");<br>prop.store(out, “–test–");<br>out.close();</font></p><p><font size="3">3.只通过Java程序操作资源文件<br>InputStream in = new FileInputStream("abc.properties"); // 放在classes同级</font></p><p><font size="3">OutputStream out = new FileOutputStream("abc.properties");</font> </p></div></div><img src ="http://www.blogjava.net/ericwang/aggbug/24301.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-12-16 22:53 <a href="http://www.blogjava.net/ericwang/archive/2005/12/16/24301.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转载】六种异常处理的陋习</title><link>http://www.blogjava.net/ericwang/archive/2005/12/16/24300.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Fri, 16 Dec 2005 14:52:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/12/16/24300.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/24300.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/12/16/24300.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/24300.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/24300.html</trackback:ping><description><![CDATA[<div class="postText">
		<p><font size="2">六种异常处理的陋习</font></p><p><font size="2">你觉得自己是一个Java专家吗？是否肯定自己已经全面掌握了Java的异常处理机制？在下面这段代码中，你能够迅速找出异常处理的六个问题吗？ <br><br></font></p><table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"><tbody><tr><td><font size="2">1 OutputStreamWriter out = ... <br>2 java.sql.Connection conn = ... <br>3 try { // ⑸ <br>4 　Statement stat = conn.createStatement(); <br>5 　ResultSet rs = stat.executeQuery( <br>6 　　"select uid, name from user"); <br>7 　while (rs.next()) <br>8 　{ <br>9 　　out.println("ID：" + rs.getString("uid") // ⑹ <br>10 　　　"，姓名：" + rs.getString("name")); <br>11 　} <br>12 　conn.close(); // ⑶ <br>13 　out.close(); <br>14 } <br>15 catch(Exception ex) // ⑵ <br>16 { <br>17 　ex.printStackTrace(); //⑴，⑷ <br>18 }</font></td></tr></tbody></table><p><br><font size="2">　　作为一个Java程序员，你至少应该能够找出两个问题。但是，如果你不能找出全部六个问题，请继续阅读本文。 <br><br>　　本文讨论的不是Java异常处理的一般性原则，因为这些原则已经被大多数人熟知。我们要做的是分析各种可称为“反例”（anti-pattern）的违背优秀编码规范的常见坏习惯，帮助读者熟悉这些典型的反面例子，从而能够在实际工作中敏锐地察觉和避免这些问题。 <br><br>　　<b>反例之一：丢弃异常 </b><br><br>　　代码：15行-18行。 <br><br>　
　这段代码捕获了异常却不作任何处理，可以算得上Java编程中的杀手。从问题出现的频繁程度和祸害程度来看，它也许可以和C/C++程序的一个恶名远播
的问题相提并论??不检查缓冲区是否已满。如果你看到了这种丢弃（而不是抛出）异常的情况，可以百分之九十九地肯定代码存在问题（在极少数情况下，这段代
码有存在的理由，但最好加上完整的注释，以免引起别人误解）。 <br><br>　　这段代码的错误在于，异常（几乎）总是意味着某些事情不对劲了，或
者说至少发生了某些不寻常的事情，我们不应该对程序发出的求救信号保持沉默和无动于衷。调用一下printStackTrace算不上“处理异常”。不
错，调用printStackTrace对调试程序有帮助，但程序调试阶段结束之后，printStackTrace就不应再在异常处理模块中担负主要责
任了。 <br><br>　　丢弃异常的情形非常普遍。打开JDK的ThreadDeath类的文档，可以看到下面这段说明：“特别地，虽然出现
ThreadDeath是一种‘正常的情形’，但ThreadDeath类是Error而不是Exception的子类，因为许多应用会捕获所有的
Exception然后丢弃它不再理睬。”这段话的意思是，虽然ThreadDeath代表的是一种普通的问题，但鉴于许多应用会试图捕获所有异常然后不
予以适当的处理，所以JDK把ThreadDeath定义成了Error的子类，因为Error类代表的是一般的应用不应该去捕获的严重问题。可见，丢弃
异常这一坏习惯是如此常见，它甚至已经影响到了Java本身的设计。 <br><br>　　那么，应该怎样改正呢？主要有四个选择： <br><br>　　1、处理异常。针对该异常采取一些行动，例如修正问题、提醒某个人或进行其他一些处理，要根据具体的情形确定应该采取的动作。再次说明，调用printStackTrace算不上已经“处理好了异常”。 <br><br>　　2、重新抛出异常。处理异常的代码在分析异常之后，认为自己不能处理它，重新抛出异常也不失为一种选择。 <br><br>　　3、把该异常转换成另一种异常。大多数情况下，这是指把一个低级的异常转换成应用级的异常（其含义更容易被用户了解的异常）。 <br><br>　　4、不要捕获异常。 <br><br>　　结论一：既然捕获了异常，就要对它进行适当的处理。不要捕获异常之后又把它丢弃，不予理睬。 <br><br>　　<b>反例之二：不指定具体的异常 </b><br><br>　　代码：15行。 <br><br>　　许多时候人们会被这样一种“美妙的”想法吸引：用一个catch语句捕获所有的异常。最常见的情形就是使用catch(Exception ex)语句。但实际上，在绝大多数情况下，这种做法不值得提倡。为什么呢？ <br><br>　
　要理解其原因，我们必须回顾一下catch语句的用途。catch语句表示我们预期会出现某种异常，而且希望能够处理该异常。异常类的作用就是告诉
Java编译器我们想要处理的是哪一种异常。由于绝大多数异常都直接或间接从java.lang.Exception派生，catch
(Exception ex)就相当于说我们想要处理几乎所有的异常。 <br><br>　　再来看看前面的代码例子。我们真正想要捕获的异常是什么
呢？最明显的一个是SQLException，这是JDBC操作中常见的异常。另一个可能的异常是IOException，因为它要操作
OutputStreamWriter。显然，在同一个catch块中处理这两种截然不同的异常是不合适的。如果用两个catch块分别捕获
SQLException和IOException就要好多了。这就是说，catch语句应当尽量指定具体的异常类型，而不应该指定涵盖范围太广的
Exception类。 <br><br>　　另一方面，除了这两个特定的异常，还有其他许多异常也可能出现。例如，如果由于某种原因，
executeQuery返回了null，该怎么办？答案是让它们继续抛出，即不必捕获也不必处理。实际上，我们不能也不应该去捕获可能出现的所有异常，
程序的其他地方还有捕获异常的机会??直至最后由JVM处理。 <br><br>　　结论二：在catch语句中尽可能指定具体的异常类型，必要时使用多个catch。不要试图处理所有可能出现的异常。 <br><br>　　<b>反例之三：占用资源不释放 </b><br><br>　　代码：3行-14行。 <br><br>　　异常改变了程序正常的执行流程。这个道理虽然简单，却常常被人们忽视。如果程序用到了文件、Socket、JDBC连接之类的资源，即使遇到了异常，也要正确释放占用的资源。为此，Java提供了一个简化这类操作的关键词finally。 <br><br>　　finally是样好东西：不管是否出现了异常，Finally保证在try/catch/finally块结束之前，执行清理任务的代码总是有机会执行。遗憾的是有些人却不习惯使用finally。 <br><br>　　当然，编写finally块应当多加小心，特别是要注意在finally块之内抛出的异常??这是执行清理任务的最后机会，尽量不要再有难以处理的错误。 <br><br>　　结论三：保证所有资源都被正确释放。充分运用finally关键词。</font></p><p><font size="2"><strong>反例之四：不说明异常的详细信息 <br><br></strong>　　代码：3行-18行。 <br><br>　　仔细观察这段代码：如果循环内部出现了异常，会发生什么事情？我们可以得到足够的信息判断循环内部出错的原因吗？不能。我们只能知道当前正在处理的类发生了某种错误，但却不能获得任何信息判断导致当前错误的原因。 <br><br>　　printStackTrace的堆栈跟踪功能显示出程序运行到当前类的执行流程，但只提供了一些最基本的信息，未能说明实际导致错误的原因，同时也不易解读。 <br><br>　　因此，在出现异常时，最好能够提供一些文字信息，例如当前正在执行的类、方法和其他状态信息，包括以一种更适合阅读的方式整理和组织printStackTrace提供的信息。 <br><br>　　结论四：在异常处理模块中提供适量的错误原因信息，组织错误信息使其易于理解和阅读。 <br><br>　　<b>反例之五：过于庞大的try块 </b><br><br>　　代码：3行-14行。 <br><br>　
　经常可以看到有人把大量的代码放入单个try块，实际上这不是好习惯。这种现象之所以常见，原因就在于有些人图省事，不愿花时间分析一大块代码中哪几行
代码会抛出异常、异常的具体类型是什么。把大量的语句装入单个巨大的try块就象是出门旅游时把所有日常用品塞入一个大箱子，虽然东西是带上了，但要找出
来可不容易。 <br><br>　　一些新手常常把大量的代码放入单个try块，然后再在catch语句中声明Exception，而不是分离各个可能出现异常的段落并分别捕获其异常。这种做法为分析程序抛出异常的原因带来了困难，因为一大段代码中有太多的地方可能抛出Exception。 <br><br>　　结论五：尽量减小try块的体积。 <br><br>　　<b>反例之六：输出数据不完整</b> <br><br>　　代码：7行-11行。 <br><br>　
　不完整的数据是Java程序的隐形杀手。仔细观察这段代码，考虑一下如果循环的中间抛出了异常，会发生什么事情。循环的执行当然是要被打断的，其次，
catch块会执行??就这些，再也没有其他动作了。已经输出的数据怎么办？使用这些数据的人或设备将收到一份不完整的（因而也是错误的）数据，却得不到
任何有关这份数据是否完整的提示。对于有些系统来说，数据不完整可能比系统停止运行带来更大的损失。 <br><br>　　较为理想的处置办法是向输出设备写一些信息，声明数据的不完整性；另一种可能有效的办法是，先缓冲要输出的数据，准备好全部数据之后再一次性输出。 <br><br>　　结论六：全面考虑可能出现的异常以及这些异常对执行流程的影响。 <br><br>　　<b>改写后的代码</b> <br><br>　　根据上面的讨论，下面给出改写后的代码。也许有人会说它稍微有点?嗦，但是它有了比较完备的异常处理机制。 <br><br></font><table align="center" bgcolor="#dadacf" border="1" bordercolor="#ffcc66" width="90%"><tbody><tr><td><font size="2">OutputStreamWriter out = ... <br>java.sql.Connection conn = ... <br>try { <br>　Statement stat = conn.createStatement(); <br>　ResultSet rs = stat.executeQuery( <br>　　"select uid, name from user"); <br>　while (rs.next()) <br>　{ <br>　　out.println("ID：" + rs.getString("uid") + "，姓名: " + rs.getString("name")); <br>　} <br>} <br>catch(SQLException sqlex) <br>{ <br>　out.println("警告：数据不完整"); <br>　throw new ApplicationException("读取数据时出现SQL错误", sqlex); <br>} <br>catch(IOException ioex) <br>{ <br>　throw new ApplicationException("写入数据时出现IO错误", ioex); <br>} <br>finally <br>{ <br>　if (conn != null) { <br>　　try { <br>　　　conn.close(); <br>　　} <br>　　catch(SQLException sqlex2) <br>　　{ <br>　　　System.err(this.getClass().getName() + ".mymethod - 不能关闭数据库连接: " + sqlex2.toString()); <br>　　} <br>　} <br><br>　if (out != null) { <br>　　try { <br>　　　out.close(); <br>　　} <br>　　catch(IOException ioex2) <br>　　{ <br>　　　System.err(this.getClass().getName() + ".mymethod - 不能关闭输出文件" + ioex2.toString()); <br>　　} <br>　} <br>} </font></td></tr></tbody></table><br><font size="2">　　本文的结论不是放之四海皆准的教条，有时常识和经验才是最好的老师。如果你对自己的做法没有百分之百的信心，务必加上详细、全面的注释。 <br><br>　
　另一方面，不要笑话这些错误，不妨问问你自己是否真地彻底摆脱了这些坏习惯。即使最有经验的程序员偶尔也会误入歧途，原因很简单，因为它们确确实实带来
了“方便”。所有这些反例都可以看作Java编程世界的恶魔，它们美丽动人，无孔不入，时刻诱惑着你。也许有人会认为这些都属于鸡皮蒜毛的小事，不足挂
齿，但请记住：勿以恶小而为之，勿以善小而不为。 <br></font></p><br><br><p id="TBPingURL">Trackback: 
http://tb.blog.csdn.net/TrackBack.aspx?PostId=553341</p>
		
	</div><img src ="http://www.blogjava.net/ericwang/aggbug/24300.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-12-16 22:52 <a href="http://www.blogjava.net/ericwang/archive/2005/12/16/24300.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Google编程大赛入围赛250分真题</title><link>http://www.blogjava.net/ericwang/archive/2005/12/15/24014.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Thu, 15 Dec 2005 05:14:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/12/15/24014.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/24014.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/12/15/24014.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/24014.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/24014.html</trackback:ping><description><![CDATA[<div class="postTitle">
		<a href="http://blog.csdn.net/cyp403/archive/2005/12/13/550943.aspx">Google编程大赛入围赛250分真题</a>
<script language="javascript">document.title="Google编程大赛入围赛250分真题 - "+document.title</script>
	</div>	
	
	
	
		<p><br>Problem Statement<br>????<br>You are given a String[] cityMap representing the layout of a city. The city <br>consists of blocks. The first element of cityMap represents the first row of <br>blocks, etc. A 'B' character indicates a location where there is a bus stop. <br>There will be exactly one 'X' character, indicating your location. All other <br>characters will be '.'. You are also given an int walkingDistance, which is the <br>maximum distance you are willing to walk to a bus stop. The distance should be <br>calculated as the number of blocks vertically plus the number of blocks <br>horizontally. Return the number of bus stops that are within walking distance of <br>your current location. Definition<br>????<br>Class:<br>BusStops<br>Method:<br>countStops<br>Parameters:<br>String[], int<br>Returns:<br>int<br>Method signature:<br>int countStops(String[] cityMap, int walkingDistance)<br>(be sure your method is public)<br>????</p><p>Constraints<br>-<br>cityMap will contain between 1 and 50 elements, inclusive.<br>-<br>Each element of cityMap will contain between 1 and 50 characters, inclusive.<br>-<br>Each element of cityMap will contain the same number of characters.<br>-<br>Each character of each element of cityMap will be 'B', 'X', or '.'.<br>-<br>There will be exactly one 'X' character in cityMap.<br>-<br>walkingDistance will be between 1 and 100, inclusive.<br>Examples<br>0)</p><p>????<br>{"...B.",<br>&nbsp;".....",<br>&nbsp;"..X.B",<br>&nbsp;".....",<br>&nbsp;"B...."}<br>3<br>Returns: 2<br>You can reach the bus stop at the top (3 units away), or on the right (2 units <br>away). The one in the lower left is 4 units away, which is too far. 1)</p><p>????<br>{"B.B..",<br>&nbsp;".....",<br>&nbsp;"B....",<br>&nbsp;".....",<br>&nbsp;"....X"}<br>8<br>Returns: 3<br>A distance of 8 can get us anywhere on the map, so we can reach all 3 bus stops.<br>2)</p><p>????<br>{"BBBBB",<br>&nbsp;"BB.BB",<br>&nbsp;"B.X.B",<br>&nbsp;"BB.BB",<br>&nbsp;"BBBBB"}<br>1<br>Returns: 0<br>Plenty of bus stops, but unfortunately we cannot reach any of them.<br>3)</p><p>????<br>{"B..B..",<br>&nbsp;".B...B",<br>&nbsp;"..B...",<br>&nbsp;"..B.X.",<br>&nbsp;"B.B.B.",<br>&nbsp;".B.B.B"}<br>3<br>Returns: 7</p><p>This problem statement is the exclusive and proprietary property of TopCoder, <br>Inc. Any unauthorized use or reproduction of this information without the prior <br>written consent of TopCoder, Inc. is strictly prohibited. (c)2003, TopCoder, <br>Inc. All rights reserved.<br></p><img src ="http://www.blogjava.net/ericwang/aggbug/24014.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-12-15 13:14 <a href="http://www.blogjava.net/ericwang/archive/2005/12/15/24014.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>面向方面的Annotation</title><link>http://www.blogjava.net/ericwang/archive/2005/12/15/24012.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Thu, 15 Dec 2005 05:12:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/12/15/24012.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/24012.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/12/15/24012.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/24012.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/24012.html</trackback:ping><description><![CDATA[<center><b><span style="font-size: 20px;">面向方面的Annotation</span></b></center><br><center>作者：Bill Burke，Enterprise JavaBeans 第四版合著者 </center><br><center>译者:<a href="http://www.matrix.org.cn/user.shtml?username=yahveyeye" target="_new">yahveyeye</a></center><br><br><br><br><br><span style="color: Red;">版权声明：任何获得Matrix授权的网站，转载时请<b><u>务必</u></b>以超链接形式标明文章原始出处和作者信息及本声明</span><br>作者:Bill Burke;<a href="http://www.matrix.org.cn/user.shtml?username=yahveyeye" target="_new">yahveyeye</a><br>原文地址:<a href="http://www.onjava.com/pub/a/onjava/2004/08/25/aoa.html%3Cbr%20/%3E" target="_new">http://www.onjava.com/pub/a/onjava/2004/08/25/aoa.html</a><br>中文地址:<a href="http://www.matrix.org.cn/resource/article/44/44052_Annotation+Aop.html" target="_new">http://www.matrix.org.cn/resource/article/44/44052_Annotation+Aop.html</a><br>关键词： Annotation Aop<br><br>Annotation
是J2SE5.0的一项新功能，它允许您附加元数据到Java构建中。同时，面向方面编程（AOP）是一个相当新的技术，它可以使您封装某些行为，这些行
为是在使用面向对象（OO）技术时会更为混乱，困难甚至是不可能完成。这两项技术结合起来给框架开发者开发的APIs更好的表达方式。本文深入结合这些技
术，使用Jboss AOP框架，以不同的代码范例向您展示如何结合两者来实际地扩展Java 语言。<br><br><span style="color: Red;">相关文章：</span><br>Java Annotation入门：<br><a href="http://www.matrix.org.cn/resource/article/44/44048_Java+Annotation.html" target="_new">http://www.matrix.org.cn/resource/article/44/44048_Java+Annotation.html</a><br> <br><b><span style="font-size: 16px;">Annotation概述</span></b><br><br>首
先让我们给出这两项技术的一个概述。Annotation是JDK5.0的新功能，它在JSR-175规范中有详细定义。它们允许您以安全的方法定义元数
据并应用到类，方法，构造程序，字段或参数中。对于你们中熟悉XDoclet的人来说，Annotation将非常直观，您可以用来声明标签以产生代码。
两者的主要不同是Annotation是Java语言的一部分而XDoclet标签可能会打错并且难以创建。我喜欢用例子来说明，所以让我们展示一个简单
的例子。<br><br>要定义一个Annotation，您所要做的就是声明一个特殊类型的Java接口。<br><br><b>清单1：</b>Orange.java<br><pre title="pre code" class="overflow">package org.jboss.collors;<br>public @interface Orange{}</pre><br><br>定义了这个接口，您就可以用来提供更多的描述给您的Java元素。<br><b>清单2：</b>Foo.java<br><pre title="pre code" class="overflow">package org.jboss.examples;<br>public class Foo<br>{<br>&nbsp;&nbsp;@Orange void someMethod();<br>&nbsp;&nbsp;@Orange private int someField;<br>}</pre><br><br>那么我们可以用Annotation来干什么呢？一些人想用Annotation来产生代码并替代XDoclet，其他人，象J2EE和EJB3.0专家组，将它视为部署描述符的替代。本文谈论在AOP中如何使用Annotation<br><br><b><span style="font-size: 16px;">AOP概述</span></b><br><br>有许多的文章和书籍解释AOP到底是什么，例如Graham O'Regan的ONJava文章“Introduction to Aspect-Oriented Programming."我将在本文给出一个快速的概览，但我鼓励您在线做更多的研究。<br><br>假设您要添加代码到一个应用程序去测试调用一个特定的java方法所需的总的时间。该代码可能看起来如下：<br><b>清单3：</b><br><pre title="pre code" class="overflow">public class BankAccount<br>{<br>public void withdraw(double amount)<br>{<br>long startTime = System.currentTimeMillis();<br>try<br>{<br>// Actual method body...<br>}<br>finally<br>{<br>long endTime = System.currentTimeMillis() - startTime;<br>System.out.println("withdraw took: " + endTime);<br>}<br>}<br>}</pre><br> <br>虽然这些代码能够正常工作，但这个方法有一些问题：<br>1.它难以打开和关闭测试，您必须在try/finally块中对每个方法或购置函数手工增加代码以进行基准测试。<br>2。这一轮廓代码并不真正属于贯穿整个应用的代码。它使得您的代码臃肿并难以理解，因为您必须将计时放在try/finally块中。<br>3、如果您想扩展它的功能以包含一个方法或是失败计数，或甚至是注册这些统计数据到一个更为复杂的报告机制中，您必须修改大量不同文件（又一次）。<br><br>Metrics
类提供了一个什么是横切（cross-cutting）关系的完美，简洁的小例子。Jboss
AOP以一种含蓄的方式提供了一个简单的方法来封装和应用这样的关系，这样某些象度量操作代码不会弄乱您的编码。让我们稍为深入到Jboss
AOP一些来看看如何实现。<br>为了使用Jboss AOP封装度量功能，您首先需要定义一个方面来指出该度量行为。<br><b>清单4：</b><br><pre title="pre code" class="overflow">public class Metrics<br>{<br>public Object profile(MethodInvocation invocation) throws Throwable<br>{<br>long startTime = System.currentTimeMillis();<br>try<br>{<br>return invocation.invokeNext();<br>}<br>finally<br>{<br>long endTime = System.currentTimeMillis() - startTime;<br>java.lang.reflect.Method m = invocation.getMethod();<br>System.out.println("method " + m.toString() +<br>" time: " + endTime + "ms");<br>} }<br>}</pre><br><br>一
个方面只是一个具有定义了您想要附加到您的对象模型的行为的普通Java类。这些方法的签名必须返回一个java.lang.Object并且必须具有一
个（并且只有一个）Jboss AOP
调用对象参数，它被用来封装方法，构造函数或字段调用。方法名可以是任何你想要的并且当您绑定该方面到您的代码片断时被引用。<br><br>下面要做的事情就是实际应用方面到您想要它勾勒一个方法的执行的某个程序点。大多数AOP框架提供了一个指向表达式语言，在此处您可以定义您想要某个方面行为被附加到的位置。下面是在Jboss AOP中的做法。<br><b>清单5：</b>jboss-aop.xml<br><pre title="pre code" class="overflow">&lt;aop&gt;<br>&lt;aspect class="Metrics"/&gt;<br> <br>&lt;bind pointcut="execution(public void BankAccount-&gt;withdraw(double amount))"&gt;<br>&lt;advice name="profile" aspect="Metrics"/&gt;<br>&lt;/bind&gt;<br>&lt;/aop&gt;</pre><br> <br>采用在Metrics.java中对方面的定义和jboss-aop.xml中的指向定义，该度量代码现在以含蓄而又透明地应用到BankAccount.withdraw（）方法中并能在勾勒代码不再需要时轻易地移除。<br>对于Jboss AOP更多的信息，请查询分发包中的指南。其中具有大约20个例子来带领您漫游如何使用Jboss AOP框架。<br><br>嘘！现在我们已经进行了一个概览，让我们深入到本文的中心内容。我将再次给您提供一些例子，因为这是我所知道的讲授一个新的概念的最好的方法。<br>正如我前面说的，Annotation加上AOP几乎是给予您扩展Java语言的能力。Annotation提供了声明新的，可兼容的，类型安全的语法机制。AOP提供了封装和应用新的行为到一个语法表达式的机制。<br><br><b><span style="font-size: 16px;">方法Annotation和AOP</span></b><br><br>让
我们看看如何使用方法Annotation和AOP。使用Annotation和AOP并应用到一个方法类似于使用Java的synchronized关
键字。当您设定一个方法为synchronized，您在告诉JVM：您想该方法在被调用时以一种特殊的方式进行。Annotation允许您定义一个新
的关键字来触发您自己的特殊的定制行为。AOP给予您封装这一行为的能力并将其“编织”进该方法的执行中。再次的，这一概念的最佳描述是通过一个例子。<br> <br>让我们假设我们想要添加新的语法，使用该语法使得我们可以在方法被标签为@Oneway时，在后台以另一个线程调用这个void方法。可以象这样使用新的语法：<br> <br><b>清单6：</b><br><pre title="pre code" class="overflow">Import org.jboss.aspects.Oneway；<br>public class Foo<br>{<br>&nbsp;&nbsp;@Oneway public static void someMethord（）{…}<br>public static void main(String[] args){<br>somMethod();//executes in<br> backgroud<br>}<br>}</pre><br><br>当someMethod（）在main中被调用，它将异步运行，这样main中的代码可以并行执行其他任务。<br>要实现这一功能，首先要在一个Annotation中为我们的@Oneway标签定义新的Java语法.<br><b>清单7：</b>Oneway.java<br><pre title="pre code" class="overflow">package org.jboss.aspects;<br> <br>import java.lang.annotation.ElementType;<br>import java.lang.annotation.Target;<br> <br>@Target({ElementType.METHOD})<br>public @interface Oneway {}</pre><br><br>够简单的。@Target标签允许您缩小Annotation可以应用的地方。在本例中，我们的@OnewayAnnotation只能应用到一个方法。记住，这些都是J2SE5.0百分之百可用的纯Java。<br>下面要做的事是定义一个封装我们的@Oneway行为的方面类。<br><b>清单8：</b>OnewayAspect.java<br> <br><pre title="pre code" class="overflow">package org.jboss.aspects;<br> <br>public OnewayAspect<br>{<br>private static class Task implements Runnable<br>{<br>private MethodInvocation invocation;<br> <br>public Task(MethodInvocation invocation)<br>{<br>this.invocation = invocation;<br>}<br>public void run()<br>{<br>try { invocation.invokeNext(); }<br>catch (Throwable ignore) { }<br>}<br>}<br> <br> <br>public Object oneway(MethodInvocation invocation) throws Throwable<br>{<br>MethodInvocation copy = invocation.copy();<br>Thread t = new Thread(new Task(copy));<br>t.setDaemon(false);<br>t.start();<br>return null;<br>}<br>}</pre><br> <br>这
个方面够简单。oneway（）方法拷贝invocation，创建一个线程，在后台启动整个调用并返回。我们可以想象一个更为复杂的例子：使用J2SE
5.0 java.util.concurrent包中的某些新的Executors，但这些代码很有希望阐明了如何基于这个例子构建更为复杂的实现。<br>最后必须要做的事情是指定当@OnewayAnnotation在一个方法中声明时触发OnewayAspect应用的指向表达式。<br><br><b>清单9：</b>jboss-aop.xml<br><pre title="pre code" class="overflow">&lt;aop&gt;<br>&lt;aspect class="org.jboss.aspects.OnewayAspect"/&gt;<br>&lt;bind pointcut="execution(void *-&gt;@org.jboss.Oneway(..))"&gt;<br>&lt;advice name="oneway"<br>aspect="org.jboss.aspects.OnewayAspect"/&gt;<br>&lt;/bind&gt;<br>&lt;/aop&gt;</pre><br> <br>该
指向表达式规定任何具有@Oneway标签的void方法都应该有OnewayAspect.oneway()方法在它本身执行前被执行。随着
Annotation，方面和现在定义的指向表达式，@Oneway语法现在可以用于您的应用程序中。一个简单，清晰，易于实现的方法来扩展Java
语言！<br><br><b><span style="font-size: 16px;">字段Annotation和AOP</span></b><br><br>让
我们看看如何使用字段Annotation和AOP。使用Annotation和AOP，您可以改变一个对象的字段或是作为一个类的静态成员的实际存储方
式。在这个例子里我们要完成的是当您将一个字段（静态或是成员）标记上@ThreadBased,尽管是将它存储在
java.lang.ThreadLocal，但它的值依然正常。当然，您可以直接使用ThreadLocal变量，但问题是ThreadLocal并非
一个类型并且您必须使用“麻烦的”（好，它们并没有那么罗嗦）get（）和set（）方法。那么我们现在做的就是创建一个ThreadLocal类型的字
段。我们主要的将创建一个称为@Thradbased变量的新的Java字段类型。<br>象这样使用新的类型：<br><b>清单10：</b><br><pre title="pre code" class="overflow">import org.jboss.aspects.Threadbased;<br>public class Foo<br>{<br>@Threadbased private int counter;<br>}</pre><br> <br>为了实现这个功能，我们必须先定义Annotation<br><b>清单11：</b>Threadbased.java<br><pre title="pre code" class="overflow">package org.jboss.aspects;<br>import java.lang.annotation.ElementType;<br>import java.lang.annotation.Target;<br>@Target({ElementType.FIELD})<br>public @interface Threadbased {}</pre><br> <br> <br>够简单。@Target标签允许您缩小Annotation可以应用的地方。在本例中，我们的@ThreadbasedAnnotation只能应用到字段。<br>下面的事情是定义封装我们的ThreadLocal行为的方面。<br><b>清单12：</b>ThreadbasedAspect.java<br><pre title="pre code" class="overflow">package org.jboss.aspects;<br>import org.jboss.aop.joinpoint.*;<br>import java.lang.reflect.Field;<br>public class ThreadbasedAspect<br>{<br>private ThreadLocal threadbased = new ThreadLocal();<br>public Object access(FieldReadInvocation invocation)<br>throws Throwable<br>{<br>// just in case we have a primitive,<br>// we can't return null<br>if (threadbased.get() == null)<br>return invocation.invokeNext();<br>return threadbased.get();<br>}<br>public Object access(FieldWriteInvocation invocation)<br>throws Throwable<br>{<br>threadbased.set(invocation.getValue());<br>return null;<br>}<br>}</pre><br> <br>ThreadbasedAspect
封装到一个Java字段的访问。它里面具有一个专门的ThreadLocal变量跟踪thradlocal变为一个特殊的字段。它还有一个单独的
access（）方法，该方法根据一个字段的get或set方法是否被调用决定它是否被调用。这些方法委托给ThreadLocal来获得字段的当前值。<br>最后，我们必须定义一个指向表达式，当@ThreadbasedAnnotation在某个字段被指定时触发ThreadbasedAspect的应用。<br><b>清单13：</b>jboss-aop.xml<br><pre title="pre code" class="overflow">&lt;aop&gt;<br>&lt;aspect class="org.jboss.aspects.ThreadbasedAspect" scope="PER_JOINPOINT"/&gt;<br>&lt;bind pointcut="field(* *-&gt;@org.jboss.aspects.Threadbased)"&gt;<br>&lt;advice name="access"<br>aspect="org.jboss.aspects.ThreadbasedAspect"/&gt;<br>&lt;/bind&gt;<br>&lt;/aop&gt;</pre><br> <br>只
有当我们具有多个@Threadbased变量定义在同一个类时，我们需要为每个静态字段分配一个ThreadbasedAspect实例。对于成员变
量，我们需要为每个字段，每个对象实例分配一个ThreadbasedAspect实例。为了促进这一行为，方面定义通过设定实例为
PER_JOINPOINT限制方面类的实例何时和何地被分配出去的范围。如果我们不做限制，Jboss <br>AOP会只分配一个ThreadbasedAspect实例并且不同的字段会共享相同的ThreadLocal接口——这不是我们所希望的。<br><br>好就这样。一个清晰容易的扩展Java来指定一个新的特殊类型的方法。注意：该特殊的方法来自Jboss AOP束。<br><br><b><span style="font-size: 16px;">依赖注入</span></b><br><br>字
段Annotation和AOP可以使用的一个有趣的地方是依赖注入。依赖注入是关于对象声明它们需要什么信息，配置或服务引用以及运行时自动注入这些依
赖而不是用代码明确地在一个注册中心查找。在J2EE领域，获得javax.transaction.TransactionManager服务的访问并
未标准化并且实际上不同的厂商有不同的实现。许多框架开发者需要使用TransactionManager来实现定制事务服务。使用字段
AnnotationAOP提供依赖注入并抽取出一个需要TransactionManager的组件如何引用它的细节是一个了不起的方法。让我们定义一
个方面，它将注入一个TransactionManager引用到一个字段值中。<br><br>首先，我们再次定义我们的Annotation。<br> <br><b>清单14：</b>Inject.java<br><pre title="pre code" class="overflow">package org.jboss.aspects;<br>import java.lang.annotation.ElementType;<br>import java.lang.annotation.Target;<br>@Target({ElementType.FIELD})<br>public @interface Inject {}</pre><br> <br>下面我们将定义方面类，它封装了TransactionManager的解析。该方面是特定于JBoss应用服务器，但您可以定义为每个厂商定义不同的实现。<br><b>清单15：</b>InjectTMAspect.java<br><pre title="pre code" class="overflow">package org.jboss.aspects;<br>import org.jboss.aop.joinpoint.*;<br>import java.lang.reflect.Field;<br>import javax.transaction.TransactionManager;<br>import org.jboss.tm.TxManager;<br>public InjectTMAspect<br>{<br>private TransactionManager tm = TxManager.getInstance();<br>public Object access(FieldReadInvocation invocation)<br>throws Throwable {<br>return tm;<br>}<br>public Object access(FieldWriteInvocation invocation)<br>throws Throwable {<br>throw new RuntimeException(<br>"Setting an @Injected variable is illegal");<br>}<br>}</pre><br> <br>最后，我们必须定义XML绑定来触发当@Inject标签应用到一个字段时InjectTMAspect的应用。指向表达式基本上说明了对任意一个标记为@Inject的TransactionManager字段应用InjectTMAspect。<br><b>清单16：</b><br><pre title="pre code" class="overflow">&lt;aop&gt;<br>&lt;aspect class="org.jboss.aspects.InjectTMAspect"/&gt;<br>&lt;bind pointcut="field(javax.transaction.TransactionManager *-&gt;@org.jboss.aspects.Inject)"&gt;<br>&lt;advice name="access"<br>aspect="org.jboss.aspects.InjectTMAspect"/&gt;<br>&lt;/bind&gt;<br>&lt;/aop&gt;</pre><br><br>现在Annotation、方面类和XML绑定已经定义，我们可以在我们的代码中使用了。<br><b>清单17：</b><br><pre title="pre code" class="overflow">import javax.transaction.TransactionManager;<br>import org.jboss.aspects.Inject;<br>public class MyTransactionalCache<br>{<br>@Inject private TransactionManager tm;<br>...<br>}</pre><br> <br><b><span style="font-size: 16px;">更多预打包例子</span></b><br><br>Jboss
AOP不仅仅是关于AOP框架。它还有一个丰富的方面库，您可以直接在您的应用中使用。在这个库中是一个比我们现在在本文展示的例子更为复杂的
Annotation方面集。这些方面包括异步调用，事务划分，事务锁定和基于角色的安全。让我们简要地浏览一下以提供给您一个更好的关于
Annotation和AOP共同工作的考虑。<br><br><b>异步方面</b><br>Jboss
AOP异步方面允许您定义任何方法为异步的，这样它可以在后台被执行。这对于我们的@Oneway例子来说有些困难，因为它使用Oswego并行包中的执
行器工具，并为那些具有一个返回类型的方法提供了一个方法来异步地接收回响应。要使用这个方面，您只需标记一个方法为@Asybchronous.<br><b>清单18：</b><br><pre title="pre code" class="overflow">public Foo {<br>@Asynchronous public int someMethod(int someArg) {...}<br>}</pre><br> <br>@Asynchronous
标签的应用做了一些事情。与在本文中的@Oneway例子一样，它应用一个在后台运行该方法的方面。而且，采用@Asynchronous标签，您并不仅
限于void方法并可于实际上返回一个值的方法进行交互。当@Asynchronous标签被应用，它强制Foo类实现
AsynchronousFacade接口。在AOP领域，这称为接口引入（interface
introduction）。AsynchronousFacade接口允许您预测一个响应或以超时限定等待一个响应。最好用一个例子来解释。<br><b>清单19：</b><br><pre title="pre code" class="overflow">Foo foo = new Foo();<br>someMethod(555); // executes in background<br>AsynchronousFacade facade = (AsynchronousFacade)foo;<br>AsynchronousResponse response = facde.waitForResponse();<br>System.out.println(response.getReturnValue());</pre><br> <br>您可以启动多个不同对象的多个不同方法的多个调用，并异步积累它们的响应。<br><br><b>事务锁定</b><br>有时在J2EE事务期间而不是一个方法执行，构造函数调用或同步块执行期间同步一个对象或类会很有用。对这类事务同步或锁定，Jboss AOP发明了@TxSynchronized关键字。您可以使用@TxSynchronized在任意成员或静态方法已经构造函数上。<br><b>清单20：</b><br><pre title="pre code" class="overflow">import org.jboss.aspects.txlock.TxSynchronized;<br>public FooBar<br>{<br>@TxSynchronized public FooBar() {}<br>@TxSynchronized static void staticMethod() {}<br>@TxSynchronized public memberMethod() {}<br>}</pre><br> <br>如
果一个被标记为@TxSynchronized的构造函数或静态方法被调用，类的锁监视器会在事务执行期间被保持着。如果一个标记为
@TxSynchronized的成员方法被调用，该对象实例的锁监视器将被保持直到目前的事务提交或回退。控制该行为的方面也将做死锁检测并在发生死锁
时抛出RuntimeException。<br><br><b>J2EE 散餐(原文法文：a la carte)之：事务划分</b><br>EJB3.0已经定义了一些Annotation进行事务划分。Jboss AOP在此基础上构建。这样您可以通过指定Annotation应用事务划分到任意方法（静态或成员）以及任何Java类构造函数。<br><b>清单21：</b><br><pre title="pre code" class="overflow">import org.jboss.aspects.tx.*;<br>public class Foo<br>{<br>@Tx(TxType.REQUIRED) public Foo {}<br>@Tx(TxType.REQUIRESNEW) public static createFoo() {<br>return new Foo();<br>}<br>}</pre><br> <br><b>J2EE 散餐之：基于角色的安全</b><br>EJB 3.0也定义了一些Annotation实现基于角色的安全。Jbos AOP是基于此构建的，所以您可以应用基于角色的安全到任何的字段或方法（静态或成员）已经构造函数。<br><b>清单22：</b><br><pre title="pre code" class="overflow">import org.jboss.aspects.security.*;<br>@SecurityDomain("LDAP Repository")<br>public class Foo<br>{<br>@Permissions({"admin"}) public Foo() {}<br>@Permissions({"developer"}) public static int status;<br>@Permissions({"anybody"}) public static void doSomething() {...}<br>}</pre><br> <br><b><span style="font-size: 16px;">EJB演变</span></b><br> <br>随
着AOP与EJB规范一起渐渐成熟，我真正希望发生的是EJB规范定义的Annotation将能在任何上下文作为新的Java语言的形容词被使用，而不
是让它们有限的使用在会话bean中。想象一下，一个真正的无状态bean仅仅成为一个明文Java类的一个静态方法集。<br><b>清单23：</b><br><pre title="pre code" class="overflow">public MySessionBean<br>{<br>@Tx(TxType.REQUIRED) public static doSomething() {...}<br>}</pre><br><br>无论如何，这些关于AOP和EJB的讨论很可能就是为了EJB4.0。<br><br><b><span style="font-size: 16px;">结论</span></b><br>并非是限制J2SE5.0Annotation用于代码生成，Annotation和AOP可以被结合起来提供新的能力给框架开发者。这一结合允许开发者定义新的具有行为附加到其上的Java语法。基本上，以安全的方式扩展Java语言的能力已尽在掌握。<br><br><b><span style="font-size: 16px;">资源 </span></b><br>·Matrix-Java开发者社区:<a href="http://www.matrix.org.cn/" target="_new">http://www.matrix.org.cn</a><br>·onjava.com:<a href="http://onjava.com/" target="_new">onjava.com</a><br><br><b><span style="font-size: 16px;">关于作者</span></b><br>Bill Burke ：JBoss 首席架构师. <img src ="http://www.blogjava.net/ericwang/aggbug/24012.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-12-15 13:12 <a href="http://www.blogjava.net/ericwang/archive/2005/12/15/24012.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转载】Tiger 中的注释，第 2 部分: 定制注释</title><link>http://www.blogjava.net/ericwang/archive/2005/12/13/23745.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Tue, 13 Dec 2005 15:28:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/12/13/23745.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/23745.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/12/13/23745.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/23745.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/23745.html</trackback:ping><description><![CDATA[<table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr valign="top"><td width="100%"><h1><span style="color: rgb(153, 153, 153);">Tiger 中的注释，第 2 部分: </span>定制注释</h1><p id="subtitle"><em>Write your own annotations in Java 5</em></p><img alt="" src="http://www.ibm.com/i/c.gif" class="display-img" height="6" width="1"></td><td class="no-print" width="192"><img alt="developerWorks" src="http://www-128.ibm.com/developerworks/i/dw.gif" height="18" width="192"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr valign="top"><td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="10"></td><td width="100%"><table class="no-print" align="right" border="0" cellpadding="0" cellspacing="0" width="160"><tbody><tr><td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="10"></td><td><table border="0" cellpadding="0" cellspacing="0" width="150"><tbody><tr><td class="v14-header-1-small">文档选项</td></tr></tbody></table><table class="v14-gray-table-border" border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="no-padding" width="150"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="8"><input value="本系列文章的第 1 部分介绍了注释 —— J2SE 5.0 中新的元数据工具，并重点讨论了 Tiger 的基本内置注释。一个更强大的相关特性是支持编写自己的注释。本文中，Brett McLauglin 说明了如何创建定制注释，如何用自己的注释注解文档，并进一步定制代码。" name="body" type="hidden"><input name="subject" value="Tiger 中的注释，第 2 部分: 定制注释" type="hidden"><input name="lang" value="cn" type="hidden"><table border="0" cellpadding="0" cellspacing="0" width="143"><form action="https://www-130.ibm.com/developerworks/secure/email-it.jsp" name="email"></form><script language="JavaScript" type="text/javascript">
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
</script><tbody><tr valign="top"><td width="8"><img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="8"></td><td width="16"><img src="http://www.ibm.com/i/v14/icons/em.gif" alt="将此页作为电子邮件发送" height="16" vspace="3" width="16"></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr><noscript><tr
valign="top"><td width="8"><img alt="" height="1" width="8"
src="//www.ibm.com/i/c.gif"/></td><td width="16"><img alt="" width="16"
height="16" src="//www.ibm.com/i/c.gif"/></td><td class="small"
width="122"><p><span class="ast">未显示需要 JavaScript
的文档选项</span></p></td></tr></noscript></tbody></table></td></tr></tbody></table><br><table border="0" cellpadding="0" cellspacing="0" width="150"><tbody><tr><td class="v14-header-1-small">对此页的评价</td></tr></tbody></table><table class="v14-gray-table-border" border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="no-padding" width="150"><table border="0" cellpadding="0" cellspacing="0" width="143"><tbody><tr valign="top"><td width="8"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="8"></td><td><img alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" border="0" height="16" vspace="3" width="16"></td><td width="125"><p><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate2.html#rate" class="smallplainlink"><b>帮助我们改进这些内容</b></a></p></td></tr></tbody></table></td></tr></tbody></table><br></td></tr></tbody></table><p>级别: 初级</p><p><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate2.html#author">Brett McLaughlin</a>, 作者/编者, O'Reilly Media, Inc<br></p><p>2004 年  9 月  01 日</p><blockquote>本系列文章的
      <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/">第 1 部分</a>介绍了注释 —— J2SE 5.0 中新的元数据工具，并重点讨论了 Tiger 的基本内置注释。一个更强大的相关特性是支持编写自己的注释。本文中，Brett McLauglin 说明了如何创建定制注释，如何用自己的注释注解文档，并进一步定制代码。
    </blockquote>



      <p>
        <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/">本系列的第一篇文章</a> 介绍了什么是元数据，元数据的重要性，以及如何使用 J2SE 5.0（也叫做 Tiger）的基本内置注释。如果习惯了这些概念，您可能已经在想，Java 5 提供的三种标准注释也并不是特别健壮，能使用的只有 
        <code>Deprecated</code> 、 
        <code>SuppressWarnings</code> 和 
        <code>Override</code>
而已。所幸的是，Tiger
还允许定义自己的注释类型。在本文中，我将通过一些示例引导您掌握这个相对简单的过程。您还将了解如何对自己的注释进行注解，以及这样做的一些好处。我要
感谢 O'Reilly Media, Inc.，他们非常慷慨地允许我在本文中使用我关于 Tiger 的书籍的“注释”一章中的代码示例（请参阅 <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate2.html#Resources">参考资料</a>）。
      </p>



      <p><a name="N10066"><span class="atitle">定义自己的注释类型</span></a></p>
      <br>通过添加了一个小小的语法（Tiger 添加了大量的语法结构），Java 语言支持一种新的类型 ——
      <i>注释类型（annotation type）</i>。注释类型看起来很像普通的类，但是有一些特有的性质。最明显的一点是，可以在类中以符号（ 
      <code>@</code> ）的形式注释其他 Java 代码。我将一步一步地介绍这个过程。


      <p><a name="N10075"><span class="smalltitle">@interface 声明</span></a></p>
      <br>定义新的注释类型与创建接口有很多类似之处，只不过 
      <code>interface</code> 关键字之前要有一个 
      <code>@</code> 符号。清单 1 中给出的是一个最简单的注释类型的示例：
      <br>
      <a name="IDALD4QB">
        <b>清单 1. 非常简单的注释类型</b>
      </a>
      <br>
       
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>package com.oreilly.tiger.ch06;<br><br>/**<br> * Marker annotation to indicate that a method or class<br> *   is still in progress.<br> */<br>public @interface InProgress { }<br></code></pre></td></tr></tbody></table><br> 
      <p>清单 1 的含义非常明显。如果编译这个注释类型，并确信其位于类路径中，那么您就可以在自己的源代码方法中使用它，以指出某个方法或类仍在处理中，如清单 2 所示：</p>
      <a name="IDAUD4QB">
        <b>清单 2. 使用定制的注释类型</b>
      </a>
      <br>


       
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section">@com.oreilly.tiger.ch06.InProgress<br>public void calculateInterest(float amount, float rate) {<br>  // Need to finish this method later<br>}<br></code></pre></td></tr></tbody></table><br> 

      <p>清单 1 所示注释类型的使用方法和内置注释类型的使用方法完全相同，只不过要同时使用名称和所在的包来指示定制注释。当然，一般的 Java 规则仍然适用，您可以导入该注释类型，直接使用 
        <code>@InProgress</code> 引用它。
      </p>


      <table align="right" border="0" cellpadding="0" cellspacing="0" width="40%"><tbody><tr><td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="10"></td><td><table border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td bgcolor="#eeeeee">

        <a name="N100AD"><b>不要漏掉本系列的另一部分</b></a><br>

        <p>一定要阅读本系列文章的“
          <a href="http://www.ibm.com/developerworks/library/j-annotate1/">第 1 部分</a>”，其中介绍了 Java 5.0 中的注释。
        </p>

      </td></tr></tbody></table></td></tr></tbody></table>



      <p><a name="N100BB"><span class="smalltitle">添加成员</span></a></p>
      <br>上面所示的基本用法还远远不够健壮。您一定还记得“第 1 部分”中曾经提到的，注释类型可以有成员变量（请参阅
      <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate2.html#Resources">参考资料</a>）。
这一点非常有用，尤其是准备将注释作为更加复杂的元数据，而不仅仅将它作为原始文档使用的时候。代码分析工具喜欢加工大量的信息，定制注释可以提供这类信息。
      <p>注释类型中的数据成员被设置成使用有限的信息进行工作。定义数据成员后不需要分别定义访问和修改的方法。相反，只需要定义一个方法，以成员的名称命名它。数据类型应该是该方法返回值的类型。清单 3 是一个具体的示例，它澄清了一些比较含糊的要求：</p>
      <a name="IDA0E4QB">
        <b>清单 3. 向注释类型添加成员</b>
      </a>
      <br>


       
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>package com.oreilly.tiger.ch06;<br><br>/**<br> * Annotation type to indicate a task still needs to be<br> *   completed.<br> */<br>public @interface TODO {<br>  String value();<br>}<br></code></pre></td></tr></tbody></table><br> 

      <p>尽管清单 3 看起来很奇怪，但这是注释类型所要求的格式。清单 3 定义了一个名为 
        <code>value</code> 的字符串，该注释类型能够接受它。然后，就可以像清单 4 中那样使用注释类型：
      </p>
      <a name="listing4">
        <b>清单 4. 使用带有成员值的注释类型</b>
      </a>
      <br>
       
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>@com.oreilly.tiger.ch06.InProgress<br>@TODO("Figure out the amount of interest per month")<br>public void calculateInterest(float amount, float rate) {<br>  // Need to finish this method later<br>}<br></code></pre></td></tr></tbody></table><br> 
      <p>这里同样没有多少花样。清单 4 假设已经引入了 
        <code>com.oreilly.tiger.ch06.TODO</code> ，因此源代码中的注释
        <i>不</i> 需要包名作前缀。此外，需要注意的是，清单 4 中采用了简写的方法：将值 
        <code>（"Figure out the amount of interest per month"）</code> 直接提供给注释，没有指定成员变量名。清单 4 和清单 5 是等价的，后者没有采用简写形式：
      </p>
      <a name="listing5">
        <b>清单 5. 清单 4 的“加长”版</b>
      </a>
      <br>
       
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>@com.oreilly.tiger.ch06.InProgress<br>@TODO(value="Figure out the amount of interest per month")<br>public void calculateInterest(float amount, float rate) {<br>  // Need to finish this method later<br>}<br></code></pre></td></tr></tbody></table><br> 
      <p>当然作为编码人员，我们都不愿意跟这种“加长”版搅在一起。不过要注意，只有当注释类型只有
        <i>一个</i> 成员变量，而且变量名为 
        <code>value</code> 时，才能使用简写形式。如果不符合这个条件，那么就无法利用这种特性。
      </p>
      <p><a name="N10110"><span class="smalltitle">设置默认值</span></a></p>
      <br>迄
今为止，您已经有了一个很好的起点，但是要做得完美，还有很长的一段路要走。您可能已经想到，下一步就要为注释设置某个默认值。如果您希望用户指定某些
值，但是只有这些值与默认值不同的时候才需要指定其他的值，那么设置默认值就是一种很好的办法。清单 6 用另一个定制注释 —— 来自 <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate2.html#listing4">清单 4</a> 的 
      <code>TODO</code> 注释类型的一个全功能版本，示范了这个概念及其实现：
      <br>
      <a name="IDA1G4QB">
        <b>清单 6. 带有默认值的注释类型</b>
      </a>
      <br>
       
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>package com.oreilly.tiger.ch06;<br><br>public @interface GroupTODO {<br><br>  public enum Severity { CRITICAL, IMPORTANT, TRIVIAL, DOCUMENTATION };<br><br>  Severity severity() <br>        <span class="boldcode">default Severity.IMPORTANT;</span><br>  String item();<br>  String assignedTo();<br>  String dateAssigned();<br>}<br><br>      </code></pre></td></tr></tbody></table><br> 
      <p>清单 6 中的 
        <code>GroupTODO</code> 注释类型中添加了几个新的变量。因为该注释类型的成员变量不是一个，所以将一个变量命名为 
        <code>value</code> 没有任何意义。只要成员变量多于一个，就应该尽可能准确地为其命名。因为不可能从 
        <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate2.html#listing5">清单 5</a>所示的简写形式中获益，所以您需要创建虽然稍微有点冗长，但是更容易理解的注释类型。
      </p>
      <p>清单 6 中出现的另一个新特性是注释类型定义了自己的枚举（枚举，即 enumeration，通常也称为
        <i>enums</i>，是 Java 5 的另一个新特性。它并没有多么地不同凡响，对注释类型更是如此）。然后，清单 6 使用新定义的枚举作为一个成员变量的类型。
      </p>
      <p>最后，再回到我们的主题 —— 默认值。建立默认值的过程非常琐碎，需要在成员声明的后面添加关键字 
        <code>default</code> ，然后提供默认值。正如您所料，默认值的类型必须与成员变量声明的类型完全相同。同样，这也不是什么火箭科学，只不过是词法上的变异。清单 7 给出了一个具体应用中的 
        <code>GroupTODO</code> 注释，其中
        <i>没有</i> 指定 
        <code>severity</code> 成员：
      </p>
      <a name="IDAKQ4QB">
        <b>清单 7. 使用默认值</b>
      </a>
      <br>
       
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>  @com.oreilly.tiger.ch06.InProgress<br>  @GroupTODO(<br>    item="Figure out the amount of interest per month",<br>    assignedTo="Brett McLaughlin",<br>    dateAssigned="08/04/2004"<br>  )<br>  public  void calculateInterest(float amount, float rate) {<br>    // Need to finish this method later<br>  }<br></code></pre></td></tr></tbody></table><br> 
      <p>清单 8 中使用了同一个注释，但这一次给出了 
        <code>severity</code> 的值：
      </p>
      <a name="IDAYQ4QB">
        <b>清单 8. 改写默认值</b>
      </a>
      <br>
       
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>  @com.oreilly.tiger.ch06.InProgress<br>  @GroupTODO(<br>    severity=GroupTODO.Severity.DOCUMENTATION,<br>    item="Need to explain how this rather unusual method works",<br>    assignedTo="Jon Stevens",<br>    dateAssigned="07/30/2004"<br>  )<br>  public  void reallyConfusingMethod(int codePoint) {<br>    // Really weird code implementation<br>  }<br></code></pre></td></tr></tbody></table><br> 
      <br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate2.html#main" class="fbox"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="N1017A"><span class="atitle">对注释的注释</span></a></p>
      <br>结
束关于注释的讨论之前（至少在本系列文章中），我想简要地讨论一下注释的注释。第 1
部分中所接触的预定义注释类型都有预定义的目的。但是在编写自己的注释类型时，注释类型的目的并不总是显而易见的。除了基本的文档外，可能还要针对某个特
定的成员类型或者一组成员类型编写类型。这就要求您为注释类型提供某种元数据，以便编译器保证按照预期的目的使用注释。 <p>当然，首先想到的就是 Java 语言选择的元数据形式 —— 注释。您可以使用 4 种预定义的注释类型（称为
        <i>元注释</i>）对您的注释进行注释。我将对这 4 种类型分别进行介绍。
      </p>
      <p><a name="N10188"><span class="smalltitle">指定目标</span></a></p>
      <br>最明显的元注释就是允许何种程序元素具有定义的注释类型。毫不奇怪，这种元注释被称为 
      <code>Target</code> 。但是在了解如何使用 
      <code>Target</code> 之前，您还需要认识另一个类，该类被称为 
      <code>ElementType</code> ，它实际上是一个枚举。这个枚举定义了注释类型可应用的不同程序元素。清单 9 给出了完整的 
      <code>ElementType</code> 枚举：
      <br>
      <a name="IDACS4QB">
        <b>清单 9. ElementType 枚举</b>
      </a>
      <br>
       
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>package java.lang.annotation;<br><br>public enum ElementType {<br>  TYPE,			// Class, interface, or enum (but not annotation)<br>  FIELD,		// Field (including enumerated values)<br>  METHOD,		// Method (does not include constructors)<br>  PARAMETER,		// Method parameter<br>  CONSTRUCTOR,		// Constructor<br>  LOCAL_VARIABLE,	// Local variable or catch clause<br>  ANNOTATION_TYPE,	// Annotation Types (meta-annotations)<br>  PACKAGE		// Java package<br>}<br></code></pre></td></tr></tbody></table><br> 
      <p>清单 9 中的枚举值意义很明确，您自己可以分析其应用的目标（通过后面的注解）。使用 
        <code>Target</code> 元注释时，至少要提供这些枚举值中的一个并指出注释的注释可以应用的程序元素。清单 10 说明了 
        <code>Target</code> 的用法：
      </p>
      <a name="IDAUS4QB">
        <b>清单 10. 使用 Target 元注释</b>
      </a>
      <br>
       
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>package com.oreilly.tiger.ch06;<br><br>import java.lang.annotation.ElementType;<br>import java.lang.annotation.Target;<br><br>/**<br> * Annotation type to indicate a task still needs to be completed<br> */<br>@Target({ElementType.TYPE,<br>         ElementType.METHOD,<br>         ElementType.CONSTRUCTOR,<br>         ElementType.ANNOTATION_TYPE})<br>public @interface TODO {<br>  String value();<br>}<br></code></pre></td></tr></tbody></table><br> 
      <p>现在，Java 编译器将把 
        <code>TODO</code> 应用于类型、方法、构造函数和其他注释类型。这样有助于避免他人误用您的注释类型（或者最好的地方是，
        <i>您自己</i>也不会因为疲惫而误用它）。
      </p>
      <p><a name="N101D1"><span class="smalltitle">设置保持性</span></a></p>
      <br>下一个要用到的元注释是 
      <code>Retention</code> 。这个元注释和 Java 编译器处理注释的注释类型的方式有关。编译器有几种不同选择：

      <ul><li>将注释保留在编译后的类文件中，并在第一次加载类时读取它。</li><li>将注释保留在编译后的类文件中，但是在运行时忽略它。</li><li>按照规定使用注释，但是并不将它保留到编译后的类文件中。</li></ul>
      <p>这三种选项用 
        <code>java.lang.annotation.RetentionPolicy</code> 枚举表示，如清单 11 所示：
      </p>
      <a name="IDAZT4QB">
        <b>清单 11.  RetentionPolicy 枚举</b>
      </a>
      <br>
       
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>package java.lang.annotation;<br><br>public enum RetentionPolicy {<br>  SOURCE,		// Annotation is discarded by the compiler<br>  CLASS,		// Annotation is stored in the class file, but ignored by the VM<br>  RUNTIME		// Annotation is stored in the class file and read by the VM<br>}<br></code></pre></td></tr></tbody></table><br> 
      <p>现在可以看出， 
        <code>Retention</code> 元注释类型使用清单 11 所示的枚举值中的一个作为惟一的参数。可以将该元注释用于您的注释，如清单 12 所示：
      </p>
      <a name="IDAHU4QB">
        <b>清单 12. 使用 Retention 元注释</b>
      </a>
      <br>
       
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>@Retention(RetentionPolicy.SOURCE)<br>public @interface SuppressWarnings {<br>  // annotation type body<br>}<br></code></pre></td></tr></tbody></table><br> 
      <p>如清单 12 所示，这里可以使用简写形式，因为 
        <code>Retention</code> 只有一个成员变量。如果要将保持性设为 
        <code>RetentionPolicy.CLASS</code> ，那么什么也不需要做，因为这就是默认行为。
      </p>
      <p><a name="N1021C"><span class="smalltitle">添加公共文档</span></a></p>
      <br>下一个元注释是 
      <code>Documented</code> 。这个元注释也非常容易理解，部分原因是 
      <code>Documented</code> 是一个标记注释。您应该还记得第 1 部分中曾经提到，标记注释没有成员变量。 
      <code>Documented</code> 表示注释应该出现在类的 Javadoc 中。在默认情况下，注释
      <i>不</i>包括在 Javadoc 中，如果花费大量时间注释一个类、详细说明未完成的工作、正确完成了什么或者描述行为，那么您应该记住这一点。
      <p>清单 13 说明了 
        <code>Documented</code> 元注释的用法：
      </p>
      <a name="IDAUV4QB">
        <b>清单 13. 使用 Documented 元注释</b>
      </a>
      <br>
       
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>package com.oreilly.tiger.ch06;<br><br>import java.lang.annotation.Documented;<br>import java.lang.annotation.Retention;<br>import java.lang.annotation.RetentionPolicy;<br><br>/**<br> * Marker annotation to indicate that a method or class<br> *   is still in progress.<br> */<br><br>        <span class="boldcode">@Documented</span><br>@Retention(RetentionPolicy.RUNTIME)<br>public @interface InProgress { }<br><br>      </code></pre></td></tr></tbody></table><br> 
      <p> 
        <code>Documented</code> 的一个实用技巧是保持性策略。注意，清单 13 中规定注释的保持性（retention）是 
        <code>RUNTIME</code> ，这是使用 
        <code>Documented</code> 注释类型所
        <i>必需</i>的。Javadoc 使用虚拟机从其类文件（而非源文件）中加载信息。确保 VM 从这些类文件中获得生成 Javadoc 所需信息的惟一方法是将保持性规定为 
        <code>RetentionPolicy.RUNTIME</code> 。这样，注释就会保留在编译后的类文件中
        <i>并且</i>由虚拟机加载，然后 Javadoc 可以从中抽取出来添加到类的 HTML 文档中。
      </p>
      <p><a name="N10263"><span class="smalltitle">设置继承</span></a></p>
      <br>最后一个元注释 
      <code>Inherited</code> ，可能是最复杂、使用最少、也最容易造成混淆的一个。这就是说，我们简单地看一看就可以了。
      <p>首先考虑这样一种情况：假设您通过定制的 
        <code>InProgress</code> 注释标记一个类正在开发之中，这完全没有问题，对吧？这些信息还会出现在 Javadoc 中，只要您正确地应用了 
        <code>Documented</code> 元注释。现在，假设您要编写一个新类，扩展那个还在开发之中的类，也不难，是不是？但是要记住，那个超类还在开发之中。如果您使用子类，或者查看它的文档，根本没有线索表明还有什么地方没有完成。您本来希望看到 
        <code>InProgress</code> 注释被带到子类中 —— 因为这是
        <i>继承</i> 的 —— 但情况并非如此。您必须使用 
        <code>Inherited</code> 元注释说明所期望的行为，如清单 14 所示：
      </p>
      <a name="IDATX4QB">
        <b>清单 14. 使用 Inherited 元注释</b>
      </a>
      <br>
       
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br><br>package com.oreilly.tiger.ch06;<br><br>import java.lang.annotation.Documented;<br>import java.lang.annotation.Inherited;<br>import java.lang.annotation.Retention;<br>import java.lang.annotation.RetentionPolicy;<br><br>/**<br> * Marker annotation to indicate that a method or class<br> *   is still in progress.<br> */<br>@Documented<br><br>        <span class="boldcode">@Inherited</span><br>@Retention(RetentionPolicy.RUNTIME)<br>public @interface InProgress { }<br><br>      </code></pre></td></tr></tbody></table><br> 
      <p>添加 
        <code>@Inherited</code> 后，您将看到 
        <code>InProgress</code> 出现在注释类的子类中。当然，您并不希望所有的注释类型都具有这种行为（因此默认值是
        <i>不</i> 继承的）。比如， 
        <code>TODO</code> 注释就不会（也不应该）被传播。但是对于这里示范的情况， 
        <code>Inherited</code> 可能非常有用。
      </p>

      <br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate2.html#main" class="fbox"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="N102AB"><span class="atitle">结束语</span></a></p>
      <br>现
在，您也许已经准备回到 Java 世界为所有的事物编写文档和注释了。这不禁令我回想起人们了解 Javadoc
之后发生的事情。我们都陷入了文档过滥的泥潭，直到有人认识到最好使用 Javadoc 来理清容易混淆的类或者方法。无论用 Javadoc
做了多少文章，也没有人会去看那些易于理解的 <code>getXXX()</code> 和 
      <code>setXXX()</code> 方法。
      <p>注
释也可能出现同样的趋势，虽然不一定到那种程度。经常甚至频繁地使用标准注释类型是一种较好的做法。所有的 Java 5
编译器都支持它们，它们的行为也很容易理解。但是，如果要使用定制注释和元注释，那么就很难保证花费很大力气创建的那些类型在您的开发环境之外还有什么意
义。因此要慎重。在合理的情况下使用注释，不要荒谬使用。无论如何，注释都是一种很好的工具，可以在开发过程中提供真正的帮助。</p>



    <br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate2.html#main" class="fbox"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="resources"><span class="atitle">参考资料 </span></a></p>

      <ul><li>您可以参阅本文在 developerWorks 全球站点上的
          <a href="http://www.ibm.com/developerworks/java/library/j-annotate2.html">英文原文</a>。
        <br><br></li><li>不要遗漏“
          <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate2.shtml">Tiger 中的注释，第 2 部分</a>”，即本系列文章的第 2 部分，研究了定制注释。
          <br>
          <br>
        <br><br></li><li>开放源代码
          <a href="http://xdoclet.sourceforge.net/xdoclet/index.html">XDoclet</a>代码生成引擎支持面向属性的 Java 语言编程。
          <br>
          <br>
        <br><br></li><li>
          <a href="http://www.jcp.org/en/jsr/detail?id=175">JSR 175</a>，将元数据工具合并到 Java 语言中的规范，处于 Java Community Process 的提议最终草案状态。
          <br>
          <br>
        <br><br></li><li>访问 Sun 的主页，获取
          <a href="http://java.sun.com/j2se/1.5.0/index.jsp">J2SE 5.0 的所有信息</a>。
          <br>
          <br>
        <br><br></li><li>可以
          <a href="http://java.sun.com/j2se/1.5.0/download.jsp">下载 Tiger</a>并自己试用。
          <br>
          <br>
        <br><br></li><li>John Zukowski 的系列文章
          <a href="http://www.ibm.com/developerworks/views/java/articles.jsp?sort_order=desc&amp;expand=&amp;ort_by=Date&amp;show_abstract=true&amp;view_by=Search&amp;search_by=taming%20tiger%3A">
            <i>Taming Tiger</i>
          </a>以实用的基于技巧的形式讲述了 Java 5.0 的新功能。
          <br>
          <br>
        <br><br></li><li>由 Brett McLaughlin 和 David Flanagan 撰写的
          <a href="http://www.oreilly.com/catalog/javaadn/">
            <i>Java 1.5 Tiger: A Developer's Notebook</i>
          </a>一书 (O'Reilly &amp; Associates; 2004)，以代码为中心、开发人员友好的形式，讲述了几乎所有的 Tiger 的最新功能 — 包括注释。
          <br>
          <br>
        <br><br></li><li>在
          <a href="http://www-128.ibm.com/developerworks/cn/java/">
            <i>developerWorks</i>Java 技术专区
          </a>可以找到数百篇有关 Java 技术的参考资料。
          <br>
          <br>
        <br><br></li><li>访问
          <a href="http://devworks.krcinfo.com/">Developer Bookstore</a>，获得技术书籍的完整列表，其中包括数百本
          <a href="http://devworks.krcinfo.com/WebForms/ProductList.aspx?Search=Category&amp;id=1200&amp;p=Java">Java 相关主题</a>的书籍。
          <br>
          <br>
        <br><br></li><li>是否对无需通常的高成本入口点（entry point）或短期评估许可证的 IBM 测试产品感兴趣？
          <a href="http://www-128.ibm.com/developerworks/cn/subscription/">developerWorks Subscription</a>为 WebSphere?、DB2?、Lotus?、Rational? 和 Tivoli? 产品提供了低成本的 12 个月单用户许可证，包括基于 Eclipse 的 WebSphere Studio? IDE，用于开发、测试、评估和展示您的应用程序。
          <br>
          <br>
        <br></li></ul>

    <br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate2.html#main" class="fbox"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="author"><span class="atitle">关于作者</span></a></p><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td colspan="2"><img alt="" src="http://www.ibm.com/i/c.gif" height="5" width="100%"></td></tr><tr align="left" valign="top"><td><p><img alt="作者照片" src="http://www.ibm.com/developerworks/i/p-brett.jpg" align="left" height="80" width="64"></p></td><td><p>Brett
McLaughlin 从 Logo 时代（还记得那个小三角吗？）就开始从事计算机工作。在最近几年里，他已经成为 Java 和 XML
社区最知名的作者和程序员之一。他曾经在 Nextel Communications 实现复杂的企业系统，在 Lutris
Technologies 编写应用程序服务器，目前在为 O'Reilly Media, Inc 撰写和编辑 <a href="http://www.oreillynet.com/cs/catalog/view/au/152?x-t=book.view&amp;CMP=IL7015">书籍</a>。
      </p></td></tr></tbody></table></td></tr></tbody></table><img src ="http://www.blogjava.net/ericwang/aggbug/23745.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-12-13 23:28 <a href="http://www.blogjava.net/ericwang/archive/2005/12/13/23745.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转载】Tiger 中的注释，第 1 部分: 向 Java 代码中添加元数据</title><link>http://www.blogjava.net/ericwang/archive/2005/12/13/23744.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Tue, 13 Dec 2005 15:26:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/12/13/23744.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/23744.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/12/13/23744.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/23744.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/23744.html</trackback:ping><description><![CDATA[<table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr valign="top"><td width="100%"><h1><span style="color: rgb(153, 153, 153);">Tiger 中的注释，第 1 部分: </span>向 Java 代码中添加元数据</h1><p id="subtitle"><em>如何使用 Java 5 的内置注释</em></p><img alt="" src="http://www.ibm.com/i/c.gif" class="display-img" height="6" width="1"></td><td class="no-print" width="192"><img alt="developerWorks" src="http://www-128.ibm.com/developerworks/i/dw.gif" height="18" width="192"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr valign="top"><td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="10"></td><td width="100%"><table class="no-print" align="right" border="0" cellpadding="0" cellspacing="0" width="160"><tbody><tr><td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="10"></td><td><table border="0" cellpadding="0" cellspacing="0" width="150"><tbody><tr><td class="v14-header-1-small">文档选项</td></tr></tbody></table><table class="v14-gray-table-border" border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="no-padding" width="150"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="8"><input value="注释，J2SE 5.0 (Tiger) 中的新功能，将非常需要的元数据工具引入核心 Java 语言。该系列文章分为两部分，在这第 1 部分中，作者 Brett McLaughlin 解释了元数据如此有用的原因，向您介绍了 Java 语言中的注释，并研究了 Tiger 的内置注释。第 2 部分将介绍定制注释。" name="body" type="hidden"><input name="subject" value="Tiger 中的注释，第 1 部分: 向 Java 代码中添加元数据" type="hidden"><input name="lang" value="cn" type="hidden"><table border="0" cellpadding="0" cellspacing="0" width="143"><form action="https://www-130.ibm.com/developerworks/secure/email-it.jsp" name="email"></form><script language="JavaScript" type="text/javascript">
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
</script><tbody><tr valign="top"><td width="8"><img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="8"></td><td width="16"><img src="http://www.ibm.com/i/v14/icons/em.gif" alt="将此页作为电子邮件发送" height="16" vspace="3" width="16"></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr><noscript><tr
valign="top"><td width="8"><img alt="" height="1" width="8"
src="//www.ibm.com/i/c.gif"/></td><td width="16"><img alt="" width="16"
height="16" src="//www.ibm.com/i/c.gif"/></td><td class="small"
width="122"><p><span class="ast">未显示需要 JavaScript
的文档选项</span></p></td></tr></noscript></tbody></table></td></tr></tbody></table><br><table border="0" cellpadding="0" cellspacing="0" width="150"><tbody><tr><td class="v14-header-1-small">对此页的评价</td></tr></tbody></table><table class="v14-gray-table-border" border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="no-padding" width="150"><table border="0" cellpadding="0" cellspacing="0" width="143"><tbody><tr valign="top"><td width="8"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="8"></td><td><img alt="" src="http://www.ibm.com/i/v14/icons/d_bold.gif" border="0" height="16" vspace="3" width="16"></td><td width="125"><p><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/#rate" class="smallplainlink"><b>帮助我们改进这些内容</b></a></p></td></tr></tbody></table></td></tr></tbody></table><br></td></tr></tbody></table><p>级别: 初级</p><p><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/#author">Brett McLaughlin</a>, 作者/编者, O'Reilly Media, Inc<br></p><p>2004 年  9 月  01 日</p><blockquote>注
释，J2SE 5.0 (Tiger) 中的新功能，将非常需要的元数据工具引入核心 Java 语言。该系列文章分为两部分，在这第 1
部分中，作者 Brett McLaughlin 解释了元数据如此有用的原因，向您介绍了 Java 语言中的注释，并研究了 Tiger
的内置注释。</blockquote>
			
      <p>编程的一个最新的趋势，尤其是在 Java 编程方面，是使用
        <i>元数据</i>。简单地说，元数据就是
        <i>关于</i>数据的数据。元数据可以用于创建文档，跟踪代码中的依赖性，甚至执行基本编译时检查。许多元数据工具，如 XDoclet（请参阅
        <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/#Resources">参考资料</a>），将这些功能添加到核心 Java 语言中，暂时成为 Java 编程功能的一部分。
      </p>
			
      <p>直到可以使用 J2SE 5.0（也叫做 Tiger，现在是第二个 beta 版本），核心 Java 语言才最接近具有 Javadoc 方法的元数据工具。您使用特殊的标签集合来标记代码，并执行 
        <code>javadoc</code>
命令来将这些标签转化成格式化的 HTML 页面，该页面说明标签所附加到的类。然而，Javadoc
是有缺陷的元数据工具，因为除了生成文档之外，您没有固定、实用、标准化的方式来将数据用于其他用途。HTML 代码经常混入到 Javadoc
输出中这一事实甚至更进一步降低了其用于任何其他目的的价值。 </p>
			
      <p>Tiger 通过名为
        <i>注释</i>的新功能将一个更通用的元数据工
具合并到核心 Java 语言中。注释是可以添加到代码中的修饰符，可以用于包声明、类型声明、构造函数、方法、字段、参数和变量。Tiger
包含内置注释，还支持您自己编写的定制注释。本文将概述元数据的优点并向您介绍 Tiger 的内置注释。本系列文章的 <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate2.shtml">第 2 部分</a>将研究定制注释。我要感谢 O'Reilly Media, Inc.，他们非常慷慨地 允许我在本文中使用我关于 Tiger 的书籍的“注释”一章中的代码示例（请参阅
        <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/#Resources">参考资料</a>）。

      </p>
			
      <p><a name="N1006D"><span class="atitle">元数据的价值</span></a></p>
			
      <br>一
般来说，元数据的好处分为三类：文档编制、编译器检查和代码分析。代码级文档最常被引用。元数据提供了一种有用的方法来指明方法是否取决于其他方法，它们
是否完整，特定类是否必须引用其他类，等等。这确实非常有用，但对于将元数据添加到 Java 语言中来说，文档编制可能是 <i>最不</i>相关的理由。Javadoc 已经提供了非常容易理解和健壮的方法来文档化代码。另外，当已经存在文档编制工具，并且在大多数时候都工作得很好时，谁还要编写文档编制工具？
      <br>
			
      <table align="right" border="0" cellpadding="0" cellspacing="0" width="40%"><tbody><tr><td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="10"></td><td><table border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td bgcolor="#eeeeee">
				
        <a name="N1007C"><b>不要漏掉本系列的另一部分</b></a><br>
				
        <p>一定要阅读本系列的“
          <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate2.shtml">第 2 部分</a>”，该部分研究了定制注释。

        </p>
			
      </td></tr></tbody></table></td></tr></tbody></table>
			
      <p><a name="N1008A"><span class="smalltitle">编译器检查</span></a></p>
			
      <br>元数据更重要的优点是编译器可以使用它来执行基本的编译时检查。例如，您将在本文后面的
      <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/#override">Override 注释</a>中
看到 Tiger 引入了一个这样的注释，用于允许您指定一种方法覆盖超类中的另一种方法。Java
编译器可以确保在元数据中指明的行为实际发生在代码级别。如果从来没有找出过这种类型的 bug，这样做似乎有点傻，但是大多数年龄很大的 Java
编程老手都曾经花费至少多个晚上来查明他们的代码为什么不能用。当最后认识到方法的参数有错，且该方法实际上 <i>没有</i> 覆盖超类中的方法时，您可能更感到难受。使用元数据的工具有助于轻松地查明这种类型的错误，从而可以节省那些晚上来看长期进行的 Halo 联赛。


      <table align="right" border="0" cellpadding="0" cellspacing="0" width="40%"><tbody><tr><td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="10"></td><td><table border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td bgcolor="#eeeeee">
				
        <a name="N1009B"><b>JSR 175</b></a><br>
				
        <p>JSR 175，
          <i>Java 编程语言的元数据工具</i>，为将元数据合并到核心 Java 语言中提供了正式理由和说明（请参阅
          <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/#Resources">参考资料</a>）。根据 JSR，注释“不直接影响程序的语义。然而，开发和部署工具可以读取这些注释，并以某种形式处理这些注释，可能生成其他 Java 编程语言源文件、XML 文档或要与包含注释的程序一起使用的其他构件。”
        </p>
			
      </td></tr></tbody></table></td></tr></tbody></table>
			
      <p><a name="N100AC"><span class="smalltitle">代码分析</span></a></p>
			
      <br>可
以证明，任何好的注释或元数据工具的最好功能就是可以使用额外数据来分析代码。在一个简单的案例中，您可能构建代码目录，提供必需的输入类型并指明返回类
型。但是，您可能想，Java
反射具有相同的优点；毕竟，可以为所有这些信息内省代码。这从表面上看似乎是正确的，但是在实际中通常不使用。许多时候，方法作为输入接受的或者作为输出
返回的类型实际上不是该方法想要的类型。例如，参数类型可能是 <code>Object</code> ，但方法可能仅使用 
      <code>Integer</code> 。这在好些情况下很容易发生，比如在方法被覆盖而超类使用常规参数声明方法时，还有正在进行许多序列化的系统中也容易发生。在这两种情况中，元数据可以指示代码分析工具，虽然参数类型是 
      <code>Object</code> ，但 
      <code>Integer</code> 才是真正需要的。这类分析非常有用，但也不能夸大它的价值。
      <p>在
更复杂的情况下，代码分析工具可以执行所有种类的额外任务。示例 du jour 是 Enterprise JavaBean (EJB)
组件。甚至简单 EJB 系统中的依赖性和复杂性都非常令人吃惊。您具有了 home 接口和远程接口，以及本地接口和本地 home
接口，还有一个实现类。保持所有这些类同步非常困难。但是，元数据可以提供这个问题的解决放案。好的工具（还是要提一下
XDoclet）可以管理所有这些依赖性，并确保无“代码级”连接、但有“逻辑级”捆绑的类保持同步。元数据在这里确实可以发挥它的作用。</p>
			
      <br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/#main" class="fbox"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="N100C7"><span class="atitle">注释的基本知识</span></a></p>
			
      <br>现在已经了解了元数据的好处，我将介绍 Tiger 中的注释。注释采用“at”标记形式 ( 
      <code>@</code> )，后面是注释名称。然后在需要数据时，通过 
      <code>name=value</code> 对向注释提供数据。每次使用这类表示法时，就是在生成注释。一段代码可能会有 10 个、50 个或更多的注释。不过，您将发现多个注释都可能使用相同的
      <i>注释类型</i>。类型是实际使用的结构，在特定上下文中，注释本身是该类型的具体使用（请参阅侧栏
      <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/#type">注释或注释类型？</a>）。


      <table align="right" border="0" cellpadding="0" cellspacing="0" width="40%"><tbody><tr><td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="10"></td><td><table border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td bgcolor="#eeeeee">
				
        <a name="N100E0"><b>注释或注释类型？</b></a><br>
				
        <p>是否对什么是注释与什么是注释类型感到迷惑？了解这个的最简单方法就是对比所熟悉的 Java 语言概念来想。可以定义一个类（例如 
          <code>Person</code> ），则在 JVM 中将总是仅有该类的一个版本（假设没有进行麻烦的类路径设置）。然而，在任何给定时间，可能会使用该类的 10 个或 20 个
          <i>实例</i>。仍然是只有一个 
          <code>Person</code> 类，但是它以不同的方式使用多次。注释类型和注释也是这样。注释类型类似于类，注释类似于该类的实例。
        </p>
			
      </td></tr></tbody></table></td></tr></tbody></table>
			
      <p>注释分为三个基本种类：</p>
			
      <ul><li>
					
          <b>标记注释</b>没有变量。注释显示简单，由名称标识，没有提供其他数据。例如， 
          <code>@MarkerAnnotation</code> 是标记注释。它不包含数据，仅有注释名称。
          <br>
					
          <br>
				
        </li><li>
					
          <b>单一值注释</b>与标记注释类似，但提供一段数据。因为仅提供很少的一点数据，所以可以使用快捷语法（假设注释类型接受此语法）： 
          <code>@SingleValueAnnotation("my data")</code> 。除了 
          <code>@</code> 标记外，这应该与普通的 Java 方法调用很像。
          <br>
					
          <br>
				
        </li><li>
					
          <b>完整注释</b>有多个数据成员。因此，必须使用更完整的语法（注释不再像普通的 Java 方法）： 
          <code>@FullAnnotation(var1="data value 1", var2="data value 2", var3="data value 3")</code> 。
        </li></ul>
			
      <p>除了通过默认语法向注释提供值外，还可以在需要传送多个值时使用名称-值对。还可以通过花括号为注释变量提供值数组。清单 1 显示了注释中的值数组的示例。</p>
			
      <a name="IDAGW4QB">
				
        <b>清单 1. 在注释中使用按数组排列的值</b>
			
      </a>
			
      <br>
			
      
			
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>@TODOItems({    // Curly braces indicate an array of values is being supplied<br>  @TODO(<br>    severity=TODO.CRITICAL,<br>    item="Add functionality to calculate the mean of the student's grades",<br>    assignedTo="Brett McLaughlin"<br>  ),<br>  @TODO(<br>    severity=TODO.IMPOTANT,<br>    item="Print usage message to screen if no command-line flags specified",<br>    assignedTo="Brett McLaughlin"<br>  ),<br>  @TODO(<br>    severity=TODO.LOW,<br>    item="Roll a new website page with this class's new features",<br>    assignedTo="Jason Hunter"<br>  )<br>})<br></code></pre></td></tr></tbody></table><br>
			
      <p>清单 1 中的示例并没有乍一看那样复杂。 
        <code>TODOItems</code> 注释类型有一个具有值的变量。这里提供的值比较复杂，但 
        <code>TODOItems</code> 的使用实际与单一值注释类型相符，只是这里的单一值是数组而已。该数组包含三个 
        <code>TODO</code> 注释，其中每个注释都是多值的。逗号分隔每个注释内的值，以及单个数组内的值。非常容易，是吧？
      </p>
			
      <p>但是我讲的可能超前了些。 
        <code>TODOItems</code> 和 
        <code>TODO</code> 是 
        <i>定制注释</i>，
是本系列文章第 2 部分中的主题。但是我想让您看到，即使复杂注释（清单 1 几乎是最复杂的注释）也不是非常令人害怕的。当提到 Java
语言的标准注释类型时，将很少看到如此复杂的情况。正如将在下面三个部分了解到的，Tiger 的基本注释类型的使用都极其简单。 </p>
			
      <br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/#main" class="fbox"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="N10152"><span class="atitle">Override 注释</span></a></p>
			
      <br>Tiger 的第一个内置注释类型是 
      <code>Override</code> 。 
      <code>Override</code> 应该仅用于方法（不用于类、包声明或其他构造）。它指明注释的方法将覆盖超类中的方法。清单 2 显示了简单的示例。
      <a name="IDAXX4QB">
				
        <b>清单 2. 操作中的 Override 注释</b>
			
      </a>
			
      <br>
			
      
			
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>package com.oreilly.tiger.ch06;<br><br>public class OverrideTester {<br><br>  public OverrideTester() { }<br><br>  @Override<br>  public String toString() {<br>    return super.toString() + " [Override Tester Implementation]";<br>  }<br><br>  @Override<br>  public int hashCode() {<br>    return toString().hashCode();<br>  }<br>}<br></code></pre></td></tr></tbody></table><br>
			
      <p>清单 2 应该很容易理解。 
        <code>@Override</code> 注释对两个方法进行了注释 — 
        <code>toString()</code> 和 
        <code>hashCode()</code> ，来指明它们覆盖 
        <code>OverrideTester</code> 类的超类 ( 
        <code>java.lang.Object</code> ) 中的方法的版本。开始这可能看起来没什么作用，但它实际上是非常好的功能。如果不覆盖这些方法，根本
        <i>无法</i> 编译此类。该注释还确保当您将 
        <code>toString()</code> 弄乱时，至少还有某种指示，即应该确保 
        <code>hashCode()</code> 仍旧匹配。
      </p>
			
      <p>当编码到很晚且输错了某些东西时，此注释类型真的可以发挥很大的作用，如清单 3 中所示。</p>
			
      <a name="IDADZ4QB">
				
        <b>清单 3. 使 Override 注释捕获打字稿</b>
			
      </a>
			
      <br>
			
      
			
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br><br>package com.oreilly.tiger.ch06;<br><br>public class OverrideTester {<br><br>  public OverrideTester() { }<br><br>  @Override<br>  public String toString() {<br>    return super.toString() + " [Override Tester Implementation]";<br>  }<br><br>  @Override<br>public int hasCode() {<br>    return toString().hashCode();<br>  }<br>}<br></code></pre></td></tr></tbody></table><br>
			
      <p>在清单 3 中， 
        <code>hashCode()</code> 错误地输入为 
        <code>hasCode()</code> 。注释指明 
        <code>hasCode()</code> 应该覆盖方法。但是在编译中， 
        <code>javac</code> 将发现超类 ( 
        <code>java.lang.Object</code> ) 没有名为 
        <code>hasCode()</code> 的方法可以覆盖。因此，编译器将报错，如图 1 中所示。
      </p>
			
      <p>
				
        <a name="fig1">
					
          <b>图 1. 来自 Override 注释的编译器警告</b>
				
        </a>
				
        <br>
				
        <img alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/Figure1.gif" height="303" width="600">
			
      </p>
			
      <table align="right" border="0" cellpadding="0" cellspacing="0" width="40%"><tbody><tr><td width="10"><img alt="" src="http://www.ibm.com/i/c.gif" height="1" width="10"></td><td><table border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td bgcolor="#eeeeee">
				
        <a name="N101D0"><b>缺少的功能</b></a><br>
				
        <p>在单一值注释类型中，如果 
          <code>Deprecated</code> 允许包含错误类型消息将更好。然后，当用户使用声明为过时的方法时，编译器可以打印消息。该消息可以指明使用方法的结果如何重要，说明何时将停止方法，甚至建议备用方法。可能 J2SE 的下一版本（“Mustang”，他们这样命名）将提供这种功能。
        </p>
			
      </td></tr></tbody></table></td></tr></tbody></table>
			
      <p>这个便捷的小功能将帮助快速捕获打字稿。</p>
			
      <br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/#main" class="fbox"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="N101E1"><span class="atitle">Deprecated 注释</span></a></p>
			
      <br>下一个标准注释类型是 
      <code>Deprecated</code> 。与 
      <code>Override</code> 一样， 
      <code>Deprecated</code> 是标记注释。正如您可能期望的，可以使用 
      <code>Deprecated</code> 来对不应再使用的方法进行注释。与 
      <code>Override</code> 不一样的是， 
      <code>Deprecated</code> 应该与正在声明为过时的方法放在同一行中（为什么这样？说实话我也不知道），如清单 4 中所示。
      <a name="IDA414QB">
				
        <b>清单 4. 使用 Deprecated 注释</b>
			
      </a>
			
      <br>
			
      
			
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>package com.oreilly.tiger.ch06;<br><br>public class DeprecatedClass {<br><br>  @Deprecated public void doSomething() {<br>    // some code<br>  }<br><br>  public void doSomethingElse() {<br>    // This method presumably does what doSomething() does, but better<br>  }<br>}<br></code></pre></td></tr></tbody></table><br>
			
      <p>单独编译此类时，不会发生任何不同。但是如果通过覆盖或调用来使用声明为过时的方法，编译器将处理注释，发现不应该使用该方法，并发出错误消息，如图 2 中所示。</p>
			
      <p>
				
        <a name="fig2">
					
          <b>图 2. 来自 Deprecated 注释的编译器警告</b>
				
        </a>
				
        <br>
				
        <img alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/Figure2.gif" height="303" width="600">
			
      </p>
			
      <p>注意需要开启编译器警告，就像是必须向 Java 编译器指明想要普通的声明为过时警告。可以使用下列两个标记之一和 
        <code>javac</code> 命令： 
        <code>-deprecated</code> 或新的 
        <code>-Xlint:deprecated</code> 标记。
      </p>
			
      <br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/#main" class="fbox"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="N10232"><span class="atitle">SuppressWarnings 注释</span></a></p>
			
      <br>从 Tiger “免费”获得的最后一个注释类型是 
      <code>SuppressWarnings</code> 。发现该类型的作用应该不难，但是
      <i>为什么</i>该注释类型如此重要通常不是很明显。它实际上是 Tiger 的所有新功能的副功能。例如，以泛型为例；泛型使所有种类的新类型安全操作成为可能，特别是当涉及 Java 集合时。然而，因为泛型，当使用集合而
      <i>没有</i> 类型安全时，编译器将抛出警告。这对于针对 Tiger 的代码有帮助，但它使得为 Java 1.4.x 或更早版本编写代码非常麻烦。将不断地收到关于根本无关的事情的警告。如何才能使编译器不给您增添麻烦？


      <p>
				
        <code>SupressWarnings</code> 可以解决这个问题。 
        <code>SupressWarnings</code> 与 
        <code>Override</code> 和 
        <code>Deprecated</code> 不同，
        <i>是</i>具有变量的 — 所以您将单一注释类型与该变量一起使用。可以以值数组来提供变量，其中每个值指明要阻止的一种特定警告类型。请看清单 5 中的示例，这是 Tiger 中通常会产生错误的一些代码。
      </p>
			
      <a name="IDAB44QB">
				
        <b>清单 5. 不是类型安全的 Tiger 代码</b>
			
      </a>
			
      <br>
			
      
			
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>public void nonGenericsMethod() {<br>  List wordList = new ArrayList();    // no typing information on the List<br><br>  wordList.add("foo");                // causes error on list addition<br>}<br></code></pre></td></tr></tbody></table><br>
			
      <p>图 3 显示了清单 5 中代码的编译结果。</p>
			
      <p>
				
        <a name="fig3">
					
          <b>图 3. 来自非标准代码的编译器警告</b>
				
        </a>
				
        <br>
				
        <img alt="" src="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/figure3.gif" height="303" width="600">
			
      </p>
			
      <p>清单 6 通过使用 
        <code>SuppressWarnings</code> 注释消除了这种问题。
      </p>
			
      <a name="IDA044QB">
				
        <b>清单 6. 阻止警告</b>
			
      </a>
			
      <br>
			
      
			
      <table bgcolor="#eeeeee" border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td><pre><code class="section"><br>@SuppressWarings(value={"unchecked"})<br>public void nonGenericsMethod() {<br>  List wordList = new ArrayList();    // no typing information on the List<br><br>  wordList.add("foo");                // causes error on list addition<br>}<br></code></pre></td></tr></tbody></table><br>
			
      <p>非常简单，是吧？仅需要找到警告类型（图 3 中显示为“unchecked”），并将其传送到 
        <code>SuppressWarnings</code> 中。
      </p>
			
      <p>
				
        <code>SuppressWarnings</code> 中变量的值采用数组，使您可以在同一注释中阻止多个警告。例如， 
        <code>@SuppressWarnings(value={"unchecked", "fallthrough"})</code> 使用两个值的数组。此功能为处理错误提供了非常灵活的方法，无需进行大量的工作。
      </p>
			
      <br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/#main" class="fbox"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="N102A2"><span class="atitle">结束语</span></a></p>
			
      <p>虽
然这里看到的语法可能都是新的，但您应该知道注释非常容易理解和使用。也就是说，与 Tiger
一起提供的标准注释相当简单，可以添加许多功能。元数据正日益变得有帮助，您肯定会提出非常适用于自己的应用程序的注释类型。在本系列文章的第 2
部分，我将详细说明 Tiger 对编写自己的注释类型的支持。您将了解如何创建 Java
类以及将其定义为注释类型，如何使编译器识别您的注释类型，以及如何使用该类型对代码进行注释。我甚至会更深入地说明奇异但有用的对注释进行注释的任务。
您将快速熟悉 Tiger 中的这一新构造。</p>
		
    <br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/#main" class="fbox"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="resources"><span class="atitle">参考资料 </span></a></p>
			
      <ul><li>您可以参阅本文在 developerWorks 全球站点上的
          <a href="http://www.ibm.com/developerworks/java/library/j-annotate1/">英文原文</a>。
        <br><br></li><li>不要遗漏“
          <a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate2.shtml">Tiger 中的注释，第 2 部分</a>”，即本系列文章的第 2 部分，研究了定制注释。
          <br>
					
          <br>
				
        <br><br></li><li>开放源代码
          <a href="http://xdoclet.sourceforge.net/xdoclet/index.html">XDoclet</a>代码生成引擎支持面向属性的 Java 语言编程。
          <br>
					
          <br>
				
        <br><br></li><li>
					
          <a href="http://www.jcp.org/en/jsr/detail?id=175">JSR 175</a>，将元数据工具合并到 Java 语言中的规范，处于 Java Community Process 的提议最终草案状态。
          <br>
					
          <br>
				
        <br><br></li><li>访问 Sun 的主页，获取
          <a href="http://java.sun.com/j2se/1.5.0/index.jsp">J2SE 5.0 的所有信息</a>。
          <br>
					
          <br>
				
        <br><br></li><li>可以
          <a href="http://java.sun.com/j2se/1.5.0/download.jsp">下载 Tiger</a>并自己试用。
          <br>
					
          <br>
				
        <br><br></li><li>John Zukowski 的系列文章
          <a href="http://www.ibm.com/developerworks/views/java/articles.jsp?sort_order=desc&amp;expand=&amp;ort_by=Date&amp;show_abstract=true&amp;view_by=Search&amp;search_by=taming%20tiger%3A">
						
            <i>Taming Tiger</i>
					
          </a>以实用的基于技巧的形式讲述了 Java 5.0 的新功能。
          <br>
					
          <br>
				
        <br><br></li><li>由 Brett McLaughlin 和 David Flanagan 撰写的
          <a href="http://www.oreilly.com/catalog/javaadn/">
						
            <i>Java 1.5 Tiger: A Developer's Notebook</i>
					
          </a>一书 (O'Reilly &amp; Associates; 2004)，以代码为中心、开发人员友好的形式，讲述了几乎所有的 Tiger 的最新功能 — 包括注释。
          <br>
					
          <br>
				
        <br><br></li><li>在
          <a href="http://www-128.ibm.com/developerworks/cn/java/">
						
            <i>developerWorks</i>Java 技术专区
          </a>可以找到数百篇有关 Java 技术的参考资料。
          <br>
					
          <br>
				
        <br><br></li><li>访问
          <a href="http://devworks.krcinfo.com/">Developer Bookstore</a>，获得技术书籍的完整列表，其中包括数百本
          <a href="http://devworks.krcinfo.com/WebForms/ProductList.aspx?Search=Category&amp;id=1200&amp;p=Java">Java 相关主题</a>的书籍。
          <br>
					
          <br>
				
        <br><br></li><li>是否对无需通常的高成本入口点（entry point）或短期评估许可证的 IBM 测试产品感兴趣？
          <a href="http://www-128.ibm.com/developerworks/cn/subscription/">developerWorks Subscription</a>为 WebSphere?、DB2?、Lotus?、Rational? 和 Tivoli? 产品提供了低成本的 12 个月单用户许可证，包括基于 Eclipse 的 WebSphere Studio? IDE，用于开发、测试、评估和展示您的应用程序。
          <br>
					
          <br>
				
        <br></li></ul>
		
    <br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img src="http://www.ibm.com/i/v14/rules/blue_rule.gif" alt="" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img src="http://www.ibm.com/i/v14/icons/u_bold.gif" alt="" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a href="http://www-128.ibm.com/developerworks/cn/java/j-annotate1/#main" class="fbox"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="author"><span class="atitle">关于作者</span></a></p><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td colspan="2"><img alt="" src="http://www.ibm.com/i/c.gif" height="5" width="100%"></td></tr><tr align="left" valign="top"><td><p><img alt="作者照片" src="http://www.ibm.com/developerworks/i/p-brett.jpg" align="left" height="80" width="64"></p></td><td><p>Brett
McLaughlin 从 Logo 时代（还记得那个小三角吗？）就开始从事计算机工作。在最近几年里，他已经成为 Java 和 XML
社区最知名的作者和程序员之一。他曾经在 Nextel Communications 实现复杂的企业系统，在 Lutris
Technologies 编写应用程序服务器，目前在为 O'Reilly Media, Inc 撰写和编辑 <a href="http://www.oreillynet.com/cs/catalog/view/au/152?x-t=book.view&amp;CMP=IL7015">书籍</a>。
      </p></td></tr></tbody></table></td></tr></tbody></table><img src ="http://www.blogjava.net/ericwang/aggbug/23744.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-12-13 23:26 <a href="http://www.blogjava.net/ericwang/archive/2005/12/13/23744.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java Annotation入门</title><link>http://www.blogjava.net/ericwang/archive/2005/12/13/23743.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Tue, 13 Dec 2005 15:22:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/12/13/23743.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/23743.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/12/13/23743.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/23743.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/23743.html</trackback:ping><description><![CDATA[<center><span style="font-size: 20px;"><b>Java Annotation入门</b></span></center><br><center>作者：<a href="http://blog.matrix.org.cn/page/cleverpig" target="_new">cleverpig</a></center><br><br><br><br><br><span style="color: Red;">版权声明：本文可以自由转载，转载时请务必以超链接形式标明文章原始出处和作者信息及本声明</span><br>作者:cleverpig(作者的Blog:<a href="http://blog.matrix.org.cn/page/cleverpig" target="_new">http://blog.matrix.org.cn/page/cleverpig</a>)<br>原
文:[http://www.matrix.org.cn/resource/article/44/44048_Java+Annotation.html]http://www.matrix.org.cn/resource/article/44/44048_Java+Annotation.html[/url]<br>关键字:Java,annotation,标注<br><br><br><span style="color: blue;">摘要：</span><br>本
文针对java初学者或者annotation初次使用者全面地说明了annotation的使用方法、定义方式、分类。初学者可以通过以上的说明制作简
单的annotation程序，但是对于一些高级的annotation应用（例如使用自定义annotation生成javabean映射xml文件）
还需要进一步的研究和探讨。涉及到深入annotation的内容，作者将在后文《Java Annotation高级应用》中谈到。<br><br>同时，annotation运行存在两种方式：运行时、编译时。上文中讨论的都是在运行时的annotation应用，但在编译时的annotation应用还没有涉及，<br><br><span style="color: blue;">一、为什么使用Annotation：</span><br><br>在JAVA应用中，我们常遇到一些需要使用模版代码。例如，为了编写一个JAX-RPC web service，我们必须提供一对接口和实现作为模版代码。如果使用annotation对远程访问的方法代码进行修饰的话，这个模版就能够使用工具自动生成。<br>另
外，一些API需要使用与程序代码同时维护的附属文件。例如，JavaBeans需要一个BeanInfo
Class与一个Bean同时使用/维护，而EJB则同样需要一个部署描述符。此时在程序中使用annotation来维护这些附属文件的信息将十分便利
而且减少了错误。<br><br><span style="color: blue;">二、Annotation工作方式：</span><br><br>在5.0
版之前的Java平台已经具有了一些ad hoc
annotation机制。比如，使用transient修饰符来标识一个成员变量在序列化子系统中应被忽略。而@deprecated这个
javadoc tag也是一个ad hoc
annotation用来说明一个方法已过时。从Java5.0版发布以来，5.0平台提供了一个正式的annotation功能：允许开发者定义、使用
自己的annoatation类型。此功能由一个定义annotation类型的语法和一个描述annotation声明的语法，读取annotaion
的API，一个使用annotation修饰的class文件，一个annotation处理工具（apt）组成。<br>annotation并不直接影响代码语义，但是它能够工作的方式被看作类似程序的工具或者类库，它会反过来对正在运行的程序语义有所影响。annotation可以从源文件、class文件或者以在运行时反射的多种方式被读取。<br>当然annotation在某种程度上使javadoc tag更加完整。一般情况下，如果这个标记对java文档产生影响或者用于生成java文档的话，它应该作为一个javadoc tag；否则将作为一个annotation。<br><br><span style="color: blue;">三、Annotation使用方法：</span><br><br><b>1。类型声明方式：</b><br>通常，应用程序并不是必须定义annotation类型，但是定义annotation类型并非难事。Annotation类型声明于一般的接口声明极为类似，区别只在于它在interface关键字前面使用“@”符号。<br>annotation
类型的每个方法声明定义了一个annotation类型成员，但方法声明不必有参数或者异常声明；方法返回值的类型被限制在以下的范围：
primitives、String、Class、enums、annotation和前面类型的数组；方法可以有默认值。<br><br>下面是一个简单的annotation类型声明：<br><b>清单1:</b><br><pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;/**<br>&nbsp;&nbsp;&nbsp;&nbsp; * Describes the Request-For-Enhancement(RFE) that led<br>&nbsp;&nbsp;&nbsp;&nbsp; * to the presence of the annotated API element.<br>&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;public @interface RequestForEnhancement {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int&nbsp;&nbsp;&nbsp;&nbsp;id();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String synopsis();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String engineer() default "[unassigned]"; <br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String date();&nbsp;&nbsp;&nbsp;&nbsp;default "[unimplemented]"; <br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></pre><br>代码中只定义了一个annotation类型RequestForEnhancement。<br><br><b>2。修饰方法的annotation声明方式：</b><br>annotation
是一种修饰符，能够如其它修饰符（如public、static、final）一般使用。习惯用法是annotaions用在其它的修饰符前面。
annotations由“@+annotation类型+带有括号的成员-值列表”组成。这些成员的值必须是编译时常量（即在运行时不变）。<br><br>A：下面是一个使用了RequestForEnhancement annotation的方法声明：<br><b>清单2:</b><br><pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;@RequestForEnhancement(<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;id&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; = 2868724,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;synopsis = "Enable time-travel",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;engineer = "Mr. Peabody",<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;date&nbsp;&nbsp;&nbsp;&nbsp; = "4/1/3007"<br>&nbsp;&nbsp;&nbsp;&nbsp;)<br>&nbsp;&nbsp;&nbsp;&nbsp;public static void travelThroughTime(Date destination) { ... }<br></pre><br><br>B：当声明一个没有成员的annotation类型声明时，可使用以下方式：<br><b>清单3:</b><br><pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;/**<br>&nbsp;&nbsp;&nbsp;&nbsp; * Indicates that the specification of the annotated API element<br>&nbsp;&nbsp;&nbsp;&nbsp; * is preliminary and subject to change.<br>&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;public @interface Preliminary { }<br></pre><br><br>作为上面没有成员的annotation类型声明的简写方式：<br><b>清单4:</b><br><pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;@Preliminary public class TimeTravel { ... }<br></pre><br><br>C：如果在annotations中只有唯一一个成员，则该成员应命名为value：<br><b>清单5:</b><br><pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;/**<br>&nbsp;&nbsp;&nbsp;&nbsp; * Associates a copyright notice with the annotated API element.<br>&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;public @interface Copyright {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String value();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></pre><br><br>更为方便的是对于具有唯一成员且成员名为value的annotation（如上文），在其使用时可以忽略掉成员名和赋值号（=）：<br><b>清单6:</b><br><pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;@Copyright("2002 Yoyodyne Propulsion Systems")<br>&nbsp;&nbsp;&nbsp;&nbsp;public class OscillationOverthruster { ... }<br></pre><br><br><b>3。一个使用实例：</b><br>结合上面所讲的，我们在这里建立一个简单的基于annotation测试框架。首先我们需要一个annotation类型来表示某个方法是一个应该被测试工具运行的测试方法。<br><b>清单7:</b><br><pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;import java.lang.annotation.*;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;/**<br>&nbsp;&nbsp;&nbsp;&nbsp; * Indicates that the annotated method is a test method.<br>&nbsp;&nbsp;&nbsp;&nbsp; * This annotation should be used only on parameterless static methods.<br>&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;@Retention(RetentionPolicy.RUNTIME)<br>&nbsp;&nbsp;&nbsp;&nbsp;@Target(ElementType.METHOD)<br>&nbsp;&nbsp;&nbsp;&nbsp;public @interface Test { }<br></pre><br><br>值得注意的是annotaion类型声明是可以标注自己的，这样的annotation被称为“meta-annotations”。<br><br>在
上面的代码中，@Retention(RetentionPolicy.RUNTIME)这个meta-annotation表示了此类型的
annotation将被虚拟机保留使其能够在运行时通过反射被读取。而@Target(ElementType.METHOD)表示此类型的
annotation只能用于修饰方法声明。<br><br>下面是一个简单的程序，其中部分方法被上面的annotation所标注：<br><b>清单8:</b><br><pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;public class Foo {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Test public static void m1() { }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void m2() { }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Test public static void m3() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new RuntimeException("Boom");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void m4() { }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Test public static void m5() { }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void m6() { }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Test public static void m7() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new RuntimeException("Crash");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void m8() { }<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>Here is the testing tool:<br><br>&nbsp;&nbsp;&nbsp;&nbsp;import java.lang.reflect.*;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;public class RunTests {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static void main(String[] args) throws Exception {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int passed = 0, failed = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (Method m : Class.forName(args[0]).getMethods()) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (m.isAnnotationPresent(Test.class)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m.invoke(null);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; passed++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (Throwable ex) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.printf("Test %s failed: %s %n", m, ex.getCause());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; failed++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.printf("Passed: %d, Failed %d%n", passed, failed);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br></pre><br><br>这
个程序从命令行参数中取出类名，并且遍历此类的所有方法，尝试调用其中被上面的测试annotation类型标注过的方法。在此过程中为了找出哪些方法被
annotation类型标注过，需要使用反射的方式执行此查询。如果在调用方法时抛出异常，此方法被认为已经失败，并打印一个失败报告。最后，打印运行
通过/失败的方法数量。<br>下面文字表示了如何运行这个基于annotation的测试工具：<br><br><b>清单9:</b><br><pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;$ java RunTests Foo<br>&nbsp;&nbsp;&nbsp;&nbsp;Test public static void Foo.m3() failed: java.lang.RuntimeException: Boom <br>&nbsp;&nbsp;&nbsp;&nbsp;Test public static void Foo.m7() failed: java.lang.RuntimeException: Crash <br>&nbsp;&nbsp;&nbsp;&nbsp;Passed: 2, Failed 2<br></pre><br><br><span style="color: blue;">四、Annotation分类：</span><br><br>根据annotation的使用方法和用途主要分为以下几类：<br><br><b>1。内建Annotation——Java5.0版在java语法中经常用到的内建Annotation：</b><br>@Deprecated用于修饰已经过时的方法；<br>@Override用于修饰此方法覆盖了父类的方法（而非重载）；<br>@SuppressWarnings用于通知java编译器禁止特定的编译警告。<br><br>下面代码展示了内建Annotation类型的用法：<br><b>清单10:</b><br><pre title="pre code" class="overflow"><br>package com.bjinfotech.practice.annotation;<br><br>/**<br> * 演示如何使用java5内建的annotation<br> * 参考资料：<br> * http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html<br> * http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html<br> * http://mindprod.com/jgloss/annotations.html<br> * @author cleverpig<br> *<br> */<br>import java.util.List;<br><br>public class UsingBuiltInAnnotation {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//食物类<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class Food{}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//干草类<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class Hay extends Food{}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//动物类<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class Animal{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Food getFood(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return null;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//使用Annotation声明Deprecated方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Deprecated<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void deprecatedMethod(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//马类-继承动物类<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class Horse extends Animal{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//使用Annotation声明覆盖方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@Override<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Hay getFood(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;return new Hay();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//使用Annotation声明禁止警告<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@SuppressWarnings({"deprecation","unchecked"})<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;void callDeprecatedMethod(List horseGroup){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Animal an=new Animal();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;an.deprecatedMethod();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;horseGroup.add(an);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></pre><br><br><b>2。开发者自定义Annotation：由开发者自定义Annotation类型。</b><br>下面是一个使用annotation进行方法测试的sample：<br><br>AnnotationDefineForTestFunction类型定义如下：<br><b>清单11:</b><br><pre title="pre code" class="overflow"><br>package com.bjinfotech.practice.annotation;<br><br>import java.lang.annotation.*;<br>/**<br> * 定义annotation<br> * @author cleverpig<br> *<br> */<br>//加载在VM中，在运行时进行映射<br>@Retention(RetentionPolicy.RUNTIME)<br>//限定此annotation只能标示方法<br>@Target(ElementType.METHOD)<br>public @interface AnnotationDefineForTestFunction{}<br></pre><br><br>测试annotation的代码如下：<br><br><b>清单12:</b><br><pre title="pre code" class="overflow"><br>package com.bjinfotech.practice.annotation;<br><br>import java.lang.reflect.*;<br><br>/**<br> * 一个实例程序应用前面定义的Annotation：AnnotationDefineForTestFunction<br> * @author cleverpig<br> *<br> */<br>public class UsingAnnotation {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@AnnotationDefineForTestFunction public static void method01(){}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void method02(){}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;@AnnotationDefineForTestFunction public static void method03(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new RuntimeException("method03");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void method04(){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new RuntimeException("method04");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] argv) throws Exception{<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;int passed = 0, failed = 0;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//被检测的类名<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String className="com.bjinfotech.practice.annotation.UsingAnnotation";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;//逐个检查此类的方法，当其方法使用annotation声明时调用此方法<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (Method m : Class.forName(className).getMethods()) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (m.isAnnotationPresent(AnnotationDefineForTestFunction.class)) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; m.invoke(null);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; passed++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (Throwable ex) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.printf("测试 %s 失败: %s %n", m, ex.getCause());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; failed++;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.printf("测试结果： 通过: %d, 失败： %d%n", passed, failed);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>}<br></pre><br><br><b>3。使用第三方开发的Annotation类型</b><br>这也是开发人员所常常用到的一种方式。比如我们在使用Hibernate3.0时就可以利用Annotation生成数据表映射配置文件，而不必使用Xdoclet。<br><br><span style="color: blue;">五、总结：</span><br><br>1。
前面的文字说明了annotation的使用方法、定义方式、分类。初学者可以通过以上的说明制作简单的annotation程序，但是对于一些高级的
annotation应用（例如使用自定义annotation生成javabean映射xml文件）还需要进一步的研究和探讨。<br><br>2。同时，annotation运行存在两种方式：运行时、编译时。上文中讨论的都是在运行时的annotation应用，但在编译时的annotation应用还没有涉及，因为编译时的annotation要使用annotation processing tool。<br><br>涉及以上2方面的深入内容，作者将在后文《Java Annotation高级应用》中谈到。<br><br><span style="color: blue;">六、参考资源：</span><br>·Matrix-Java开发者社区:<a href="http://www.matrix.org.cn/" target="_new">http://www.matrix.org.cn</a><br>·<a href="http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html" target="_new">http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html</a><br>·<a href="http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html" target="_new">http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html</a><br>·<a href="http://www.javaworld.com/javaworld/jw-03-2005/jw-0321-toolbox.html" target="_new">http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html</a><br>·<a href="http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html" target="_new">http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html</a><br>·作者的Blog:<a href="http://blog.matrix.org.cn/page/cleverpig" target="_new">http://blog.matrix.org.cn/page/cleverpig</a><br><img src ="http://www.blogjava.net/ericwang/aggbug/23743.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-12-13 23:22 <a href="http://www.blogjava.net/ericwang/archive/2005/12/13/23743.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>将 Java Swing 应用程序连接到 Geronimo 服务器</title><link>http://www.blogjava.net/ericwang/archive/2005/12/09/23229.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Fri, 09 Dec 2005 15:22:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/12/09/23229.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/23229.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/12/09/23229.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/23229.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/23229.html</trackback:ping><description><![CDATA[<table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr valign="top"><td width="100%"><h1>将 Java Swing 应用程序连接到 Geronimo 服务器</h1><p id="subtitle"><em>创建可以与 Geronimo EJB 应用程序对话的独立客户端</em></p><img class="display-img" src="http://www.ibm.com/i/c.gif" alt="" height="6" width="1"></td><td class="no-print" width="192"><img src="http://www-128.ibm.com/developerworks/i/dw.gif" alt="developerWorks" height="18" width="192"></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr valign="top"><td width="10"><img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="10"></td><td width="100%"><table class="no-print" align="right" border="0" cellpadding="0" cellspacing="0" width="160"><tbody><tr><td width="10"><img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="10"></td><td><table border="0" cellpadding="0" cellspacing="0" width="150"><tbody><tr><td class="v14-header-1-small">文档选项</td></tr></tbody></table><table class="v14-gray-table-border" border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="no-padding" width="150"><img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="8"><input name="body" value="在前两篇 developerWorks 文章中，作者 Neal Sanche 使用简单的电话簿应用程序来展示如何将 Apache Geronimo 应用服务器连接到数据库，以及如何使用 Geronimo 创建带有 Enterprise JavaBeans (EJB) 后端的基于 Struts 的 Web 应用程序。本文进一步使用电话簿应用程序来展示如何创建独立客户端应用程序以操作电话号码数据库。您还将学习如何配置 Geronimo 以允许来自特定客户端的安全访问。" type="hidden"><input value="将 Java Swing 应用程序连接到 Geronimo 服务器" name="subject" type="hidden"><input value="cn" name="lang" type="hidden"><table border="0" cellpadding="0" cellspacing="0" width="143"><form name="email" action="https://www-130.ibm.com/developerworks/secure/email-it.jsp"></form><script language="JavaScript" type="text/javascript">
<!--
document.write('<tr valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8" height="1" alt=""/></td><td width="16"><img src="//www.ibm.com/i/v14/icons/em.gif" height="16" width="16" vspace="3" alt="将此页作为电子邮件发送" /></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr>');
//-->
</script><tbody><tr valign="top"><td width="8"><img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="8"></td><td width="16"><img src="http://www.ibm.com/i/v14/icons/em.gif" alt="将此页作为电子邮件发送" height="16" vspace="3" width="16"></td><td width="122"><p><a class="smallplainlink" href="javascript:document.email.submit();"><b>将此页作为电子邮件发送</b></a></p></td></tr><noscript><tr
valign="top"><td width="8"><img src="//www.ibm.com/i/c.gif" width="8"
height="1" alt="" /></td><td width="16"><img
src="//www.ibm.com/i/c.gif" height="16" width="16" alt="" /></td><td
width="122" class="small"><p><span class="ast">未显示需要 JavaScript
的文档选项</span></p></td></tr></noscript><tr valign="top"><td width="8"><img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="8"></td><td width="16"><img src="http://www.ibm.com/i/v14/icons/fw_bold.gif" alt="" border="0" height="16" vspace="3" width="16"></td><td width="122"><p><a class="smallplainlink" href="javascript:void forumWindow()"><b>讨论</b></a></p></td></tr><tr valign="top"><td width="8"><img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="8"></td><td width="16"><img src="http://www.ibm.com/i/v14/icons/dn.gif" alt="" border="0" height="16" vspace="3" width="16"></td><td width="122"><p><a class="smallplainlink" href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#download"><b>样例代码</b></a></p></td></tr></tbody></table></td></tr></tbody></table><br><table border="0" cellpadding="0" cellspacing="0" width="150"><tbody><tr><td class="v14-header-1-small">对此页的评价</td></tr></tbody></table><table class="v14-gray-table-border" border="0" cellpadding="0" cellspacing="0"><tbody><tr><td class="no-padding" width="150"><table border="0" cellpadding="0" cellspacing="0" width="143"><tbody><tr valign="top"><td width="8"><img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="8"></td><td><img src="http://www.ibm.com/i/v14/icons/d_bold.gif" alt="" border="0" height="16" vspace="3" width="16"></td><td width="125"><p><a class="smallplainlink" href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#rate"><b>帮助我们改进这些内容</b></a></p></td></tr></tbody></table></td></tr></tbody></table><br></td></tr></tbody></table><p>级别: 初级</p><p><a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#author">Neal Sanche </a>, Java 开发人员, Pure Technologies<br></p><p>2005 年  8 月  24 日</p><blockquote>在前两篇 developerWorks 文章（参阅 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#resources">参考资料</a>）
中，作者 Neal Sanche 使用简单的电话簿应用程序来展示如何将 Apache Geronimo 应用服务器连接到数据库，以及如何使用
Geronimo 创建具有 Enterprise JavaBeans (EJB) 后端的基于 Struts 的 Web
应用程序。本文进一步使用电话簿应用程序来展示如何创建独立客户端应用程序以操作电话号码数据库。您还将学习如何配置 Geronimo
以允许来自特定客户端的安全访问。</blockquote><p><a name="IDADD0KB"><span class="atitle">简介</span></a></p><p>本文将展示如何开发可以与运行在 Geronimo 应用服务器内部的 EJB 应用程序通信的独立（胖）客户端。基于我的前两篇文章 ——“<a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-jdbc/">将数据库连接到 Geronimo 应用服务器的三种方法</a>”（developerWorks，2005 年 6 月）和“<a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-ejbweb/">利用 Geronimo 深入 EJB Web 应用程序</a>”
（developerWorks，2005 年 7 月）—— 本文向您展示一个连接到使用 Geronimo EJB
应用程序构建的小型电话簿数据库的 Swing
客户端。您将阅读简要的设计说明，然后阅读有关运行该应用程序所需的客户端库的信息。接下来我将介绍联系服务器并对服务器上远程无状态会话 bean
执行操作的方法。最后，您将学习如何开发、编译并运行客户端应用程序，以及如何配置服务器以允许来自网络中特定客户端的安全访问。</p><p>要最有效地利用本文，您需要熟悉用于构建 Java 桌面应用程序的 Java Swing API 以及 Apache Maven 构建系统（参阅 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#Resources">参考资料</a> 以链接到 Maven Web 站点）。</p><br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#main"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="IDAYD0KB"><span class="atitle">设计概述</span></a></p><p>首先简要介绍一下示例应用程序设计 —— 一个描述电话簿客户端应用程序的统一建模语言 (Unified Modeling Language, UML) 部署图 —— 如 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#fig1">图 1</a> 所示。客户端应用程序通过其 EJB 端口连接到 Geronimo，并与 PhoneBook Session EJB 对话以通过 PhoneBook Entry Container-Managed Persistence (CMP) 操作数据库中的数据。</p><br><a name="fig1"><b>图 1. 电话簿客户端部署图</b></a><br><img alt="电话簿客户端部署图" src="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/fig01.gif" height="317" width="484"><br><p>Geronimo 的默认发行版对 EJB 端口有限制。仅当客户端应用程序运行在同一机器上并且通过环回地址（localhost 或 127.0.0.1） 连接时才能连接到该端口。本文稍后的 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#configuring">配置 Geronimo 的 EJB 端口</a> 一节提供了有关如何让其他机器上的客户端访问服务器的详细信息。</p><br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#main"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="IDAUE0KB"><span class="atitle">用于连接到 Geronimo 的客户端库</span></a></p><p>要让客户端应用程序能够连接到 Geronimo 的 EJB 端口并与 EJB 层通信，客户端类路径中必须要有下列 Java 库：</p><ul><li>geronimo-spec-j2ee-1.4-rc4.jar</li><li>geronimo-kernel-1.0-SNAPSHOT.jar</li><li>geronimo-j2ee-1.0-SNAPSHOT.jar</li><li>geronimo-security-1.0-SNAPSHOT.jar</li><li>cglib-nodep-2.1.jar</li><li>openejb-core-2.0-SNAPSHOT.jar</li></ul><table align="right" border="0" cellpadding="0" cellspacing="0" width="40%"><tbody><tr><td width="10"><img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="10"></td><td><table border="1" cellpadding="5" cellspacing="0" width="100%"><tbody><tr><td bgcolor="#eeeeee"><a name="IDACF0KB"><b>对构建结构的备注</b></a><br><p>因为对前面文章中代码的构建结构进行了一些小的修改，所以本文的代码包含了电话簿应用程序和电话簿客户端应用程序。<a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#download">下载</a> 包括了有关如何使用 Maven 构建这两个应用程序的详细说明。</p></td></tr></tbody></table></td></tr></tbody></table><p>从源代码编译 Geronimo 时，当您使用 Maven 构建脚本来编译电话簿客户端应用程序时，这些库被放置到本地 Maven 资源库中且可供访问。您可以在 project.xml 文件的依赖关系部分中查看所有这些库位于 Maven 资源库的哪个位置。</p><p>其中一些库在客户端与服务器的通信中起着非常重要的作用。Geronimo 使用 CGLib 库来执行动态代理生成。这使得服务器动态生成远程调用服务器端组件的代码。如果在调试器中检查客户端上 <code>InitialContext</code> 对象的 <code>lookup()</code> 方法返回的一个对象，可以看到动态生成的对象的类名包括 <code>CGLib</code>。geronimo-spec-j2ee.jar
文件包含所有的 Sun Java 2 Platform, Enterprise Edition (J2EE)
接口和类。没有该文件，客户端将无法理解任何动态代理实例。openejb-core.jar 文件是与服务器的 EJB
端口进行对话所必需的。用于在 Geronimo 服务器中执行远程目录查询的 Java Naming and Directory
Interface (JNDI) 类就在该 .jar 文件中。最后的三个 .jar 文件提供了其他支持类，比如与 Geronimo
对话的安全主体。</p><br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#main"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="IDA2F0KB"><span class="atitle">执行远程会话本地查询</span></a></p><p>客
户端通信部分的实现十分简单。将客户端连接到服务器时，Geronimo 与其他任何 J2EE 服务器没有任何不同，遵守健全的通过 JNDI
查询和远程方法调用 (RMI) 进行的通信标准。JNDI 查询是获得对远程对象的引用的标准访问。要通过 JNDI 进行连接，必须使用大量特定于
Geronimo 的属性来创建 <code>InitialContext</code> 实例，该实例用于执行查询。 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#listing1">清单 1</a> 展示了有关如何创建会话的示例。</p><br><a name="listing1"><b>清单 1. 创建到 Geronimo 托管会话 bean 的远程会话</b></a><br><table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="70%"><tbody><tr><td><pre><code class="section">import java.util.Properties;<br>import javax.naming.Context;<br>import javax.naming.InitialContext;<br>import javax.rmi.PortableRemoteObject;<br><br>public void Connect() {<br>      String hostName = getHostName();<br>      String port = getPort();<br><br>      Properties props = new Properties();<br><br>      props.setProperty("java.naming.factory.initial",<br>                  "org.openejb.client.RemoteInitialContextFactory");<br>      props.setProperty("java.naming.provider.url", hostName+":"+port);<br>      props.setProperty("java.naming.security.principal", "username");<br>      props.setProperty("java.naming.security.credentials", "passwd");<br><br>      Context ic;<br>      try {<br>            ic = new InitialContext(props);<br>            PhoneBookSessionHome sessionHome = (PhoneBookSessionHome)<br>                  PortableRemoteObject.narrow(<br>                  ic.lookup(PhoneBookSessionHome.JNDI_NAME),<br>                  PhoneBookSessionHome.class);<br>            phoneBookSession = sessionHome.create();<br>      } catch (Throwable ex) {<br>            ex.printStackTrace();<br>      } finally {<br>            if (ic != null) {<br>                  ic.close();<br>            }<br>      }<br>}<br></code></pre></td></tr></tbody></table><br><p>如 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#listing1">清单 1</a> 所示，创建了 <code>Properties</code> 对象并设置了四个属性。第一个并且是最重要的属性是 java.naming.factory.initial 属性，它必须设置为 <code>org.openejb.client.RemoteInitialContextFactory</code>。其他属性指定提供者 URL 以及安全主体和凭证。提供者 URL 是用冒号隔开的主机名和端口。</p><p>前已提及，EJB 端口当前只接受从客户端连接到 127.0.0.1 或 localhost 的连接。默认端口是 4201。尽管如此，主机名和端口都可以进行配置。有关详细信息，请参阅 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#configuring">配置 Geronimo 的 EJB 端口</a> 一节。</p><p>创建好属性之后，就可以创建并使用 <code>InitialContext</code> 实例了。通过将属性传递给构造函数完成这一操作。创建好实例之后，可以执行查询。<a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#listing1">清单 1</a> 包括一行复杂的代码，该行执行查询并对结果执行 <code>PortableRemoteObject.narrow()</code>。对于向用户隐藏有关协议传输 —— RMI 或者可能是 Internet Inter-ORB Protocol (IIOP)—— 的详细信息，这是必需的。完成之后，远程会话就可供使用了。在 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#listing1">清单 1</a> 中，该行只创建了新 PhoneBookSession，并将其存储在一个字段中以供将来使用。</p><p>具有对远程会话的引用之后，该引用可用于所有操纵电话簿数据库信息的操作。现在只需要一个应用程序来练习该远程会话。</p><br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#main"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="IDA1H0KB"><span class="atitle">客户端的设计和开发</span></a></p><p>现
在我们来深入研究一个小 Swing 应用程序的设计和开发，这个小应用程序用来浏览、创建、删除和修改电话簿数据库条目。我尽量将 Swing
行话减到最少，以防您比较熟悉的是另一种 GUI 技术，比如 Standard Widget Toolkit
(SWT)。事实上，如果需要的话，应用程序架构已经使得将显示从应用程序内部逻辑分离出来并将其连接到另一种 GUI 技术变得非常容易。</p><p>应用程序架构如 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#fig2">图 2</a> 所示，这是一个详细显示应用程序静态结构的 UML 类图。</p><br><a name="fig2"><b>图 2. 客户端应用程序的 UML 类图</b></a><br><img alt="客户端应用程序的 UML 类图" src="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/fig02.gif" height="473" width="508"><br><p><a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#fig2">图 2</a> 中带绿色阴影的类是主要的应用程序类。<code>Main</code> 类是包含菜单和拆分窗格（其左侧是 <code>PhoneNumberListPanel</code>，右侧是 <code>PhoneBookEditorPanel</code>）的框架。该菜单还允许用户设置连接到哪个服务器的首选项，连接到服务器，并退出程序。<code>Application</code> 类是一个单身类（singleton class），用作应用程序的所有操作的控制器。它是惟一一个执行 EJB 操作的类，并保存对 PhoneBookSession 无状态会话 bean 的引用。</p><p>橙色的两个接口定义系统中的主要事件。每当 <code>Application</code> 决定电话号码列表需要更新时，就会激活 <code>DataChangeEvent</code>。<code>PhoneNumberListModel</code> 注册该事件。因为它是 <code>PhoneNumberListPanel</code> 中的主要数据模型列表视图，所以列表是通过模型更改来更新的。这与 Swing 应用程序的设计方法一致。</p><p><code>PhoneNumberListPanel</code> 和 <code>PhoneBookEditorPanel</code> 类都实现 <code>PhoneBookSelectionListener</code> 接口并注册来自 <code>Application</code> 单身类的事件。当它们收到事件时，它们相应地更新当前的选择。如果是 <code>PhoneBookEditorPanel</code>，当前选择导致 Name 和 Number 字段由来自当前选择的电话簿条目中的数据填充。</p><p>如果希望节省编写用户接口代码的时间，通常可以在 Internet 上找到高质量的免费工具。优秀的工具有 JGoodies Forms 1.0.5 和 FormLayoutMaker，FormLayoutMaker 是一个用于可视化创建窗体的小工具（参阅 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#resources">参考资料</a> 以获得到这些工具的链接）。FormLayoutMaker 工具生成代表 JGoodies 窗体布局约束的 XML 文件。这些工具帮助我快速创建了 Phone Number 编辑面板和 Preferences 面板的窗体。</p><br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#main"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="IDAXCGLB"><span class="atitle">构建应用程序</span></a></p><p>编
译应用程序有两种方法。我使用 Eclipse Visual Editor (VE) 插件 1.2 版本在 Eclipse
中开发了该应用程序。它生成应用程序的大部分代码框架，但它是以一种非入侵的方式完成的（没有代码标记和不可访问的代码块），所以如果没有安装 VE
的话也应该没有问题。可以只加载项目并尝试运行它。</p><p>您可能需要设置 MAVEN_REPO 构建变量以指向本地 Maven 资源库。还需要构建与本文一起提供的源代码中包括的 Geronimo 和 PhoneBook 服务器应用程序（参阅 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#download">下载</a>）。这是因为，要编译客户端应用程序，包含服务器应用程序中 EJB 接口的 .jar 文件必须发布到本地 Maven 资源库中。PhoneBook 的 Maven 构建脚本通过下列 Maven 构建脚本段完成该操作：</p><br><a name="listing2"><b>清单 2. Maven 构建脚本段</b></a><br><table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="70%"><tbody><tr><td><pre><code class="section">&lt;goal name="client" prereqs="java:compile"&gt;<br>      &lt;ant:jar destfile="target/${pom.artifactId}-client.jar"&gt;<br>            &lt;fileset dir="target/classes"&gt;<br>                  &lt;include name="**/*.class"/&gt;<br>            &lt;/fileset&gt;<br>      &lt;/ant:jar&gt;<br>      &lt;artifact:install artifact="target/${pom.artifactId}-client.jar"<br>            type="jar" project="${pom}"/&gt;<br>&lt;/goal&gt;<br></code></pre></td></tr></tbody></table><br><p>用于构建应用程序的第二种方法就是使用
Maven。在 PhoneBook 目录中解压文件并运行 maven 命令。然后在 PhoneBookClient
目录中进行相同操作。如果一切顺利，就已经在目标子目录中创建了 UberJar —— 一个包含运行客户端所需的所有内容的 JAR 文件。</p><p>两种构建方法运行得同样好。使用 Maven 方法的优点是如果您尚未下载依赖关系，则它会自动从 ibiblio Web 站点（参阅 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#resources">参考资料</a>）上的远程 Maven 资源库中下载这些依赖关系。所以如果 Eclipse 中的依赖关系有问题，就在项目上至少运行一次 Maven 来校正缺少的库。</p><br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#main"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="IDATDGLB"><span class="atitle">运行应用程序</span></a></p><p>确保 PhoneBook 服务器应用程序部署到 Geronimo 服务器中且正在运行。然后键入下列命令：</p><table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="70%"><tbody><tr><td><pre><code class="section">java -jar phonebook-client-uber.jar</code></pre></td></tr></tbody></table><br><p>将会看到应用程序弹出，如 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#fig3">图 3</a> 所示。</p><br><a name="fig3"><b>图 3. Geronimo 电话簿客户端应用程序</b></a><br><img alt="Geronimo 电话簿客户端应用程序" src="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/fig03.jpg" height="277" width="563"><br><p>首先，从 <b>File</b> 菜单中选择 <b>Connect</b>。如果是连接到 localhost:4201 端口，则应该获得一个连接；否则，控制台窗口将会显示错误消息。可以通过选择 <b>Edit &gt; Preferences</b>、更改信息并尝试重新连接来更改连接的服务器和端口。一旦连接上之后，可以通过在电话号码编辑器中键入姓名和号码并单击 <b>Save</b> 来创建新记录。该记录将显示在姓名列表中。通过选择条目并单击 <b>Delete</b> 来删除条目。通过选择条目、进行修改并单击 <b>Save</b> 来更改条目。</p><br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#main"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="configuring"><span class="atitle">配置 Geronimo 的 EJB 端口</span></a></p><p>当
前，配置 Geronimo 的 EJB 端口的方法需要编辑 XML 文件，然后重新编译 Geronimo。Tom McQueeney 的大型
Geronimo Live blog 上的一篇短文清楚介绍了如何使用
openejb\modules\assembly\src\plan\j2ee-server-plan.xml 文件更改 Geronimo
Jetty 监听端口的详细信息（参阅 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#resources">参考资料</a> 以链接到该 blog）。同一文件还包含 EJB 端口的配置信息（参阅 <a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#list3">清单 3</a>）。</p><br><a name="list3"><b>清单 3. j2ee-server-plan.xml 文件中的代码段</b></a><br><table bgcolor="#cccccc" border="1" cellpadding="5" cellspacing="0" width="70%"><tbody><tr><td><pre><code class="section">&lt;gbean name="EJBNetworkService" <br>class="org.openejb.server.StandardServiceStackGBean"&gt;<br>        &lt;attribute name="name"&gt;EJB&lt;/attribute&gt;<br>        &lt;attribute name="port"&gt;4201&lt;/attribute&gt;<br>        &lt;attribute name="address"&gt;127.0.0.1&lt;/attribute&gt;<br>        &lt;attribute name="allowHosts"&gt;127.0.0.1&lt;/attribute&gt;<br>        &lt;attribute name="logOnSuccess"&gt;HOST,NAME,THREADID,USERID&lt;/attribute&gt;<br>        &lt;attribute name="logOnFailure"&gt;HOST,NAME&lt;/attribute&gt;<br>        &lt;reference name="Executor"&gt;&lt;name&gt;DefaultThreadPool&lt;/name&gt;&lt;/reference&gt;<br>        &lt;reference name="Server"&gt;<br>            &lt;gbean-name&gt;openejb.server:name=EJBServer,*&lt;/gbean-name&gt;<br>        &lt;/reference&gt;<br>&lt;/gbean&gt;<br></code></pre></td></tr></tbody></table><br><p>您需要编辑 j2ee-server-plan.xml 文件并更改 <code>allowHosts</code> 属性。Geronimo 支持许多不同类型的地址。必须用下列模式之一输入逗号分隔的地址列表：</p><ul><li>最后一格为 0 的 IP 地址。例如，192.168.10.0 允许 192.168.10 网络上的任何机器与服务器通信。</li><li>任何完全指定的 IP 地址。</li><li>分解的 IP 地址。这是一种特殊模式，允许指定地址的网络部分和以大括号扩住的主机地址列表。例如，192.168.10.{5,6,7} 允许以下三个机器访问服务器：192.168.10.5、192.168.10.6 和 192.168.10.7。</li><li>网络掩码 IP 地址。这是网络管理员熟悉的一种地址。基于精确的位模式匹配规则（超出本文范围），IP 地址与网络掩码相匹配。例如，192.168.255.255 允许 192.168.* 网络中的所有地址访问服务器。</li><li>准确的 IPv6 地址。当将来的 IP 网络到来时，Geronimo 将准备好服务，允许列出特定的 IP 地址。</li><li>网络掩码 IPv6 地址。</li></ul><p>有
关服务器接受的特定模式的详细信息，请咨询源代码文件 —— ServiceAccessController.java —— 位于来源的
openejb\modules\core\src\java\org\openejb\server
目录中。在此将会找到与支持的每个地址类型相匹配的明确的正则表达式。</p><p>对 j2ee-server-plan.xml 文件进行修改之后，重新编译并更新服务器部署，您将具有一个专门满足您需要的服务器。（如果只想查看针对同一机器上的服务器运行的客户端，则无需这样做。默认情况下，Geronimo 被配置来完成这些操作。）</p><br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#main"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="IDADGGLB"><span class="atitle">结束语</span></a></p><p>本
文提供了一个构建独立（胖）客户端的具体示例，该客户端可以与运行在 Geronimo 应用服务器内部的 EJB
应用程序进行对话。Geronimo 团队已经认真实现了健全的标准，推广用简单的 JNDI 查询方法获得与无状态会话 bean
的远程连接。如果只想让简单的应用程序运行，那么这是一个好消息，因为它只需要您编写少量代码。</p><p>按照我所介绍的模式，您能够将许多比较大的
数据库连接到与 Geronimo 服务器位于同一机器上的客户端应用程序。使用本文提供的指示，您还能够配置 Geronimo
服务器以允许从连接到您的网络或 Internet 上的其他机器访问服务器 EJB 端口。不妨尝试一下。</p><br><br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#main"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><span class="atitle"><a name="download">下载</a></span></p><table class="data-table-1" border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><th>描述</th><th>名字</th><th style="text-align: right;">大小</th><th>&nbsp;下载方法</th></tr><tr><td class="tb-row">Source code for the phonebook application</td><td nowrap="nowrap">PhonebookClient.zip</td><td style="text-align: right;" nowrap="nowrap">227 KB</td><td nowrap="nowrap">
&nbsp;<a class="fbox" href="ftp://www6.software.ibm.com/software/developer/library/os-ag-swing/PhonebookClient.zip"><b>FTP</b></a></td></tr></tbody></table><table border="0" cellpadding="0" cellspacing="0"><tbody><tr valign="top"><td colspan="5"><img src="http://www.ibm.com/i/c.gif" alt="" border="0" height="12" width="12"></td></tr><tr><td><img src="http://www.ibm.com/i/v14/icons/fw.gif" alt="" height="16" width="16"></td><td><a href="http://www-128.ibm.com/developerworks/cn/whichmethod.html" class="fbox">关于下载方法的信息</a></td><td><img src="http://www.ibm.com/i/c.gif" alt="" height="1" width="50"></td><td><img src="http://www.ibm.com/i/v14/icons/sout.gif" alt="" height="16" width="16"></td><td><a href="http://www.adobe.com/products/acrobat/readstep2.html" class="fbox">获取 Adobe® Reader®</a></td></tr></tbody></table><br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#main"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="resources"><span class="atitle">参考资料 </span></a></p><ul><li>加入本文的<a href="javascript:void forumWindow()">论坛</a> 。(您也可以通过点击文章顶部或者底部的论坛链接参加讨论。)<br><br></li><li> 您可以参阅本文在 developerWorks 全球站点上的 <a href="http://www.ibm.com/developerworks/opensource/library/os-ag-swing/" target="_blank">英文原文</a>。<br><br></li><li>访问 <a href="http://geronimo.apache.org/">Apache Geronimo 官方网站</a> 获得文档、新闻，并下载信息。<br><br></li><li>阅读“<a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-jdbc/">将数据库连接到 Geronimo 应用服务器的三种方法</a>”（developerWorks，2005 年 6 月），学习如何使用三种不同的 JDBC 数据源将数据库连接到 Geronimo。<br><br></li><li>阅读“<a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-ejbweb/">利用 Geronimo 深入 EJB Web 应用程序
</a>”（developerWorks，2005 年 7 月），学习如何使用 Geronimo 开发数据库应用程序。<br><br></li><li>阅读“<a href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-client/">创建 Geronimo 客户端应用程序</a>”（developerWorks，2005 年 6 月），深入了解用 Geronimo 生成客户端的备选方法。<br><br></li><li>访问 <a href="http://maven.apache.org/">Apache Maven Web 站点</a> 下载信息和文档。Maven 是用于自动化 Geronimo 应用程序部署的软件项目管理和理解工具。 <br><br></li><li>访问 <a href="http://www.ibiblio.org/">iBiblio repository</a>。<br><br></li><li>在 <a href="http://maven.ozacc.com/search">Maven Repo Search</a> 站点上搜索 Maven 资源库。<br><br></li><li>使用 Sing Li 的“<a href="http://www-128.ibm.com/developerworks/cn/java/j-geron1/index.html">Geronimo！第 1 部分: 支持 J2EE 1.4 引擎</a>”（developerWorks，2005 年 5 月）作为向导熟悉 Geronimo，然后在“<a href="http://www-128.ibm.com/developerworks/cn/java/j-geron2/index.html">Geronimo！第 2 部分: 驯服 J2EE 1.4 这匹野马</a>”（developerWorks，2005 年 5 月）中实践。<br><br></li><li>阅读 Tom McQueeney 的 Geronimo Live 博客中的“<a href="http://jroller.com/page/GeronimoLive?anchor=geronimo_changing_jetty_listen_port">Geronimo: Changing Jetty listen port from default 8080</a>”。<br><br></li><li>获得 <a href="http://www.jgoodies.com/freeware/forms/index.html">JGoodies Forms 1.0.5</a>，这是一个免费的布局管理器，用于创建外观漂亮的窗体。<br><br></li><li>获得 <a href="http://formlayoutmaker.sourceforge.net/">FormLayoutMaker rc7</a>，这是一个拖放启用的窗体布局设计器，与 JGoodies Forms 结合使用生成 XML 文件。<br><br></li><li>下载 <a href="http://www.gluecode.com/website/community/downloads/index.jsp">Gluecode Standard Edition</a>，这是一个基于 Apache Geronimo 的免费应用服务器。<br><br></li><li>访问 developerWorks <a href="http://www.ibm.com/developerworks/opensource/top-projects/geronimo.html">Apache Geronimo</a> 项目区，它为 Geronimo 开发人员提供资源。<br><br></li><li>访问 <a href="http://www-128.ibm.com/developerworks/cn/opensource">developerWorks 开放源代码专区</a> 以获得广泛的技术信息、工具和项目更新，以帮助您用开源技术进行开发，并与 IBM 产品结合使用。<br><br></li><li>在 developerWorks 开放源代码专区中浏览所有 <a href="http://www.ibm.com/developerworks/views/opensource/libraryview.jsp?topic_by=All+topics+and+related+products&amp;sort_order=asc&amp;lcl_sort_order=desc&amp;search_by=apache&amp;search_flag=true&amp;type_by=Articles&amp;show_abstract=true&amp;start_no=1&amp;sort_by=Title&amp;end_no=100&amp;show_all=false">Apache 文章</a> 和免费的 <a href="http://www.ibm.com/developerworks/views/opensource/libraryview.jsp?topic_by=All+topics+and+related+products&amp;sort_order=desc&amp;lcl_sort_order=asc&amp;search_by=apache&amp;search_flag=true&amp;type_by=Tutorials&amp;show_abstract=true&amp;start_no=1&amp;sort_by=Title&amp;end_no=100&amp;show_all=false">Apache 教程</a>。<br><br></li><li>使用 <a href="http://www.ibm.com/developerworks/downloads/?S_TACT=105AGX44">IBM 试用软件</a> 改革您的下一个开源开发项目，该软件可以下载，也可以从 DVD 安装。<br><br></li><li>通过参与 <a href="http://www.ibm.com/developerworks/blogs/">developerWorks blogs</a> 加入 developerWorks 社区。<br></li></ul><br><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td><img alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" height="1" width="100%"></td></tr></tbody></table><table class="no-print" align="right" cellpadding="0" cellspacing="0"><tbody><tr align="right"><td><table border="0" cellpadding="0" cellspacing="0"><tbody><tr><td valign="middle"><img alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" border="0" height="16" width="16"><br></td><td align="right" valign="top"><a class="fbox" href="http://www-128.ibm.com/developerworks/cn/opensource/os-ag-swing/#main"><b>回页首</b></a></td></tr></tbody></table></td></tr></tbody></table><br><br><p><a name="author"><span class="atitle">关于作者</span></a></p><table border="0" cellpadding="0" cellspacing="0" width="100%"><tbody><tr><td colspan="2"><img src="http://www.ibm.com/i/c.gif" alt="" height="5" width="100%"></td></tr><tr align="left" valign="top"><td><p><img alt="作者照片" src="http://www.ibm.com/developerworks/i/p-sanche.jpg" align="left" height="80" width="64"></p></td><td><p>Neal
Sanche 是一位最近才涉足 Microsoft® .NET 世界的 Java
开发人员，他正在克服困难脱离他原来习惯了的开发环境。他的经验包括开发多个商业 J2EE 应用程序和多个独立 Java
应用程序。在业余时间，他作曲、摄影并撰写技术文章。访问他的 <a href="http://www.nsdev.org/">Web 站点</a> 可以看到相应的多个示例。您可以通过 <a href="mailto:neal@nsdev.org">neal@nsdev.org</a> 与 Neal 联系。</p></td></tr></tbody></table></td></tr></tbody></table><img src ="http://www.blogjava.net/ericwang/aggbug/23229.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-12-09 23:22 <a href="http://www.blogjava.net/ericwang/archive/2005/12/09/23229.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是Portlet ?</title><link>http://www.blogjava.net/ericwang/archive/2005/12/05/22626.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Mon, 05 Dec 2005 12:11:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/12/05/22626.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/22626.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/12/05/22626.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/22626.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/22626.html</trackback:ping><description><![CDATA[<center><b><span style="font-size: 20px;">什么是Portlet ?</span></b></center><br><center>作者：Sunil Patil</center><br><center>译者:<a href="http://www.matrix.org.cn/user.shtml?username=observer" target="_new">observer</a></center><br><br><br><br><br><span style="color: Red;">版权声明：任何获得Matrix授权的网站，转载时请<b>务必</b>以超链接形式标明文章原始出处和作者信息及本声明</span><br>作者:Sunil Patil;<a href="http://www.matrix.org.cn/user.shtml?username=observer" target="_new">observer</a><br>原文地址:<a href="http://www.onjava.com/pub/a/onjava/2005/09/14/what-is-a-portlet.html" target="_new">http://www.onjava.com/pub/a/onjava/2005/10/19/challenging-java-dominance.html</a><br>中文地址:<a href="http://www.matrix.org.cn/resource/article/44/44029_Portlet.html" target="_new">http://www.matrix.org.cn/resource/article/44/44029_Portlet.html</a><br>关键词： Portlet Java<br><br><br><b>Portlets </b><br>“Portlets
是一种Web组件－就像servlets－是专为将合成页面里的内容聚集在一起而设计的。通常请求一个portal页面会引发多个portlets被调
用。每个portlet都会生成标记段，并与别的portlets生成的标记段组合在一起嵌入到portal页面的标记内。”（摘自Portlet规范，
JSR 168）<br><br>本文探讨了以下内容：<br><span style="color: Purple;">1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Portal页面的元素 <br>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Portal是什么？<br>3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Portlets是什么？<br>4.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;开发“Hello World” Portlet<br>5.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在Pluto上部署HelloWorld Portlet<br>6.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如何创建Portal页面<br>7.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;结束语 <br>8.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;资源</span><br><br>　　Portlet规范将portlet定义为一种“基于Java技术的web组件，由处理请求和生成动态内容的portlet容器管理”。这段话听起来是不是有些费解？本文将说明portlets是什么以及能用它们做什么。<br><br><br>图1显示了在访问一个portal服务器时浏览器中页面的样子。<br> <br><img src="http://www.matrix.org.cn/resource/upload/forum/2005_12_05_021236_BwaZOpizXk.gif" alt="image" style="display: inline;" onmouseover="javascript:ImgShowTip(this);" onload="javascript:ImgLoad(this);" onclick="javascript:ImgClick(this);" border="0" width="600"><br>图1 典型的portal服务器的页面（点击查看原图）<br><br>　
　如果仔细查看浏览器里的页面，就会看到页面是由不同的“窗口”组成的。一个窗口用于刷新天气，另一个用于新闻，还有一个用于刷新股价，等等。这里的每一
个窗口都代表了一个portlets。如果看得再仔细些，还会发现每个窗口都有一个标题条和一些按钮，包括最小化和最大化按钮。<br><br>　　在系
统里，这些窗口是相互独立开发、各不同的应用。新闻portlet的开发者创建应用并打包成war格式的文件,随后portal服务器的管理员在服务器上
部署该war文件并创建页面，接下来每个用户会选择在他的页面里有哪些应用。例如，如果用户对股价不感兴趣而对体育感兴趣，他可以用“体育”窗口替换“股
价”窗口。<br><br>　　Portlet技术需要学习许多新概念，本文不可能全都涵盖，因此本文分为两部分。在第一部分里我们详细说明portals和portlets，并开发一个简单的“Hello World”portlet；在第二部分我们将探讨一些高级主题。<br><br>　　我们将用Apache的Pluto服务器（Portlet API 1.0规范的参考实现）来测试我们的示例portlets，我们还会花些时间探讨如何安装和使用Pluto服务器。<br><br><b><span style="font-size: 16px;">Portal页面的元素</span></b><br><br>图2显示了Portal页面的各种元素。<br> <br><img src="http://www.matrix.org.cn/resource/upload/forum/2005_12_05_021329_NzXorUBuvR.gif" alt="image" style="display: inline;" onmouseover="javascript:ImgShowTip(this);" onload="javascript:ImgLoad(this);" onclick="javascript:ImgClick(this);" border="0"><br>图2 portal页面的元素<br><br>　　每个portlet页面由一个或多个portlet窗口组成，每个portlet窗口又分为两部分：一个是外观，它决定了portlet窗口的标题条、控制和边界的样式；另一个是portlet段，它由portlet应用填充。<br><br>　　Portal服务器决定了portal页面的整体观感，像标识、标题条颜色、控制图标等。通过修改几个JSP和css模板文件就可以改变portal的整个观感。我们将在“如何创建portal页面”部分对此做深入讨论。<br><br><b><span style="font-size: 16px;">Portal是什么？</span></b><br><br>　
　在了解portlet之前有必要先了解portal。在Portlet规范里是这样讲的：“portal是一种web应用，通常用来提供个性化、单次登
录、聚集各个信息源的内容，并作为信息系统表现层的宿主。聚集是指将来自各个信息源的内容集成到一个web页面里的活动”。<br><br>　　Portal的功能可以分为三个主要方面：<br>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Portlet
容器：Portlet容器与servlet容器非常类似，所有的portlet都部署在portlet容器里，portlet容器控制portlet的生
命周期并为其提供必要的资源和环境信息。Portlet容器负责初始化和销毁portlets，向portlets传送用户请求并合成响应。<br>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;内容聚集：Portlet规范中规定portal的主要工作之一是聚集由各种portlet应用生成的内容，我们将在“如何创建Portal页面”部分对此做进一步讨论。<br>3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;公共服务：portlet服务器的一个强项是它所提供的一套公共服务。这些服务并不是portlet规范所要求的，但portal的商业实现版本提供了丰富的公共服务以有别于它们的竞争者。在大部分实现中都有望找到的几个公共服务有：<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; o 单次登录：只需登录portal服务器一次就可以访问所有其它的应用，这意味着你无需再分别登录每一个应用。例如一旦我登录了我的intranet网站，我就能访问mail应用、IM消息应用和其它的intranet应用，不必再分别登录这些应用。<br>　
　Portal服务器会为你分配一个通行证库。你只需要在mail应用里设定一次用户名和密码，这些信息将以加密的方式存储在通行证库中。在你已登录到
intranet网站并要访问mail应用的时候，portal服务器会从通行证库中读取你的通行证替你登录到mail服务器上。你对其它应用的访问也将
照此处理。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;o个性化：个性化服务的基本实现使用户能从两方面个性化她的页面：第一，用户可以根据她的自身喜好决定标题条的颜
色和控制图标。第二，用户可以决定在她的页面上有哪些portlets。例如，如果我是个体育迷，我可能会用一个能提供我钟爱球队最新信息的
portlet来取代股票和新闻portlets。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;　　一些在个性化服务方面领先的商业实现版本允许你建立为用户显示什么样的应用所
依据的标准（如收入和兴趣）。在这种情况下，可以设定一些像“对任何收入为X的用户显示馈赠商品的portlet”和“对任何收入为X的用户显示打折商品
的portlet”这样的商业规则。<br><br>　　&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;此外还有一些公共服务，比如机器翻译，是由portal服务器将portlet生成的内容翻译为用户要求的语言。大部分的商业portal服务器都支持手持设备访问并具有针对不同的浏览终端生成不同内容的能力。<br><br><b><span style="font-size: 16px;">Portlets是什么？</span></b><br><br>　　与servlets类似，portlets是部署在容器内用来生成动态内容的web组件。从技术角度讲portlet是一个实现了javax.portlet.Portlet接口的类，它被打包成war文件格式部署到portlet容器里。<br><br>　　Portlets在以下方面与servlets相似：<br>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;portlets由特定的容器管理。<br>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;portlets生成动态内容。<br>3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;portlet的生命周期由容器管理。<br>4.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;portlets通过请求/响应模式与web客户端交互。<br><br>　　Portlets在以下方面与servlets相异：<br>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;portlets只能生成标记段，而不是整个文档。<br>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;portlets没有可供直接访问的URL地址。不过你还是能够让别人通过URL访问到portlet，你可以把包含该portlet的页面的URL发给他。<br>3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;portlets
不能随意地生成内容，这是因为portlet生成的内容最终要成为portal页面的一部分。如果portal服务器要求的是html/text类型，那
么所有的portlets都应生成html/text类型的内容。再比方说，如果portal服务器要求的是WML类型，那么所有的portlets都应
生成WML类型的内容。<br><br>　　portlets还提供了一些附加的功能：<br>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;设置参数的持久化存储：portlets提供了一个PortletPreferences对象用来保存用户的设置参数。这些参数被存入一个持久化数据库，这样服务器重启后数据依然有效。开发者不必关心这些数据存储的具体实现机制。<br>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
请求处理：portlets提供了更为细粒度的请求处理。对于用户在portlet上动作时向该portlet发出的请求(一种称为活跃期的状态)，或者
因用户在其它portlet上动作而引发的刷新页面请求，Portal服务器提供了两种不同的回调方法来处理。<br>3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Portlet
模式：portlets用模式的概念来表示用户在做什么。在使用mail应用的时候，你可能会用它来读信、写信或检查信件――这些都是mail应用的预定
功能，Portlets通常以VIEW模式提供这些功能。但还有一些活动，像指定刷新时间或（重新）设置用户名和密码，这些活动允许用户定制应用的行为，
因此它们用的是EDIT模式。Mail应用的帮助功能用的是HELP模式。<br><br>　　如果仔细想想其实这里面并没有什么新东西，它们反而大部分都是普通的业务需求。Portlet规范的作用在于它提供了一个抽象层，这才是它对所有与之相关的人－最终用户、开发者和管理员－的价值所在。<br><br>　　作为一个开发者，我会将所有与VIEW模式有关的业务逻辑放入doView()方法，将与应用配置有关的业务逻辑放入doEdit()方法，将与帮助有关的逻辑放入doHelp()方法<br><br>　　这就简化了管理员对portlet应用的访问控制管理，因为他只需改变portlet的访问权限就能决定用户能做什么。例如，如果mail应用的一个用户能够在EDIT模式下设定用户名和密码，那么就可以断定他具有EDIT模式访问权限。<br><br>　
　不妨考虑这样一种情形：我是一个intranet网站的管理员，我的公司买了一个能显示新闻信息的第三方portlet应用，该应用允许用户指定跟踪新
闻更新的URL地址，我想借助它为用户显示公司的内部新闻。另一个需求是我不想让用户通过该应用来跟踪任何其它的新闻信息来源。作为管理员，我可以为所有
的用户指定一个用于内部新闻更新的URL地址，同时通过改变portlet应用的部署描述符来取消其它人修改该地址的权限。<br><br>　　由于所有的portlet应用都具有相似的UI界面，因此采用portlets可使网站对最终用户更具吸引力。如果她想阅读任何一个应用的帮助信息，她可以点击帮助按钮；她也知道点击编辑按钮能让她进入应用的配置屏。标准化的用户界面使你的portlet应用更引人。<br><br>4.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
窗口状态：窗口状态决定了portal页面上留给portlet生成内容的空间。如果点击最大化按钮，portlet将占据整个屏幕，成为用户唯一可用的
portlet；而在最小化状态，portlet只显示为标题条。作为开发者应当根据可用空间的大小来定做内容。<br><br>5.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;用
户信息：通常portlets向发出请求的用户提供个性化的内容，为了能更加行之有效，portlets需要访问用户的属性信息，如姓名、email、电
话等。Portlet
API为此提供了用户属性的概念，开发者能够用标准的方式访问这些属性，并由管理员负责在这些属性与真实的用户信息数据库（通常是LDAP服务器）之间建
立映射关系。<br><br>　　我们将在本文的第二部分深入讨论这些特点－请求处理、用户信息和portlet模式。<br><br><b><span style="font-size: 16px;">开发"Hello World" Portlet</span></b><br><br>　　现在我们就来开发一个简单的HelloWorld portlet。<br>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;创建一个名为HelloWorld的web项目，它与通常的servlet项目类似，有一个/WEB-INF/web.xml文件作为项目的部署描述符。<br><br>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在build path里加入portlet-api-1.0.jar文件，该jar文件是Pluto发行包的一部分。<br><br>3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在Source文件夹中按如下内容创建HelloWorld.java文件：<br><pre title="pre code" class="overflow">public class HelloWorld extends GenericPortlet{<br>&nbsp;&nbsp;protected void doView(RenderRequest request,<br>&nbsp;&nbsp;RenderResponse response) throws<br>&nbsp;&nbsp;PortletException, IOException {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.setContentType("text/html");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;response.getWriter().println("Hello Portlet");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</pre><br><br>　
　每个portlet都要实现Portlet接口，该接口为portlet定义了生命周期方法。由于不想覆盖所有这些方法，我们只对
GenericPortlet类进行扩展，它是一个实现了Portlet接口的适配器类。GenericPortlet类提供了所有生命周期方法的默认实
现，所以我们只需实现我们所需要的方法。<br><br>　　我们在 HelloWorld portlet里要做的只是显示“Hello
Portlet”，所以我们将覆盖GenericPortlet类的doView()方法，该方法以PortletRequest 和
PortletResponse作为参数。在doView()方法中首先调用response.setContentType()以通知portlet容
器该portlet将要生成何种类型的内容－如果不这样做就会导致IllegalStateException异常。一旦设置了内容的类型，就可以从
response对象中获得PrintWriter并开始写入。<br><br>4.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;每个portlet应用在/WEB-INF文件夹中都有一个portlet.xml文件，它是portlet应用的部署描述符。按以下内容创建portlet.xml文件：<br><pre title="pre code" class="overflow">&lt;portlet&gt;<br>&nbsp;&nbsp;&lt;description&gt;HelloWorldDescription<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/description&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;portlet-name&gt;HelloWorld<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/portlet-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;display-name&gt;Hello World<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/display-name&gt;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;portlet-class&gt;com.test.HelloWorld<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/portlet-class&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;expiration-cache&gt;-1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/expiration-cache&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;supports&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;mime-type&gt;text/html&lt;/mime-type&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;portlet-mode&gt;VIEW<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/portlet-mode&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/supports&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;supported-locale&gt;en<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/supported-locale&gt;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;portlet-info&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;title&gt;Hello World&lt;/title&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;short-title&gt;Hello World<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/short-title&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;keywords&gt;Hello,pluto&lt;/keywords&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/portlet-info&gt;<br>&lt;/portlet&gt;</pre><br><br>　
　&lt;portlet-name&gt;元素声明了portlet的名字，&lt;portlet-class&gt;元素指定了portlet的全
限定类名，&lt;expiration-cache&gt;元素以秒为单位指定了内容超期的时间。这里面有一点需要注意：你在portlet上的某些动
作可能会导致内容刷新，这与缓存时间无关。<br>　　&lt;supports&gt;元素指定对于给定的&lt;mime-type&gt;有哪些模
式可供支持。在示例中我们假定HelloWorld只能生成text/html类型的内容，且只有view模式可支持该内容类型。如果要增加对其它内容类
型的支持，需要添加新的&lt;support&gt;元素并指定支持该MIME类型的模式有哪些。通常portlet对于text/html类型有
VIEW、EDIT和HELP模式可供支持，而对于WML MIME类型则只有VIEW模式。<br>　　还可以用&lt;supported-
locale&gt;元素来指定portlet支持哪些本地化。&lt;title&gt;元素用来指定portlet的标题。如果要对标题做国际化处
理，可以用元素&lt;resource-bundle&gt;指定资源（比例properties文件）的文件名。在这种情况下，容器将根据用户所在的
地区从适当的properties文件中选择标题。<br><br>5.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;每个portlet应用都是一个web应用，因此除了portlet.xml文件之外还需要有web.xml文件。<br><pre title="pre code" class="overflow">&lt;web-app&gt;<br>&nbsp;&nbsp;&lt;display-name&gt;Hello World Portlet<br>&nbsp;&nbsp;&lt;/display-name&gt;<br>&nbsp;&nbsp;&lt;welcome-file-list<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;welcome-file&gt;index.jsp<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/welcome-file&gt;<br>&nbsp;&nbsp;&lt;/welcome-file-list&gt;<br>&lt;/web-app&gt;</pre><br><br>6.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;接下来将这些文件进行编译并打包为war文件。你可以自己完成这些工作，或者下载带有build.xml 的示例代码（参见“资源”部分）来创建war文件。<br>在Pluto上部署HelloWorld Portlet<br><br>　
　Pluto尚处于开发阶段的早期，因此还没有一套易于使用的管理工具。为了能使用Pluto服务器，需要将编译和源代码两个版本都下载。需要注意的是以
下说明是针对Windows平台的，Unix用户通过修改斜杠符号和执行sh shell脚本（不是bat批命令文件）会得到类似的结果。<br><br>1.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;创建一个文件夹，比如C:\PlutoInstallation。<br>2.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;从Pluto的网站下载pluto-1.0.1-rc1.zip和pluto-src-1.0.1-rc1.zip。<br>3.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;将pluto-1.0.1-rc1.zip解压到C:\PlutoInstallation.文件夹，它应被解压到C:\PlutoInstallation\pluto-1.0.1-rc1文件夹下。<br>4.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;执行C:\PlutoInstallation\pluto-1.0.1-rc1\bin\startup.bat启动Pluto，现在可以通过地址http://localhost:8080/pluto/portal访问Pluto服务器。<br>5.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;将pluto-src-1.0.1-rc1.zip解压到C:\PlutoInstallation\PlutoSrc文件夹。<br>6.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
进入C:\PlutoInstallation\PlutoSrc文件夹，执行maven
distribute:all.，编译并下载运行常规管理任务所必需的相关资源文件。现在可以将HelloWorldPortlet.war作为
portlet进行安装了。<br>7.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;首先将HelloWorldPortlet.war文件拷贝到C:\PlutoInstallation\portlets目录，如果没这个目录就创建它。<br>8.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;将C:\PlutoInstallation\plutosrc\build.properties.sample更名为build.properties。<br>9.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编辑build.properties，将maven.tomcat.home指向Pluto编译版的安装位置，在本例中应改为maven.tomcat.home=C:/PlutoInstallation/pluto-1.0.1-rc1。<br>10.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
为了安装portlet，进入C:\plutoInstallation\plutosrc\deploy文件夹，执行maven deploy
-Ddeploy=c:\PlutoInstallation\portlets\HelloWorldPortlet.war，应能看到“build
successful”信息。<br>11.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在C:\PlutoInstallation\pluto-1.0.1-rc1\webapps文件夹下，应该有一个HelloWorldPortlet文件夹。<br>12.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;现在进入C:\PlutoInstallation\pluto-1.0.1-rc1\webapps\HelloWorld\WEB-INF\ folder文件夹，打开portlet的web.xml文件，你会发现里面自动多了几行，如下所示：<br><pre title="pre code" class="overflow">&lt;servlet&gt;<br>&nbsp;&nbsp;&lt;servlet-name&gt;HelloWorld&lt;/servlet-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;display-name&gt;HelloWorld Wrapper&lt;/display-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;description&gt;Automated generated<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Portlet Wrapper&lt;/description&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;servlet-class&gt;org.apache.pluto.core.PortletServlet<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/servlet-class&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;portlet-class&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;com.test.HelloWorld<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/param-value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;init-param&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-name&gt;portlet-guid&lt;/param-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;param-value&gt;HelloPluto.HelloWorld<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/param-value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/init-param&gt;<br>&lt;/servlet&gt;</pre><br>13.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
接下来我们将该portlet加到页面里。进入C:\PlutoInstallation\pluto-1.0.1-rc1\webapps\pluto
\WEB-INF\data文件夹，可以看到有两个XML文件：pageregistry.xml和
portletentityregistry.xml。<br>14.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;portletentityregistry.xml包含了portlet的定义，在该文件中加入以下几行：<br><pre title="pre code" class="overflow"> &lt;application id="5"&gt;<br>&nbsp;&nbsp; &lt;definition-id&gt;HelloWorld&lt;/definition-id&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;portlet id="1"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;definition-id&gt;HelloWorld.HelloWorld&lt;/definition-id&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp; &lt;/portlet&gt;<br>&lt;/application&gt;</pre><br>　　应用的&lt;definition-id&gt;应为web应用所在文件夹的名字，portlet的&lt;definition-id&gt;应与web.xml中生成的portlet-guid相一致。<br>15.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;pageregistry.xml定义了页面中包含了哪些portlets，对该文件做如下改动：<br><pre title="pre code" class="overflow">&nbsp;&nbsp;&lt;fragment name="p2" type="portlet"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;property name="portlet" value="5.1"/&gt;<br>&lt;/fragment&gt;</pre><br>16.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;执行shutdown命令和startup命令重启Pluto服务器，返回到地址http://localhost:8080/pluto/portal并点击“Test Link”－此时页面中将出现我们的<br><br><span style="color: Blue;">HelloWorld portlet。</span><br><br>图3的右侧显示了HelloWorld portlet看上去的样子。<br> <br><img src="http://www.matrix.org.cn/resource/upload/forum/2005_12_05_021757_PvflGMTbcZ.gif" alt="image" style="display: inline;" onmouseover="javascript:ImgShowTip(this);" onload="javascript:ImgLoad(this);" onclick="javascript:ImgClick(this);" border="0" width="600"><br>图3 portlet的屏幕截图<br><br><b><span style="font-size: 16px;">如何创建Portal页面</span> </b><br><br>图4显示了portal容器如何将分离的portlets组装为页面。<br> <br><img src="http://www.matrix.org.cn/resource/upload/forum/2005_12_05_021832_mFdIUWtdpN.gif" alt="image" style="display: inline;" onmouseover="javascript:ImgShowTip(this);" onload="javascript:ImgLoad(this);" onclick="javascript:ImgClick(this);" border="0"><br>图4 创建Portal页面<br><br>　
　大部分的portal服务器基本上都是部署于应用服务器上的web应用，通过servlet来处理访问portal服务器的请求。查看一下Pluto的
安装目录就会发现Pluto不过是一个部署于Tomcat服务器上的一个普通web应用，再看看C:\PlutoInstallation\pluto-
1.0.1-rc1\webapps\pluto\WEB-INF\web.xml会发现所有发往Pluto服务器的请求都被映射到
org.apache.pluto.portalImpl.Servlet上。<br><br>　　在本文开始部分“Portal页面的元素”中，我们提到portal页面由两部分组成。一部分是由页面中的portlets生成的内容，另一部分是由portal服务器生成的内容。<br><br>　　在Pluto里，只要用户发出请求，就会由servlet进行控制，根据用户所请求的页面来确定需要显示的portlets的列表。一旦生成了列表，servlet就将控制转给这些portlets线程并收集由它们生成的内容。<br><br>　
　对于由portal服务器生成的内容（像portal网站的观感及每个portlet的外观和控制之类）则取决于C:\
PlutoInstallation\pluto-1.0.1-rc1\webapps\pluto\WEB-INF\aggregation文件夹下的
JSP文件。RootFragment.jsp是主JSP文件，它决定了整体的观感和对齐方式；它还包含了Heads以定义在生成的页面中的&lt;
HEAD&gt;标签里的内容。TabNavigation.jsp用来选择在banner中该显示什么（默认情况下在banner显示列表中也包扩了
pluto.png图片）。TabNavigation.jsp用来确定portal网站的导航方案。这意味着只需改动该文件夹下少量的几个JSP文件，
就能改变整个portal网站的观感。<br><br>　　Pluto根据pageregistry.xml中的设置确定页面中有多少行，并用
RowFragment.jsp去填充。ColumnFragment.jsp用来填充每个栏目。PortletFragmentHeader.jsp用
来填充每个portlet的页头，像标题条及最大化和最小化控制。footer.jsp用来填充JSP的页脚。如果去看一下portal页面的HTML代
码就会发现每个portlet窗口无非都是嵌入&lt;TD&gt;标签的内容块。<br><br><b><span style="font-size: 16px;">结束语</span></b><br><br>　　任何一种新技术要想获得成功都应具备以下条件：首先，它能提升现有技术；其次，它能解决现有技术遇到的普遍问题；再次，它能提供多于一个的抽象层（有人说，每抽象出一层，问题就解决一半）。<br><br>　
　由于portlet与现有的应用服务器架构兼容，这对Portlet
API来说是一次发展servlet技术的好机会。你可以从portlet里调用EJB，或者用它启动和参与由应用服务器控制的全局性事务。换句话说，在
以商业逻辑为核心的领域里，portlet完全可以做得和servlet一样好。<br><br>　　Portlets提供了一个抽象层，现在你不必再担
心客户端使用了什么样的HTTP方法，也不必自己编写程序去捕获像点击按钮这样的客户端事件。最后但绝不是最次要的一点是，portlets以提供像单次
登录、个性化等服务的方式解决了servlets不能解决的大部分问题。<br><br><b>资源</b> <br>·<a href="http://www.matrix.org.cn/resource/upload/forum/2005_12_05_021921_tLDwmSMZnA.zip" target="_new">本文的示例代码</a><br>·JSR 168的首页:http://www.jcp.org/en/jsr/detail?id=168<br>·Pluto的首页:http://portals.apache.org/pluto/ <br>·onjava.com:<a href="http://onjava.com/" target="_new">onjava.com</a><br>·Matrix-Java开发者社区:<a href="http://www.matrix.org.cn/" target="_new">http://www.matrix.org.cn</a><br><br><br>Sunil Patil从事J2EE技术工作已有5年，他感兴趣的领域包括对象关系映射工具、UI框架以及portals。 <br><img src ="http://www.blogjava.net/ericwang/aggbug/22626.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-12-05 20:11 <a href="http://www.blogjava.net/ericwang/archive/2005/12/05/22626.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转载】利用XMLBean轻轻松松读写XML</title><link>http://www.blogjava.net/ericwang/archive/2005/12/05/22624.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Mon, 05 Dec 2005 11:52:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/12/05/22624.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/22624.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/12/05/22624.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/22624.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/22624.html</trackback:ping><description><![CDATA[<center><span style="font-size: 20px;"><b>利用XMLBean轻轻松松读写XML</b></span></center><br><center>作者：叶枫</center><br><br><br><br><span style="color: Red;">版权声明：本文可以自由转载，转载时请务必以超链接形式标明文章原始出处和作者信息及本声明</span><br>作者:叶枫(<a href="http://blog.matrix.org.cn/page/%E5%8F%B6%E6%9E%AB" target="_new">http://blog.matrix.org.cn/page/叶枫</a>)<br>原
文:[http://www.matrix.org.cn/resource/article/44/44027_XMLBean.html]http://www.matrix.org.cn/resource/article/44/44027_XMLBean.html[/url]<br>关键字:XML XMLBean Parser<br><br><b><span style="font-size: 16px;">一、关于XML解析</span></b><br><br>&nbsp;&nbsp;XML在Java应用程序里变得越来越重要, 广泛应用于数据存储和<br>交换. 比如我们常见的配置文件,都是以XML方式存储的. XML还应用<br>于Java Message Service和Web Services等技术作为数据交换.<br>因此,正确读写XML文档是XML应用的基础.<br>&nbsp;&nbsp;Java提供了SAX和DOM两种方式用于解析XML,但即便如此,要读写一个<br>稍微复杂的XML,也不是一件容易的事.<br><br><b><span style="font-size: 16px;">二、XMLBean简介</span></b><br><br>&nbsp;&nbsp;&nbsp;&nbsp;Hibernate已经成为目前流行的面向Java环境的对象/关系数据库映射工具.<br>在Hibernate等对象/关系数据库映射工具出现之前,对数据库的操作是<br>通过JDBC来实现的,对数据库的任何操作,开发人员都要自己写SQL语句<br>来实现. 对象/关系数据库映射工具出现后,对数据库的操作转成对<br>JavaBean的操作,极大方便了数据库开发. 所以如果有一个类似的工具能够<br>实现将对XML的读写转成对JavaBean的操作,将会简化XML的读写,即使对XML<br>不熟悉的开发人员也能方便地读写XML. 这个工具就是XMLBean.<br><br><b><span style="font-size: 16px;">三、准备XMLBean和XML文档</span></b><br><br>&nbsp;&nbsp; XMLBean是Apache的一个开源项目,可以从http://www.apache.org下载,<br>最新的版本是2.0. 解压后目录如下:<br><span style="color: Blue;">xmlbean2.0.0<br>&nbsp;&nbsp;&nbsp;&nbsp; +---bin<br>&nbsp;&nbsp;&nbsp;&nbsp; +---docs<br>&nbsp;&nbsp;&nbsp;&nbsp; +---lib<br>&nbsp;&nbsp;&nbsp;&nbsp; +---samples<br>&nbsp;&nbsp;&nbsp;&nbsp; +---schemas</span><br><br>另外还要准备一个XML文档(customers.xml),<br>在本文的例子里,我们将对这个文档进行读写操作. 文档源码如下:<br><pre title="pre code" class="overflow"><br>&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br>&lt;Customers&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;customer&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;id&gt;1&lt;/id&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;gender&gt;female&lt;/gender&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;firstname&gt;Jessica&lt;/firstname&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;lastname&gt;Lim&lt;/lastname&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;phoneNumber&gt;1234567&lt;/phoneNumber&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;address&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;primaryAddress&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;postalCode&gt;350106&lt;/postalCode&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;addressLine1&gt;#25-1&lt;/addressLine1&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;addressLine2&gt;SHINSAYAMA 2-CHOME&lt;/addressLine2&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/primaryAddress&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;billingAddress&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;receiver&gt;Ms Danielle&lt;/receiver&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;postalCode&gt;350107&lt;/postalCode&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;addressLine1&gt;#167&lt;/addressLine1&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;addressLine2&gt;NORTH TOWER HARBOUR CITY&lt;/addressLine2&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/billingAddress&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/address&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/customer&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;customer&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;id&gt;2&lt;/id&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;gender&gt;male&lt;/gender&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;firstname&gt;David&lt;/firstname&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;lastname&gt;Bill&lt;/lastname&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;phoneNumber&gt;808182&lt;/phoneNumber&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;address&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;primaryAddress&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;postalCode&gt;319087&lt;/postalCode&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;addressLine1&gt;1033 WS St.&lt;/addressLine1&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;addressLine2&gt;Tima Road&lt;/addressLine2&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/primaryAddress&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;billingAddress&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;receiver&gt;Mr William&lt;/receiver&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;postalCode&gt;672993&lt;/postalCode&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;addressLine1&gt;1033 WS St.&lt;/addressLine1&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;addressLine2&gt;Tima Road&lt;/addressLine2&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/billingAddress&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/address&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/customer&gt;<br>&lt;/Customers&gt;<br></pre><br><br>这是一个客户的数据模型,每个客户都有客户编号(ID),姓名,性别(gender),<br>电话号码(phoneNumber)和地址,其中地址有两个: 首要地址(PrimaryAddress)<br>和帐单地址(BillingAddress),每个地址有邮编,地址1,和地址2组成.<br>其中帐单地址还有收件人(receiver).<br><br>&nbsp;&nbsp;&nbsp;&nbsp;此外,还要准备一个配置文件(文件名customer.xsdconfig),这个文件的<br>作用我后面会讲,它的内容如下:<br><pre title="pre code" class="overflow"><br>&lt;xb:config xmlns:xb="http://xml.apache.org/xmlbeans/2004/02/xbean/config"&gt;<br><br>&nbsp;&nbsp;&lt;xb:namespace&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;xb:package&gt;sample.xmlbean&lt;/xb:package&gt;<br>&nbsp;&nbsp;&lt;/xb:namespace&gt;<br><br>&lt;/xb:config&gt;<br></pre><br><br><b><span style="font-size: 16px;">四、XMLBean使用步骤</span></b><br><br>&nbsp;&nbsp;&nbsp;&nbsp;和其他面向Java环境的对象/关系数据库映射工具的使用步骤一样,<br>在正式使用XMLBean前,我们要作两个准备.<br><br>&nbsp;&nbsp;&nbsp;&nbsp;1. 生成XML Schema文件<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 什么是XML Schema文件? 正常情况下,每个XML文件都有一个Schema文件,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XML Schema文件是一个XML的约束文件,它定义了XML文件的结构和元素.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 以及对元素和结构的约束. 通俗地讲,如果说XML文件是数据库里的记录,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 那么Schema就是表结构定义.<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 为什么需要这个文件? XMLBean需要通过这个文件知道一个XML文件的<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 结构以及约束,比如数据类型等. 利用这个Schema文件,XMLBean将会产生<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 一系列相关的Java Classes来实现对XML的操作. 而作为开发人员,则是<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 利用XMLBean产生的Java Classes来完成对XML的操作而不需要SAX或DOM.<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 怎样产生这个Schema文件呢? 如果对于熟悉XML的开发人员,可以自己来<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 写这个Schema文件,对于不熟悉XML的开发人员,可以通过一些工具来完成.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 比较有名的如XMLSPY和Stylus Studio都可以通过XML文件来生成Schema<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 文件. 加入我们已经生成这个Schema文件(customer.xsd):<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;?xml version="1.0" encoding="UTF-8"?&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;elementFormDefault="qualified"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="Customers"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:complexType&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:sequence&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element maxOccurs="unbounded" name="customer"<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type="customerType"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xs:sequence&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xs:complexType&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xs:element&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:complexType name="customerType"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:sequence&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="id" type="xs:int"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="gender" type="xs:string"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="firstname" type="xs:string"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="lastname" type="xs:string"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="phoneNumber" type="xs:string"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="address" type="addressType"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xs:sequence&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xs:complexType&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:complexType name="addressType"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:sequence&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="primaryAddress" type="primaryAddressType"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="billingAddress" type="billingAddressType"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xs:sequence&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xs:complexType&gt;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:complexType name="primaryAddressType"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:sequence&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="postalCode" type="xs:string"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="addressLine1" type="xs:string"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="addressLine2" type="xs:string"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xs:sequence&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xs:complexType&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:complexType name="billingAddressType"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:sequence&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;xs:element name="receiver" type="xs:string"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="postalCode" type="xs:string"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="addressLine1" type="xs:string"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xs:element name="addressLine2" type="xs:string"/&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xs:sequence&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xs:complexType&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xs:schema&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </pre><br><br>&nbsp;&nbsp;&nbsp;&nbsp;2. 利用scomp来生成Java Classes<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scomp是XMLBean提供的一个编译工具,它在bin的目录下. 通过这个工具,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 我们可以将以上的Schema文件生成Java Classes.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scomp的语法如下:-<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scomp [options] [dirs]* [schemaFile.xsd]* [service.wsdl]* [config.xsdconfig]*<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </pre><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 主要参数说明:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -src [dir]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-- 生成的Java Classes存放目录<br>&nbsp;&nbsp;&nbsp;&nbsp; -srconly&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-- 不编译Java Classes,不产生Jar文件<br>&nbsp;&nbsp;&nbsp;&nbsp; -out [jarFileName]&nbsp;&nbsp;-- 生成的Jar文件,缺省是xmltypes.jar<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -compiler&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -- Java编译器的路径,即Javac的位置<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; schemaFile.xsd&nbsp;&nbsp;&nbsp;&nbsp;-- XML Schema文件位置<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; config.xsdconfig&nbsp;&nbsp; -- xsdconfig文件的位置, 这个文件主要用来制定生成的Java Class<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;的一些文件名规则和Package的名称,在本文,package是sample.xmlbean<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在本文,我是这样运行的:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; scomp -src build\src&nbsp;&nbsp;-out build\customerXmlBean.jar schema\customer.xsd<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -compiler C:\jdk142_04\bin\javac customer.xsdconfig<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </pre><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 这个命令行的意思是告诉scomp生成customerXmlBean.jar,放在build目录下,同时<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 生成源代码放在build\src下, Schema文件是customer.xsd,xsdconfig文件是customer.xsdconfig.<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 其实, 生成的Java源代码没有多大作用,我们要的是jar文件.我们先看一下build\src\sample\xmlbean下生成的Classes.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CustomersDocument.java&nbsp;&nbsp;&nbsp;&nbsp;-- 整个XML文档的Java Class映射<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CustomerType.java&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-- 节点sustomer的映射<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AddressType.java&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; -- 节点address的映射<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BillingAddressType.java&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-- 节点billingAddress的映射<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PrimaryAddressType.java&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-- 节点primaryAddress的映射<br>&nbsp;&nbsp;&nbsp;&nbsp; </pre><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 好了,到此我们所有的准备工作已经完成了. 下面就开始进入重点：利用刚才生成的jar文件读写XML.<br><br><b><span style="font-size: 16px;">五、利用XMLBean读XML文件</span></b><br><br>&nbsp;&nbsp;&nbsp;&nbsp;新建一个Java Project,将XMLBean2.0.0\lib\下的Jar文件和刚才我们生成的customerXmlBean.jar加入<br>&nbsp;&nbsp;&nbsp;&nbsp;到Project的ClassPath.<br><br>&nbsp;&nbsp;&nbsp;&nbsp;新建一个Java Class: CustomerXMLBean.&nbsp;&nbsp;源码如下:<br>&nbsp;&nbsp;&nbsp;&nbsp;<pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;package com.sample.reader;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;import java.io.File;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;import sample.xmlbean.*;<br>&nbsp;&nbsp;&nbsp;&nbsp;import org.apache.commons.beanutils.BeanUtils;<br>&nbsp;&nbsp;&nbsp;&nbsp;import org.apache.xmlbeans.XmlOptions;<br>&nbsp;&nbsp;&nbsp;&nbsp;public class CustomerXMLBean {<br>&nbsp;&nbsp;&nbsp;&nbsp;private String filename = null;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;public CustomerXMLBean(String filename) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;super();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;this.filename = filename;<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;public void customerReader() {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File xmlFile = new File(filename);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CustomersDocument doc = CustomersDocument.Factory.parse(xmlFile);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CustomerType[] customers = doc.getCustomers().getCustomerArray();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; customers.length; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CustomerType customer = customers[i];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("Customer#" + i);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("Customer ID:" + customer.getId());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("First name:" + customer.getFirstname());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("Last name:" + customer.getLastname());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("Gender:" + customer.getGender());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("PhoneNumber:" + customer.getPhoneNumber());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Primary address<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PrimaryAddressType primaryAddress = customer.getAddress().getPrimaryAddress();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("PrimaryAddress:");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("PostalCode:" + primaryAddress.getPostalCode());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("AddressLine1:" + primaryAddress.getAddressLine1());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("AddressLine2:" + primaryAddress.getAddressLine2());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Billing address<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BillingAddressType billingAddress = customer.getAddress().getBillingAddress();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("BillingAddress:");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("Receiver:" + billingAddress.getReceiver());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("PostalCode:" + billingAddress.getPostalCode());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("AddressLine1:" + billingAddress.getAddressLine1());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;println("AddressLine2:" + billingAddress.getAddressLine2());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (Exception ex) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;private void println(String str) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.out.println(str);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp; public static void main(String[] args) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;String filename = "F://JavaTest//Eclipse//XMLBean//xml//customers.xml";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp;&nbsp; CustomerXMLBean customerXMLBean = new CustomerXMLBean(filename);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; customerXMLBean.customerReader();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</pre><br><br>&nbsp;&nbsp;&nbsp;&nbsp;运行它,参看输出结果:<br>&nbsp;&nbsp;&nbsp;&nbsp;<pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Customer#0<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Customer ID:1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; First name:Jessica<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Last name:Lim<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Gender:female<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PhoneNumber:1234567<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PrimaryAddress:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PostalCode:350106<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AddressLine1:#25-1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AddressLine2:SHINSAYAMA 2-CHOME<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BillingAddress:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Receiver:Ms Danielle<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PostalCode:350107<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AddressLine1:#167<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AddressLine2:NORTH TOWER HARBOUR CITY<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Customer#1<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Customer ID:2<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; First name:David<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Last name:Bill<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Gender:male<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PhoneNumber:808182<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PrimaryAddress:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PostalCode:319087<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AddressLine1:1033 WS St.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AddressLine2:Tima Road<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; BillingAddress:<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Receiver:Mr William<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; PostalCode:672993<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AddressLine1:1033 WS St.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; AddressLine2:Tima Road<br>&nbsp;&nbsp;&nbsp;&nbsp;</pre><br>&nbsp;&nbsp;&nbsp;&nbsp;怎么样,是不是很轻松? XMLBean的威力.<br><br><b><span style="font-size: 16px;">六、利用XMLBean写XML文件</span></b><br><br>&nbsp;&nbsp;&nbsp;&nbsp;利用XMLBean创建一个XML文档也是一件轻而易举的事.我们再增加一个Method,<br>&nbsp;&nbsp;&nbsp;&nbsp;请看一下的Java Class:<br>&nbsp;&nbsp;&nbsp;&nbsp;<pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;public void createCustomer() {<br>&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Create Document<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CustomersDocument doc = CustomersDocument.Factory.newInstance();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Add new customer<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CustomerType customer = doc.addNewCustomers().addNewCustomer();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// set customer info<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;customer.setId(3);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;customer.setFirstname("Jessica");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;customer.setLastname("Lim");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;customer.setGender("female");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;customer.setPhoneNumber("1234567");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Add new address<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;AddressType address = customer.addNewAddress();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Add new PrimaryAddress<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;PrimaryAddressType primaryAddress = address.addNewPrimaryAddress();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;primaryAddress.setPostalCode("350106");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;primaryAddress.setAddressLine1("#25-1");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;primaryAddress.setAddressLine2("SHINSAYAMA 2-CHOME");<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Add new BillingAddress<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;BillingAddressType billingAddress = address.addNewBillingAddress();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;billingAddress.setReceiver("Ms Danielle");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;billingAddress.setPostalCode("350107");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;billingAddress.setAddressLine1("#167");<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;billingAddress.setAddressLine2("NORTH TOWER HARBOUR CITY");<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File xmlFile = new File(filename);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;doc.save(xmlFile);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (Exception ex) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</pre><br>&nbsp;&nbsp;&nbsp;&nbsp;修改main method.<br>&nbsp;&nbsp;&nbsp;&nbsp;<pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) {<br>&nbsp;&nbsp;&nbsp;&nbsp;String filename = "F://JavaTest//Eclipse//XMLBean//xml//customers_new.xml";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CustomerXMLBean customerXMLBean = new CustomerXMLBean(filename);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;customerXMLBean.createCustomer();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</pre><br>&nbsp;&nbsp;&nbsp;&nbsp;运行,打开customers_new.xml:<br>&nbsp;&nbsp;&nbsp;&nbsp;<pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;?xml version="1.0" encoding="UTF-8"?&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;Customers&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;customer&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;id&gt;3&lt;/id&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;gender&gt;female&lt;/gender&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;firstname&gt;Jessica&lt;/firstname&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;lastname&gt;Lim&lt;/lastname&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;phoneNumber&gt;1234567&lt;/phoneNumber&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;address&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;primaryAddress&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;postalCode&gt;350106&lt;/postalCode&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;addressLine1&gt;#25-1&lt;/addressLine1&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;addressLine2&gt;SHINSAYAMA 2-CHOME&lt;/addressLine2&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/primaryAddress&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;billingAddress&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;receiver&gt;Ms Danielle&lt;/receiver&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;postalCode&gt;350107&lt;/postalCode&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;addressLine1&gt;#167&lt;/addressLine1&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;addressLine2&gt;NORTH TOWER HARBOUR CITY&lt;/addressLine2&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/billingAddress&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/address&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/customer&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/Customers&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;</pre><br><br><br><b><span style="font-size: 16px;">七、利用XMLBean修改XML文件</span></b><br><br>&nbsp;&nbsp;&nbsp;&nbsp;我们再增加一个Method:<br>&nbsp;&nbsp;&nbsp;&nbsp;<pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;public void updateCustomer(int id,String lastname) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File xmlFile = new File(filename);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CustomersDocument doc = CustomersDocument.Factory.parse(xmlFile);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CustomerType[] customers = doc.getCustomers().getCustomerArray();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;for (int i = 0; i &lt; customers.length; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; CustomerType customer = customers[i];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(customer.getId()==id){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;customer.setLastname(lastname);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;doc.save(xmlFile);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } catch (Exception ex) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</pre><br>&nbsp;&nbsp;&nbsp;&nbsp;main method:<br>&nbsp;&nbsp;&nbsp;&nbsp;<pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) {<br>&nbsp;&nbsp;&nbsp;&nbsp; String filename = "F://JavaTest//Eclipse//XMLBean//xml//customers_new.xml";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;CustomerXMLBean customerXMLBean = new CustomerXMLBean(filename);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;customerXMLBean.updateCustomer(3,"last");<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;</pre><br>&nbsp;&nbsp;&nbsp;&nbsp;运行之后,我们将会看到客户编号为3的客户的lastname已经改为last.<br><br><b><span style="font-size: 16px;">八、利用XMLBean删除一个customer</span></b><br><br>&nbsp;&nbsp;&nbsp;&nbsp;再增加一个Method:<br>&nbsp;&nbsp;&nbsp;&nbsp;<pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;public void deleteCustomer(int id) {<br>&nbsp;&nbsp;&nbsp;&nbsp; try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File xmlFile = new File(filename);<br>&nbsp;&nbsp;&nbsp;&nbsp; CustomersDocument doc = CustomersDocument.Factory.parse(xmlFile);<br>&nbsp;&nbsp;&nbsp;&nbsp;CustomerType[] customers = doc.getCustomers().getCustomerArray();<br><br>&nbsp;&nbsp; for (int i = 0; i &lt; customers.length; i++) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;CustomerType customer = customers[i];<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;if(customer.getId()==id){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;customer.setNil() ;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;break;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br>&nbsp;&nbsp; }<br>&nbsp;&nbsp; doc.save(xmlFile);<br>&nbsp;&nbsp; } catch (Exception ex) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp; }<br> </pre><br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; main method:<br>&nbsp;&nbsp;&nbsp;&nbsp;<pre title="pre code" class="overflow"><br>&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) {<br>&nbsp;&nbsp;&nbsp;&nbsp;String filename = "F://JavaTest//Eclipse//XMLBean//xml//customers_new.xml";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;CustomerXMLBean customerXMLBean = new CustomerXMLBean(filename);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;customerXMLBean.deleteCustomer(3);<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br> </pre><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br> 运行,我们将会看到客户编号为3的客户的资料已经被删除.<br><br><b><span style="font-size: 16px;">九、查询XML</span></b><br><br>&nbsp;&nbsp;&nbsp;&nbsp;除了本文在以上讲述的,利用XMLBean能轻轻松松完成XML的读写操作外,结合XPath和XQuery,<br>&nbsp;&nbsp; XMLBean还能完成象SQL查询数据库一样方便地查询XML数据. 关于XML查询以及如何创建XML数据库, 我将在另一篇文章里讨论.<br><br><br><br><b><span style="font-size: 16px;">十、结束语</span></b><br>&nbsp;&nbsp;&nbsp;&nbsp;XMLBean能帮助我们轻易读写XML,这将有助于我们降低XML的学习和使用,有了这个基础,<br>&nbsp;&nbsp;&nbsp;&nbsp;开发人员将为学习更多地XML相关技术和Web Services,JMS等其他J2EE技术打下良好地基础.<br><br><br><b>关于作者：</b><br>叶枫：热爱Java和Oracle. 在软件开发有近10年, 目前在国外一家美国大公司担任SA, 负责技术研究。作者Blog：<a href="http://blog.matrix.org.cn/page/%E5%8F%B6%E6%9E%AB" target="_new">http://blog.matrix.org.cn/page/叶枫</a><br><img src ="http://www.blogjava.net/ericwang/aggbug/22624.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-12-05 19:52 <a href="http://www.blogjava.net/ericwang/archive/2005/12/05/22624.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java JDBC里如何取得Oracle存储过程返回的动态结果集</title><link>http://www.blogjava.net/ericwang/archive/2005/12/02/22293.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Fri, 02 Dec 2005 13:59:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/12/02/22293.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/22293.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/12/02/22293.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/22293.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/22293.html</trackback:ping><description><![CDATA[<span style="font-size: 20px;"><b>Java JDBC里如何取得Oracle存储过程返回的动态结果集</b></span><br><center>作者：叶枫</center><br><br><span style="color: Red;">版权声明：本文可以自由转载，转载时请务必以超链接形式标明文章原始出处和作者信息及本声明</span><br>作者:叶枫(<a href="http://blog.matrix.org.cn/page/%E5%8F%B6%E6%9E%AB" target="_new">http://blog.matrix.org.cn/page/叶枫</a>)<br>原
文:[http://www.matrix.org.cn/resource/article/43/43999_JDBC_Oracle.html]http://www.matrix.org.cn/resource/article/43/43999_JDBC_Oracle.html[/url]<br>关键字:JDBC Oracle Cursor<br><br><br><br><b><span style="font-size: 16px;">1. 关于oracle和结果集</span></b><br><br>其实在大多数情况下,我们并不需要从oracle存储过程里返回一个或多个结果集,<br>除非迫不得已。<br><br>如果大家用过MS SQL Server或Sybase SQL Server,那么从存储过程返回一个<br>动态的结果集是一件非常容易的事情,只要在存储过程结束时写上<br><br><span style="color: Blue;">“select column1,column2,.... from table_list where condition“</span><br><br>就可以了。<br><br>但在Oracle中不能这样做. 我们必须使用Oracle Cursor.<br>在Oracle PL/SQL中,Cursor用来返回一行或多行记录,借助Cursor,我们可以从结果集中取得所有记录.<br><br>Cursor并不难,但是要从Oracle存储过程中返回结果集, 就需要用到Cursor变量,Cursor变量Oracle PL/SQL<br>的类型是REF CURSOR, 我们只要定义了REF CURSOR 类型就可以使用Cursor变量. 比如我们可以这样定义:<br>TYPE ref_cursor IS REF CURSOR;<br>了解了Cursor以及Cursor变量,下面就介绍如何使用Cursor变量给JDBC返回结果集.<br><br><b><span style="font-size: 16px;">2. 定义表结构</span></b><br><br>在以下例子里，我们要用到一张表Hotline.<br><br><pre title="pre code" class="overflow">Create table hotline(<br>country varchar2(50),<br>pno varchar2(50));</pre><br><br><b><span style="font-size: 16px;">3. 定义存储过程</span></b><br><br><pre title="pre code" class="overflow">create or replace package PKG_HOTLINE is<br><br>type HotlineCursorType is REF CURSOR;<br><br>function getHotline return HotlineCursorType;<br><br>end;<br><br>create or replace package body PKG_HOTLINE is<br>function getHotline return HotlineCursorType is<br>hotlineCursor HotlineCursorType;<br>begin<br>open hotlineCursor for select * from hotline;<br>return hotlineCursor;<br>end;<br>end;</pre><br><br>在这个存储过程里,我们定义了HotlineCursorType 类型,并且在存储过程中<br>简单地查找所有的记录并返回HotlineCursorType.<br><br><b><span style="font-size: 16px;">4. 测试存储过程</span></b><br><br>在Oracle SQL/Plus里登陆到数据库. 按以下输入就看到返回的结果集.<br><br><pre title="pre code" class="overflow">SQL&gt; var rs refcursor;<br>SQL&gt; exec :rs := PKG_HOTLINE.getHotline;<br>SQL&gt; print rs;</pre><br><br><b><span style="font-size: 16px;">5. Java调用</span></b><br><br>简单地写一个Java Class.<br><br><pre title="pre code" class="overflow">....<br>public void openCursor(){<br>Connection conn = null;<br>ResultSet rs = null;<br>CallableStatement stmt = null;<br>String sql = “{? = call PKG_HOTLINE.getHotline()}“;<br><br>try{<br>conn = getConnection();<br>stmt = conn.prepareCall(sql);<br>stmt.registerOutParameter(1,OracleTypes.CURSOR);<br>stmt.execute();<br>rs = ((OracleCallableStatement)stmt).getCursor(1);<br>while(rs.next()){<br>String country = rs.getString(1);<br>String pno = rs.getString(2);<br>System.out.println(“country:“+country+“|pno:”+pno);<br>}<br><br>}catch(Exception ex){<br>ex.printStackTrace();<br>}finally{<br>closeConnection(conn,rs,stmt);<br>}<br><br>}<br>.....</pre><br><br>好了，大功告成.<br><img src ="http://www.blogjava.net/ericwang/aggbug/22293.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-12-02 21:59 <a href="http://www.blogjava.net/ericwang/archive/2005/12/02/22293.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Quartz - Quartz 1 - CronTriggers Tutorial</title><link>http://www.blogjava.net/ericwang/archive/2005/11/25/21465.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Fri, 25 Nov 2005 15:13:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/11/25/21465.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/21465.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/11/25/21465.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/21465.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/21465.html</trackback:ping><description><![CDATA[<h1 class="first">Quartz - Quartz 1 - 
        CronTriggers Tutorial</h1>


    
		    
			    
				    
<div class="information-block" align="center"><div class="informationMacroPadding"><table class="noteMacro" border="0" cellpadding="5" cellspacing="0" width="85%"><tbody><tr><td valign="top" width="16"><img src="http://www.opensymphony.com/images/icons/emoticons/warning.gif" alt="" align="middle" border="0" height="16" width="16"></td><td>
<p>Some of the content in this tutorial is taken from the Quartz 1.4.2 javadocs for <tt>CronTrigger</tt>.</p></td></tr></tbody></table></div></div>


<h3><a name="CronTriggersTutorial-Introduction">Introduction</a></h3>


<p><tt>cron</tt> is a UNIX tool that has been around for a long time, so its scheduling capabilities are powerful and proven. The <tt>CronTrigger</tt> class is based on the scheduling capabilities of cron.</p>


<p><tt>CronTrigger</tt> uses "cron expressions", which are able to
create firing schedules such as: "At 8:00am every Monday through
Friday" or "At 1:30am every last Friday of the month".</p>


<p>Cron expressions are powerful, but can be pretty confusing. This
tutorial aims to take some of the mystery out of creating a cron
expression, giving users a resource which they can visit before having
to ask in a forum or mailing list.</p>


<h3><a name="CronTriggersTutorial-Format">Format</a></h3>


<p>A cron expression is a string comprised of 6 or 7 fields separated
by white space. Fields can contain any of the allowed values, along
with various combinations of the allowed special characters for that
field. The fields are as follows:</p>


<table class="confluenceTable">

<tbody><tr>
<th class="confluenceTh">Field Name</th>
<th class="confluenceTh">Mandatory?</th>
<th class="confluenceTh">Allowed Values</th>
<th class="confluenceTh">Allowed Special Characters</th>
</tr>
<tr>
<td class="confluenceTd">Seconds</td>
<td class="confluenceTd">YES</td>
<td class="confluenceTd">0-59</td>
<td class="confluenceTd">, - * /</td>
</tr>
<tr>
<td class="confluenceTd">Minutes</td>
<td class="confluenceTd">YES</td>
<td class="confluenceTd">0-59</td>
<td class="confluenceTd">, - * /</td>
</tr>
<tr>
<td class="confluenceTd">Hours</td>
<td class="confluenceTd">YES</td>
<td class="confluenceTd">0-23</td>
<td class="confluenceTd">, - * /</td>
</tr>
<tr>
<td class="confluenceTd">Day of month</td>
<td class="confluenceTd">YES</td>
<td class="confluenceTd">1-31</td>
<td class="confluenceTd">, - * ? / L W C</td>
</tr>
<tr>
<td class="confluenceTd">Month</td>
<td class="confluenceTd">YES</td>
<td class="confluenceTd">1-12 or JAN-DEC</td>
<td class="confluenceTd">, - * /</td>
</tr>
<tr>
<td class="confluenceTd">Day of week</td>
<td class="confluenceTd">YES</td>
<td class="confluenceTd">1-7 or SUN-SAT</td>
<td class="confluenceTd">, - * ? / L C #</td>
</tr>
<tr>
<td class="confluenceTd">Year</td>
<td class="confluenceTd">NO</td>
<td class="confluenceTd">empty, 1970-2099</td>
<td class="confluenceTd">, - * /</td>
</tr>
</tbody>
</table>


<p>So cron expressions can be as simple as this: <tt>* * * * ? *</tt><br>
or more complex, like this: <tt>0 0/5 14,18,3-39,52 ? JAN,MAR,SEP MON-FRI 2002-2010</tt></p>


<h3><a name="CronTriggersTutorial-Specialcharacters">Special characters</a></h3>


<ul>
<li><b><tt>*</tt></b> (<em>"all values"</em>) - used to select all values within a field. For example, "*" in the minute field means <em>"every minute"</em>.</li>
</ul>



<ul>
<li><b><tt>?</tt></b> (<em>"no specific value"</em>) - useful
when you need to specify something in one of the two fields in which
the character is allowed, but not the other. For example, if I want my
trigger to fire on a particular day of the month (say, the 10th), but
don't care what day of the week that happens to be, I would put "10" in
the day-of-month field, and "?" in the day-of-week field. See the
examples below for clarification.</li>
</ul>



<ul>
<li><b><tt>-</tt></b> - used to specify ranges. For example, "10-12" in the hour field means <em>"the hours 10, 11 and 12"</em>.</li>
</ul>



<ul>
<li><b><tt>,</tt></b> - used to specify additional values. For example, "MON,WED,FRI" in the day-of-week field means <em>"the days Monday, Wednesday, and Friday"</em>.</li>
</ul>



<ul>
<li><b><tt>/</tt></b> - used to specify increments. For example, "0/15" in the seconds field means <em>"the seconds 0, 15, 30, and 45"</em>. And "5/15" in the seconds field means <em>"the seconds 5, 20, 35, and 50"</em>.
You can also specify '/' after the '*' character - in this case '*' is
equivalent to having '0' before the '/'. '1/3' in the day-of-month
field means <em>"fire every 3 days starting on the first day of the month"</em>.</li>
</ul>



<ul>
<li><b><tt>L</tt></b> (<em>"last"</em>) - has different meaning
in each of the two fields in which it is allowed. For example, the
value "L" in the day-of-month field means <em>"the last day of the month"</em>
- day 31 for January, day 28 for February on non-leap years. If used in
the day-of-week field by itself, it simply means "7" or "SAT". But if
used in the day-of-week field after another value, it means <em>"the last xxx day of the month"</em> - for example "6L" means <em>"the last friday of the month"</em>. When using the 'L' option, it is important not to specify lists, or ranges of values, as you'll get confusing results.</li>
</ul>



<ul>
<li><b><tt>W</tt></b> (<em>"weekday"</em>) - used to specify the
weekday (Monday-Friday) nearest the given day. As an example, if you
were to specify "15W" as the value for the day-of-month field, the
meaning is: <em>"the nearest weekday to the 15th of the month"</em>.
So if the 15th is a Saturday, the trigger will fire on Friday the 14th.
If the 15th is a Sunday, the trigger will fire on Monday the 16th. If
the 15th is a Tuesday, then it will fire on Tuesday the 15th. However
if you specify "1W" as the value for day-of-month, and the 1st is a
Saturday, the trigger will fire on Monday the 3rd, as it will not
'jump' over the boundary of a month's days. The 'W' character can only
be specified when the day-of-month is a single day, not a range or list
of days.</li>
</ul>



<div class="information-block" align="center"><div class="informationMacroPadding"><table class="tipMacro" border="0" cellpadding="5" cellspacing="0" width="85%"><tbody><tr><td valign="top" width="16"><img src="http://www.opensymphony.com/images/icons/emoticons/check.gif" alt="" align="middle" border="0" height="16" width="16"></td><td>
<p>The 'L' and 'W' characters can also be combined in the day-of-month field to yield 'LW', which translates to <em>"last weekday of the month"</em>.</p></td></tr></tbody></table></div></div>


<ul>
<li><b><tt>#</tt></b> - used to specify "the nth" XXX day of the month. For example, the value of "6#3" in the day-of-week field means <em>"the third Friday of the month"</em>
(day 6 = Friday and "#3" = the 3rd one in the month). Other examples:
"2#1" = the first Monday of the month and "4#5" = the fifth Wednesday
of the month. Note that if you specify "#5" and there is not 5 of the
given day-of-week in the month, then no firing will occur that month.</li>
</ul>



<ul>
<li><b><tt>C</tt></b> (<em>"calendar"</em>) - this means values
are calculated against the associated calendar, if any. If no calendar
is associated, then it is equivalent to having an all-inclusive
calendar. A value of "5C" in the day-of-month field means <em>"the first day included by the calendar on or after the 5th"</em>. A value of "1C" in the day-of-week field means <em>"the first day included by the calendar on or after Sunday"</em>.</li>
</ul>



<div class="information-block" align="center"><div class="informationMacroPadding"><table class="infoMacro" border="0" cellpadding="5" cellspacing="0" width="85%"><tbody><tr><td valign="top" width="16"><img src="http://www.opensymphony.com/images/icons/emoticons/information.gif" alt="" align="middle" border="0" height="16" width="16"></td><td>
<p>The legal characters and the names of months and days of the week are not case sensitive. <tt>MON</tt> is the same as <tt>mon</tt>.</p></td></tr></tbody></table></div></div>


<h3><a name="CronTriggersTutorial-Examples">Examples</a></h3>


<p>Here are some full examples:</p>


<table class="confluenceTable">

<tbody><tr>
<th class="confluenceTh">Expression</th>
<th class="confluenceTh">Meaning</th>
</tr>
<tr>
<td class="confluenceTd"><tt>0 0 12 * * ?</tt></td>
<td class="confluenceTd">Fire at 12pm (noon) every day</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 15 10 ? * *</tt></td>
<td class="confluenceTd">Fire at 10:15am every day</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 15 10 * * ?</tt></td>
<td class="confluenceTd">Fire at 10:15am every day</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 15 10 * * ? *</tt></td>
<td class="confluenceTd">Fire at 10:15am every day</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 15 10 * * ? 2005</tt></td>
<td class="confluenceTd">Fire at 10:15am every day during the year 2005</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 * 14 * * ?</tt></td>
<td class="confluenceTd">Fire every minute starting at 2pm and ending at 2:59pm, every day</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 0/5 14 * * ?</tt></td>
<td class="confluenceTd">Fire every 5 minutes starting at 2pm and ending at 2:55pm, every day</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 0/5 14,18 * * ?</tt></td>
<td class="confluenceTd">Fire every 5 minutes starting at 2pm and
ending at 2:55pm, AND fire every 5 minutes starting at 6pm and ending
at 6:55pm, every day</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 0-5 14 * * ?</tt></td>
<td class="confluenceTd">Fire every minute starting at 2pm and ending at 2:05pm, every day</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 10,44 14 ? 3 WED</tt></td>
<td class="confluenceTd">Fire at 2:10pm and at 2:44pm every Wednesday in the month of March.</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 15 10 ? * MON-FRI</tt></td>
<td class="confluenceTd">Fire at 10:15am every Monday, Tuesday, Wednesday, Thursday and Friday</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 15 10 15 * ?</tt></td>
<td class="confluenceTd">Fire at 10:15am on the 15th day of every month</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 15 10 L * ?</tt></td>
<td class="confluenceTd">Fire at 10:15am on the last day of every month</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 15 10 ? * 6L</tt></td>
<td class="confluenceTd">Fire at 10:15am on the last Friday of every month</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 15 10 ? * 6L</tt></td>
<td class="confluenceTd">Fire at 10:15am on the last Friday of every month</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 15 10 ? * 6L 2002-2005</tt></td>
<td class="confluenceTd">Fire at 10:15am on every last friday of every month during the years 2002, 2003, 2004 and 2005</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 15 10 ? * 6#3</tt></td>
<td class="confluenceTd">Fire at 10:15am on the third Friday of every month</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 0 12 1/5 * ?</tt></td>
<td class="confluenceTd">Fire at 12pm (noon) every 5 days every month, starting on the first day of the month.</td>
</tr>
<tr>
<td class="confluenceTd"><tt>0 11 11 11 11 ?</tt></td>
<td class="confluenceTd">Fire every November 11th at 11:11am.</td>
</tr>
</tbody>
</table>


<table class="warningMacro" border="0" cellpadding="5" cellspacing="0" width="85%">
<tbody><tr><td valign="top" width="16"><img src="http://www.opensymphony.com/images/icons/emoticons/forbidden.gif" alt="" align="middle" border="0" height="16" width="16"></td><td>
<p>Pay attention to the effects of '?' and '*' in the day-of-week and day-of-month fields!</p></td></tr></tbody>
</table>
<img src ="http://www.blogjava.net/ericwang/aggbug/21465.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-11-25 23:13 <a href="http://www.blogjava.net/ericwang/archive/2005/11/25/21465.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转载】J2EE中软件基础结构的瓶颈</title><link>http://www.blogjava.net/ericwang/archive/2005/11/25/21399.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Fri, 25 Nov 2005 02:35:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/11/25/21399.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/21399.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/11/25/21399.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/21399.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/21399.html</trackback:ping><description><![CDATA[<center><b><span style="font-size: 20px;">J2EE中软件基础结构的瓶颈</span></b></center>
<br>
<center>作者：Deepak Goel</center>
<br>
<center>译者:<a href="http://www.matrix.org.cn/user.shtml?username=xMatrix" target="_new">xMatrix</a></center>
<br>
<br>
<span style="color: Red;">版权声明：任何获得Matrix授权的网站，转载时请<b>务必</b>以超链接形式标明文章原始出处和作者信息及本声明</span><br>
作者:Deepak Goel;<a href="http://www.matrix.org.cn/user.shtml?username=xMatrix" target="_new">xMatrix</a><br>
原文地址:<a href="http://www.onjava.com/pub/a/onjava/2005/01/19/j2ee-bottlenecks.html" target="_new">http://www.onjava.com/pub/a/onjava/2005/01/19/j2ee-bottlenecks.html</a><br>
中文地址:<a href="http://www.matrix.org.cn/resource/article/43/43964_J2EE_bottlenecks.html" target="_new">http://www.matrix.org.cn/resource/article/43/43964_J2EE_bottlenecks.html</a><br>
关键词： bottlenecks J2EE<br>
<br>
<br>
可扩展性是系统中的一个非常重要的非功能性需求。但系统中可能有多个瓶颈会阻碍系统的扩展性。在这篇文章中，我们尝试分析软件基础结构成为瓶颈的案例，而不考虑硬件资源上的限制（如CPU、磁盘空间、网络速度等）。下面我们将探讨一下这个问题。<br>
<br>
下面是一些整篇文章中用到的一些术语：<br>
·吞吐量：系统支持的每秒能够处理的事务个数。<br>
·服务请求：每个事务中特定硬件的使用率，等于硬件使用率除以吞吐量。<br>
·硬件资源：指处理器、内存、磁盘和网络。<br>
·软件资源：指WEB线程、执行线程、BEAN池及数据库连接池等。<br>
·预计时间：用户预计两次并发提交请求之间的时间。<br>
·短时间法则：一个验证测试及确信测试环境不是瓶颈的法则。<br>
·响应时间：用户等待他提交的请求返回响应的时间。<br>
<br>
<b><span style="font-size: 16px;">理论基础</span></b><br>
<br>
任何J2EE应用通常都有下面几个层次，如图1：<br>
1、硬件基础结构资源（处理器、内存、磁盘和网络）<br>
2、软件基础结构资源（JVM,WEB服务器、应用服务器、数据库服务器）<br>
3、软件应用（J2EE应用）<br>
<br>
<img src="http://www.matrix.org.cn/resource/upload/forum/2005_11_20_220347_gmyuwCtfLC.gif" alt="image" style="display: inline;" onmouseover="javascript:ImgShowTip(this);" onload="javascript:ImgLoad(this);" onclick="javascript:ImgClick(this);" border="0">Figure 1. Snapshot of a J2EE system<br>
<br>
这儿有两个可能性导致瓶颈：硬件成为主要瓶颈或者软件成为主要瓶颈。在第一种情况，硬件资源不足够而软件资源很充分，如图2。随着负载的增加，硬件资源成为瓶颈，而软件可以继续扩展。减轻这个瓶颈的方案通常是扩大或者增加硬件。<br>
 <br>
<img src="http://www.matrix.org.cn/resource/upload/forum/2005_11_20_220422_GANdpBDXQy.gif" alt="image" style="display: inline;" onmouseover="javascript:ImgShowTip(this);" onload="javascript:ImgLoad(this);" onclick="javascript:ImgClick(this);" border="0"><br>
Figure 2. The hardware pipe becomes a bottleneck<br>
<br>
<br>
在第二种情况，硬件资源是足够的而软件资源相对有限。随着负载的增加，软件资源成为瓶颈，如图3。减轻这种瓶颈的方案通常是使用软件群集或优化软件。<br>
<br>
<img src="http://www.matrix.org.cn/resource/upload/forum/2005_11_20_220500_LJjdrEtBvU.gif" alt="image" style="display: inline;" onmouseover="javascript:ImgShowTip(this);" onload="javascript:ImgLoad(this);" onclick="javascript:ImgClick(this);" border="0"><br>
Figure 3. Software pipe becomes a bottleneck<br>
<br>
<b><span style="font-size: 16px;">应用服务器如何工作？</span></b><br>
<br>
来
考虑一下应用服务器的内部机制。应用服务器的基本功能包括事务管理、数据持久、对象池、SOCKET处理和请求处理。这些功能的流程如图4。有各种组件来
处理这些功能。这些组件需要同步应用服务器中的线程来维护数据的一致性并操作数据。虽然这种同步对应用服务器的功能正确性是必须而且有用的，但是也成为高
负载的限制，即使有足够的硬件资源。<br>
 <br>
<img src="http://www.matrix.org.cn/resource/upload/forum/2005_11_20_220535_YXCZiwyNLO.gif" alt="image" style="display: inline;" onmouseover="javascript:ImgShowTip(this);" onload="javascript:ImgLoad(this);" onclick="javascript:ImgClick(this);" border="0" height="450" width="106"><br>
Figure 4. Internals of an application server<br>
<br>
<b><span style="font-size: 16px;">实验</span></b><br>
<br>
为
了理解瓶颈的状况，我们在基于Windows/Intel平台用流行的J2EE应用服务器来测试一下JAVA
PETSTORE应用。一些测试用例如PetStore应用的浏览和购买周期来测试扩展性。我们确信整个测试环境（包括操作系统、JVM、应用服务器和应
用自身）已经尽可能优化了，而且J2EE应用没有任何瓶颈或同步问题。我们使用了多用户负载测试并观察了响应时间、吞吐量、资源利用率等指标。<br>
<br>
环境如下：<br>
1、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;J2EE PetStore应用<br>
2、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;J2EE应用服务器<br>
3、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Sun JVM 1.3<br>
4、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Windows 2000高级服务器<br>
5、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Intel Dell PowerEdge 8450 (8Intel至强800MHz处理器, 4GB RAM) <br>
6、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;100Mbps Cisco dedicated network<br>
7、&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;负载测试工具WebLoad<br>
<br>
<img src="http://www.matrix.org.cn/resource/upload/forum/2005_11_20_220735_KqAVrKWgxR.gif" alt="image" style="display: inline;" onmouseover="javascript:ImgShowTip(this);" onload="javascript:ImgLoad(this);" onclick="javascript:ImgClick(this);" border="0" width="600"><br>
<br>
<br>
<br>
 <br>
在这个测试中我们看到即使有足够的硬件资源，应用服务器的实例个数限制了扩展的能力。在这里软件资源（如执行线程、BEAN池大小、数据库池和其他应用服务器参数）优化后即使这些资源不足也不会影响系统的扩展。下面我们来研究一下减轻这种问题的方案。<br>
注：Sun J2EE PetStore可以被更多地优化来改善性能和可扩展性。<br>
<br>
<br>
<b><span style="font-size: 16px;">解决方案</span></b><br>
<br>
<b>同一机器上的群集</b><br>
<br>
当吞吐量满载了应用服务器的一个实例时，需要增加一个实例来减轻这种问题。这个方案如图5。<br>
<br>
<img src="http://www.matrix.org.cn/resource/upload/forum/2005_11_20_220827_hQvEqYkjsT.gif" alt="image" style="display: inline;" onmouseover="javascript:ImgShowTip(this);" onload="javascript:ImgLoad(this);" onclick="javascript:ImgClick(this);" border="0"><br>
Figure 5. Instance clusters on the same hardware box<br>
<br>
当前机器的CPU使用率只有40%因而有足够空间来增加一个实例。我们可以发现在增加了实例后，吞吐量也增加了50%，如表2<br>
<br>
<img src="http://www.matrix.org.cn/resource/upload/forum/2005_11_20_220925_ZROrHmmMeM.gif" alt="image" style="display: inline;" onmouseover="javascript:ImgShowTip(this);" onload="javascript:ImgLoad(this);" onclick="javascript:ImgClick(this);" border="0" width="600"><br>
<br>
<b>在不同机器的群集</b><br>
当吞吐量满载了应用服务器的一个实例时，机器的CPU使用率只有40%。因为8CPU的机器未完全利用，所以我们测试一下更低配置的机器。现在我们使用两个4CPU的机器，如图6。<br>
 <br>
<img src="http://www.matrix.org.cn/resource/upload/forum/2005_11_20_220946_hbrSzJRpcj.gif" alt="image" style="display: inline;" onmouseover="javascript:ImgShowTip(this);" onload="javascript:ImgLoad(this);" onclick="javascript:ImgClick(this);" border="0"><br>
Figure 6. Instance clusters on different hardware boxes<br>
<br>
<br>
我们发现4CPU机器的CPU使用率已达到80%，再增加实例也没有什么用处了。因此我们又增加一台4CPU的机器来运行应用的实例。在增加机器后，吞吐量几乎翻了一倍。在这里我们确信数据库服务器不会成为瓶颈。<br>
<br>
注：在上面的两台机器的测试中，负载平衡是通过一种不增加应用服务器实例功能负载的方式来处理的。但是在实际的生产环境中很难做到。<br>
<br>
因为我们观察到8CPU的机器被没有被完全利用，所以我们使用4CPU的机器重新测试了一遍。测试的结果可以在下表中看到，分别对应配置1和2。4CPU的配置几乎被完全利用了，这意味着使用4CPU的配置比8CPU的配置更实际，因为前者花费更少。<br>
<br>
<img src="http://www.matrix.org.cn/resource/upload/forum/2005_11_20_221315_XknXFnBVgV.gif" alt="image" style="display: inline;" onmouseover="javascript:ImgShowTip(this);" onload="javascript:ImgLoad(this);" onclick="javascript:ImgClick(this);" border="0"><br>
<br>
<b><span style="font-size: 16px;">小结</span></b><br>
<br>
这
些实验指出了软件基础结构如应用服务器实例可能成为瓶颈，并给出了一些解决方案来减轻这种问题（包括在相同或不同机器上的群集）。这个问题需要在J2EE
应用的负载计划或大小确定时优先考虑，因为这直接影响到应用的扩展性。这种想法很重要，下面我通过一个情景对话来表达。<br>
<br>
项目经理：你的意思是应用服务器（代表软件基础结构）会成为系统的瓶颈。<br>
性能架构师：是的<br>
项目经理：那为什么这种情况不是经常发生。<br>
性能架构师：是的，因为有时候瓶颈首先发生在硬件或应用本身。这时候应用服务器还没有使用到所有的扩展性。<br>
项目经理：这是事实。那么解决这种瓶颈的方法就是使用群集。如果有足够的硬件资源就在同一台机器上跑群集否则在多台机器中配置。<br>
性能架构师：是的。这也是在这篇文章中所得到的。<br>
<br>
<b>资源</b><br>
·onjava.com:<a href="http://www.onjava.com/" target="_new">onjava.com</a><br>
·Matrix-Java开发者社区:<a href="http://www.matrix.org.cn/" target="_new">http://www.matrix.org.cn/</a><br>
<br>
<br>
Deepak Goel是Infosys技术有限公司软件工程实验室的技术架构师。<br>
<br>
<img src ="http://www.blogjava.net/ericwang/aggbug/21399.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-11-25 10:35 <a href="http://www.blogjava.net/ericwang/archive/2005/11/25/21399.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>【转载】Quartz从入门到进阶</title><link>http://www.blogjava.net/ericwang/archive/2005/11/25/21398.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Fri, 25 Nov 2005 02:34:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/11/25/21398.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/21398.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/11/25/21398.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/21398.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/21398.html</trackback:ping><description><![CDATA[<center><b><span style="font-size: 20px;">Quartz从入门到进阶</span></b></center>
<br>
<center>作者：Cavaness</center>
<br>
<center>译者:<a href="http://www.matrix.org.cn/user.shtml?username=David_w_johnson" target="_new">David_w_johnson</a></center>
<br>
<br>
<span style="color: Red;">版权声明：任何获得Matrix授权的网站，转载时请<b>务必</b>以超链接形式标明文章原始出处和作者信息及本声明</span><br>
作者:Cavaness;<a href="http://www.matrix.org.cn/user.shtml?username=David_w_johnson" target="_new">David_w_johnson</a><br>
原文地址:<a href="http://www.onjava.com/pub/a/onjava/2005/09/28/what-is-quartz.html" target="_new">http://www.onjava.com/pub/a/onjava/2005/09/28/what-is-quartz.html</a><br>
中文地址:<a href="http://www.matrix.org.cn/resource/article/43/43968_Quartz.html" target="_new">http://www.matrix.org.cn/resource/article/43/43968_Quartz.html</a><br>
关键词： Quartz<br>
<br>
<br>
<b><span style="font-size: 16px;">Quartz</span></b><br>
<br>
Quartz
是一个开源的作业调度框架，它完全由java写成，并设计用于J2SE和J2EE应用中。它提供了巨大的灵活性而不牺牲简单性。你能够用它来为执行一个作
业而创建简单的或复杂的调度。它有很多特征，如：数据库支持，集群，插件，EJB作业预构建，JavaMail及其它，支持cron-like表达式等
等。<br>
<br>
<b>本文内容</b><br>
<span style="color: Maroon;">1．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Quartz让任务调度简单<br>2．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Quartz的发展史<br>3．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上手Quartz<br>4．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Quartz内部架构<br>5．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;作业<br>6．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;作业管理和存储<br>7．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;有效作业存储<br>8．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;作业和触发器<br>9．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;调度一个作业<br>10．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;用调度器（Scheduler）调用你的作业<br>11．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;编程调度同声明性调度<br>12．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;有状态和无状态作业<br>13．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Quartz框架的其他特征<br>14．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Quartz下一步计划<br>15．&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;了解更多Quartz特征</span><br>
<br>
你
曾经需要应用执行一个任务吗？这个任务每天或每周星期二晚上11：30，或许仅仅每个月的最后一天执行。一个自动执行而无须干预的任务在执行过程中如果发
生一个严重错误，应用能够知到其执行失败并尝试重新执行吗？你和你的团队是用java编程吗？如果这些问题中任何一个你回答是，那么你应该使用
Quartz调度器。<br>
<br>
<span style="color: Red;"><b>旁注：</b></span>Matrix目前就大量使用到了Quartz。比如，排名统计功能的实现，在Jmatrix里通过Quartz定义了一个定时调度作业，在每天凌晨一点，作业开始工作，重新统计大家的Karma和排名等。<br>
还有，RSS文件的生成，也是通过Quartz定义作业，每隔半个小时生成一次RSS XML文件。<br>
所以Quartz使用的地方很多，本文无疑是一篇很好的入门和进阶的文章，在此，感谢David w Johnson的努力！<br>
<br>
<br>
<b><span style="font-size: 16px;">Quartz让作业调度简单</span></b><br>
<br>
Quartz
是一个完全由java编写的开源作业调度框架。不要让作业调度这个术语吓着你。尽管Quartz框架整合了许多额外功能，
但就其简易形式看，你会发现它易用得简直让人受不了！。简单地创建一个实现org.quartz.Job接口的java类。Job接口包含唯一的方法：<br>
<br>
<pre title="pre code" class="overflow">public void execute(JobExecutionContext context) <br>&nbsp;&nbsp;&nbsp;&nbsp; throws JobExecutionException;</pre>
<br>
<br>
在
你的Job接口实现类里面，添加一些逻辑到execute()方法。一旦你配置好Job实现类并设定好调度时间表，Quartz将密切注意剩余时间。当调
度程序确定该是通知你的作业的时候，Quartz框架将调用你Job实现类（作业类）上的execute()方法并允许做它该做的事情。无需报告任何东西
给调度器或调用任何特定的东西。仅仅执行任务和结束任务即可。如果配置你的作业在随后再次被调用，Quartz框架将在恰当的时间再次调用它。<br>
<br>
如
果你使用了其它流行的开源框架象struts，你会对Quartz的设计和部件感到舒适。虽然两个开源工程是解决完全不同的问题，还是有很多相似的之处，
就是开源软件用户每天感觉很舒适。Quartz能用在单机J2SE应用中，作为一个RMI服务器，也可以用在web应用中，甚至也可以用在J2EE应用服
务器中。<br>
<br>
<b><span style="font-size: 16px;">Quartz的发展史</span></b><br>
<br>
尽
管Quartz今年开始受到人们注意，但还是暂时流行。Quartz由James
House创建并最初于2001年春天被加入sourceforge工程。接下来的几年里，有许多新特征和版本出现，但是直到项目迁移到新的站点并成为
OpenSymphony项目家族的一员，才开始真正启动并受到应有的关注。<br>
James House仍然和几个协助他的业余开发者参与大量开发工作。Quartz开发团队今年能发布几个新版本，包括当前正处在候选发布阶段的1.5版。<br>
<br>
<b><span style="font-size: 16px;">上手Quartz</span></b><br>
Quartz工程驻留在OpenSymphony站点上。在Quartz站点上可以找到许多有用的资源：JavaDocs，包含指南的文档，CVS访问，用户和开发者论坛的连接，当然也有下载。<br>
从
下载连接取得Quartz的发布版本，并且解压到到本地目录。这个下载文件包含了一个预先构建好的Quartz二进制文件（quartz.jar），你可
以将它放进自己的应用中。Quartz框架只需要少数的第三方库，并且这些三方库是必需的，你很可能已经在使用这些库了。<br>
<br>
你要把
Quartz的安装目录的&lt;quartz- install&gt;/lib/core 和
&lt;quartz-install&gt;/lib/optional目录中的第三方库加进你自己的工程中。大多数第三方库是我们所熟知和喜欢的标准
Jakarta Commons库，像Commons Logging, Commons BeantUtils等等。<br>
&nbsp;&nbsp;&nbsp;&nbsp; <br>
<b>quartz.properties文件</b><br>
Quartz
有一个叫做quartz.properties的配置文件，它允许你修改框架运行时环境。缺省是使用Quartz.jar里面的
quartz.properties文件。当然，你应该创建一个quartz.properties文件的副本并且把它放入你工程的classes目录中
以便类装载器找到它。quartz.properties样本文件如例1所示。<br>
<br>
例1.quartz.properties文件允许修改Quartz运行环境：<br>
<br>
<pre title="pre code" class="overflow">#===============================================================<br># Configure Main Scheduler Properties&nbsp;&nbsp;<br>#===============================================================<br><br>org.quartz.scheduler.instanceName = QuartzScheduler<br>org.quartz.scheduler.instanceId = AUTO<br><br>#===============================================================<br># Configure ThreadPool&nbsp;&nbsp;<br>#===============================================================<br><br>org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool<br>org.quartz.threadPool.threadCount =&nbsp;&nbsp;5<br>org.quartz.threadPool.threadPriority = 5<br><br>#===============================================================<br># Configure JobStore&nbsp;&nbsp;<br>#===============================================================<br><br>org.quartz.jobStore.misfireThreshold = 60000<br>org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore</pre>
<br>
<br>
一旦将Quartz.jar文件和第三方库加到自己的工程里面并且quartz.properties文件在工程的classes目录中，就可以创建作业了。然而，在做这之前，我们暂且回避一下先简短讨论一下Quartz架构。<br>
<br>
<b><span style="font-size: 16px;">Quartz内部架构</span></b><br>
在
规模方面，Quartz跟大多数开源框架类似。大约有300个java类和接口，并被组织到12个包中。这可以和Apache
Struts把大约325个类和接口以及组织到11个包中相比。尽管规模几乎不会用来作为衡量框架质量的一个特性，但这里的关键是quarts内含很多功
能，这些功能和特性集是否成为、或者应该成为评判一个开源或非开源框架质量的因素。<br>
<br>
<b>Quartz调度器</b><br>
Quartz
框架的核心是调度器。调度器负责管理Quartz应用运行时环境。调度器不是靠自己做所有的工作，而是依赖框架内一些非常重要的部件。Quartz不仅仅
是线程和线程管理。为确保可伸缩性，Quartz采用了基于多线程的架构。启动时，框架初始化一套worker线程，这套线程被调度器用来执行预定的作
业。这就是Quartz怎样能并发运行多个作业的原理。Quartz依赖一套松耦合的线程池管理部件来管理线程环境。本片文障中，我们会多次提到线程池管
理，但Quartz里面的每个对象是可配置的或者是可定制的。所以，例如，如果你想要插进自己线程池管理设施，我猜你一定能！<br>
<br>
<b><span style="font-size: 16px;">作业</span></b><br>
用Quartz
的行话讲，作业是一个执行任务的简单java类。任务可以是任何java代码。只需你实现org.quartz.Job接口并且在出现严重错误情况下抛出
JobExecutionException异常即可。Job接口包含唯一的一个方法execute()，作业从这里开始执行。一旦实现了Job接口和
execute()方法，当Quartz确定该是作业运行的时候，它将调用你的作业。Execute()方法内就完全是你要做的事情。下面有一些你要在作
业里面做事情的例子：<br>
·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;用JavaMail（或者用其他的像Commons Net一样的邮件框架）发送邮件<br>
·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;创建远程接口并且调用在EJB上的方法<br>
·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;获取Hibernate Session，查询和更新关系数据库里的数据<br>
·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用OSWorkflow并且从作业调用一个工作流<br>
·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;使用FTP和到处移动文件<br>
·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;调用Ant构建脚本开始预定构建<br>
<br>
这种可能性是无穷的，正事这种无限可能性使得框架功能如此强大。Quartz给你提供了一个机制来建立具有不同粒度的、可重复的调度表，于是，你只需创建一个java类，这个类被调用而执行任务。<br>
<br>
<b><span style="font-size: 16px;">作业管理和存储</span></b><br>
作
业一旦被调度，调度器需要记住并且跟踪作业和它们的执行次数。如果你的作业是30分钟后或每30秒调用，这不是很有用。事实上，作业执行需要非常准确和即
时调用在被调度作业上的execute()方法。Quartz通过一个称之为作业存储（JobStore）的概念来做作业存储和管理。<br>
<br>
<br>
<b><span style="font-size: 16px;">有效作业存储</span></b><br>
<br>
Quartz
提供两种基本作业存储类型。第一种类型叫做RAMJobStore，它利用通常的内存来持久化调度程序信息。这种作业存储类型最容易配置、构造和运行。对
许多应用来说，这种作业存储已经足够了。然而，因为调度程序信息是存储在被分配给JVM的内存里面，所以，当应用程序停止运行时，所有调度信息将被丢失。
如果你需要在重新启动之间持久化调度信息，则将需要第二种类型的作业存储。<br>
<br>
第二种类型的作业存储实际上提供两种不同的实现，但两种实现一
般都称为JDBC作业存储。两种JDBC作业存储都需要JDBC驱动程序和后台数据库来持久化调度程序信息。这两种类型的不同在于你是否想要控制数据库事
务或这释放控制给应用服务器例如BEA's WebLogic或Jboss。（这类似于J2EE领域中，Bean管理的事务和和容器管理事务之间的区别）<br>
这两种JDBC作业存储是：<br>
<br>
·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JobStoreTX：当你想要控制事务或工作在非应用服务器环境中是使用<br>
·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JobStoreCMT：当你工作在应用服务器环境中和想要容器控制事务时使用。<br>
<br>
JDBC作业存储为需要调度程序维护调度信息的用户而设计。<br>
<br>
<b><span style="font-size: 16px;">作业和触发器</span></b><br>
<br>
Quartz
设计者做了一个设计选择来从调度分离开作业。Quartz中的触发器用来告诉调度程序作业什么时候触发。框架提供了一把触发器类型，但两个最常用的是
SimpleTrigger和CronTrigger。SimpleTrigger为需要简单打火调度而设计。典型地，如果你需要在给定的时间和重复次数
或者两次打火之间等待的秒数打火一个作业，那么SimpleTrigger适合你。另一方面，如果你有许多复杂的作业调度，那么或许需要
CronTrigger。<br>
<br>
CronTrigger是基于Calendar-like调度的。当你需要在除星期六和星期天外的每天上午10点半执行作业时，那么应该使用CronTrigger。正如它的名字所暗示的那样，CronTrigger是基于Unix克隆表达式的。<br>
<br>
作为一个例子，下面的Quartz克隆表达式将在星期一到星期五的每天上午10点15分执行一个作业。<br>
<span style="color: Blue;">0 15 10 ? * MON-FRI</span><br>
<br>
下面的表达式<br>
<span style="color: Blue;">0 15 10 ? * 6L 2002-2005</span><br>
将在2002年到2005年的每个月的最后一个星期五上午10点15分执行作业。<br>
<br>
你不可能用SimpleTrigger来做这些事情。你可以用两者之中的任何一个，但哪个跟合适则取决于你的调度需要。<br>
<br>
<br>
<b><span style="font-size: 16px;">调度一个作业</span></b><br>
<br>
让
我们通过看一个例子来进入实际讨论。现假定你管理一个部门，无论何时候客户在它的FTP服务器上存储一个文件，都得用电子邮件通知它。我们的作业将用
FTP登陆到远程服务器并下载所有找到的文件。然后，它将发送一封含有找到和下载的文件数量的电子邮件。这个作业很容易就帮助人们整天从手工执行这个任务
中解脱出来，甚至连晚上都无须考虑。我们可以设置作业循环不断地每60秒检查一次，而且工作在7×24模式下。这就是Quartz框架完全的用途。<br>
<br>
首先创建一个Job类，将执行FTP和Email逻辑。下例展示了Quartz的Job类，它实现了org.quartz.Job接口。<br>
<br>
例2．从FTP站点下载文件和发送email的Quartz作业<br>
<br>
<pre title="pre code" class="overflow">public class ScanFTPSiteJob implements Job {<br>&nbsp;&nbsp;&nbsp;&nbsp;private static Log logger = LogFactory.getLog(ScanFTPSiteJob.class);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;/*<br>&nbsp;&nbsp;&nbsp;&nbsp; * Called the scheduler framework at the right time<br>&nbsp;&nbsp;&nbsp;&nbsp; */<br>&nbsp;&nbsp;&nbsp;&nbsp;public void execute(JobExecutionContext context)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throws JobExecutionException {<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JobDataMap jobDataMap = context.getJobDataMap();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Check the ftp site for files<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;File[] files = JobUtil.checkForFiles(jobDataMap);<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JobUtil.sendEmail(jobDataMap, files);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (Exception ex) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;throw new JobExecutionException(ex.getMessage());<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</pre>
<br>
<br>
我
们故意让ScanFTPSiteJob保持很简单。我们为这个例子创建了一个叫做JobUtil的实用类。它不是Quartz的组成部分，但对构建各种作
业能重用的实用程序库来说是有意义的。我们可以轻易将那种代码组织进作业类中，quarts
调度器一样好用，因为我们一直在使用quarts，所以那些代码可继续重用。<br>
<br>
JobUtil.checkForFiles() and JobUtil.sendEmail()方法使用的参数是Quartz创建的JobDataMap的实例。实例为每个作业的执行而创建，它是向作业类传递配置参数的方法。<br>
这里并没有展示JobUtil的实现，但我们能用Jakarta上的Commons Net轻易地实现FTP和Email功能。<br>
<br>
<b><span style="font-size: 16px;">用调度器调用作业</span></b><br>
<br>
首先创建一个作业，但为使作业能被调度器调用，你得向调度程序说明你的作业的调用时间和频率。这个事情由与作业相关的触发器来完成。因为我们仅仅对大约每60秒循环调用作业感兴趣，所以打算使用SimpleTrigger。<br>
<br>
作业和触发器通过Quartz调度器接口而被调度。我们需要从调度器工厂类取得一个调度器的实例。最容易的办法是调用StdSchedulerFactory这个类上的静态方法getDefaultScheduler()。<br>
使用Quartz框架，你需要调用start()方法来启动调度器。例3的代码遵循了大多数Quartz应用的一般模式：创建一个或多个作业，创建和设置触发器，用调度器调度作业和触发器，启动调度器。<br>
<br>
例3．Quartz作业通过Quartz调度器而被调度<br>
<br>
<pre title="pre code" class="overflow">public class MyQuartzServer {<br><br>&nbsp;&nbsp;&nbsp;&nbsp;public static void main(String[] args) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;MyQuartzServer server = new MyQuartzServer();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;server.startScheduler();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} catch (SchedulerException ex) {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ex.printStackTrace();<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br><br>&nbsp;&nbsp;&nbsp;&nbsp;protected void startScheduler() throws SchedulerException {<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Use the factory to create a Scheduler instance<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// JobDetail holds the definition for Jobs<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JobDetail jobDetail = <br>new JobDetail("ScanFTPJob", Scheduler.DEFAULT_GROUP,<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ScanFTPSiteJob.class);<br><br>// Store job parameters to be used within execute()<br>jobDetail.getJobDataMap().put(<br>"FTP_HOST", <br>"\\home\\cavaness\\inbound");<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Other neccessary Job parameters here<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Create a Trigger that fires every 60 seconds<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Trigger trigger = TriggerUtils.makeSecondlyTrigger(60);<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Setup the Job and Trigger with the Scheduler<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;scheduler.scheduleJob(jobDetail, trigger );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// Start the Scheduler running<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;scheduler.start();<br>&nbsp;&nbsp;&nbsp;&nbsp;}<br>}</pre>
<br>
<br>
<br>
<b><span style="font-size: 16px;">编程调度同声明性调度</span></b><br>
<br>
例3中，我们通过编程的方法调度我们的ScanFTPSiteJob作业。就是说，我们用java代码来设置作业和触发器。Quartz框架也支持在xml文件里面申明性的设置作业调度。申明性方法允许我们更快速地修改哪个作业什么时候被执行。<br>
<br>
Quartz
框架有一个插件，这个插件负责读取xml配置文件。xml配置文件包含了关于启动Quartz应用的作业和触发器信息。所有xml文件中的作业连同相关的
触发器都被加进调度器。你仍然需要编写作业类，但配置那些作业类的调度器则非常动态化。例4展示了一个用申明性方式执行与例3代码相同的逻辑的xml配置
文件。<br>
<br>
例4．能使用xml文件调度的作业<br>
<br>
<pre title="pre code" class="overflow">&lt;?xml version='1.0' encoding='utf-8'?&gt;<br>&lt;quartz&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;job&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;job-detail&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;ScanFTPSiteJob&lt;/name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;group&gt;DEFAULT&lt;/group&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;description&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;A job that scans an ftp site for files<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/description&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;job-class&gt;ScanFTPSiteJob&lt;/job-class&gt;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;job-data-map allows-transient-data="true"&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;entry&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;key&gt;FTP_HOST&lt;/key&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;\home\cavaness\inbound&lt;/value&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/entry&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!--&nbsp;&nbsp;Other neccessary Job parameters here --&gt;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/job-data-map&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/job-detail&gt;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;trigger&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;simple&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;name&gt;ScanFTPSiteJobTrigger&lt;/name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;group&gt;DEFAULT&lt;/group&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;job-name&gt;ScanFTPSiteJob&lt;/job-name&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;job-group&gt;DEFAULT&lt;/job-group&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;start-time&gt;2005-09-11 6:10:00 PM&lt;/start-time&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;!-- repeat indefinitely every 60 seconds --&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;repeat-count&gt;-1&lt;/repeat-count&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;repeat-interval&gt;60000&lt;/repeat-interval&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/simple&gt;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;/trigger&gt;<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&lt;/job&gt;<br>&lt;/quartz&gt;</pre>
<br>
<br>
你可以将xml文件中的元素跟例3代码作个比较，它们从概念上来看是相同的。使用例4式的申明性方法的好处是维护变得极其简单，只需改变xml配置文件和重新启动Quartz应用即可。无须修改代码，无须重新编译，无须重新部署。<br>
<br>
<b><span style="font-size: 16px;">有状态和无状态作业</span></b><br>
<br>
在本文中你所看到的作业到是无状态的。这意味着在两次作业执行之间，不会去维护作业执行时JobDataMap的状态改变。如果你需要能增、删，改JobDataMap的值，而且能让作业在下次执行时能看到这个状态改变，则需要用Quartz有状态作业。<br>
如
果你是一个有经验的EJB开发者的话，深信你会立即退缩，因为有状态带有负面含义。这主要是由于EJB带来的伸缩性问题。Quartz有状态作业实现了
org.quartz.StatefulJob接口。无状态和有状态作业的关键不同是有状态作业在每次执行时只有一个实例。大多数情况下，有状态的作业不
回带来大的问题。然而，如果你有一个需要频繁执行的作业或者需要很长时间才能完成的作业，那么有状态作业可能给你带来伸缩性问题。<br>
<br>
<b><span style="font-size: 16px;">Quartz框架的其他特征</span></b><br>
<br>
Quartz框架有一个丰富的特征集。事实上，quarts有太多特性以致不能在一种情况中全部领会，下面列出了一些有意思的特征，但没时间在此详细讨论。<br>
<br>
<b>监听器和插件</b><br>
<br>
每
个人都喜欢监听和插件。今天，几乎下载任何开源框架，你必定会发现支持这两个概念。监听是你创建的java类，当关键事件发生时会收到框架的回调。例如，
当一个作业被调度、没有调度或触发器终止和不再打火时，这些都可以通过设置来来通知你的监听器。Quartz框架包含了调度器监听、作业和触发器监听。你
可以配置作业和触发器监听为全局监听或者是特定于作业和触发器的监听。<br>
<br>
一旦你的一个具体监听被调用，你就能使用这个技术来做一些你想要在
监听类里面做的事情。例如，你如果想要在每次作业完成时发送一个电子邮件，你可以将这个逻辑写进作业里面，也可以JobListener里面。写进
JobListener的方式强制使用松耦合有利于设计上做到更好。<br>
<br>
Quartz插件是一个新的功能特性，无须修改Quartz源码便可
被创建和添加进Quartz框架。他为想要扩展Quartz框架又没有时间提交改变给Quartz开发团队和等待新版本的开发人员而设计。如果你熟悉
Struts插件的话，那么完全可以理解Quartz插件的使用。<br>
<br>
与其Quartz提供一个不能满足你需要的有限扩展点，还不如通过使用插件来拥有可修整的扩展点。<br>
<br>
<b>集群Quartz应用</b><br>
Quartz应用能被集群，是水平集群还是垂直集群取决于你自己的需要。集群提供以下好处：<br>
·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;伸缩性<br>
·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;搞可用性<br>
·&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;负载均衡<br>
目前，Quartz只能借助关系数据库和JDBC作业存储支持集群。将来的版本这个制约将消失并且用RAMJobStore集群将是可能的而且将不需要数据库的支持。<br>
<br>
<b>Quartz web应用</b><br>
使
用框架几个星期或几个月后，Quartz用户所显示的需求之一是需要集成Quartz到图形用户界面中。目前Quartz框架已经有一些工具允许你使用
Java
servlet来初始化和启动Quartz。一旦你可以访问调度器实例，你就可以把它存储在web容器的servlet上下文中
（ServletContext中）并且可以通过调度器接口管理调度环境。<br>
<br>
幸运的是一些开发者已正影响着单机Quartz web应用，它用来更好地管理调度器环境。构建在若干个流行开源框架如Struts和Spring之上的图形用户界面支持很多功能，这些功能都被包装进一个简单接口。GUI的一个画面如图1所示：<br>
<br>
 <br>
<img src="http://www.matrix.org.cn/resource/upload/article/2005_11_21_215014_IqWaiHmDYR.gif" alt="image" style="display: inline;" onmouseover="javascript:ImgShowTip(this);" onload="javascript:ImgLoad(this);" onclick="javascript:ImgClick(this);" border="0"><br>
图1.Quartz Web应用允许比较容易地管理Quartz环境。<br>
<br>
<b><span style="font-size: 16px;">Quartz的下一步计划</span></b><br>
<br>
Quartz是一个活动中的工程。Quartz开发团队明确表示不会停留在已有的荣誉上。Quartz下一个主要版本已经在启动中。你可以在OpenSymphony的 wiki上体验一下Quartz 2.0的设计和特征。<br>
总之，Quartz用户每天都自由地添加特性建议和设计创意以便能被核心框架考虑（看重）。<br>
<br>
<b><span style="font-size: 16px;">了解更多Quartz特征</span></b><br>
<br>
当你开始使用Quartz框架的更多特性时，User and Developer Forum论坛变成一个回答问题和跟其他Quartz用户沟通的极其有用的资源。经常去逛逛这个论坛时很有好处的，你也可以依靠James House来共享与你的需要相关的知识和意见。<br>
<br>
这个论坛时免费的，你不必登陆便可以查找和查看归档文件。然而，如果你觉得这个论坛比较好而且需要向某人回复问题时，你必须得申请一个免费帐号并用该帐号登陆。<br>
<br>
<b>资源</b><br>
·onjava.com:<a href="http://www.onjava.com/" target="_new">onjava.com</a><br>
·Matrix-Java开发者社区:<a href="http://www.matrix.org.cn/" target="_new">http://www.matrix.org.cn/</a><br>
<br>
Chuck
Cavaness毕业于Georgia
Tech并获得计算机科学与技术学位。他在健康，银行，B2B领域开发过基于java的企业系统。同时也是O'Reilly出版的两本书
Programming Jakarta Struts 和 Jakarta Struts Pocket Reference的作者。<br>
<br>
<img src ="http://www.blogjava.net/ericwang/aggbug/21398.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-11-25 10:34 <a href="http://www.blogjava.net/ericwang/archive/2005/11/25/21398.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>[收藏]如何获取客户端MAC地址</title><link>http://www.blogjava.net/ericwang/archive/2005/11/23/21179.html</link><dc:creator>Dion</dc:creator><author>Dion</author><pubDate>Wed, 23 Nov 2005 12:33:00 GMT</pubDate><guid>http://www.blogjava.net/ericwang/archive/2005/11/23/21179.html</guid><wfw:comment>http://www.blogjava.net/ericwang/comments/21179.html</wfw:comment><comments>http://www.blogjava.net/ericwang/archive/2005/11/23/21179.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/ericwang/comments/commentRss/21179.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/ericwang/services/trackbacks/21179.html</trackback:ping><description><![CDATA[<div class="post">
	<div class="postTitle">
		<a id="viewpost1_TitleUrl" class="postTitle2" href="../../eamoi/archive/2005/01/20/491.html">[收藏]如何获取客户端MAC地址</a>
	</div>
	<p>方法一:</p>
<p>调用Windows的DOS命令,从输出结果中读取MAC地址:</p>
<p>public static String getMACAddress() {<br><br>String address = "";<br>String os = System.getProperty("os.name");<br>if ( os != null &amp;&amp; os.startsWith("Windows")) {<br>try {<br>String command = "cmd.exe /c ipconfig /all";<br>Process p = Runtime.getRuntime().exec(command);<br>BufferedReader br =<br>new BufferedReader(<br>new InputStreamReader(p.getInputStream()));<br>String line;<br>while ((line = br.readLine()) != null) {<br>if (line.indexOf("Physical Address") &gt; 0) {<br>int index = line.indexOf(":");<br>index += 2;<br>address = line.substring(index);<br>break;<br>}<br>}<br>br.close();<br>return address.trim();<br>}<br>catch (IOException e) { }<br>}<br>return address;<br>}<br><br>We can replace the "ipconfig" to "ping x.x.x.x" and "arp -a"...We can get the mac list...haha!!<br></p>
<p>缺点:只能取得服务器端MAC地址.如果要取得客户端的MAC地址,需用Applet.只针对MS-WIN系统.</p>
<p>&nbsp;</p>
<p>方法二:</p>
<p>可以用JS或vbscript来调用WMI接口来获取Client端的MAC地址.</p>
<p><br>&nbsp;<br>&nbsp;<br>&nbsp;<script for="foo" event="OnCompleted(hResult,pErrorObject, pAsyncContext)" language="JScript"></P>
<P>             document.forms[0].txtMACAddr.value=unescape(MACAddr);<BR>             document.forms[0].txtIPAddr.value=unescape(IPAddr);<BR>             document.forms[0].txtDNSName.value=unescape(sDNSName);<BR>             //document.formbar.submit();<BR>        </script><br>&nbsp;<br>&nbsp;<script for="foo" event="OnObjectReady(objObject,objAsyncContext)" language="JScript"></P>
<P>         if(objObject.IPEnabled != null && objObject.IPEnabled != "undefined" && objObject.IPEnabled == true)<BR>                  {</P>
<P>                   if(objObject.MACAddress != null && objObject.MACAddress != "undefined")<BR>                         MACAddr = objObject.MACAddress;</P>
<P>                   if(objObject.IPEnabled && objObject.IPAddress(0) != null && objObject.IPAddress(0) != "undefined")<BR>                         IPAddr = objObject.IPAddress(0);</P>
<P>                   if(objObject.DNSHostName != null && objObject.DNSHostName != "undefined")<BR>                      sDNSName = objObject.DNSHostName;</P>
<P>                   }<BR>        </script><br>&nbsp;<br>&nbsp;<br>&nbsp;&nbsp;<object classid="CLSID:76A64158-CB41-11D1-8B02-00600806D9B6" id="locator" viewastext=""><br>&nbsp;&nbsp;</object><br>&nbsp;&nbsp;<object classid="CLSID:75718C9A-F029-11d1-A1AC-00C04FB6C223" id="foo"><br>&nbsp;&nbsp;</object></p>
<p>&nbsp;&nbsp;<script language="JScript"><BR>               var service = locator.ConnectServer();<BR>               var MACAddr ;<BR>               var IPAddr ;<BR>               var DomainAddr;<BR>               var sDNSName;<BR>               service.Security_.ImpersonationLevel=3;<BR>               service.InstancesOfAsync(foo, 'Win32_NetworkAdapterConfiguration');<BR>         </script></p>
<p><br>&nbsp;&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;<input name="txtMACAddr" type="text&nbsp;"><br>&nbsp;&nbsp;&nbsp;<input name="txtIPAddr" type="text&nbsp;"><br>&nbsp;&nbsp;&nbsp;<input name="txtDNSName" type="text&nbsp;"></p>
<p>&nbsp;&nbsp;<br>&nbsp;<br></p>
<p>忘了附上原文的出处了：<br>How to get IP address of the browser when its operating behind a proxy/firewall? (applets...activex....??)<br><a class="embed_link" href="http://www.faqts.com/knowledge_base/view.phtml/aid/9005/fid/125" target="_blank">http://www.faqts.com/knowledge_base/view.phtml/aid/9005/fid/125</a><br><br>关于WMI的详细信息可以参看MSDN:<br><a class="embed_link" href="http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/wmi_tasks_for_scripts_and_applications.asp" target="_blank">http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/wmi_tasks_for_scripts_and_applications.asp</a><br><br>平心而论，WMI的很强大的。原先需要动用重量级编程工具才能做到的事，现在用js/vbscript就可以做了。<br></p>
<p>获取多块网卡的MAC地址:</p>
<p>if(objObject.MACAddress != null &amp;&amp; objObject.MACAddress != "undefined"){<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
MACAddr = objObject.MACAddress;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
alert( MACAddr );<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br></p>
<p>缺点:需要ActiveX支持.对MS-WIN系统有效.</p>
<p>方法三:</p>
<p>想137口发送UDP查询:</p>
<p>WINDOWS平台的客户端(当获取时它转换为服务端角色),NETBIOS协议在137口上,我们只要向它的137口发送UDP查询,获取它的返回值就可以获取到它所有的网卡地址.</p>
<p><font color="#800080">以上内容来自dev2dev的讨论帖:</font></p>
<p><a href="http://dev2dev.bea.com.cn/bbs/thread.jspa?forumID=121&amp;threadID=12941&amp;tstart=0">http://dev2dev.bea.com.cn/bbs/thread.jspa?forumID=121&amp;threadID=12941&amp;tstart=0</a></p>
<p>&nbsp;</p>
	<div class="postDesc">posted on 2005-01-20 08:50 <a href="../../eamoi/">eamoi</a> 阅读(636) <a href="../../eamoi/archive/2005/01/20/491.html#Post">评论(1)</a> &nbsp;<a href="../../eamoi/admin/EditPosts.aspx?postid=491">编辑</a>&nbsp;<a href="../../eamoi/AddToFavorite.aspx?id=491">收藏</a> <a href="javascript:d=document;t=d.selection?(d.selection.type!='None'?d.selection.createRange().text:''):(d.getSelection?d.getSelection():'');void(keyit=window.open('http://www.365key.com/storeit.aspx?t='+escape(d.title)+'&u='+escape(d.location.href)+'&c='+escape(t),'keyit','scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes'));keyit.focus();" title="功能强大的网络收藏夹，一秒钟操作就可以轻松实现保存带来的价值、分享带来的快乐">收藏至365Key</a> 所属分类: <a href="../../eamoi/category/291.html">Java</a> </div>
</div>

<img src="../../eamoi/aggbug/491.html?webview=1" height="1" width="1"><!--
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/">
<rdf:Description
rdf:about="http://www.blogjava.net/eamoi/archive/2005/01/20/491.html"
dc:identifier="http://www.blogjava.net/eamoi/archive/2005/01/20/491.html"
dc:title="[收藏]如何获取客户端MAC地址"
trackback:ping="http://www.blogjava.net/eamoi/services/trackbacks/491.aspx" />
</rdf:RDF>
--><!--done-->





<br>

<b>评论:</b>

	

		
			
			<a title="permalink: Java系统如何获取客户端的MAC地址? " href="../../eamoi/archive/2005/01/20/491.html#499">#</a>&nbsp;<a name="499"></a>Java系统如何获取客户端的MAC地址? <a name="Post"></a>[TrackBack]
				2005-01-20 11:01 | <a id="Comments1_CommentList__ctl0_NameLink" href="http://blog.csdn.net/eamoi/archive/2005/01/20/260611.aspx" target="_blank">eamoi</a><br>

				Ping Back来自：blog.csdn.net<br>
eamoi引用了该文章,地址:<a href="http://blog.csdn.net/eamoi/archive/2005/01/20/260611.aspx">http://blog.csdn.net/eamoi/archive/2005/01/20/260611.aspx</a><img src ="http://www.blogjava.net/ericwang/aggbug/21179.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/ericwang/" target="_blank">Dion</a> 2005-11-23 20:33 <a href="http://www.blogjava.net/ericwang/archive/2005/11/23/21179.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>