示例介紹
本文的示例實現對員工信息的增刪查改等基本功能。用 Tapestry 實現表示層,用 Hibernate 開發持久層,用 SPRing 提供事務控制等跨模塊服務,并用 Acegi 進行安全管理。本示例只用到一個域模型:Employee,下面是它的 UML 圖。
搭建開發環境
本文的代碼開發平臺采用的是 Windows 操作系統,因此,以下環境設置也是針對 Windows 操作系統的。
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 -- 新建項目
腳本運行成功后,新項目創建在 c:/opt/myapp 下(與 AppFuse 目錄同級),myapp 是 AppFuse 默認的項目名稱。將該項目導入到 Eclipse 中,并根據 《用Eclipse開發AppFuse應用》 進行必要的設置。以下是兩個你可能需要進行的配置:
create database if not exists @DB-NAME@ CHARACTER SET utf8 COLLATE utf8_general_ci;
在 c:/opt/myapp 下運行“ant setup test-all”?!皊etup” 完成了很多“設置”工作:創建數據庫、構建 dao 和 serive 類、加載樣本數據、創建 war 文件并部署到 tomcat,等等?!皌est-all” 運行所有的測試用例:對 dao,service 以及頁面的測試。如果這個腳本運行成功,說明開發環境一切就緒。這時,啟動 Tomcat,通過訪問 http://localhost:8080/myapp 就能夠看到 AppFuse 的登錄界面了。AppFuse 預定義了兩個用戶:mraible 和 tomcat,密碼都是 tomcat。mraible 屬于管理員角色(能夠管理用戶信息),tomcat 屬于普通用戶角色。用 mraible 登錄可以看到 圖 3的界面。
圖 3. 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”。
...[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 中會看到如下的目錄結構:
表 2 列出了 "install-detailed" 生成的主要文件。
不過,AppFuse 并不知道開發者需要加載哪些 hbm 文件,所以要手工將 Employee.hbm.xml 文件添加到配置文件中:打開 applicationContext-hibernate.xml,在 “sessionFactory” 的 bean 聲明中,找到 “mappingResources” 屬性的定義,增加 “<value>org/appfuse/hr/model/Employee.hbm.xml</value>”。
...<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” 已經被添加到菜單里了。
點擊 “Employee List” 鏈接,進入“員工信息列表”頁面。
點擊“添加”按鈕或點擊任意一行數據,進入“員工信息添加/修改/刪除”頁面。
不難看出,雖然 AppFuse 幫我們生成了頁面,但是這些頁面并非那么“理想”,我們仍然需要根據實際的需求做些調整。
根據項目需求調整代碼
在本文中,做了如下代碼修改:
應用了上述修改后,在 c:/opt/myapp 中運行 “ant deploy” 重新打包整個項目并發布。以下是修改后的界面截圖:
圖 8. 修改后的 myapp 主頁面
圖 9. 修改后的 myapp 員工信息列表頁面
圖 10. 修改后的 myapp 員工信息添加/修改/刪除頁面
圖 11. 修改后的 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 的屬性窗口
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
系統默認使用的主題由 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> ...
本文關于系統安全的實現如下:
...<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> ...
// initialize drop-downsif (getAvailableRoles() == null) {List roles =
(List) getServletContext().getAttribute(Constants.AVAILABLE_ROLES); setAvailableRoles(new RoleModel(roles)); }
// initialize drop-downsif (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)); }
rolelabel_admin=系統管理員rolelabel_user=普通用戶 rolelabel_hr=人事管理
<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>
<!--Employee-START--><Menu name="EmployeeMenu" title="employeeList.title"
page="/employees.html" roles="hr"/> <!--Employee-END-->
事務控制
AppFuse 利用 Spring 的事務管理機制。Spring 可以以聲明的方式,對方法進行事務控制,并且可以根據實際的需要,調整控制粒度?!奥暶鞣绞健钡暮锰幵谟冢汉诵拇a只需要關注業務邏輯,而將事務控制完全交由配置文件管理,一方面是核心代碼簡潔清晰,另一方面也便于進行集中配置管理。
事務控制一般是定義在 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 開始。
關于作者
沈銳在 J2EE 項目開發方面有多年的經驗,目前在 IBM CSDL 從事 IBM Workplace Dashboard Framework 產品的開發工作。他對 Java 的開源技術有著濃厚的興趣,歡迎使用 shenrui@cn.ibm.com 與他交流。
(出處:http://www.49028c.com)
新聞熱點
疑難解答