J2EE开发平台:Eclipse之Appfuse浅析

    很久没来更新过Blog了,工作忙啊,从J2ME转换到J2EE,需要学习的东西好好多啊,再加上这两年纷至沓来的各种框架,简直让人有点目不暇接啊,但是,没办法啊,所有的用人单位都需要你这个的会,那个也得会,谁让我自己没当老板呢,呵呵;  
    话扯远了,闲暇时间整理点东西,希望和大家一起进步;
   
    首先,我们为什么要用这个平台,Appfuse有什么好处和有点呢?

    开始学习在 Java™ 平台上使用诸如 Spring、Hibernate 或 MySQL 之类的开放源码工具时可能非常困难。再加上 Ant 或 Maven,以及与 DWR 一起的小 Ajax,还有 Web 框架 —— 即 JSF,我们必须睁大眼睛盯着如何配置应用程序。AppFuse 减少了集成开放源码项目的痛苦。它可以把测试变成一等公民,让我们可以从数据库表生成整个 UI,并使用 XFire 来支持 Web 服务。另外,AppFuse 的社区也非常健全,这是不同 Web 框架用户可以一起融洽相处的地方之一。

    AppFuse 是一个开放源码的项目和应用程序,它使用了在 Java 平台上构建的开放源码工具来帮助我们快速而高效地开发 Web 应用程序。我最初开发它是为了减少在为客户构建新 Web 应用程序时所花费的那些不必要的时间。从核心上来说,AppFuse 是一个项目骨架,类似于通过向导创建新 Web 项目时 IDE 所创建的东西。当我们使用 AppFuse 创建一个项目时,它会提示我们将使用开放源码框架,然后才创建项目。它使用 Ant 来驱动测试、代码生成、编译和部署。它提供了目录和包结构,以及开发基于 Java 语言的 Web 应用程序所需要的库。

    与大部分 “new project” 向导不同,AppFuse 创建的项目从最开始就包含很多类和文件。这些文件用来实现特性,不过它们同时也会在您开发应用程序时被用作示例。通过使用 AppFuse 启动新项目,我们通常可以减少一到两周的开发时间。我们不用担心如何将开放源码框架配置在一起,因为这都已经完成了。我们的项目都已提前配置来与数据库进 行交互,它会部署到应用服务器上,并对用户进行认证。我们不必实现安全特性,因为这都早已集成了。

    当我最初开发 AppFuse 时,它只支持 Struts 和 Hibernate。经过几年的努力,我发现了比 Struts 更好的 Web 框架,因此我还添加了为这些 Web 框架使用的选项。现在,AppFuse 可以支持 Hibernate 或 iBATIS 作为持久性框架。对于 Web 框架来说,我们可以使用 JavaServer Faces(JSF)、Spring MVC、Struts、Tapestry 或 WebWork。

    AppFuse 提供了很多应用程序需要的一些特性,包括:

  • 认证和授权
  • 用户管理
  • Remember Me(这会保存您的登录信息,这样就不用每次都再进行登录了)
  • 密码提醒
  • 登记和注册
  • SSL 转换
  • E-mail
  • URL 重写
  • 皮肤
  • 页面修饰
  • 模板化布局
  • 文件上载

    这种 “开箱即用” 的功能是 AppFuse 与其他 CRUD 代 框架的区别之一(CRUD 取自创建、检索、更新删除 几个操作的英文首字母),包括 Ruby on Rails、Trails 和 Grails。上面提到的这些框架,以及 AppFuse,都让我们可以从数据库表或现有的模型对象中生成主页/细节页。

图 1 阐述了一个典型 AppFuse 应用程序的概念设计:


图 1. 典型的 AppFuse 应用程序
典型的 AppFuse 应用程序

清单 1 给出了我们在创建 devworks 项目时所使用的命令行交互操作,同时还给出了所生成的结果。这个项目使用了 WebWork 作为自己的 Web 框架(请参考下面 参考资料 一节给出的链接)。


清单 1. 使用 AppFuse 创建新项目
alotta:~/dev/appfuse mraible$ ant new
Buildfile: build.xml

clean:
[echo] Cleaning build and distribution directories

init:

new:
[echo]
[echo] +-------------------------------------------------------------+
[echo] | -- Welcome to the AppFuse New Application Wizard! -- |
[echo] | |
[echo] | To create a new application, please answer the following |
[echo] | questions. |
[echo] +-------------------------------------------------------------+

[input] What would you like to name your application [myapp]?
devworks
[input] What would you like to name your database [mydb]?
devworks
[input] What package name would you like to use [org.appfuse]?
com.ibm
[input] What web framework would you like to use [webwork,tapestry,spring,js
f,struts]?
webwork
[echo] Creating new application named 'devworks'...
[copy] Copying 359 files to /Users/mraible/Work/devworks
[copy] Copying 181 files to /Users/mraible/Work/devworks/extras
[copy] Copying 1 file to /Users/mraible/Work/devworks
[copy] Copying 1 file to /Users/mraible/Work/devworks

install:
[echo] Copying WebWork JARs to ../../lib
[copy] Copying 6 files to /Users/mraible/Work/devworks/lib
[echo] Adding WebWork entries to ../../lib.properties
[echo] Adding WebWork classpath entries
[echo] Removing Struts-specific JARs
[delete] Deleting directory /Users/mraible/Work/devworks/lib/struts-1.2.9
[delete] Deleting directory /Users/mraible/Work/devworks/lib/strutstest-2.1.3
[echo] Deleting struts_form.xdt for XDoclet
[delete] Deleting directory /Users/mraible/Work/devworks/metadata/templates
[echo] Deleting Struts merge-files in metadata/web
[delete] Deleting 7 files from /Users/mraible/Work/devworks/metadata/web
[echo] Deleting unused Tag Libraries and Utilities
[delete] Deleting 2 files from /Users/mraible/Work/devworks/src/web/org/appfu
se/webapp
[echo] Modifying appgen for WebWork
[copy] Copying 12 files to /Users/mraible/Work/devworks/extras/appgen
[echo] Replacing source and test files
[delete] Deleting directory /Users/mraible/Work/devworks/src/web/org/appfuse/
webapp/form
[delete] Deleting directory /Users/mraible/Work/devworks/src/web/org/appfuse/
webapp/action
[copy] Copying 13 files to /Users/mraible/Work/devworks/src
[delete] Deleting directory /Users/mraible/Work/devworks/test/web/org/appfuse
/webapp/form
[delete] Deleting directory /Users/mraible/Work/devworks/test/web/org/appfuse
/webapp/action
[copy] Copying 5 files to /Users/mraible/Work/devworks/test
[echo] Replacing web files (images, scripts, JSPs, etc.)
[delete] Deleting 1 files from /Users/mraible/Work/devworks/web/scripts
[copy] Copying 34 files to /Users/mraible/Work/devworks/web
[delete] Deleting: /Users/mraible/Work/devworks/web/WEB-INF/validator-rules-c
ustom.xml
[echo] Modifying Eclipse .classpath file
[echo] Refactoring build.xml
[echo] ----------------------------------------------
[echo] NOTE: It's recommended you delete extras/webwork as you shouldn't ne
ed it anymore.
[echo] ----------------------------------------------
[echo] Repackaging info written to rename.log
[echo]
[echo] +-------------------------------------------------------------+
[echo] | -- Application created successfully! -- |
[echo] | |
[echo] | Now you should be able to cd to your application and run: |
[echo] | > ant setup test-all |
[echo] +-------------------------------------------------------------+

BUILD SUCCESSFUL
Total time: 15 seconds
   
    在创建一个新项目之后,我们就得到了一个类似于图 2 所示的目录结构。Eclipse 和 Intellij IDEA 项目文件都是作为这个过程的一部分创建的。
图 2. 项目的目录结构
项目的目录结构

    这个目录结构与 Sun 为 Java 2 Platform Enterprise Edition(J2EE)Web 应用程序推荐的目录结构非常类似。在 2.0 版本的 AppFuse 中,这个结构会变化成适合 Apache Maven 项目的标准目录结构(有关这两个目录介绍的内容,请参看 参考资料 中的链接)。AppFuse 还会从 Ant 迁移到 Maven 2 上,从而获得相关下载的能力和对生成 IDE 项目文件的支持。目前基于 Ant 的系统要求提交者维护项目文件,而 Maven 2 可以通过简单地使用项目的 pom.xml 文件生成 IDEA、Eclipse 和 NetBeans 项目文件。(这个文件位于您项目的根目录中,是使用 Maven 构建应用程序所需要的主要组件)。它与利用 Ant 所使用的 build.xml 文件非常类似。)

    现在我们对 AppFuse 是什么已经有一点概念了,在本文剩下的部分中,我们将介绍使用 AppFuse 的 7 点理由。即使您选择不使用 AppFuse 来开始自己的项目,也会看到 AppFuse 可以为您提供很多样板代码,这些代码可以在基于 Java 语言的 Web 应用程序中使用。由于它是基于 Apache 许可证的,因此非常欢迎您在自己的应用程序中重用这些代码。

理由 1:测试

测试是在软件开发项目中很少被给予足够信任的一个环节。注意我并不是说在软件开发的一些刊物中没有得到足够的信任!很多文章和案例研究都给出了测试 优先的开发方式和足够的测试覆盖面以提高软件的质量。然而,测试通常都被看作是一件只会延长项目开发时间的事情。实际上,如果我们使用测试优先的方法在编 写代码之前就开始撰写测试用例,我相信我们可以发现这实际上会加速 开发速度。另外,测试优先也可以使维护和重用更加 容易。如果我们不编写代码来测试自己的代码,那么就需要手工对应用程序进行测试 —— 这通常效率都不高。自动化才是关键。

当我们首次开始使用 AppFuse 时,我们可能需要阅读这个项目 Web 站点上提供的快速入门指南和教程(请参看 参考资料 中的链接)。这些教程的编写就是为了您可以首先编写测试用例;它们直到编写接口和/或实现之后才能编译。如果您有些方面与我一样,就会在开始编写代码之前 就已经编写好测试用例了;这是真正可以加速编写代码的惟一方式。如果您首先编写了代码的实现,通过某种方式验证它可以工作,那么您可能会对自己说,“哦, 看起来不错 —— 谁需要测试呢?我还有更多的代码需要编写!”这种情况不幸的一面是您通常都会做一些事情 来测试自己的代码;您简单地跳过了可以自动化进行测试的地方。

AppFuse 的文档展示了如何测试应用程序的所有 层次。它从数据库层开始入手,使用了 DbUnit(请参看 参考资料)在运行测试之前提前使用数据来填充自己的数据库。在数据访问(DAO)层,它使用了 Spring 的 AbstractTransactionalDataSourceSpringContextTests 类(是的,这的确是一个类的名字!)来允许简单地加载 Spring 上下文文件。另外,这个类对每个 testXXX() 方法封装了一个事务,并当测试方法存在时进行回滚。这种特性使得测试 DAO 逻辑变得非常简单,并且不会对数据库中的数据造成影响。

在服务层,jMock (请参看 参考资料)用来编写那些可以消除 DAO 依赖的真正 单元测试。这允许进行验证业务逻辑正确的快速测试;我们不用担心底层的持久性逻辑。

HtmlUnit 支持
HtmlUnit 团队在 1.8 发行版中已经完成了相当多的工作来确保包可以与流行的 Ajax 框架(Prototype 和 Scriptaculous)很好地工作。

在 Web 层,测试会验证操作(Struts/WebWork)、控件(Spring MVC)、页面(Tapestry)和管理 bean(JSF)如我们所期望的一样进行工作。Spring 的 spring-mock.jar 可以非常有用地用来测试所有这些框架,因为它包含了一个 Servlet API 的仿真实现。如果没有这个有用的库,那么测试 AppFuse 的 Web 框架就会变得非常困难。

UI 通常是开发 Web 应用程序过程中最为困难的一部分。它也是顾客最经常抱怨的地方 —— 这既是由于它并不是非常完美,也是由于它的工作方式与我们期望的并不一样。另外,没有什么会比在客户面前作演示的过程中看到看到异常堆栈更糟糕的了!您的 应用程序可能会非常可怕,但是客户可能会要求您做到十分完美。永远不要让这种事情发生。Canoo WebTest 可以对 UI 进行测试。它使用了 HtmlUnit 来遍历测试 UI,验证所有的元素都存在,并可以填充表单的域,甚至可以验证一个假想的启用 Ajax 的 UI 与我们预期的工作方式一样。(有关 WebTest 和 HTMLUnit 的链接请参看 参考资料。)

为了进一步简化 Web 的测试,Cargo(请参看 参考资料)对 Tomcat 的启动和停止(分别在运行 WebTest 测试之前和之后)进行了自动化。





理由 2:集成

正如我在本文简介中提到的一样,很多开放源码库都已经预先集成到 AppFuse 中了。它们可以分为以下几类:

  • 编译、报告和代码生成:Ant、Ant Contrib Tasks、Checkstyle、EMMA、Java2Html、PMD 和 Rename Packages
  • 测试框架:DbUnit、Dumbster、jMock、JUnit 和 Canoo WebTest
  • 数据库驱动程序:MySQL 和 PostgreSQL
  • 持久性框架:Hibernate 和 iBATIS
  • IoC 框架:Spring
  • Web 框架:JSF、Spring MVC、Struts、Tapestry 和 WebWork
  • Web 服务:XFire
  • Web 工具:Clickstream、Display Tag、DWR、JSTL、SiteMesh、Struts Menu 和 URL Rewrite Filter
  • Security:Acegi Security
  • JavaScript 和 CSS:Scriptaculous、Prototype 和 Mike Stenhouse 的 CSS Framework

除了这些库之外,AppFuse 还使用 Log4j 来记录日志,使用 Velocity 来构建 e-mail 和菜单模板。Tomcat 可以支持最新的开发,我们可以使用 1.4 或 5 版本的 Java 平台来编译或构建程序。我们应该可以将 AppFuse 部署到任何 J2EE 1.3 兼容的应用服务器上;这已经经过了测试,我们知道它在所有主要版本的 J2EE 服务器和所有主要的 servlet 容器上都可以很好地工作。

图 3 给出了上面创建的 devworks 项目的 lib 目录。这个目录中的 lib.properties 文件控制了每个依赖性的版本号,这意味着我们可以简单地通过把这些包的新版本放到这个目录中并执行诸如 ant test-all -Dspring.version=2.0 之类的命令来测试这些包的新版本。


图 3. 项目依赖性
AppFuse 项目依赖性

预先集成这些开放源码库可以在项目之初极大地提高生产效率。尽管我们可以找到很多文档介绍如何集成这些库,但是定制工作示例并简单地使用它来开发应用程序要更加简单。

除了可以简化 Web 应用程序的开发之外,AppFuse 让我们还可以将 Web 服务简单地集成到自己的项目中。尽管 XFire 也在 AppFuse 下载中一起提供了,不过如果我们希望,也可以自己集成 Apache Axis(请参看 参考资料 中有关 Axis 集成的教程)。另外,Spring 框架和 XFire 可以一起将服务层作为 Web 服务非常简单地呈现出来,这就为我们提供了开发面向服务架构的能力。

另外,AppFuse 并不会将我们限定到任何特定的 API 上。它只是简单地对可用的最佳开放源码解决方案重新进行打包和预先集成。AppFuse 中的代码可以处理这种集成,并实现了 AppFuse 的基本安全性和可用性特性。只要可能,就会减少代码,以便向 AppFuse 的依赖框架添加一个特性。例如,AppFuse 自带的 Remember Me 和 SSL 切换特性最近就因为类似的特性而从 Acegi Security 中删除了。





理由 3:自动化

Ant 使得简化了从编译到构建再到部署的自动化过程。Ant 是 AppFuse 中的一等公民,这主要是因为我发现在命令行中执行操作比从 IDE 中更加简单。我们可以使用 Ant 实现编译、测试、部署和执行任何代码生成的任务。

尽管这种能力对于有些人来说非常重要,但是它并不适用于所有的人。很多 AppFuse 用户目前都使用 Eclipse 或 Intellij IDEA 来构建和测试自己的项目。在这些 IDE 中运行 Ant 的确可以工作,但是这样做的效率通常都不如使用 IDE 内置的 JUnit 支持来运行测试效率高。

幸运的是,AppFuse 支持在 IDE 中运行测试,不过管理这种特性对于 AppFuse 开发人员来说就变得非常困难了。最大的痛苦在于 XDoclet 用来生成 Hibernate 映射文件和 Web 框架所使用的一些工件(例如 ActionForms 和 Struts 使用的 struts-config.xml)。IDE 并不知道需要生成的代码,除非我们配置使用 Ant 来编译它们,或者安装了一些可以认识 XDoclet 的插件。

这种对知识的缺乏是 AppFuse 2.0 切换到 JDK 5 和 Maven 2 上的主要原因。JDK 5、注释和 Struts 2 将让我们可以摆脱 XDoclet。Maven 2 将使用这些生成的文件和动态类路径来生成 IDE 项目文件,这样对项目的管理就可以进行简化。目前基于 Ant 的编译系统已经为不同的层次生成了一些工件(包括 dao.jar、service.jar 和 webapp.war),因此切换到 Maven 的模型上应该是一个非常自然的调整。

除了 Ant 之外(它对于编译、测试、部署和报告具有丰富的支持),对于 CruiseControl 的支持也构建到了 AppFuse 中。CruiseControl 是一个 Continuous Integration 应用程序,让我们可以在源代码仓库中代码发生变化时自动运行所有的测试。extras/cruisecontrol 目录包含了我们为基于 AppFuse 的项目快速、简单地设置 Continuous Integration 时所需要的文件。

设置 Continuous Integration 是软件开发周期中我们首先要做的事情之一。它不但激发程序员去编写测试用例,而且还通过 “You broke the build!” 游戏促进了团队之间的合作和融合。





理由 4:安全特性和可扩展性

AppFuse 最初是作为我为 Apress 编写的书籍 Pro JSP 中示例应用程序的一部分开发的。这个示例应用程序展示了很多安全特性和用于简化 Struts 开发的特性。这个应用程序中的很多安全特性在 J2EE 的安全框图中都不存在。使用容器管理认证(CMA)的认证方法非常简单,但是 Remember Me、密码提示、SSL 切换、登记和用户管理等功能却都不存在。另外,基于角色的保护方法功能在非 EJB 环境中也是不可能的。

最初,AppFuse 使用自己的代码和用于 CMA 的解决方案完全实现了这些特性。我在 2004 年年初开始学习 Spring 时就听说过有关 Acegi Security 的知识。我对 Acegi 所需要的 XML 的行数(175)与 web.xml 中所需要的 CMA 的行数(20)进行了比较,很快就决定丢弃 Acegi 了,因为它太过复杂了。

一年半之后 —— 在为另外一本书 Spring Live 中编写了一章有关使用 Acegi Security 的内容之后 —— 我就改变了自己的想法。Acegi 的确(目 前仍然)需要很多 XML,但是一旦我们理解了这一点,它实际上是相当简单的。当我们最终作出改变,使用 Acegi Security 的特性来全部取代 AppFuse 的特性之后,我们最终删除了大量的代码。类之上的类都已经没有了,“Acegi handles that now” 中消失的部分现在全部进入了 CVS 的 Attic 中了。

Acegi Security 是 J2EE 安全模型中曾经出现过的最好模型。它让我们可以实现很多有用的特性,这些特性在 Servlet API 的安全模型中都不存在:认证、授权、角色保护方法、Remember Me、密码加密、SSL 切换、用户切换和注销。它让我们还可以将用户证书存储到 XML 文件、数据库、LDAP 或单点登录系统(例如 Yale 的 Central Authentication Service (CAS) 或者 SiteMinder)中。

AppFuse 对很多与安全性相关的特性的实现从一开始都是非常优秀的。现在 AppFuse 使用了 Acegi Security,这些特性 —— 以及更多特性 —— 都非常容易实现。Acegi 有很多地方都可以进行扩充:这是它使用巨大的 XML 配置文件的原因。正如我们已经通过去年的课程对 Acegi 进行集成一样,我们已经发现对很多 bean 的定义进行定制可以更加紧密地与 AppFuse 建立联系。

Spring IoC 容器和 Acegi Security 所提供的简单开发、容易测试的代码和松耦合特性的组合是 AppFuse 是这么好的一种开发平台的主要原因。这些框架都是不可插入的,允许生成干净的可测试代码。AppFuse 集成了很多开放源码项目,依赖注入允许对应用程序层进行简单的集成。





理由 5:使用 AppGen 生成代码

有些人会将代码生成称为代码气味的散播(code smell)。在他们的观点中,如果我们需要生成代码,那么很可能就会做一些错事。我倾向于这种确定自己代码使用的模式和自动化生成代码的能力应该称为代码香味的弥漫(code perfume)。如果我们正在编写类似的 DAO、管理器、操作或控件,并且不想为它们生成代码,那么这就需要根据代码的气味来生成代码。当然,当语言可以为我们提供可以简化任务的特性时,一切都是那么美好;不过代码生成通常都是一个必需 —— 通常其生产率也非常高 —— 的任务。

AppFuse 中提供了一个基于 Ant 和 XDoclet 的代码生成工具,名叫 AppGen。默认情况下,常见的 DAO 和管理器都可以允许我们对任何普通老式 Java 对象(POJO)进行 CRUD 操作,但是在 Web 层上这样做有些困难。AppGen 有几个特性可以用来执行以下任务:

  • (使用 Middlegen 和 Hibernate 工具)从数据库表中生成 POJO
  • 从 POJO 生成 UI
  • 为 DAO、管理器、操作/控制器和 UI 生成测试

在运行 AppGen 时,您会看到提示说 AppGen 要从数据库表或 POJO 中生成代码。如果在命令行中执行 ant install-detailed,AppGen 就会安装 POJO 特定的 DAO、管理器以及它们的测试。运行 ant install 会导致 Web 层的类重用通用的 DAO 和默认存在的管理器。

为了阐述 AppGen 是如何工作的,我们在 devworks MySQL 数据库中创建了如清单 2 所示的表:


清单 2. 创建一个名为 cat 的数据库表
    create table cat (
cat_id int(8) auto_increment,
color varchar(20) not null,
name varchar(20) not null,
created_date datetime not null,
primary key (cat_id)
) type=InnoDB;

在 extras/appgen 目录中,运行 ant install-detailed。这个命令的输出结果对于本文来说实在太长了,不过我们在清单 3 中给出了第一部分的内容:


清单 3. 运行 AppGen 的 install-detailed 目标
$ ant install-detailed
Buildfile: build.xml

init:
[mkdir] Created dir: /Users/mraible/Work/devworks/extras/appgen/build
[echo]
[echo] +-------------------------------------------------------+
[echo] | -- Welcome to the AppGen! -- |
[echo] | |
[echo] | Use the "install" target to use the generic DAO and |
[echo] | Manager, or use "install-detailed" to general a DAO |
[echo] | and Manager specifically for your model object. |
[echo] +-------------------------------------------------------+

[input] Would you like to generate code from a table or POJO? (table,pojo)
table
[input] What is the name of your table (i.e. person)?
cat
[input] What is the name, if any, of the module for your table (i.e. organization)?

[echo] Running Middlegen to generate POJO...

要对 cat 表使用这个新生成的代码,我们需要修改 src/dao/com/ibm/dao/hibernate/applicationContext-hibernate.xml,来为 Hibernate 添加 Cat.hbm.xml 映射文件。清单 3 给出了我们修改后的 sessionFactory bean 的样子:


清单 4. 将 Cat.hbm.xml 添加到 sessionFactory bean 中
    <bean id="sessionFactory" class="...">
<property name="dataSource" ref="dataSource"/>
<property name="mappingResources">
<list>
<value>com/ibm/model/Role.hbm.xml</value>
<value>com/ibm/model/User.hbm.xml</value>
<value>com/ibm/model/Cat.hbm.xml</value>
</list>
</property>
...
</bean>

在运行 ant setup deploy 之后,我们就应该可以在部署的应用程序中对 cat 表执行 CRUD 操作了:


图 4. Cat 列表
所生成的主屏幕

图 5. Cat 表单
所生成的详细屏幕

我们在上面的屏幕快照中看到的记录都是作为代码生成的一部分创建的,因此现在就有测试数据了。





理由 6:文档

我们可以找到 AppFuse 各个风味的教程,并且它们都以 6 种不同的语言给出了:中文、德语、英语、韩语、葡萄牙语和西班牙语。使用风味(flavor) 一词,我的意思是不同框架的组合,例如 Spring MVC 加上 iBATIS、Spring MVC 加上 Hibernate 或 JSF 加上 Hibernate。使用这 5 种 Web 框架和两种持久框架,可以有好几种组合。有关它们的翻译,AppFuse 为自己的默认特性提供了 8 种翻译。可用语言包括中文、荷兰语、德语、英语、法语、意大利语、葡萄牙语和西班牙语。

除了核心教程之外,还添加了很多教程(请参看 参考资料) 来介绍与各种数据库、应用服务器和其他开放源码技术(包括 JasperReports、Lucene、Eclipse、Drools、Axis 和 DWR)的集成。





理由 7:社区

Apache 软件基金会对于开放源码有一个有趣的看法。它对围绕开放源码项目开发一个开放源码社区最感兴趣。它的成员相信如果社区非常强大,那么产生高质量的代码就是一个自然的过程。下面的内容引自 Apache 主页:

“我们认为自己不仅仅是一组共享服务器的项目,而且是一个开发人员和用户的社区。”

AppFuse 社区从 2003 年作为 SourceForge 上的一个项目(是 struts.sf.net 的一部分)启动以来,已经获得了极大的增长。通过在 2004 年 3 月转换到 java.net 上之后,它已经成为这里一个非常流行的项目,从 2005 年 1 月到 3 月成为访问量最多的一个项目。目前它仍然是一个非常流行的项目(有关 java.net 项目统计信息的链接,请参看 参考资料),不过在这个站点上它正在让位于 Sun 赞助的很多项目。

在 2004 年年末,Nathan Anderson 成为继我之后第一个提交者。此后有很多人都加入了进来,包括 Ben Gill、David Carter、Mika G?ckel、Sanjiv Jivan 和 Thomas Gaudin。很多现有的提交者都已经通过各种方式作出了自己的贡献,他们都帮助 AppFuse 社区成为一个迅速变化并且非常有趣的地方。

邮件列表非常友好,我们试图维护这样一条承诺 “没有问题是没有人理会的问题”。我们的邮件列表归档文件中惟一一条 “RTFM” 是从用户那里发出的,而不是从开发者那里发出的。我们绝对信奉 Apache 开放源码社区的哲学。引用我最好的朋友 Bruce Snyder 的一句话,“我们为代码而来,为人们而留下”。目前,大部分开发者都是用户,我们通常都喜欢有一段美妙的时间。另外,大部分文档都是由社区编写的;因此, 这个社区的知识是非常渊博的。

先总结一下哈:

我们应该尝试使用 AppFuse 进行开发,这是因为它允许我们简单地进行测试、集成、自动化,并可以安全地生成 Web 应用程序。其文档非常丰富,社区也非常友好。随着其支撑框架越来越好,AppFuse 也将不断改进。

从 AppFuse 2.0 开始,我们计划迁移到 JDK 5(仍然支持部署到 1.4)和 Maven 2 上去。这些工具可以简化使用 AppFuse 的开发、安装和升级。我们计划充分利用 Maven 2 的功能来处理相关依赖性。我们将碰到诸如 appfuse-hibernate-2.0.jar 和 appfuse-jsf-2.0.jar 之类的工件。这些工件都可以在 pom.xml 文件中进行引用,它们负责提取其他相关依赖性。除了在自己的项目中使用 AppFuse 基类之外,我们还可以像普通的框架一样在 JAR 中对这些类简单地进行扩展,这应该会大大简化它的升级过程,并鼓励更多用户将自己希望的改进提交到这个项目中。

    接下来,我们看看怎么样使用appfuse快速构建J2EE应用?

    本文的示例实现对员工信息的增删查改等基本功能。用 Tapestry 实现表示层,用 Hibernate 开发持久层,用 Spring 提供事务控制等跨模块服务,并用 Acegi 进行安全管理。本示例只用到一个域模型:Employee,下面是它的 UML 图。


1. Employee UML
Employee UML 图  

搭建开发环境

本文的代码开发平台采用的是 Windows 操作系统,因此,以下环境设置也是针对 Windows 操作系统的。

  • AppFuse 下载页面 下载 appfuse-tapestry-1.9.3-src.zip,并解压缩在任意目录下。这个 zip 已经定制了使用 Tapestry 作为表现层的实现框架,因而使用起来较为直接。
  • http://java.sun.com 下载最新的 JDK,并安装或解压缩到任意目录下。本文采用 JDK 1.5.0。设置环境变量 JAVA_HOME 指向 JDK 所在的目录,并在 PATH 中添加 %JAVA_HOME%/bin
  • http://jakarta.apache.org/tomcat 下载最新版的 Tomcat,并安装或解压缩到任意目录下。本文采用 Tomcat 5.5.17。设置环境变量 CATALINA_HOME 指向 Tomcat 的安装目录。
  • http://ant.apache.org下载最新版的 Ant,并解压缩到任意目录下。AppFuse 要求的最低版本是 1.6.2,本文采用的是 1.6.5。设置 ANT_HOME 指向 Ant 所在的目录,并在 PATH 中添加 %ANT_HOME%/bin。另外,要拷贝一个 junit.jar %ANT_HOME%/lib 下,如果 lib 下没有 junit.jarAppFuse 的脚本在运行时会给出警告信息。junit.jar 可以从 http://www.junit.org 获得,也可以从 %AppFuse%/lib/junit3.8.1 目录下获得。
  • http://www.mysql.com 下载最新版的 MySQL,并安装或解压缩到任意目录下。本文采用的是 5.0
  • http://www.eclipse.org 下载 Eclipse 3.1 3.2,安装到任意目录下。

    AppFuse 的 Ant 脚本可以在命令行中运行,也可以在 Eclipse 里运行。有关如何在 Eclipse 里执行 Ant 脚本,请参考《用Eclipse开发AppFuse应用》。到此,我们已经为 AppFuse 开发应用准备好了环境,下面让我们开始使用 AppFuse 创建项目。

新建项目

   AppFuse 的便捷与强大之处在于它已经为我们提供了多种开源框架的集成,并且通过使用 Ant 将所有的构建过程自动化。另外,AppFuse 利用 XDoclet 能够为我们生成绝大多数重要的代码,例如 dao 类、service 类以及测试用例,等等,并且能够将大量的配置文件也一并生成好,从而极大地节省了开发人员的时间。

    用 AppFuse 进行开发通常有三种模式:“自上而下”,“自下而上”以及“混合模式”。采用“自上而下”(由 Java 对象向数据库对象创建的过程)的方式固然比较符合“面向对象”的设计思维,但是为此要编写大量的 XDoclet 的 tag 也确是一件痛苦的事情。相比较而言,采用“自下而上”(由数据库对象生成 Java 对象的过程)就显得简单许多 -- 只需要提供数据库表结构。然而,对于较为复杂的系统,尤其是类之间具有大量的关联的情形,仍然需要采用“自上而下”的创建模式。因此,在实际的项目开发中,将两种模式进行混合使用比较常见,这也就是“混合”模式。本文采用“自下而上”的模式。

    本文的 AppFuse 安装在 "c:/opt" 下面。打开命令行控制台,进入 "c:/opt/appfuse",运行 “ant new”,为简单起见,所有参数选用默认值,见图 2。


图 2. ant new -- 新建项目
ant new -- 新建项目  

脚本运行成功后,新项目创建在 c:/opt/myapp 下(与 AppFuse 目录同级),myapp 是 AppFuse 默认的项目名称。将该项目导入到 Eclipse 中,并根据 《用Eclipse开发AppFuse应用》 进行必要的设置。以下是两个你可能需要进行的配置:

  • AppFuse 默认连接 MySQL 的用户名是 root,密码为空。如果你的 root 密码不是空,需要修改 C:/opt/myapp/build.properties 中的 database.admin.password 项,记得将注释去掉。
  • AppFuse 默认不是用 utf-8 创建数据库,如果你需要支持多语言,需要修改 C:/opt/myapp/metadata/sql/mysql-create.sql 中的创建数据库的语句,修改如下:

    清单 1. 创建数据库语句
create database if not exists @DB-NAME@ CHARACTER SET utf8 COLLATE utf8_general_ci;
                  

  • 注:AppFuse 会在构建期将 @DB-NAME@ 替换成你指定的数据库名(本文中为“mydb”)。

    在 c:/opt/myapp 下运行“ant setup test-all”。“setup” 完成了很多“设置”工作:创建数据库、构建 dao 和 serive 类、加载样本数据、创建 war 文件并部署到 tomcat,等等。“test-all” 运行所有的测试用例:对 dao,service 以及页面的测试。如果这个脚本运行成功,说明开发环境一切就绪。这时,启动 Tomcat,通过访问 http://localhost:8080/myapp 就能够看到 AppFuse 的登录界面了。AppFuse 预定义了两个用户:mraible 和 tomcat,密码都是 tomcat。mraible 属于管理员角色(能够管理用户信息),tomcat 属于普通用户角色。用 mraible 登录可以看到 图 3的界面。


图 3. AppFuse 的初始界面
AppFuse 的初始界面

     或许此时,你已惊奇地发现,自己不过只运行了一次 Ant 脚本,但是系统已经拥有“用户管理”、“邮件”、“文件上传” 等功能 -- 这就是 AppFuse “开箱即用”的优势。接下来让我们开始开发前述的应用示例。

创建数据库表

mydb 数据库中执行如下语句创建 employee 表:


清单 2. 创建 employee 语句

CREATE TABLE `employee` (
`id` bigint(20) NOT NULL auto_increment,
`code` varchar(10) NOT NULL,
`dept` varchar(50) NOT NULL,
`name` varchar(20) NOT NULL,
`status` varchar(10) NOT NULL,
`telephone` varchar(20) default NULL,
`title` varchar(50) NOT NULL,
PRIMARY KEY  (`id`)
                  ) ENGINE=InnoDB DEFAULT CHARSET=utf8;   

AppGen 生成代码

    AppFuse 自带了一个代码生成工具 -- AppGen,它位于 c:/opt/myapp/extras/appgen 目录下面。AppGen 可以生成绝大部分我们需要的代码,比如 dao 类,service 类,菜单、增删改的 web 页面、配置文件、样本数据,等等。AppGen 利用 XDoclet 生成代码,因此可以在 extras/appgen/src 看到很多 .xdt 文件,这些就是 XDoclet 的模版定义文件。如果你希望自己编写 dao 和 service 类,就运行“install”这个 target,否则就用 “install-detailed” ,它可以帮你搞定一切。下面就让我们来运行 “install-detailed” 生成代码。在 c:/opt/myapp/extras/appgen 下运行 “ant install-detailed”。


清单 3. 运行 install-detailed

    ...
[input] Would you like to generate code from a table or POJO? (table,pojo)
table
[input] What is the name of your table (i.e. person)?
employee
[input] What is the name, if any, of the module for your table (i.e. organization)?
hr
...

前两个问题都很直观:选择从 table 生成代码,表名是 employee。第三个问题是让用户输入使用的模块名,如果你希望 AppFuse 帮你按模块生成代码的话,就需要输入一个模块名称。这里,我们输入“hr”。如果运行成功,在 Eclipse 中会看到如下的目录结构:
图 4. “install-detailed” 执行后的 Eclipse
“install-detailed” 执行后的 Eclipse

2 列出了 "install-detailed" 生成的主要文件。


表 2. "install-detailed" 生成的主要文件列表

文件

说明

myapp/src/dao/org/appfuse/dao/hibernate/applicationContext-hibernate.xml

在其中增加了 employeeDao 的声明

myapp/src/dao/org/appfuse/hr/model/Employee.java

Employee -- Java Bean

myapp/build/dao/gen/org/appfuse/hr/model/Employee.hbm.xml

Employee 类的 Hibernate 映射文件

myapp/src/dao/org/appfuse/hr/dao/EmployeeDao.java

定义关于 employee dao 层的操作

myapp/src/dao/org/appfuse/hr/dao/hibernate/EmployeeDaoHibernate.java

EmployeeDao Hibernate 实现类

myapp/src/service/org/appfuse/service/applicationContext-service.xml

在其中增加了employeeManager的声明

myapp/src/service/org/appfuse/hr/service/EmployeeManager.java

定义关于 employee service 层的操作

myapp/src/service/org/appfuse/hr/service/impl/EmployeeManagerImpl.java

EmployeeManager 的实现类

myapp/src/web/org/appfuse/hr/webapp/action/EmployeeForm.java

employee 的添加/修改页面对应的 tapestry

myapp/src/web/org/appfuse/hr/webapp/action/EmployeeList.java

employee 的列表页面对应的 tapestry

myapp/test/dao/org/appfuse/hr/dao/EmployeeDaoTest.java

employee dao 类的测试用例

myapp/test/service/org/appfuse/hr/dao/EmployeeManagerTest.java

employee service 类的测试用例

myapp/test/web/org/appfuse/hr/webapp/action/EmployeeFormTest.java

employee 添加/修改页面类的测试用例

myapp/test/web/org/appfuse/hr/webapp/action/EmployeeFormTest.java

employee 列表页面类的测试用例

myapp/web/pages/hr/employeeForm.html

employee 添加/修改页面 html 模版文件

myapp/web/pages/hr/employees.html

employee 列表页面 html 模版文件

myapp/web/pages/hr/employeeForm.page

employee 添加/修改页面规格文件

myapp/web/pages/hr/employees.page

employee 列表页面规格文件

    不过,AppFuse 并不知道开发者需要加载哪些 hbm 文件,所以要手工将 Employee.hbm.xml 文件添加到配置文件中:打开 applicationContext-hibernate.xml,在 “sessionFactory” 的 bean 声明中,找到 “mappingResources” 属性的定义,增加 “<value>org/appfuse/hr/model/Employee.hbm.xml</value>”。


清单 4. applicationContext-hibnerate.xml 中添加 Employee.hbm.xml

    ...
<beans>
    <!-- Hibernate SessionFactory -->
 <bean id="sessionFactory" 

class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
|-- XML error:  The previous line is longer than the max of 90 characters --|
        <property name="dataSource" ref="dataSource"/>
        <property name="mappingResources">
            <list>
                <value>org/appfuse/hr/model/Employee.hbm.xml</value>
                <value>org/appfuse/model/Role.hbm.xml</value>
                <value>org/appfuse/model/User.hbm.xml</value>
            </list>
        </property>
...

c:/opt/myapp 下运行 “ant deploy”。打开 “http://localhost:8080/myapp”,用 mraible/tomcat 登录,“Employee List” 已经被添加到菜单里了。
图 5. myapp 的原始主页面
myapp 的原始主页面

点击 “Employee List” 链接,进入“员工信息列表”页面。


图 6. myapp 的原始员工信息列表页面
myapp 的原始员工信息列表页面

点击“添加”按钮或点击任意一行数据,进入“员工信息添加/修改/删除”页面。


图 7. myapp 的原始员工信息添加/修改/删除页面
myapp 的原始员工信息添加/修改/删除页面

    不难看出,虽然 AppFuse 帮我们生成了页面,但是这些页面并非那么“理想”,我们仍然需要根据实际的需求做些调整。

根据项目需求调整代码

在本文中,做了如下代码修改:

  • 将所有页面文字翻译成中文:AppFuse 中用到的所有 Resource Bundle 文件位于 myapp/web/WEB-INF/classes 目录下(以ApplicationResources开头的properties文件)。更改 ApplicationResources_zh_CN.properties 的文件编码方式为“UTF-8”。然后,把 ApplicationResources.properties “# -- Employee-START” “# -- Employee-END” 之间的项拷贝到 ApplicationResources_zh_CN.properties 中,并逐项翻译成中文。AppFuse 会在脚本运行的时候自动用 native2ascii 进行编码转换。另外,AppFuse 默认对 “button.done” 的翻译是,这不太合适,所以改为完成
  • 员工信息列表页面去掉了 id 列,并调整了列的顺序:只要修改 employees.html 就可以。
  • 员工信息添加/修改/删除页面,将所在部门职位状态改为下拉列表:需要修改 employeeForm.htmlemployeeForm.pageEmployeeForm.java。用 PropertySelection 组件实现下拉列表,用 Resource Bundle 文件定义真正显示的选项文本。
  • 增加了一个人事管理的角色,用来执行员工信息管理的权限控制:具体介绍见系统安全
  • 添加了一个新的主题 “mytheme”(只是更改了界面的颜色):具体介绍见页面布局和样式

    应用了上述修改后,在 c:/opt/myapp 中运行 “ant deploy” 重新打包整个项目并发布。以下是修改后的界面截图:


图 8. 修改后的 myapp 主页面
修改后的 myapp 主页面

图 9. 修改后的 myapp 员工信息列表页面
修改后的 myapp 员工信息列表页面

图 10. 修改后的 myapp 员工信息添加/修改/删除页面
修改后的 myapp 员工信息添加/修改/删除页面

11. 修改后的 myapp 用户管理页面
修改后的 myapp 用户管理页面  

其他功能

    一个系统除了包含核心逻辑之外,还有其他一些辅助功能,它们也是非常重要的。下面,让我们来看看如何在 AppFuse 中开发这些功能。

语言国际化

    如果你的系统不仅仅支持一种语言,那么就需要考虑这个问题。在 AppFuse 中,Resource Bundle 文件是位于 web/WEB-INF/classes 目录下的以 ApplicationResources 开头的 properties 文件。Tapestry 有自己的国际化文本机制。但是在 AppFuse 中,并不全是 Tapestry 页面,仍有些地方使用 jsp,而这些页面使用 JSTL 的 fmt 标签显示国际化文本。不过,AppFuse 已经将这二者的“源头”进行了整合,因此,对用户而言,只需要在 ApplicationResources*.properties 中定义需要国际化的文本。

     但是,在 Eclipse 中可以看到,AppFuse 的 properties 文件默认的编码不是 UTF-8,而是 ISO-8859-1,这样会导致最后通过 native2ascii 转换后的文件都是 “???”,所以用户需要自己把这些文件转成 UTF-8。转换的方法很简单:在 properties 文件上点右键,在右键菜单上选择 Properties,打开属性窗口后,更改 “Text file encoding” 为 UTF-8。在修改编码前,最好先把已有的文字拷贝出来,转换好之后再粘贴回去,否则会导致原先翻译好的文字变成乱码。


图 12. ApplicationResources_zh_CN.properties 的属性窗口
ApplicationResources_zh_CN.properties 的属性窗口

    AppFuse 在发布项目的时候,会自动用 native2ascii 转换这些资源文件。如果你想使用其他资源文件名,可以修改 web/WEB-INF/web.xml 中的 “javax.servlet.jsp.jstl.fmt.localizationContext” 的参数值。

页面布局和样式

    使用 AppFuse,能够很方便的修改系统的整体布局和样式,因为 AppFuse 使用了一种强大的 “CSS框架”。项目创建好之后,在 web/styles 目录下,有三个目录:andreas01,puzzlewithstyle 和 simplicity。这些是 AppFuse 自带的三种主题,目录名即 CSS 框架的主题名。属于“管理员”角色的用户可以在登录后通过在 url 后面添加形如 "?theme=andreas01" 的参数更改系统使用的主题。如下图:


图 13. 应用了 “puzzlewithstyle” 的 myapp
应用了 “puzzlewithstyle” 的 myapp

    系统默认使用的主题由 web/WEB-INF/web.xml 中的 “theme” 参数指定,AppFuse 默认使用的主题是 “simplicity”。更改或创建新的主题也很简单,只要在 web/styles 目录下,新建一个自己的目录,并参照已有主题的编写规范定义自己的主题。本文中,拷贝了 simplicity 目录,更名为 “mytheme”,然后将里面的字体颜色从“蓝色”基调改成了“绿色”基调,并修改 web.xml 中的 theme 参数值为 “mytheme”,这样 myapp 默认使用的就是 mytheme 的主题了,如图 8所示。你也可以从 http://css.appfuse.org/themes/ 得到更多关于 “CSS框架” 的信息。

系统安全

    AppFuse 使用 Acegi 进行安全管理。Acegi 的配置信息位于 web/WEB-INF/classes/security.xml。事实上,Acegi 是被集成到 Spring 当中的,因此这个文件是 Spring 的配置文件格式。在 web/WEB-INF/web.xml 中,该文件被指定在应用启动前会被加载:


清单 5. web.xml 关于 Spring 配置文件的定义

    ...
<!-- Context Configuration locations for Spring XML files -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext-*.xml,/WEB-INF/security.xml</param-value>
    </context-param>
                  ...

 

本文关于系统安全的实现如下:

  1. 在数据库中增加新的角色“hr”:编辑 myapp/metadata/sample-data.xml 文件,增加如下黑体的部分:

    清单 6. sample-data.xml 中角色 “hr” 的记录
      ...
<table name='role'>
    <column>id</column>
    <column>name</column>
    <column>description</column>
    <row>
      <value>1</value>
      <value>admin</value>
      <value><![CDATA[Administrator role (can edit Users)]]></value>
    </row>
    <row>
      <value>2</value>
      <value>user</value>
      <value><![CDATA[Default role for all Users]]></value>
    </row>
    <row>
      <value>3</value>
      <value>hr</value>
      <value><![CDATA[Role for employee mangement]]></value>
    </row>  
</table>
                  ...

  1. AppFuse
    使用 dbunit 加载样本数据到数据库中,sample-data.xml dbunit 提供样本数据定义。修改完 sample-data.xml,在 c:/opt/myapp/ 下运行 “ant db-load”,样本数据被重新加载。这样,我们就在数据库中定义了一个新的角色记录 “hr”
  2. 定义角色名称的中文显示文本:在 myapp/sr/web/webapp/action/UserForm.java 的方法 pageBeginRender 中找到如下代码:
// initialize drop-downs
if (getAvailableRoles() == null) {
List roles = 

  (List) getServletContext().getAttribute(Constants.AVAILABLE_ROLES);
    setAvailableRoles(new RoleModel(roles));
                  }

  1. 将其做如下修改:
// initialize drop-downs
if (getAvailableRoles() == null) {
    List roles = 

    (List) getServletContext().getAttribute(Constants.AVAILABLE_ROLES);
    for(int i=0;i<roles.size();i++){
        LabelValue role=(LabelValue) roles.get(i);
        role.setLabel(getText("rolelabel_"+role.getValue()));
    }
    setAvailableRoles(new RoleModel(roles));
                  }

  1. 并在 web/WEB-INF/classes/ApplicationResources_zh_CN.properties 中增加角色名称的定义:
rolelabel_admin=系统管理员
rolelabel_user=普通用户
                  rolelabel_hr=人事管理

  1. AppFuse
    默认在用户管理界面上显示的角色的名称是表 role 中的名称,这样无论切换到何种语言,角色名称都是 “admin”"user"“hr” 等等,角色名称不能根据 Locale 用相应的语言显示。因此,本文将角色的名称用 Resource Bundle 文件定义,数据库中存储 “key” 值。修改后的效果见 10
  2. 配置安全策略:在 web/WEB-INF/security.xml bean "filterInvocationInterceptor" 声明中增加如下黑体的一行:
<bean id="filterInvocationInterceptor" 
class="org.acegisecurity.intercept.web.FilterSecurityInterceptor">
        <property name="authenticationManager" ref="authenticationManager"/>
        <property name="accessDecisionManager" ref="accessDecisionManager"/>
        <property name="objectDefinitionSource">
            <value>
                PATTERN_TYPE_APACHE_ANT
                /clickstreams.jsp*=admin
                /flushCache.*=admin
                /passwordHint.html*=ROLE_ANONYMOUS,admin,user
                /reload.*=admin
                /signup.html*=ROLE_ANONYMOUS,admin,user
                /users.html*=admin
                /employees.html*=hr
                /**/*.html*=admin,user
            </value>
        </property>
                      </bean>

  1. “/employees.html*=hr”
    的意思是:只有 hr 这个角色可以访问形如 “/employees.html*” url
  2. 员工信息维护菜单关联到指定角色 hr:在 web/WEB-INF/menu-config.xml 中在 “EmployeeMenu” 的定义中增加 “roles='hr'”:
    <!--Employee-START-->
<Menu name="EmployeeMenu" title="employeeList.title" 

page="/employees.html" roles="hr"/>
    <!--Employee-END-->
                        

  1. 于是,员工信息维护的菜单入口只对属于人事管理角色的用户显示,对其他用户则隐藏。
  2. 分配角色 “hr” tomcat:将人事管理角色分配给某一用户,例如 tomcat。则 tomcat能够看见并访问员工信息维护相关页面,而其他用户的界面上则没有员工信息维护这个菜单入口。并且,如果用户试图通过url访问employees.html的时候会看到如下页面:

    14. “访问被拒绝页面
    “访问被拒绝”页面

    14 AppFuse 提供的默认访问被拒绝页面,你可以通过修改 web/403.jsp 把它定制成自己喜欢的页面。

事务控制

    AppFuse 利用 Spring 的事务管理机制。Spring 可以以声明的方式,对方法进行事务控制,并且可以根据实际的需要,调整控制粒度。“声明方式”的好处在于:核心代码只需要关注业务逻辑,而将事务控制完全交由配置文件管理,一方面是核心代码简洁清晰,另一方面也便于进行集中配置管理。

    事务控制一般是定义在 service 类的方法上的。AppFuse 的所有 service 类都声明在 src/service/applicationContext-service.xml 中,该文件中包含有一个 “txProxyTemplate” bean 的声明,它定义了基本事务策略。其它的 service 类从 “txProxyTemplate” 继承,并可以“重写”事务策略。例如,AppFuse 对 userManager 的声明如下:


    <!-- Transaction template for Managers, from:
http://blog.exis.com/colin/archives/2004/07/31/

concise-transaction-definitions-spring-11/ -->
|-- XML error:  The previous line is longer than the max of 90 characters --|
    <bean id="txProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
        <property name="transactionManager" ref="transactionManager"/>
        <property name="transactionAttributes">
            <props>
                <prop key="save*">PROPAGATION_REQUIRED</prop>
                <prop key="remove*">PROPAGATION_REQUIRED</prop>
                <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
            </props>
        </property>
    </bean>
    <!-- Transaction declarations for business services. 

   To apply a generic transaction proxy to
|-- XML error:  The previous line is longer than the max of 90 characters --|
         all managers, you might look into using the BeanNameAutoProxyCreator -->
    <bean id="userManager" parent="txProxyTemplate">
        <property name="target">
            <bean class="org.appfuse.service.impl.UserManagerImpl">
                <property name="userDao" ref="userDao"/>
            </bean>
        </property>
        <!-- Override default transaction attributes b/c of UserExistsException -->
        <property name="transactionAttributes">
            <props>
                <prop key="save*">PROPAGATION_REQUIRED,-UserExistsException</prop>
                <prop key="remove*">PROPAGATION_REQUIRED</prop>
                <prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
            </props>
        </property>
        <!-- This property is overriden in applicationContext-security.xml to add
             method-level role security -->
        <property name="preInterceptors">
            <list>
                <ref bean="userSecurityInterceptor"/>
            </list>
        </property>
    </bean>    
                     

 

    Spring 提供了大量的参数和选项使开发者能够灵活地管理事务。有关 Spring 使用方面的知识,请参阅 Spring 的文档。另外,《Spring in Action》也是一个不错的选择。

日志

    AppFuse 集成了 Log4j 进行日志管理,log4j.properties 位于 web/WEB-INF/classes 目录下。AppFuse 已经在绝大多数基类(诸如,BasePage.java、BaseDaoHibernate.java 以及 BaseManager.java 等)中加入了如下用于输出日志的成员变量:

protected final Log log = LogFactory.getLog(getClass());
                     

    因此,开发者只需要在自己的代码中调用 log 的方法就可以了,例如:“log.debug("entered 'delete' method");”。

邮件

    AppFuse 集成了 Spring 的发送邮件的功能。发送邮件需要用的参数,如主机、端口等信息在 web/WEB-INF/classes/mail.properties 中进行配置。和发送邮件相关的 bean 已经在 applicationContext-service.xml 中声明:mailEngine、mailSender、velocityEngine 以及 mailMessage。用户只需要在自己的类中 “注入” mainSender 的实例,就可以发送邮件了。具体使用方法,请参阅Spring的文档。

缓存

    AppFuse 对缓存机制的支持源自 Hibernate 对缓存的支持。Hibernate 提供了对五种缓存机制的集成,AppFuse 默认提供了其中的两种:Ehcache 和 Oscache。开发者也可以根据需要自行添加和配置。Acegi 默认提供了对 Ehcache 支持的实现,所以 Ehcache 是较好的选择。ehcache.xml 和 oscache.properties 位于 web/WEB-INF/classes 中。

再总结一下哈

    使用 AppFuse 创建 Web 应用,步骤非常简单,你只需要了解如何运行 Ant 就能够使用 AppFuse;使用 AppFuse 创建 Web 应用,非常快速,因为 AppFuse 已经帮我们完成大部分代码生成/集成/配置的工作;使用 AppFuse 创建 Web 应用,非常省力,因为 AppFuse 已经提供了很多“开箱即用”的功能。体验快速开发,从 AppFuse 开始。

    本文中有些东西来源于网上,只要对大家有一点点点点好处,我就粉高兴了;

    好了,该工作了哈。

 

©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页