做一个好的架构师一直是我的一个目标。但是,做过这么多项目后发现自己在设计上仍存在很多不足。下文是一篇不错的文章。希望大家能够得到一些启示或共鸣。欢迎大家发表自己在架构设计方面的观点。
我们期待自己成为一个优秀的软件模型设计者,但是,要怎样做,又从哪里开始呢?将下列原则应用到你的软件工程中,你会获得立杆见影的成果。
1. 人远比技术重要 你开发软件是为了供别人使用,没有人使用的软件只是没有意义的数据的集合而已。许多在软件方面很有成就的行家在他们事业的初期却表现平平,因为他们那时侯将主要精力都集中在技术上。显然,构件(components),EJB(Enterprise Java Beans)和代理(agent)是很有趣的东西。但是对于用户来说,如果你设计的软件很难使用或者不能满足他们的需求,后台用再好的技术也于事无补。多花点时间到软件需求和设计一个使用户能很容易理解的界面上。
2. 理解你要实现的东西 好的软件设计人员把大多数时间花费在建立系统模型上,偶尔写一些源代码,但那只不过是为了验证设计过程中所遇到的问题。这将使他们的设计方案更加可行。
3. 谦虚是必须的品格 你不可能知道一切,你甚至要很努力才能获得足够用的知识。软件开发是一项复杂而艰巨的工作,因为软件开发所用到的工具和技术是在不断更新的。而且,一个人也不可能了解软件开发的所有过程。在日常生活中你每天接触到的新鲜事物可能不会太多。但是对于从事软件开发的人来说,每天可以学习很多新东西(如果愿意的话)。
4. 需求就是需求 如果你没有任何需求,你就不要动手开发任何软件。成功的软件取决于时间(在用户要求的时间内完成)、预算和是否满足用户的需求。如果你不能确切知道用户需要的是什么,或者软件的需求定义,那么你的工程注定会失败。
5. 需求其实很少改变,改变的是你对需求的理解 Object ToolSmiths公司(www.objecttoolsmiths.com)的Doug Smith常喜欢说:“分析是一门科学,设计是一门艺术”。他的意思是说在众多的“正确”分析模型中只存在一个最“正确”分析模型可以完全满足解决某个具体问题的需要(我理解的意思是需求分析需要一丝不苟、精确的完成,而设计的时候反而可以发挥创造力和想象力 - 译者注)。
如果需求经常改动,很可能是你没有作好需求分析,并不是需求真的改变了。
你可以抱怨用户不能告诉你他们想得到什么,但是不要忘记,收集需求信息是你工作。
你可以说是新来的开发人员把事情搞得一团糟,但是,你应该确定在工程的第一天就告诉他们应该做什么和怎样去做。 如果你觉得公司不让你与用户充分接触,那只能说明公司的管理层并不是真正支持你的项目。
你可以抱怨公司有关软件工程的管理制度不合理,但你必须了解大多同行公司是怎么做的。
你可以借口说你们的竞争对手的成功是因为他们有了一个新的理念,但是为什么你没先想到呢?
需求真正改变的情况很少,但是没有做好需求分析工作的理由却很多。
6. 经常阅读 在这个每日都在发生变化的产业中,你不可能在已取得的成就上陶醉太久。
每个月至少读2、3本专业杂志或者1本专业书籍。保持不落伍需要付出很多的时间和金钱,但会使你成为一个很有实力的竞争者。
7. 降低软件模块间的耦合度 高耦合度的系统是很难维护的。一处的修改引起另一处甚至更多处的变动。
你可以通过以下方法降低程序的耦合度:隐藏实现细节,强制构件接口定义,不使用公用数据结构,不让应用程序直接操作数据库(我的经验法则是:当应用程序员在写SQL代码的时候,你的程序的耦合度就已经很高了)。
耦合度低的软件可以很容易被重用、维护和扩充。
8. 提高软件的内聚性 如果一个软件的模块只实现一个功能,那么该模块具有高内聚性。高内聚性的软件更容易维护和改进。
判断一个模块是否有高的内聚性,看一看你是否能够用一个简单的句子描述它的功能就行了。如果你用了一段话或者你需要使用类似“和”、“或”等连词,则说明你需要将该模块细化。
只有高内聚性的模块才可能被重用。
9. 考虑软件的移植性 移植是软件开发中一项具体而又实际的工作,不要相信某些软件工具的广告宣传(比如java 的宣传口号write once run many 译者注)。
即使仅仅对软件进行常规升级,也要把这看得和向另一个操作系统或数据库移植一样重要。
记得从16位Windows移植到32位windows的“乐趣”吗 ?当你使用了某个操作系统的特性,如它的进程间通信(IPC)策略,或用某数据库专有语言写了存储过程。你的软件和那个特定的产品结合度就已经很高了。
好的软件设计者把那些特有的实现细节打包隐藏起来,所以,当那些特性该变的时候,你的仅仅需要更新那个包就可以了。
10. 接受变化 这是一句老话了:惟一不变的只有变化。
你应该将所有系统将可能发生的变化以及潜在需求记录下来,以便将来能够实现。 通过在建模期间考虑这些假设的情况,你就有可能开发出足够强壮且容易维护的软件。设计强壮的软件是你最基本的目标。
11. 不要低估对软件规模的需求 Internet 带给我们的最大的教训是你必须在软件开发的最初阶段就考虑软件规模的可扩充性。
今天只有100人的部门使用的应用程序,明天可能会被有好几万人的组织使用,下月,通过因特网可能会有几百万人使用它。
在软件设计的初期,根据在用例模型中定义的必须支持的基本事务处理,确定软件的基本功能。然后,在建造系统的时候再逐步加入比较常用的功能。
在设计的开始考虑软件的规模需求,避免在用户群突然增大的情况下,重写软件。
12. 性能仅仅是很多设计因素之一 关注软件设计中的一个重要因素--性能,这好象也是用户最关心的事情。一个性能不佳的软件将不可避免被重写。
但是你的设计还必须具有可靠性,可用性,便携性和可扩展性。你应该在工程开始就应该定义并区分好这些因素,以便在工作中恰当使用。性能可以是,也可以不是优先级最高的因素,我的观点是,给每个设计因素应有的考虑。
13. 管理接口 “UML User Guide”(Grady Booch,Ivar Jacobson和Jim Rumbaugh ,Addison Wesley, 1999)中指出,你应该在开发阶段的早期就定义软件模块之间的接口。
这有助于你的开发人员全面理解软件的设计结构并取得一致意见,让各模块开发小组相对独立的工作。一旦模块的接口确定之后,模块怎样实现就不是很重要了。
从根本上说,如果你不能够定义你的模块“从外部看上去会是什么样子”,你肯定也不清楚模块内要实现什么。
14. 走近路需要更长的时间 在软件开发中没有捷径可以走。 缩短你的在需求分析上花的时间,结果只能是开发出来的软件不能满足用户的需求,必须被重写。
在软件建模上每节省一周,在将来的编码阶段可能会多花几周时间,因为你在全面思考之前就动手写程序。
你为了节省一天的测试时间而漏掉了一个bug,在将来的维护阶段,可能需要花几周甚至几个月的时间去修复。与其如此,还不如重新安排一下项目计划。
避免走捷径,只做一次但要做对(do it once by doing it right)。
15. 别信赖任何人 产品和服务销售公司不是你的朋友,你的大部分员工和高层管理人员也不是。
大部分产品供应商希望把你牢牢绑在他们的产品上,可能是操作系统,数据库或者某个开发工具。
大部分的顾问和承包商只关心你的钱并不是你的工程(停止向他们付款,看一看他们会在周围呆多长时间)。
大部分程序员认为他们自己比其他人更优秀,他们可能抛弃你设计的模型而用自己认为更好的。
只有良好的沟通才能解决这些问题。
要明确的是,不要只依靠一家产品或服务提供商,即使你的公司(或组织)已经在建模、文档和过程等方面向那个公司投入了很多钱。
16. 证明你的设计在实践中可行 在设计的时候应当先建立一个技术原型, 或者称为“端到端”原型。以证明你的设计是能够工作的。
你应该在开发工作的早期做这些事情,因为,如果软件的设计方案是不可行的,在编码实现阶段无论采取什么措施都于事无补。技术原型将证明你的设计的可行性,从而,你的设计将更容易获得支持。
17. 应用已知的模式 目前,我们有大量现成的分析和设计模式以及问题的解决方案可以使用。
一般来说,好的模型设计和开发人员,都会避免重新设计已经成熟的并被广泛应用的东西。 http://www.ambysoft.com/processPatternsPage.html 收藏了许多开发模式的信息。
18. 研究每个模型的长处和弱点 目前有很多种类的模型可以使用,用例捕获的是系统行为需求,数据模型则描述支持一个系统运行所需要的数据构成。你可能会试图在用例中加入实际数据描述,但是,这对开发者不是非常有用。同样,数据模型对描述软件需求来说是无用的。每个模型在你建模过程中有其相应的位置,但是,你需要明白在什么地方,什么时候使用它们。
19. 在现有任务中应用多个模型 当你收集需求的时候,考虑使用用例模型,用户界面模型和领域级的类模型。
当你设计软件的时候,应该考虑制作类模型,顺序图、状态图、协作图和最终的软件实际物理模型。
程序设计人员应该慢慢意识到,仅仅使用一个模型而实现的软件要么不能够很好地满足用户的需求,要么很难扩展。
20. 教育你的听众 你花了很大力气建立一个很成熟的系统模型,而你的听众却不能理解它们,甚至更糟-连为什么要先建立模型都不知道。那么你的工作是毫无意义的。 教给你开发人员基本的建模知识;否则,他们会只看看你画的漂亮图表,然后继续编写不规范的程序。
另外, 你还需要告诉你的用户一些需求建模的基础知识。给他们解释你的用例(uses case)和用户界面模型,以使他们能够明白你要表达地东西。当每个人都能使用一个通用的设计语言的时候(比如UML-译者注),你的团队才能实现真正的合作。
21. 带工具的傻瓜还是傻瓜 你给我CAD/CAM工具,请我设计一座桥。但是,如果那座桥建成的话,我肯定不想当第一个从桥上过的人,因为我对建筑一窍不通。
使用一个很优秀的CASE工具并不能使你成为一个建模专家,只能使你成为一个优秀CASE工具的使用者。成为一个优秀的建模专家需要多年的积累,不会是一周针对某个价值几千美元工具的培训。一个优秀的CASE工具是很重要,但你必须学习使用它,并能够使用它设计它支持的模型。
22. 理解完整的过程 好的设计人员应该理解整个软件过程,尽管他们可能不是精通全部实现细节。软件开发是一个很复杂的过程,除了编程、建模、测试等你擅长工作外,还有很多工作要做。好的设计者需要考虑全局。必须从长远考虑如何使软件满足用户需要,如何提供维护和技术支持等。
23. 常做测试,早做测试 如果测试对你的软件来说是无所谓的,那么你的软件多半也没什么必要被开发出来。建立一个技术原型供技术评审使用,以检验你的软件模型。在软件生命周期中,越晚发现的错误越难修改,修改成本越昂贵。尽可能早的做测试是很值得的。
24. 把你的工作归档 不值得归档的工作往往也不值得做。归档你的设想,以及根据设想做出的决定;归档软件模型中很重要但不很明显的部分。给每个模型一些概要描述以使别人很快明白模型所表达的内容。
25. 技术会变,基本原理不会 如果有人说“使用某种开发语言、某个工具或某某技术,我们就不需要再做需求分析,建模,编码或测试”。不要相信,这只说明他还缺乏经验。抛开技术和人的因素,实际上软件开发的基本原理自20世纪70年代以来就没有改变过。你必须还定义需求,建模,编码,测试,配置,面对风险,发布产品,管理工作人员等等。
软件建模技术是需要多年的实际工作才能完全掌握的。好在你可以从我的建议开始,完善你们自己的软件开发经验。以鸡汤开始,加入自己的蔬菜。然后,开始享受你自己的丰盛晚餐吧。
作者: Scott Ambler著 乐林峰 译 来源: umlchina 风之语 转载
当你使用CASE工具画出包,类,属性,方法和关系时,AndroMDA的“概貌”就形成了。然后,你把模型保存为XMI格式,并用AndroMDA和XDoclet产生整个组件模型的Java源代码。AndroMDA和XDoclet都可以和著名的构建工具Ant进行集成。你通常会使用Ant的命令行版本,但也可以在IDE如Eclipse或JBuilder中使用Ant。无论是哪一种方式,你都要使用自己定制的Ant构建脚本,并在脚本中把AndroMDA定义为其中的一个Task。 在CASE工具中用UML建模 你可以使用UML的图形符号为现实世界中的事物建模。例如,考虑一个汽车租赁系统,用于管理客户,司机,汽车和租赁合同。这个系统UML模型的类图可能是如下的样子:
将模型保存为XMI格式。某些CASE工具把这称为“export”,用于区别它本身私有的格式。 代码生成器 从XMI模型中产生代码,Ant构建脚本经过以下的步骤:
- Ant Task读入XMI格式的UML模型,并在内存中生成抽象的语法树(abstract syntax tree),其中包含包,类,属性,方法和关系的信息。
- 然后,使用Velocity模板处理引擎来处理entity bean,session bean,Hibernate类或别的代码生成模板,所有的模板都基于从CASE工具中产生的抽象语法树。它使用一个脚本helper facade来屏蔽UML元模型的复杂性,因此模板的开发者可以使用容易理解的API来为脚本写代码。这些步骤会产生一些源代码文件。
- 最后,Ant脚本调用XDoclet中的<ejbDoclet>或<hibernateDoclet> Task。对于EJB,XDoclet task读入所有的bean类信息并生成bean的接口和部署配置文件。对于Hibernate,XDoclet生成包含持久层映射信息的XML文件。
使用Cartridges定制输出 到此为止,你可能以为AndroMDA是一个EJB或Hibernate JavaBean的生成器。其实,AndorMDA可以生成任何东西! 事实上,AndroMDA对于它生成的东西一无所知。它拥有一个称为“cartridges”的可插入模块。一个Cartridge由一套定义生成格式的模板文件组成。目前,AndroMDA包括四个cartridge:
- andromda-java - 生成一般的Java源代码。
- andromda-ejb - 生成EJB。
- andromda-hibernate - 生成Hibernate ORM工具的持久层类。
- andromda-struts - 生成Jakarta Struts的web页面,form bean和action类。
你可以选择使用哪一个cartridge来产生你的应用框架。你也可以编写你自己的cartridge - 一旦你理解了cartridge的基础知识及其XML描述文件,编写一个新的cartridge非常容易! AndroMDA核心自动检测安装在类路径下的cartridge。如需要了解更多cartridge的信息,请参见本网站的其他文档。 EJB Cartridge生成代码的例子 从上面汽车租赁系统模型的客户模型部分,AndroMDA(使用andromda-ejb)和XDoclet将为你产生下面的代码。Bean类使用 标记,其他类使用 标记。你可以点击文件名查看文件的内容。
编写业务方法 你可能知道,使用代码生成器并没有完成了所有的工作。编写Bean的主体即业务逻辑是留给你的工作。AndroMDA为你构建了一个应用框架,你需要往里面填充代码。这些所谓的“implementation classes”来自bean类,是类继承结构树上的叶子。AndroMDA一次性地产生这些代码并不再修改它们。这能够确保手工编写的代码不被代码生成器覆盖。 因此,在实现类中实现你的业务方法,并启动Ant构建脚本用于编译Java文件的其他task,并把编译好的class文件打包到一个ejb-jar文件中。jar文件当然也包含了生成的部署配置文件。 最后的工作 最后的工作当然是发布到应用服务器上。以JBoss为例,只需简单地将jar文件复制到JBoss的部署路径中即可。
在中国的同行中经常看到:要么不重视软件架构,要么过分重视乃至捧为天书。如果要是碰到一个不懂技术的项目经理强行推行新技术有会有何种效果呢?
其实在做系统架构时,有时候会常常忽略以下几点:
1:软件的架构在大的方向上是固定的。
这种固定依据某些基本固定的软件模式(包括设计模式、编码模式或者某些特定的约定)来强化和固定。这使得软件开发在某种程度上具有某些可以明显看得到的风格,这个风格被整个的项目贯穿和坚持,这样当我们进入通常可怕的维护或者调整的时候,我们不会害怕我们在很多种不同的实现中常常迷失了方向。
2:软件的架构在具体的应用中可以适度的可控灵活。
毫无疑问,软件设计开发不可能没有例外,在某种程度上保留一些适度的灵活时必要的。一方面,它符合软件开发的实际;一方面,适度的可控灵活体现了一种对实现者的尊重和信任。然而,如何实现可控的灵活呢?通常,我认为可以考虑有限种类的设计选择并通过有色彩的图形来表明这种可选择性。但即便如此,设计者很重要的要考虑这些不同选择之间的关系,以及这些选择和整体设计的兼容性,这个时候,面向接口的设计和适度的“粘合”设计是必要的。
3:架构设计的支撑。
架构设计通常会涉及到一些支撑设计,这些设计和实现水准直接的体现了系统的最有价值的部分,更细的划分我们应该可以看到性能和结构相关的部分,以及必要的基础类。通常,作为设计人员,我们应该能够设计并实现系统的性能、结构、扩展、粘合等部分的工作,这部分的工作通常不应该离开设计人员的控制和把握,这些代码的书写或者至少积极的关注是设计人员必要的素质:我们不应该让更多的看到我们无法写出代码来(我也反对没有分工什么都做的设计人员!),实际上,这是软件最困难也是最有乐趣的地方。特别是在测试结果中发现这些设计实现带来的性能的极大提高的时候是非常有成就感的事情。至于基础类,应该是尽可能的减少依赖和耦合的。架构设计的支撑要尽可能的对客户程序员隐藏不必要的中间细节,对基础类要尽量简单、稳定并真正需要。
通常,新兴的技术会用在架构设计的支撑设计里面并被封装以隐藏具体的实现,而通盘应用新兴技术的风险是巨大的,并且对人员的获得也是问题。有些技术是可以提供替代技术的,一些新兴的技术实现也是可以参考的(但未必采用她的实现)。要记住软件开发是需要速度、质量、可维护而不是技术表演,这个度应该由分析设计人员负责任的来把握。
4:面向业务还是面向页面的。
也许这种说法会招致一些人的反对,认为自然而然的应该是前者,然而,实际的情况是,我们常常看到打着业务的幌子实现为面向页面的系统,原因很简单:后者更加的“简单”并似乎有更少的代码和工作量。这在某种程度上是对的。然而,区分这两种不同的设计是必要的,尽管你也可能使用了MVC2的架构,但你可以做出面向页面的设计的。观察我们的软件开发,最先提交的代码未必是最少的代价的代码:我们常常可以发现一些项目总是在那些不起眼的地方不断的“修改”,我们的程序员因此好像很忙,但对我们的项目对公司来说这是糟糕的不必要开销。因此,考虑这两种可能的情况,在做出选择之前加以思考是必要的。
5:用团队和别人的眼光考虑问题。
实际上,每个项目都是有所差异的,特别是人的差异。很糟糕的是,我们通常不得不面对人力资源的极大压力,特别是这些人之间常常都没有共事的经验也就是常常是临时组成的,而这些人员也通常也缺乏一个软件开发的基本共识:对标准的遵从哪怕是最简单的Java编码规范和对自己提交的产品的简单备注和测试都是缺乏的而通常又没有达到代码就是文档的水准。在这个时候,用团队和别人的眼光考虑问题就是必要的,这实际上就是取技术上的“交集”,也就是走简单的路线。如果没有选择的时候,培训工作就是非常必要的。
用团队和别人的眼光考虑问题也会有其它的问题,特别是当项目压力如进度压力等来临的时候或者项目成员根本不考虑公司整体和长期利益的时候,这个问题很突出,有时候作为分析设计人员你甚至很难解决,特别是你面临刚才我所提到的“没有Java开发实际经验甚至缺乏软件开发经验的项目经理试图控制技术的时候”更加如此。
软件架构的设计说起来简单,也可以说起来复杂,然而,如何把事情做到简单并且把其中的针对性能、分层、粘合、扩展等处理好,并提供可控的灵活性不是容易的。通常,只有多一些经验和教训并能够在软件代码上加以实现核心的才可能成为好的分析设计人员。
在此演练中,您将在 Sun Java™ Studio Creator 应用程序开发环境 (IDE) 中创建和部署一个简单的 Web 应用程序
"travel center"。向页面中添加两个组件,并将那些数据识别组件绑定到本地数据库。然后,生成应用程序并将其
部署到本地应用服务器,在客户机 Web 浏览器上运行该应用程序。
内容
• 创建项目
• 添加 Dropdown List 组件
• 将组件连接到数据库
• 运行 Web 应用程序
• 添加 Data Table
• 修改 SQL 查询
• 控制显示的行
要完成此演练,系统上应该已经安装了 IDE,并且您已经阅读了 Java Studio Creator 入门 教程。
创建项目
开发 Web 应用程序从创建项目开始。就像在入门教程中学习的那样,项目是 IDE 中的基本工作单元。项目包含
组成应用程序的所有源代码和资源。
1. 在“欢迎”屏幕上单击“创建新项目”。
2. 在“新建项目”对话框的“名称”文本字段中,键入 TravelCenter。
3. 选择“缺省 J2EE Web 应用程序”模板(如果尚未选定),然后单击“确定”。
通过这些步骤会创建两个重要的文件,在本教程的其他部分中也将用到这两个文件。
• Page1.jsp-JavaServer Pages™ 中包含组成 Web 应用程序的组件。最初,在应用程序中只有一个 JSP™
页,但是随后您可以添加更多的页面。
• Page1.java-包含页面状态并使其在不同的呈现中保持一致的 JavaBeans™ Bean。缺省情况下代码是不
可见的,但是您可以通过右键单击页面,然后在上下文菜单中选择“查看 Page1 Java 类”来显示它。
添加 Dropdown List 组件
接下来,向页面中添加 Dropdown List 组件。
1. 在组件面板中,选择“JSF”>“JSF 标准组件”,然后将 Dropdown List 组件拖到页面上。
Dropdown List 组件将出现在 Page1.jsp 页面上。您可以通过使用选择句柄来调整组件的大小,也可以
1
Sun Java Studio Creator
将组件移动到新位置。
2. 右键单击页面背景,然后在上下文菜单中选择“查看 Page1 Java 类”。
会在源编辑器中打开页面的 JavaBeans 源代码。
3. 在“选择类成员”下拉列表中选择 "dropdown1" 以导航到实例变量声明。(“选择类成员”下拉列表位于
源编辑器工具栏中。)
初始化 Dropdown List 组件的 Java 代码已添加到 Page1 类实现。
private HtmlSelectOneMenu dropdown1 = new HtmlSelectOneMenu();
将组件连接到数据库
在此部分中,使 Dropdown List 组件可以从数据库表获得项。
1. 单击源编辑器顶部的 Page1.jsp 标签以查看页面。
2. 从“服务器导航”中,将“数据源”> "Travel" >“表”> "PERSON" 节点拖放到 Dropdown List 组件的上
面。
将出现一个对话框,且 personRowSet 已添加到非可视组件托盘中。
注如果 PointBase Embedded 数据库没有运行,则您将看到一个“错误”对话框,告诉您这一情况。如果看
到此对话框,请关闭“错误”对话框,在“服务器导航”中右键单击“PointBase 数据库服务器”节点,
然后在上下文菜单中选择“启动 PointBase”。然后,右键单击“数据源”>
"Travel",并从上下文菜单中选择 “刷新”。现在,您应该能够定位到 "Travel" 数据源节点。
3. 单击“填充列表”。
因为 PERSON.NAME 列属于 SQL 类型 varchar,在 Dropdown List 组件中将显示文本 "abc",指示所显
示的数据是一个字符串。
注如果没看到这个对话框,是因为 PERSON 节点没有被直接放到 Dropdown List 组件的上面。在这种情况
下,在执行下一步时,请确保如图 1 所示选定了 PERSON.PERSONID 和 PERSON.NAME 列。
4. 右键单击 Dropdown List 组件,然后从上下文菜单中选择“从数据库填充列表”。
“值字段”被绑定到 PERSON.PERSONID 列,该列是 PERSON 表的主键。此字段提供由 JSF 组件的
getValue() 方法返回的值。另一方面,“显示字段”被绑定到 PERSON.NAME 列,是组件运行时所显
示的内容。
5. 单击“确定”。
运行 Web 应用程序
现在,您可以部署和运行 Web 应用程序了。IDE 附带有一个已经安装的本地部署服务器:
• Sun Java™ System Application Server Platform Edition 8
部署服务器是作为“服务器导航”中“部署服务器”节点下的一个节点安装的。通过部署服务器的上下文菜单
(可通过右键单击它进行访问),可以启动或停止它。
部署和运行 Web 应用程序
1. 单击工具栏中的“全部保存”按钮。
2. 单击工具栏中的“运行项目”按钮。
IDE 将生成、部署和运行 TravelCenter 应用程序。首先,“生成输出”窗口出现在 IDE 布局的底部。有关
编译的信息和部署准备将输出到此窗口。(如果在生成时出现问题,请首先检查“生成输出”窗口。)接
下来,将打开一个对话框,其中显示部署的状态。
部署之后,将使用 URL
http://localhost:18080/travelcenter/ 为应用程序打开一个新的 Web 浏览器
窗口。使用 PERSON 表的 NAME 列中的数据填充下拉列表。
添加 Data Table
接下来,向应用程序中添加一个 Data Table 组件,并将该组件与数据库表相连。
1. 在组件面板中,单击“JSF 标准组件”,将 Data Table 拖至页面,并将它放置在 Dropdown List 组件的下
面。
2. 拖动“数据源”> "Travel" >“表”> "TRIP" 节点,并将其放在 Data Table 上。确保整个 Data Table 组件的轮
廓为蓝色时放置节点。如果只有列或列标题的轮廓为蓝色,请将节点在组件内四处移动,直到整个组件的
轮廓为蓝色。
如果将节点放在列或列标题上,会出现“选择目标”对话框,而且 tripRowSet 已添加到非可视组件托
盘中。确保选中了 "dataTable1" 单选按钮,然后单击“确定”关闭对话框。
3. 右键单击 Data Table 组件,然后选择“表布局”。
注如果没有选定 Data Table 组件,或者选定了它的其中一个子组件(在本例中是列),则在右键单击 Data
Table 时,将出现另外的上下文菜单。在这种情况下,请选择 "dataTable1" >“表布局”。您可以按 Esc
键选择当前所选组件的父组件。
出现“表布局”对话框。这两个列表表明哪些列可用于显示以及正在显示哪些内容。将 TRIP 表放在 Data
Table 组件上时,将选择所有可用的列进行显示。
4. 选择“显示”列表中的第一列 (TRIP.TRIPID),然后单击 "<" 按钮。
将从“显示”列表中移除该列。
5. 移除 TRIP.PERSONID 和 TRIP.TRIPTYPEID 列。
仍然出现在“显示”列表中的三列:
• TRIP.DEPDATE
• TRIP.DEPCITY
• TRIP.DESTCITY
使用数据绑定组件访问数据库 ▪
Sun Java Studio Creator
6. 单击“确定”。
现在,Data Table 组件中有三个显示列。
修改 SQL 查询
在此部分中,将修改 TRIP 行集对象中的 SQL 查询,以便查询返回 TRIPTYPE 表中的数据。您还将修改 Data
Table 组件以显示新列。
1. 右键单击非可视组件托盘中的 tripRowSet,然后选择“编辑行集查询”。
在编辑器窗格中将出现查询编辑器。标签的名称是 tripRowSet。
2. 右键单击设计视图(见图 4),然后选择“添加表”。
3. 选择 TRAVEL.TRIPTYPE 表,然后单击“确定”。
将出现另一个表格图,在两个表格图之间有一个链接。
4. 在指示的表中取消选中以下复选框:
• TRIP 表中的 TRIPID
• 两个表中的 TRIPTYPEID
会从结果集中删除上述列。同时会修改源视图中的 SQL 查询以反映这些更改。
4 使用数据绑定组件访问数据库 ▪
图 2 显示的列
图 3 第 1 列处于选定状态的 Data Table
Sun Java Studio Creator
5. 选择 Page1.jsp 标签以返回到可视编辑器。
6. 右键单击 Data Table,然后选择“表布局”。
将出现“表布局”对话框。由于您已经更改了行集的 SQL 查询,因此有更多可以显示的列。
7. 将 TRIPTYPE.DESCRIPTION 列添加到“显示”列表中。
8. 单击“确定”。
第四列将出现在 Data Table 组件中。
控制显示的行
在上一部分中将 TRIP 表放在 Data Table 组件上时,IDE 使用返回表中所有列的所有行的 SQL 查询创建了一个
行集对象。如果此时部署应用程序,则数据表将包含 TRIP 表中的所有行程信息。
假定您仅希望显示其名字出现在 Dropdown List 组件中人员的行程信息。您必须通过编辑 TRIP 行集对象的缺省查
询,在 Dropdown List 组件和 Data Table 组件之间创建主从关系 (Master-Detail)。
1. 双击 tripRowSet 对象打开查询编辑器。
2. 在查询编辑器的设计网格(电子表格)中,右键单击设计网格 PERSONID 行中的“条件”单元格,然后
选择“添加查询条件”。
使用数据绑定组件访问数据库 ▪
图 4 查询编辑器
设计视图
设计网格
源代码
Sun Java Studio Creator
3. 将“比较”下拉菜单设置为“=等于”,并选择“参数”单选按钮。
4. 单击“确定”。
在 PERSONID 的“条件”列中您会看到 "=?",它在 SQL 查询中添加了以下 WHERE 子句:
WHERE TRAVEL.TRIP.PERSONID = ?
5. 通过选择 Page1.jsp 标签,返回到可视编辑器,然后双击 Dropdown List 组件。
Page1 类的源代码在编辑器区域中打开,且光标位于 dropdown1_processValueChange() 方法主体
内。此事件处理程序方法存根 (Stub) 是在您首次双击 Dropdown List 组件时创建的。
6. 从“组件面板”>“代码片段”>“演示”拖动 Travel dropdown 代码片段,将其放在
dropdown1_processValueChange() 方法体中。
6 使用数据绑定组件访问数据库 ▪
图 5 “添加查询条件”对话框
Sun Java Studio Creator
public void dropdown1_processValueChange(ValueChangeEvent vce) {
// User event code here...
try {
dataTable1Model.setObject(1, dropdown1.getValue());
dataTable1Model.execute();
} catch (Exception e) {
log("person change exception", e);
error(“Exception changing person id:”+e);
} // end try catch
}
这段代码将下拉列表的值绑定到为 dataTable1Model 准备的 SQL 语句中的参数上。
7. 在 Page1 类中找到 Page1() 构造函数。
8. 从“组件面板”>“代码片段”>“演示”拖动 Travel initialization 代码片段,并将其放在 Page1() 构造
函数实现的结尾处。
public Page1() {
// other lines of code omitted
catch ( Exception e) {
log("Page1 Initialization Failure", e);
throw new FacesException(e);
}
// Additional user provided initialization code
try {
personRowSet.execute();
personRowSet.next();
dataTable1Model.setObject(1, personRowSet.getObject("PERSONID"));
} catch (Exception ex) {
throw new FacesException(ex);
} // end try catch
}
这段代码将把下拉列表中当前选定 NAME 的 PERSONID 值绑定到为 dataTable1Model 准备的 SQL 语句
中的参数上。
9. 通过选择 Page1.jsp 标签返回到可视编辑器。
10.右键单击 Dropdown List 组件,然后选择“更改时自动提交”。
在属性表单中,以下代码将出现在 "Javascript" > "onchange" 属性中:
this.form.submit();
现在,当用户在运行的 Web 应用程序中更改下拉列表选择时,将重新提交和更新页面。
11.单击工具栏上的“全部保存”。
12.单击工具栏上的“运行项目”。
将重新生成和部署 Web 应用程序。在下拉列表中选择另一个名字,您会注意到更新了数据表。
使用数据绑定组件访问数据库 ▪ 2004 6 年月 7
Sun Java Studio Creator
请参见
• Java Studio Creator 入门
本文下载地址: http://mail.yl.gov.cn/ftp/yy/jscb2005.rar
基于JSF的Ajax实现AjaxFaces发布了1.0版本。
AjaxFaces项目是由CyberXP.net提供的。该项目使JSF的UI组件具备了Ajax的能力,可以触发Ajax过程,也可以在Ajax处理过程中更新界面组件。AjaxFaces还提供了一组功能性的组件,包括树、日历等,这些组件都内建了Ajax功能。
下载地址:http://cyberxp.net/
作者: Programmer's Life
B/S 作为如今最为流行的体系结构模式,也是受到了广大开发人员以及客户的认同,其开发模式也在不断的发展着,在这里主要就 Java B/S 的开发模式做一番回顾和探讨,也算是自己对于 Java B/S 开发模式的一种总结。
Jsp+Jdbc
在 B/S 开发中最简单的一种开发模式是页面 + 逻辑处理,映射到技术上反应出来的有 Jsp+Jdbc ,在基于这类的实现中在 View 层也就是 jsp 页面上负责数据的显示、逻辑处理,结合 jdbc 完成数据的持久化,在小型的项目中,人们确实发现这种方式是最为方便的,但在复杂的项目以及需求不断变化的项目中,人们慢慢的发现这种方式造成了不少的问题,首先是调试的问题,想想在一个 jsp 页面中进行排错是多么的困难,其次是修改的问题,为了满足用户需求的一个小小的变化,都需要去改不少的页面,而且很多时候由于写的时间长了,自己都需要回忆很久才能想起是怎么回事,更不用说如果人员流动了会怎么样,同时还带来开发效率的问题,由于需要缺少足够的调试的支持,需要较为熟练的开发人员才能快速的完成,对于一般的人员来说需要一定的适应和学习过程,当然伴随而来的还有诸如修改界面的时候一不小心少 copy 了点代码什么造成的错,最大的问题可能还是重用的问题,通常会造成 N 多同样的代码在页面上 copy 来 copy 去的,总结下来在这种模式下有几个比较重大的问题就是:
1、 调试问题。
2、 维护问题,显示和逻辑处理在一起导致了修改显示的时候较为困难,至于修改代码则因为之前的调试问题导致了困难,同时由于逻辑均在页面上后期接手人员需要一段时间去理解。
3、 代码重用性问题。
但同样它还是存在优点的,那就是可以很快的上手,但由于调试和维护性问题确实太大了,所以在现在也是基本不再采用这种方式了。
Jsp+JavaBean
在经历了 jsp+jdbc 阶段后,开始考虑怎么样去解决上面三个问题,这个时候就诞生了诸 JSP+JavaBean 这样的技术体系,在这个体系中由 jsp 页面负责显示以及接收页面请求,并调用相应的 JavaBean 来完成逻辑处理,在获取其返回的处理数据后转到相应的页面进行显示。在这样的技术体系中,由于逻辑是由 JavaBean 来完成的,可以对其进行调试了,代码的重用性一定程度上也得到了提高。刚开始的时候用这样的技术体系确实发现比以前用 jsp+jdbc 爽了很多,但随着用多了,慢慢又发现了问题,那就是在页面中需要编写对于页面请求数据的获取,还得根据请求去调用相应的 javabean ,并根据 javabean 的处理结果转入相应的页面,这同样造成了修改的麻烦,毕竟是去页面上修改这些逻辑,总结下来在这种模式下有比较重大的问题就是:
1、 代码重用性以及维护性问题。但这里的代码重用性问题和 jsp+jdbc 的就不同,在逻辑处理部分现在已经可以重用了,但现在在各个页面就不得不重复的写获取页面请求的参数、相应的调用 Model 、根据 Model 的处理结果转发页面,这样的话就导致了在改的时候需要到处去找,造成了维护的复杂。
2、 系统结构不清晰。毕竟仍然是在页面控制整个响应页面事件的处理流程,这个时候就造成了很多页面中出现完全相同的 jsp 代码,而且控制代码在页面,仍然是不便操作,例如对于 JavaBean 的调用等,而且由于获取 javabean 的数据需要转发的缘故,其实通常就是在最终的显示页面上加上上面的控制事件处理流程的代码,并没有真正的做到显示和处理的分离。
同样,它的优点在于分离了显示和业务逻辑处理,增强了可调试以及维护性,而且也是很容易上手的,对于小型项目来说仍然是可选的方案之一。
基于 MVC Framework
在经历了上面的 Jsp+JavaBean 后,我们发现其实现在最需要的就是在 jsp 、 javabean 之间能有个东西自动完成页面请求数据的封装、根据请求调用相应的 javabean 、同时根据 javabean 的处理结果返回至相应的 View ,有了这样的思想后,发现 smalltalk 中的 MVC 思想很适合这种场景,于是便在 Java B/S 开发中引入了 MVC 思想,在这里也简单的介绍下 MVC 思想, MVC 强调 View 和 Model 的分离, View 所面对的是 Controller ,由 Controller 负责与 Model 进行交互, View 只负责显示页面以及显示逻辑的处理,显示逻辑指的是诸如第一行要显示蓝色、第二行要显示红色这样的显示方面的处理, Controller 负责接受页面请求,并将其请求数据进行封装,同时根据请求调用相应的 Model 进行逻辑处理,在 Model 处理后返回结果数据到 Controller , Controller 将根据此数据调用相应的 View ,并将此数据传递给 View ,由 View 负责将数据进行融合并最终展现。 MVC 带来的优点很明显的体现出来了,基于一个这样的 MVC Framework 的话开发人员可以按照一种固定的模式进行开发,规范了整个开发过程,提高了质量以及系统结构的清晰性,并由于保证了 View/Model 的分离,使得一个 Model 可以对于多种显示形式的 View ,需要的仅仅是去改变 View 和 Controller 。
按照 MVC 思想,最容易想到的实现方案莫过于 jsp+servlet+javabean ,在这里面 jsp 对应着 View , servlet 对应着 Controller , javabean 对应着 Model ,因为采用 servlet 可使用 servlet container 已经封装好的页面数据请求对象 HttpServletRequest ,这样就省去了自己封装页面请求数据的工作,作为 Controller 同时还需要承担根据请求调用对应的 javabean ,最简单的做法无非就是在 Servlet 中直接根据某种逻辑 ( 诸如反射或接口 ) 调用相应的 bean 进行执行,之后将 HttpServletRequest 、 HttpServletResponse 作为参数传入 javabean 进行处理, javabean 从 HttpServletRequest 中获取请求数据,将返回的结果数据放入 HttpServletResponse ,整个过程结束后继续由 Controller 接手进行处理,这个时候作为 Controller 的 servlet 将根据处理的结果返回相应的页面,在这个模型使用时人们慢慢的发现了一个问题,那就是随着 jsp 、 javabean 的变化造成了 controller 的不断修改,需要修改其中调用相应 javabean 以及转发相应页面的部分,为了解决这个问题,首先想到的是应该分离根据请求调用相应 javabean 的步骤,这个时候采用了设计模式中的 front controller+application controller 的方法, front controller 负责接受页面请求并进行封装,同时将此数据对象传递至 application controller ,由 application controller 来负责调用相应的 bean ,这样的设计其实都是遵循着一个设计原则,就是职责单一,通常实现 application controller 的模式是 Command 模式,在这种情况下 MVC Framework 的结构体系就演变成了 view+controller(front+application)+model 。
在完成了上述演变后慢慢又发现了一个问题,就是 model 依赖于了 httpservletrequest ,这样造成的一个问题就是没法测试,仍然要不断重启服务器来测试,当然与此同时的发展是 model 层的细化,细化成用于响应页面请求的 action Layer+Domain Model Layer+Persistent Layer ,在这里不去讨论后面层次的问题,因为作为 MVC Framework 它并不管你 Model 层是怎么个处理流程的。
慢慢也发现了另外一个问题,那就是变化经常要影响到 controller 的修改,于是便引入了采用配置文件的解决方法,编写 action 的配置文件,在配置文件中控制根据 action 的返回结果转入相应的 View ,这样的话在将来需要改变的时候只需要去改变这个配置文件就可以了,保证了 Controller 的稳定,这是典型的设计中的重点考虑因素,分离变化和不变化的,让变化造成的影响最小。
但在引入了上面的配置文件后,慢慢又发现了问题,那就是手写配置文件总是容易出各种各样的问题,这个时候采用图形化的界面来生成配置文件的想法又有了,这也就造就了 page flow 的诞生,当然,这只是 page flow 的一小部分功能。
当然,随着MVC的发展,也带动了其他相关技术的发展,如异步请求/响应模式(ajax、amowa,^_^)等。
在 MVC 思想接受后开源界的 MVC Framework 也是如雨后春笋般的冒出,比较知名的有 struts 、 webwork 、 spring mvc 等,这些 MVC Framework 基本都已经做到了上面提及的 MVC 思想演变的一些需求,当然,即使现在的 MVC Framework 是做到了,但在使用这些 MVC Framework 的时候我们通常又开始违背 MVC 思想的基本要素,就是保持 View 仅仅是 View 的原则,所以我比较推荐在 View 使用 Velocity 这之类的东西作为 View ,尽量保持 View 的纯洁性,任何技术的发展都是循序渐进的,不站在那个高度的时候是不知道前面还有什么样的高山的,那么现在我们缺少的又是什么呢?现在的 MVC Framework 中还存在着什么不足呢?这是值得我们思考的。
Frank W. Zametti有一篇文章,向我们展示了对Ajax(Asynchronous Javascript+XML)技术的应用,尤其是Struts的应用。他认为在应用Ajax中有很多有趣的事情,包括Ajax怎样工作,为什么Ajax会被用到等等。
文章地址:
http://www.omnytex.com/articles/xhrstruts/
产生验证码图片的文件-----image.jsp
<%@ page contentType="image/jpeg" import="java.awt.*,java.awt.image.*,java.util.*,javax.imageio.*" %>
<%!
Color getRandColor(int fc,int bc){//给定范围获得随机颜色
Random random = new Random();
if(fc>255) fc=255;
if(bc>255) bc=255;
int r=fc+random.nextInt(bc-fc);
int g=fc+random.nextInt(bc-fc);
int b=fc+random.nextInt(bc-fc);
return new Color(r,g,b);
}
%>
<%
//设置页面不缓存
response.setHeader("Pragma","No-cache");
response.setHeader("Cache-Control","no-cache");
response.setDateHeader("Expires", 0);
// 在内存中创建图象
int width=60, height=20;
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 获取图形上下文
Graphics g = image.getGraphics();
//生成随机类
Random random = new Random();
// 设定背景色
g.setColor(getRandColor(200,250));
g.fillRect(0, 0, width, height);
//设定字体
g.setFont(new Font("Times New Roman",Font.PLAIN,18));
//画边框
//g.setColor(new Color());
//g.drawRect(0,0,width-1,height-1);
// 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到
g.setColor(getRandColor(160,200));
for (int i=0;i<155;i++)
{
int x = random.nextInt(width);
int y = random.nextInt(height);
int xl = random.nextInt(12);
int yl = random.nextInt(12);
g.drawLine(x,y,x+xl,y+yl);
}
// 取随机产生的认证码(4位数字)
String sRand="";
for (int i=0;i<4;i++){
String rand=String.valueOf(random.nextInt(10));
sRand+=rand;
// 将认证码显示到图象中
g.setColor(new Color(20+random.nextInt(110),20+random.nextInt(110),20+random.nextInt(110)));//调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
g.drawString(rand,13*i+6,16);
}
// 将认证码存入SESSION
session.setAttribute("rand",sRand);
// 图象生效
g.dispose();
// 输出图象到页面
ImageIO.write(image, "JPEG", response.getOutputStream());
%>
---------------使用验证码图片的文件---------a.jsp------------------------------------
<%@ page contentType="text/html;charset=gb2312" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>认证码输入页面</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="0">
</head>
<body>
<form method=post action="check.jsp">
<table>
<tr>
<td align=left>系统产生的认证码:</td>
<td><img border=0 src="image.jsp"></td>
</tr>
<tr>
<td align=left>输入上面的认证码:</td>
<td><input type=text name=rand maxlength=4 value=""></td>
</tr>
<tr>
<td colspan=2 align=center><input type=submit value="提交检测"></td>
</tr>
</form>
</body>
</html>
-----------------验证的页面----------check.jsp
<%@ page contentType="text/html; charset=gb2312" language="java" import="java.sql.*" errorPage="" %>
<html>
<head>
<title>认证码验证页面</title>
<meta http-equiv="Content-Type" content="text/html; charset=gb2312">
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="0">
</head>
<body>
<%
String rand = (String)session.getAttribute("rand");
String input = request.getParameter("rand");
%>
系统产生的认证码为: <%= rand %><br>
您输入的认证码为: <%= input %><br>
<br>
<%
if (rand.equals(input)) {
%>
<font color=green>输入相同,认证成功!</font>
<%
} else {
%>
<font color=red>输入不同,认证失败!</font>
<%
}
%>
</body>
</html>
JSF在很大程度上类似Struts,而不是类似Tapestry,可以说是一种Struts 2.0,都是采取标签库+组件的形式,只是JSF的组件概念没有象Struts那样必须继承ActionForm的限制;JSF在事件粒度上要细腻,不象Struts那样,一个表单一个事件,JSF可以细化到表单中的每个字段上。
JSF只有在组件和事件机制这个概念上类似Tapestry,但是不似Tapestry那样是一个完全组件的框架,所以,如果你做一个对页面要求灵活度相当高的系统,选用Tapestry是第一考虑。
Struts/JSF则适合在一般的数据页面录入的系统中,对于Struts和JSF的选用,我目前个人观点是:如果你是一个新的系统,可以直接从JSF开始;如果你已经使用Struts,不必转换,如果需要切换,可以将JSF和Tapestry一起考虑。
另外,JSF/Tapestry不只是支持Html,也支持多种客户端语言如WML或XUI等。
这三者之间关系:如果说Struts是左派;那Tapestry则是右派;而JSF则是中间派,中庸主义是SUN联盟的一贯策略。
当然,你也可以发表你在实践中这三者任何一个的使用感受,以使得后来者有一个比较。
我们通过下表来比较这 三种框架在实现上图各个功能时技术细节,从而得出他们的异同点和偏重点。
|
Struts |
Tapestry3.0 |
JSF |
在View显示的组件要求 |
组件必须继承ActionForm |
分显式调用和隐式调用 组件必须继承BaseComponent |
普通POJO 无需继承 Managed Bean |
组件在View显示粒度 |
View页面只能显示与表单对应的ActionForm,配置中Action ActionForm 页面一般只能1:1:1关系。 |
可将组件嵌入页面任何一行,对使用组件数量无限制。 |
同Tapestry |
页面分区tiles |
使用Tiles标签库实现,需要另外tiles-def.xml配置文件 |
组件有自己的视图页面,通过调用组件即直接实现多个页面组合。强大自然的页面组合是其特点。 |
通过组件+标签库实现Subview,但如需重用Layout,还要结合Tiles. |
页面跳转 |
使用标签库html:link中写明目标URL,URL名称需要对照配置文件的path命名,与组件Action耦合。 |
URL名称是目标的组件名称,不涉及URL和路径等操作,方便稳固。 |
类似Struts,也需要在配置文件中查找,与组件分离。 |
参数传递 |
使用html:link时传递参数超过一个以上处理麻烦。 |
直接调用组件,直接赋予参数,没有参数个数限制 |
参数分离传递给组件 |
事件触发 |
通过表单提交submit激活,不能细化到表单里字段。 |
能够给于表单每个字段贴一个事件,事件组件必须实现PageListener接口 |
同Tapestry,事件组件必须实习ActionListener 接口 | |
Struts和JSF/Tapestry都属于表现层框架,这两种分属不同性质的框架,后者是一种事件驱动型的组件模型,而Struts只是单纯的MVC模式框架,老外总是急吼吼说事件驱动型就比MVC模式框架好,何以见得,我们下面进行详细分析比较一下到底是怎么回事?
首先事件是指从客户端页面(浏览器)由用户操作触发的事件,Struts使用Action来接受浏览器表单提交的事件,这里使用了Command模式,每个继承Action的子类都必须实现一个方法execute。
在struts中,实际是一个表单Form对应一个Action类(或DispatchAction),换一句话说:在Struts中实际是一个表单只能对应一个事件,struts这种事件方式称为application event,application event和component event相比是一种粗粒度的事件。
struts重要的表单对象ActionForm是一种对象,它代表了一种应用,这个对象中至少包含几个字段,这些字段是Jsp页面表单中的input字段,因为一个表单对应一个事件,所以,当我们需要将事件粒度细化到表单中这些字段时,也就是说,一个字段对应一个事件时,单纯使用Struts就不太可能,当然通过结合JavaScript也是可以转弯实现的。
而这种情况使用JSF就可以方便实现,
<h:inputText id="userId" value="#{login.userId}"> <f:valueChangeListener type="logindemo.UserLoginChanged" /> </h:inputText> |
#{login.userId}表示从名为login的JavaBean的getUserId获得的结果,这个功能使用struts也可以实现,name="login" property="userId"
关键是第二行,这里表示如果userId的值改变并且确定提交后,将触发调用类UserLoginChanged的processValueChanged(...)方法。
JSF可以为组件提供两种事件:Value Changed和 Action. 前者我们已经在上节见识过用处,后者就相当于struts中表单提交Action机制,它的JSF写法如下:
<h:commandButton id="login" commandName="login"> <f:actionListener type=”logindemo.LoginActionListener” /> </h:commandButton> |
从代码可以看出,这两种事件是通过Listerner这样观察者模式贴在具体组件字段上的,而Struts此类事件是原始的一种表单提交Submit触发机制。如果说前者比较语言化(编程语言习惯做法类似Swing编程);后者是属于WEB化,因为它是来自Html表单,如果你起步是从Perl/PHP开始,反而容易接受Struts这种风格。
基本配置
Struts和JSF都是一种框架,JSF必须需要两种包JSF核心包、JSTL包(标签库),此外,JSF还将使用到Apache项目的一些commons包,这些Apache包只要部署在你的服务器中既可。
JSF包下载地址:http://java.sun.com/j2ee/javaserverfaces/download.html选择其中Reference Implementation。
JSTL包下载在http://jakarta.apache.org/site/downloads/downloads_taglibs-standard.cgi
所以,从JSF的驱动包组成看,其开源基因也占据很大的比重,JSF是一个SUN伙伴们工业标准和开源之间的一个混血儿。
上述两个地址下载的jar合并在一起就是JSF所需要的全部驱动包了。与Struts的驱动包一样,这些驱动包必须位于Web项目的WEB-INF/lib,和Struts一样的是也必须在web.xml中有如下配置:
<web-app> <servlet> <servlet-name>Faces Servlet</servlet-name> <servlet-class>javax.faces.webapp.FacesServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet>
<servlet-mapping> <servlet-name>Faces Servlet</servlet-name> <url-pattern>*.faces</url-pattern> </servlet-mapping> </web-app> |
这里和Struts的web.xml配置何其相似,简直一模一样。
正如Struts的struts-config.xml一样,JSF也有类似的faces-config.xml配置文件:
<faces-config> <navigation-rule> <from-view-id>/index.jsp</from-view-id> <navigation-case> <from-outcome>login</from-outcome> <to-view-id>/welcome.jsp</to-view-id> </navigation-case> </navigation-rule>
<managed-bean> <managed-bean-name>user</managed-bean-name> <managed-bean-class>com.corejsf.UserBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> </faces-config>
|
在Struts-config.xml中有ActionForm Action以及Jsp之间的流程关系,在faces-config.xml中,也有这样的流程,我们具体解释一下Navigation:
在index.jsp中有一个事件:
<h:commandButton label="Login" action="login" />
action的值必须匹配form-outcome值,上述Navigation配置表示:如果在index.jsp中有一个login事件,那么事件触发后下一个页面将是welcome.jsp
JSF有一个独立的事件发生和页面导航的流程安排,这个思路比struts要非常清晰。
managed-bean类似Struts的ActionForm,正如可以在struts-config.xml中定义ActionForm的scope一样,这里也定义了managed-bean的scope为session。
但是如果你只以为JSF的managed-bean就这点功能就错了,JSF融入了新的Ioc模式/依赖性注射等技术。
Ioc模式
对于Userbean这样一个managed-bean,其代码如下:
public class UserBean {
private String name;
private String password;
// PROPERTY: name
public String getName() { return name; }
public void setName(String newValue) { name = newValue; }
// PROPERTY: password
public String getPassword() { return password; }
public void setPassword(String newValue) { password = newValue; }
}
<managed-bean> <managed-bean-name>user</managed-bean-name> <managed-bean-class>com.corejsf.UserBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope>
<managed-property> <property-name>name</property-name> <value>me</value> </managed-property>
<managed-property> <property-name>password</property-name> <value>secret</value> </managed-property> </managed-bean> |
faces-config.xml这段配置其实是将"me"赋值给name,将secret赋值给password,这是采取Ioc模式中的Setter注射方式。
Backing Beans
对于一个web form,我们可以使用一个bean包含其涉及的所有组件,这个bean就称为Backing Bean, Backing Bean的优点是:一个单个类可以封装相关一系列功能的数据和逻辑。
说白了,就是一个Javabean里包含其他Javabean,互相调用,属于Facade模式或Adapter模式。
对于一个Backing Beans来说,其中包含了几个managed-bean,managed-bean一定是有scope的,那么这其中的几个managed-beans如何配置它们的scope呢?
<managed-bean> ... <managed-property> <property-name>visit</property-name> <value>#{sessionScope.visit}</value> </managed-property>
|
这里配置了一个Backing Beans中有一个setVisit方法,将这个visit赋值为session中的visit,这样以后在程序中我们只管访问visit对象,从中获取我们希望的数据(如用户登陆注册信息),而visit是保存在session还是application或request只需要配置既可。
UI界面
JSF和Struts一样,除了JavaBeans类之外,还有页面表现元素,都是是使用标签完成的,Struts也提供了struts-faces.tld标签库向JSF过渡。
使用Struts标签库编程复杂页面时,一个最大问题是会大量使用logic标签,这个logic如同if语句,一旦写起来,搞的JSP页面象俄罗斯方块一样,但是使用JSF标签就简洁优美:
<jia:navigatorItem name="inbox" label="InBox" icon="/images/inbox.gif" action="inbox" disabled="#{!authenticationBean.inboxAuthorized}"/>
|
如果authenticationBean中inboxAuthorized返回是假,那么这一行标签就不用显示,多干净利索!
先写到这里,我会继续对JSF深入比较下去,如果研究过Jdon框架的人,可能会发现,Jdon框架的jdonframework.xml中service配置和managed-bean一样都使用了依赖注射,看来对Javabean的依赖注射已经迅速地成为一种新技术象征,如果你还不了解Ioc模式,赶紧补课。
附Jsf核心教程一个JSF案例:login.rar