2007年7月31日

开始学习在 Java&S482; 平台上使用诸如 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

为什么使用 WebWork?
Struts 社区最近在热情地拥抱 WebWork,这种结合导致为 Java 平台提供了一个非常优秀的新 Web 框架:Struts 2。当然,Spring MVC 是一个非常优秀的基于请求的框架,但是它不能像 Struts 2 一样支持 JSF。基于内容的框架(例如 JSF 和 Tapestry)也都很好,但是我发现 WebWork 更为直观,更容易使用(更多有关 Structs 2 和 JSF 的内容请参看 参考资料)。

在创建一个新项目之后,我们就得到了一个类似于图 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 可以让您始终处于 Java Web 开发的技术前沿上 —— 就像我们一样!



参考资料

学习

获得产品和技术
  • AppFuse on java.net:下载不同风味的 AppFuse。

  • WebWork:了解这个易于使用的 Web 框架。

  • DbUnit:查看更多有关 JUnit 扩展的内容。

  • jMock:创建动态仿真对象来简化真正的单元测试。

  • Canoo WebTest:自动化 Web 应用程序的 UI 测试。

  • HtmlUnit:WebTest 的优秀 JavaScript 支持背后的基础。

  • Cargo:自动启动和停止容器。

  • Greenbox:一种代码生成框架。


讨论


关于作者

Matt Raible 居住在美国科罗拉多州的丹佛,他在那里是 Spring 和 Web 框架对 Virtuas Open Source Solutions 的实践先驱。他在开放源码领域具有丰富的经验,是这个领域的专家。他在这个领域中既是用户,又是一名开发人员。Matt 是 SourceBeat PublishingSpring Live 的作者。他还为 Apress 的书籍 Pro JSP Third Edition 作出了很大的贡献。他是很多开放源码会议的积极倡导者,包括 ApacheCon、MySQL User's Conference 和 OSCON,同时他还是 http://raibledesigns.com/ 上一名非常活跃的博客。Raible 的大部分生活都被计算机所包围了,尽管他是在连电都没有的 Montana 长大的。当不工作的时候时,他总是试图让妻子 Julie 成为世界上最幸福的女人,或者与他们的孩子 Abbie 和 Jack 一起玩耍。

posted @ 2007-07-31 11:17 戴生 阅读(353) | 评论 (0)编辑 收藏
 

displayTag作为当前还算得上比较流行的表现层工具插件,在sourceForge官方网站(http://displaytag.sourceforge.net/)上,平均每天的访问量数以万计,本文是建立在实际开发过程中
碰到的导出excel报表的实际需求,所获得的一些学习心得与技巧与大家分享.
默认的displayTag导出的Excel格式会有中文乱码,网上大部分文章都说只有改一下,org.displaytag.export.ExcelView类中,
public String getMimeType()
{
return "application/vnd.ms-excel"; //$NON-NLS-1$
}
方法,在方法后面追加;chartset=gbk;其它不尽然,如果这样改的话在tomcat作为web服务端的话,如果列表中有中文,很慢很慢才会有导出(不过,我至今尚未在tomcat下导出过中文),
把WEB应用布署在JBOSS下面,导出的话,如果数据量大的话也会有20-40秒不等,后来到displayTag的官网上,去看它的bugtrack,发现其实不尽然。其它displayTag如果在你需要导出excel并且想自已利用apache的
POI的话,要再去到官方网上下载一个displaytag-export-poi-1.1.jar的包,详细操作请看http://displaytag.sourceforge.net/11/install.html

其中里面谈到,如果,每次导出的excel数据总是有缓存的话,可以在web.xml文件中加入如下配置进行过滤,这样就可以导出实时数据了。
Configure the Filter in your web.xml:

<filter>
<filter-name>ResponseOverrideFilter</filter-name>
<filter-class>org.displaytag.filter.ResponseOverrideFilter</filter-class>
</filter>And add mappings for the pages that you will intercept, for example:

<filter-mapping>
<filter-name>ResponseOverrideFilter</filter-name>
<url-pattern>*.do</url-pattern>
</filter-mapping>
<filter-mapping>
<filter-name>ResponseOverrideFilter</filter-name>
<url-pattern>*.jsp</url-pattern>
</filter-mapping>

从以上内容来看,也只是告诉你,如果要用apache的最新的POI的话,需要把displaytag-export-poi-1.1.jar从官网下载下来,放入你的工程文件中,
并没有说如何调用写自己的所需的excel的报表格式.下面就实际问题,来讨论一下如何让displayTag导出自己所需的excel格式.
在displayTag所提供的接口类中,导出自已所需的Excel有两种方式,一种是通过指定的excel模板,一种是对excel的所有的格子,一个个样式的处理,
后一种方式完全体现了“慢工出细活”的格言,而前一种方式实现起来显然要好用得多,只用读模板的样式就行了。如何调用displayTag对导出自定制的Excel文件
所提供的接口呢,请看下文,(不要急噢!^_^)
要调用displayTag给Poi所提供的接口操作步骤如下:
1,先要在你的appfuse工程中新建一个类,让其实现org.displaytag.export.BinaryExportView接口,其中关键的方法是
public void doExport(OutputStream out) throws JspException {
String captionvalue = this.model.getCaption();
// ExcelHssfView1 tempExcelView=new ExcelHssfView1();
// try {
// BeanUtils.setProperty(tempExcelView, "action",captionvalue);
// } catch (IllegalAccessException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// } catch (InvocationTargetException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
if (captionvalue != null) {
captionvalue = captionvalue.replaceAll("(\\r)|(\\n)|(\\t)", "");

doExportCommon(out);

} else {
System.out.println("exec Common");
doExportCommon(out);
}
}
对这个方法按照你的POI的定制excel报表的的方法,然后实现它,再把内容写入outputStream中去,基本上就可以了,当然如果有上面的

this.model.getCaption()是读取displayTag的页面标签<displayTag:caption/>标签中的内容,可以在不同的JSP页面放入不同的caption的值
从而判断调用不同的方法,生成不同的excel样式的报表,灵活性兼而有之.
写完以上类以后,最好先写个测试方法,用main或junit工具都成,看看你的调用poi的逻辑有没有问题。
当以上的类及方法写完后,就要在你的appfuse工程中找displaytag.properties文件了,一般就在web/class/目录下,找到后,打开此文件,添加如下一段配置:
export.excel.class=org.displaytag.export.excel.ExcelHssfView1
后面一段是你的新建的类的文件的路径
因为是多国语言系统,所以最好把display_en.properties及display_zh_Cn.properties都加上.
到此就完成了,使用自己的POI来在appfuse中导出指定格式样式的excel文件.先别急噢,还有更精彩的等着你。这个时候又有一个问题来了,如果你
页面想要显示某些列表字段列,而导出的excel文件中又不出现这样的字段,又该如何处理呢,嘿嘿,请接着看下文。
如果要实现以上功能请如下操作:
1,调整页面上的displayTag标签的参数值,呵,比如:

<display:table name="testList" cellspacing="0" cellpadding="0" requestURI="" sort="list"
id="testList" pagesize="5" class="table testList" export="true"
defaultsort="1" defaultorder="descending" >
<display:caption media="excel">ExportByCommon</display:caption>
<display:column title="ID" sortable="true" media="html">
<a href="/aaa.html?id=<c:out value="${testList.id }" /> "
target="operationFrame">
<c:out value="${testList.id }" />
</a>

</display:column>
<display:column title="Status" sortable="true" media="html">
<img src="<c:out value="${testList.status}" />.gif" title="<c:out value="Status:${testList.status}" />">
</display:column>

<display:column property="remarks" escapeXml="true" sortable="true"
title="Remarks" style="word-wrap: break-word;word-break: break-all; width:90px;"/>
<display:footer media="excel">
<c:out value="${aa }"></c:out>|
<c:out value="(${bb })"></c:out>|
<c:out value="${currentDate }"></c:out>
</display:footer>
<display:setProperty name="item_name" value="Info"/>
<display:setProperty name="items_name" value="Infos"/>
<display:setProperty name="export.excel" value="true" />
<display:setProperty name="export.excel.filename" value="<%=exportFileName%>"/>
<display:setProperty name="export.csv" value="false" />
<display:setProperty name="export.xml" value="false" />
<display:setProperty name="export.pdf" value="false" />
</display:table>
</form>
注意上面用了多种配置需求,可以自已加链接,自定义导出名,自定义表头表尾,自定义是否全排序,自定义样式等,这些网上都有,就不细说了,
关键的需求点media的配置参数噢,如果media="excel"表示只在excel中显示,如果media=html表示仅在页面出现,没有此参数是两个都显示噢
其它的格式也类同设置,到此displayTag的点点心得分享与大家完毕,谢谢大家花费时间分享我的快乐!^_^
噢,上文提到中文问题,用POI后就解决了!

posted @ 2007-07-31 11:12 戴生 阅读(1341) | 评论 (0)编辑 收藏

2006年6月16日

     摘要: appfuse在eclipse下的入门  阅读全文
posted @ 2006-06-16 15:25 戴生 阅读(4126) | 评论 (9)编辑 收藏
仅列出标题