Jakarta Commons 隐藏的宝贝.docx
- 文档编号:25057685
- 上传时间:2023-06-04
- 格式:DOCX
- 页数:15
- 大小:24.68KB
Jakarta Commons 隐藏的宝贝.docx
《Jakarta Commons 隐藏的宝贝.docx》由会员分享,可在线阅读,更多相关《Jakarta Commons 隐藏的宝贝.docx(15页珍藏版)》请在冰豆网上搜索。
JakartaCommons隐藏的宝贝
JakartaCommons隐藏的宝贝
如果你不熟悉JakartaCommons话,那么很有可能你已经重新发明了好几个轮子。
在你编写更多的普通的框架或工具之前,体验一下Commons吧。
它将会大大地节约你的时间。
太多的人自己写一个,其实是与CommonsLang中的StringUtils重复的StringUtils类,或者,开发者不知道从CommonsCollections中重新创建工具,哪怕commons-collections.jar已经在classpath中可用了。
真的,请停一下。
看看CommonsCollectionsAPI,然后再回到你的任务中;我发誓你会发现一些简单有用的东西可以帮你在明年节省一周的时间。
如果大家花一点时间看看JakartaCommons,我们将会得到更少的重复代码—我们将在重用的宗旨下真正做一些有用的事情。
我确实看到这样的情况发生过:
一些人研究了一下CommonsBeanUtils或者CommonsCollections,然后总是有“啊,如果我那时知道这个的话,我就不会写那一万行的代码了”这样的时刻。
JakartaCommons仍有一部分保持相当的神秘;比如,许多人还没有听说过CommonsCLI和CommonsConfiguration,并且大多数人还没有注意到CommonsCollections中的functors(算子)包的价值。
在这一系列中,我会专门强调一些JakartaCommons中较少得到重视的工具和功能。
在这一系列的第一部分,我将探索定义在CommonsDigester中的XML规则,CommonsCollections中的功能,和使用一个有趣的应用,CommonsJXPath,来查询一个对象的List。
JakartaCommons包含的功能目的在于帮助你解决低层次的编程问题:
遍历集合,解析XML和从List中检出对象。
我建议你花一些时间在这些小功能上,学习JakartaCommons真的会为你节省不少时间。
并不简单地是学习使用CommonsDigester来解析XML或者使用CollectionUtils的Predicate来过滤一个集合,而是当你一旦意识到如何将这些功能组合起来使用并且如何将Commons集成到你的项目中去的时候,你才会真正地看到它的好处。
如果你这样做地话,你将会把commons-lang.jar,commons-beanutils.jar,和commons-digester.jar当成JVM本身来看待。
如果你对JakartaCommons更深的内容感兴趣的话,可以看一下JakartaCommonsCookbook。
这本书给你很多方法来更好的使用Commons,并告诉你如何将JakartaCommons与其它的小的开源组件集成,如Velocity,FreeMarker,Lucene,和JakartaSlide。
这本书,我介绍了一组广泛的工具从CommonsLang中的简单工具到组合了CommonsDigester,CommonsCollections,和JakartaLucene来搜索威廉.莎士比亚的著作。
我希望这一系列和JakartaCommonsCookbook这本书能够提供给你一些有趣的低层次的编程问题的解决方案。
1.用于CommonsDigester的基于XML的规则集
CommonsDigester1.6提供了将XML转化为对象的最简单的方法。
Digester已经由O'Reilly网站上的两篇文章介绍过了:
“学习和使用JakartaDigester”,作者是PhilippK.Janert,和“使用JakartaCommons,第二部分”,作者是VikramGoyal。
两篇文章都演示了XML规则集的使用,但如何在XML中定义规则集并没有理解。
大多所见到的Digester的使用是程序化地定义规则集,以已编译的形式。
你应该避免硬编码的Digester规则,特别是当你可以将映射信息存储在外部文件中或一个类路径资源中时。
外部化一个Digester规则可以更好地适应一个演化中的XML文档结构或者说一个演化中的对象模型。
为了演示在XML中定义规则集与硬编码的规则集之间的区别,考虑系统解析XML给一个Personbean,包括在下面定义的属性—id,name和age。
packageorg.test;
publicclassPerson{
publicStringid;
publicStringname;
publicintage;
publicPerson(){}
publicStringgetId(){returnid;}
publicvoidsetId(Stringid){
this.id=id;
}
publicStringgetName(){returnname;}
publicvoidsetName(Stringname){
this.name=name;
}
publicintgetAge(){returnage;}
publicvoidsetAge(intage){
this.age=age;
}
}
确认你的应用需要解析一个包含了多个person元素的XML文件。
下面的XML文件,data.xml,包含了两个person元素,你想要把它们解析到Person对象中:
你希望如果结构和XML文件的内容在未来几个月中变化,你不需要在已编译的Java代码中硬编码XML文件的结构。
为了做到这一点,你需要在一个XML文件中定义Digester的规则,并且它可以作为一种资源从类路径中装入。
下面的XML文档,person-rules.xml,映射person元素到Personbean:
paramtype="java.lang.Object"/> 上述所做的是指示Digester创建一个新的Person实例,当它遇到一个person元素时,调用add()来将Person对象加入到一个ArrayList中,设置person元素中相匹配的属性,并从下一级元素name和age中设置name和age的属性。 现在你已经看到了Person类,会被解析的文档,和以XML的形式定义的Digester规则。 现在你需要创建一个由person-rules.xml定义了规则的Digester的实例。 下面的代码创建了一个Digester,通过将person-rules.xml的URL传递给DigesterLoader 既然person-rules.xml文件是与解析它的类在同一个包内的类路径资源,URL可以通过getClass().getResource()来得到。 DigesterLoader然后解析规则并将它加到新创建的Digester上: importmons.digester.Digester; importmons.digester.xmlrules.DigesterLoader; //从XML规则集中配置Digester URLrules=getClass().getResource("./person-rules.xml"); Digesterdigester= DigesterLoader.createDigester(rules); //将空的List推入到Digester的堆栈 Listpeople=newArrayList(); digester.push(people); //解析XML文档 InputStreaminput=newFileInputStream("data.xml"); digester.parse(input); 一旦Digester完成对data.xml的解析,三个Person对象将会在ArrayListpeople中。 与将规则定义在XML不同的方法是使用简便的方法将它们加入到一个Digester实例中。 大多数文章和例子都用这种方法,使用addObjectCreate()和addBeanPropertySetter()这样的方法来将规则加入中Digester上。 下面的代码加入了与定义在person-rules.xml中相同的规则: digester.addObjectCreate("people/person", Person.class); digester.addSetNext("people/person", "add", "java.lang.Object"); digester.addBeanPropertySetter("people/person", "name"); digester.addBeanPropertySetter("people/person", "age"); 如果你曾经发现自己正在用一个有着2500行代码的类,用SAX来解析一个巨大的XML文档,或者使用DOM或JDOM的完整的一个集合类,你就会理解XML的解析比它应该做的要复杂的多,就大多数情况来说。 如果你正在建一个有着严格的速度和内存要求的高效的系统,你会需要SAX解析器的速度。 如果你需要DOM级别3的复杂度,你会需要像ApacheXerces的解析器。 但如果你只是简单的试图将几个XML文档解析到对象中去的话,看一下CommonsDigester,并把你的规则定义在一个XML文件中。 任何时候你都应该将配置信息从硬编码中移出来。 我会建议你在一个XML文件中定义规则并从文件系统或类路径中装入它。 这样可以使你的程序更好地适应XML文档以及对象模型的变化。 有关在XML文件中定义Digester规则的更多的资料,参看JakartaCommonsCookbook一书的6.2节,“将XML文档转换为对象” 2.CommonsCollections中的算子 算子成为CommonsCollections3.1中的有趣的部分有两个原因: 它们没有得到应得的重视并且它们有改变你编程的方式的潜力。 算子只是一个奇特的名字,它代表了一个包装了函数的对象—一个“函数对象”。 当然,它们不是一回事。 如果你曾经使用过C和C++的方法指针,你就会理解算子的威力。 一个算子是一个对象—一个Predicate,一个Closure,一个Transformer。 Predicates求对象的值并返回一个boolean,Transformer求对象的值并返回新对象,Closure接受对象并执行代码。 算子可以被组合成组合算子来模仿循环,逻辑表达式,和控制结构,并且算子也可以被用来过滤和操作集合中的元素。 在这么短的篇幅中解释清楚算子是不可能的,所以跳过介绍,我将会通过使用和不使用算子来解决同一问题(解释算子)。 在这个例子中,从一个ArrayList中而来的Student对象会被排序到两个List中,如果他们符合某种标准的话。 成绩为A的学生会被加到honorRollStudents(光荣榜)中,得D和F的学生被加到problemStudents(问题学生)list中。 学生分开以后,系统将会遍历每个list,给加入到光荣榜中学生一个奖励,并安排与问题学生的家长谈话的时间表。 下面的代码不使用算子实现了这个过程: ListallStudents=getAllStudents(); //创建两个ArrayList来存放荣誉学生和问题学生 ListhonorRollStudents=newArrayList(); ListproblemStudents=newArrayList(); //遍历所有学生,将荣誉学生放入一个List,问题学生放入另一个 IteratorallStudentsIter=allStudents.iterator(); while(allStudentsIter.hasNext()){ Students=(Student)allStudentsIter.next(); if(s.getGrade().equals("A")){ honorRollStudents.add(s); }elseif(s.getGrade().equals("B")&& s.getAttendance()==PERFECT){ honorRollStudents.add(s); }elseif(s.getGrade().equals("D")|| s.getGrade().equals("F")){ problemStudents.add(s); }elseif(s.getStatus()==SUSPENDED){ problemStudents.add(s); } } //对于的有荣誉学生,增加一个奖励并存储到数据库中 IteratorhonorRollIter= honorRollStudents.iterator(); while(honorRollIter.hasNext()){ Students=(Student)honorRollIter.next(); //给学生记录增加一个奖励 s.addAward("honorroll",2005); Database.saveStudent(s); } //对所有问题学生,增加一个注释并存储到数据库中 IteratorproblemIter=problemStudents.iterator(); while(problemIter.hasNext()){ Students=(Student)problemIter.next(); //将学生标记为需特殊注意 s.addNote("talktostudent",2005); s.addNote("meetingwithparents",2005); Database.saveStudent(s); } 上述例子是非常过程化的;要想知道Student对象发生了什么事必须遍历每一行代码。 例子的第一部分是基于成绩和考勤对Student对象进行逻辑判断。 第二部分对Student对象进行操作并存储到数据库中。 像上述这个有着50行代码程序也是大多程序所开始的—可管理的过程化的复杂性。 但是当需求变化时,问题出现了。 一旦判断逻辑改变,你就需要在第一部分中增加更多的逻辑表达式。 举例来说,如果一个有着成绩B和良好出勤记录,但有五次以上的留堂记录的学生被判定为问题学生,那么你的逻辑表达式将会如何处理? 或者对于第二部分中,只有在上一年度不是问题学生的学生才能进入光荣榜的话,如何处理? 当例外和需求开始改变进而影响到过程代码时,可管理的复杂性就会变成不可维护的面条式的代码。 从上面的例子中回来,考虑一下那段代码到底在做什么。 它在一个List遍历每一个对象,检查标准,如果适用该标准,对此对象进行某些操作。 上述例子可以进行改进的关键一处在于从代码中将标准与动作解藕开来。 下面的两处代码引用以一种非常不同的方法解决了上述的问题。 首先,荣誉榜和问题学生的标准被两个Predicate对象模型化了,并且加之于荣誉学生和问题学生上的动作也被两个Closure对象模型化了。 这四个对象如下定义: importmons.collections.Closure; importmons.collections.Predicate; //匿名的Predicate决定一个学生是否加入荣誉榜 PredicateisHonorRoll=newPredicate(){ publicbooleanevaluate(Objectobject){ Students=(Student)object; return((s.getGrade().equals("A"))|| (s.getGrade().equals("B")&& s.getAttendance()==PERFECT)); } }; //匿名的Predicate决定一个学生是否是问题学生 PredicateisProblem=newPredicate(){ publicbooleanevaluate(Objectobject){ Students=(Student)object; return((s.getGrade().equals("D")|| s.getGrade().equals("F"))|| s.getStatus()==SUSPENDED); } }; //匿名的Closure将一个学生加入荣誉榜 ClosureaddToHonorRoll=newClosure(){ publicvoidexecute(Objectobject){ Students=(Student)object; //对学生增加一个荣誉记录 s.addAward("honorroll",2005); Database.saveStudent(s); } }; //匿名的Closure将学生标记为需特殊注意 ClosureflagForAttention=newClosure(){ publicvoidexecute(Objectobject){ Students=(Student)object; //标记学生为需特殊注意 s.addNote("talktostudent",2005); s.addNote("meetingwithparents",2005); Database.saveStudent(s); } }; 这四个匿名的Predicate和Closure是从作为一个整体互相分离的。 flagForAttention(标记为注意)并不知道什么是确定一个问题学生的标准。 现在需要的是将正确的Predicate和正确的Closure结合起来的方法,这将在下面的例子中展示: importmons.collections.ClosureUtils; importmons.collections.CollectionUtils; importmons.collections.functors.NOPClosure; MappredicateMap=newHashMap(); predicateMap.put(isHonorRoll,addToHonorRoll); predicateMap.put(isProblem,flagForAttention); predicateMap.put(null,ClosureUtils.nopClosure()); ClosureprocessStudents= ClosureUtils.switchClosure(predicateMap); CollectionUtils.forAllDo(allStudents,processStudents); 在上面的代码中,predicateMap将Predicate与Closure进行了配对;如果一个学生满足作为键值的Predicate的条件,那么它将把它的值传到作为Map的值的Closure中。 通过提供一个NOPClosure值和null键对,我们将把不符合任何Predicate条件的Student对象传给由ClosureUtils调用创建的“不做任何事”或者“无操作”的NOPClosure。 一个SwitchClosure,processStudents,从predicateMap中创建。 并且通过使用CollectionUtils.forAllDo()方法,将processStudentsClosure应用到allStudents中的每一个Student对象上。 这是非常不一样的处理方法;记住,你并没有遍历任何队列。 而是通过设置规则和因果关系,以及CollectionUtils和SwitchClosur来完成了这些操作。 当你将使用Predicate的标准与使用Closure的动作将分离开来时,你的代码的过程式处理就少了,而且更容易测试了。 isHonorRollPredicate能够与addToHonorRoll
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Jakarta Commons 隐藏的宝贝 隐藏 宝贝