數(shù)據(jù)庫 Orders表中的每一行存儲(chǔ)一個(gè)單獨(dú)的訂單,其中的信息包括聯(lián)系人姓名、送貨地址以及信用卡細(xì)目。Orders表有6列:OrderId(定單標(biāo)識), ContactName(聯(lián)系人姓名), DeliveryAddress(送貨地址), CCName(信用卡所屬人姓名), CCNumber(信用卡號碼)和 CCExpiryDate(信用卡有效期限)。 用于在一個(gè)Oracle數(shù)據(jù)庫中創(chuàng)建所需的表的腳本文件pizzaria-oracle.sql存放在pizzaria.zip文件中。 業(yè)務(wù)對象 以下是在該應(yīng)用程序中使用的業(yè)務(wù)對象: ProductBean用于封裝一個(gè)產(chǎn)品信息。它具有如下屬性:id(標(biāo)識)、name(名稱)、description(說明)和price(價(jià)格)。每次details.asp頁被訪問的時(shí)候,JSF實(shí)現(xiàn)(implementation)就會(huì)自動(dòng)創(chuàng)建一個(gè)ProductBean實(shí)例。該JSF實(shí)現(xiàn)調(diào)用ProductBean的無參數(shù)構(gòu)造器,從數(shù)據(jù)庫中獲取相關(guān)的數(shù)據(jù),并且將其填入相應(yīng)的字列中。 ProductSummary。ProductSummary(產(chǎn)品概要)用于表示產(chǎn)品的概要。該類包含2個(gè)屬性:id(標(biāo)識)和name(名稱)。 ShoppingItemBean。ShoppingItemBean用于表示購物項(xiàng)目。該類包含4個(gè)屬性:productId(產(chǎn)品標(biāo)識), productName(產(chǎn)品名稱), price(價(jià)格)以及 quantity(數(shù)量)。 ShoppingCartBean。ShoppingCartBean用于表示一個(gè)存儲(chǔ)在對話(session)對象中的購物車。該類允許用戶添加購物項(xiàng)目(使用addShopping方法),獲取包含所有購物項(xiàng)目的列表(使用getShoppingItems方法),獲得所購貨物的總價(jià)值(使用getTotal方法)。 OrderBean。OrderBean表示一個(gè)訂單。該類具有如下5個(gè)屬性:contactName, deliveryAddress, creditCardName, creditCardNumber以及 creditCardExpiryDate。 MenuBean。MenuBean使用getMenu方法顯示可供產(chǎn)品的目錄。該方法返回一個(gè)包含到產(chǎn)品細(xì)節(jié)的鏈接的HTML表。
應(yīng)用程序上下文監(jiān)聽器 應(yīng)用程序上下文監(jiān)聽器(AppContextListener類)從web.xml文件讀出用于訪問數(shù)據(jù)庫的初始參數(shù),然后將其寫入ServletContext對象。用到的初始參數(shù)如下:jdbcDriver, dbUrl, dbUserName和 dbPassword。在你的web.xml文件中編輯這些值,以便反應(yīng)你數(shù)據(jù)庫的真實(shí)值。 JSF應(yīng)用程序配置 JSF允許編程人員僅僅通過應(yīng)用程序配置文件就可以輕松配置應(yīng)用程序。該文件如果存在的話,則它應(yīng)該被命名為faces-config.xml,并且應(yīng)該位于你應(yīng)用程序下的WEB-INF 目錄。 可以在faces-config.xmlz文件中對該應(yīng)用程序的多個(gè)方面進(jìn)行配置,包括bean管理、頁面導(dǎo)航、定制UI(用戶界面)組件、定制驗(yàn)證程序和消息資源。在 PizzaRia 應(yīng)用程序中,我將該faces-config.xml用于bean管理和頁面導(dǎo)航的配置。 <!ELEMENT managed-bean (description*, display-name*, icon*, managed-bean name, managed-bean-class, managed-bean-scope, (managed-property* | map-entries | list-entries))> 每個(gè)managed-bean元件都必須包含一個(gè)managed-bean-name元件,一個(gè)managed-bean-class元件,以及一個(gè)managed-bean-scope元件,并且可選擇性地包含一些描述、顯示名、圖標(biāo)和managed-property/map-entries/list-entries元件。 managed-bean-name指定了被用來在整個(gè)應(yīng)用程序中引用該JavaBean的名稱。managed-bean-class元件包含該JavaBean的完全限度的類名。managed-bean-scope元件定義該JavaBean的作用域。該元件可能的值是:application、session、request或者none。如果managed-bean-scope元件是none以外的其他值,那么,所創(chuàng)建的該JavaBean元件將會(huì)被存儲(chǔ)在相應(yīng)的對象中。比如說,如果值是"session",那么,該JavaBean就會(huì)被存儲(chǔ)在一個(gè)給定用戶的session對象中。 在PizzaRia應(yīng)用程序中,我注冊了如代碼清單1所示的4個(gè)JavaBeans。 頁面導(dǎo)航:頁面導(dǎo)航?jīng)Q定了Web應(yīng)用程序的控制流。本節(jié)演示如何在JSF中創(chuàng)立一個(gè)頁面導(dǎo)航。 JSF使用navigation-rule元件來為頁面導(dǎo)航定義規(guī)則。其定義如下: <!ELEMENT navigation-rule (description*, display-name*, icon*, from-view-id?, navigation-case*)> from-view-id元件是首頁(起始頁)的標(biāo)識符。為了說明被稱之為index.jsp的JSP頁面的導(dǎo)航規(guī)則,下面給出子元件from-view-id的值: <from-view-id>/index.jsp</from-view-id> navigation-case元件表示一個(gè)可能的目標(biāo)頁面。navigation-rule一個(gè)元件可以有零個(gè)或者數(shù)個(gè)navigation-case子元件。 每個(gè)navigation-case元件都指定from-view-id的特定處理結(jié)果的目標(biāo)頁面。結(jié)果可以來自from-view-id元件中 UICommand組件的行動(dòng)(action)屬性。 navigation-case元件由如下所示的代碼描述: <!ELEMENT navigation-case (description*, display-name*, icon*, from-action?, from-outcome?, to-view-id, redirect?)> to-view-id元件指定目標(biāo)頁面。from-outcome值是處理from-view-id的結(jié)果。該值來自于在from-view-id中觸發(fā)了ActionEvent的 UICommand組件的行動(dòng)屬性。 from-action元件也表示處理from-view-id的結(jié)果。但其值來自于引發(fā)了ActionEvent的UICommand組件的行動(dòng)屬性的運(yùn)算值。 代碼清單2展示了在PizzaRia應(yīng)用程序中使用的navigation-rule元件。 在JSP頁面中使用UI組件 JSF提供兩個(gè)定制標(biāo)記庫來幫助用戶快速編寫Web應(yīng)用程序:HTML和Core。HTML定制標(biāo)記庫定義了用來表示UI組件的標(biāo)記。Core定制標(biāo)記庫使用具有組件的驗(yàn)證器(validators)定義了注冊事件處理器的核心行動(dòng),以及其他一些行動(dòng)。你可以在自己的JSF應(yīng)用程序的JSP頁面中使用這兩個(gè)庫的標(biāo)記。 為了在JSP頁面中使用HTML和Core定制標(biāo)記庫,必須在頁面中包含如下所示的taglib指令: <%@ taglib uri="http://java.sun.com /jsf/html/" prefix="h" %> <%@ taglib uri="http://java.sun.com/ jsf/core/" prefix="f" %> Prefix的屬性值可以是任意值。但是,根據(jù)慣例,最好是使用"h"和"f"。 在JSF應(yīng)用程序中編寫JSP頁面是每一個(gè)頁面制作者的責(zé)任。除了布置組件之外,他們的責(zé)任還包括把組件綁定到模型對象數(shù)據(jù)并且把Core標(biāo)記(諸如事件監(jiān)聽器和驗(yàn)證器)添加到組件標(biāo)記中。 在HTML定制標(biāo)記庫中有25個(gè)標(biāo)記。每個(gè)組件都呈現(xiàn)為一個(gè)HTML元件,而多個(gè)標(biāo)記被呈現(xiàn)為同一個(gè)HTML元件。表1列出了HTML定制標(biāo)記庫中的標(biāo)記。
使用驗(yàn)證器 驗(yàn)證器是一個(gè)實(shí)現(xiàn)類(implementation class),它可以驗(yàn)證輸入值,如果是非法輸入,就會(huì)發(fā)出一個(gè)錯(cuò)誤信息。可以通過將一個(gè)驗(yàn)證器嵌入一個(gè)其輸入需要驗(yàn)證的輸入組件中來使用它。如果該驗(yàn)證器判斷出用戶的輸入是非法的,那么JSF servlet就會(huì)重新顯示剛才提交了表單的那個(gè)JSP頁面,而不會(huì)將本地值復(fù)制給綁定到該輸入組件上的JavaBean實(shí)例。
另外,HTML定制標(biāo)記庫中的input_text和input_textarea標(biāo)記有必填的屬性。如果將該屬性標(biāo)賦值為真,那么用戶在繼續(xù)進(jìn)行操作之前,就必須對文本輸入框元件或者文本輸入?yún)^(qū)域進(jìn)行填寫。 JSP應(yīng)用程序是事件驅(qū)動(dòng)型的程序。在JSF中處理事件令人驚奇的簡單。以下是處理步驟:
在JSF中的事件對象。JSF中的所有事件對象必須提供javax.faces .event.FacesEvent類,以便這些事件被請求處理生命周期支持。FacesEvent類是java.util.EventObject的子類,并添加了getComponent方法,該方法返回引發(fā)該事件的UIComponent組件。 FacesEvent類有兩個(gè)子類:ActionEvent和 ValueChangeEvent。ActionEvent類激活諸如UICommand組件之類的UI組件。 為捕獲一個(gè)JSF事件,需要使用一個(gè)事件監(jiān)聽器。JSF程序中的所有監(jiān)聽器都必須實(shí)現(xiàn)javax.faces.event.FacesListener接口。該接口提供java.util.EventListener接口,后者是必須由所有Java事件監(jiān)聽器實(shí)現(xiàn)的接口。 public void processAction(ActionEvent event) throws AbortProcessingException ValueChangeListener接口是為了捕獲ValueChangeEvent而實(shí)現(xiàn)的接口。該接口添加了一個(gè)方法:processValueChange。當(dāng)ValueChangeEvent動(dòng)作被其監(jiān)聽者監(jiān)聽到時(shí),就會(huì)調(diào)用processValueChange方法。processValueChange方法的代碼如下: public void processValueChange(ValueChangeEvent event) throws AbortProcessingException
AppActionListener類使用兩個(gè)非常有用的方法:getValueBinding 和getDatabaseUtil。getValueBinding接受指定對象名的字符串,并返回一個(gè)可以向下轉(zhuǎn)換類型為對象類型的ValueBinding對象。比如說,為獲得用戶的在應(yīng)用程序配置文件中被注冊成shoppingCartBean 的ShoppingCartBean實(shí)例,開發(fā)人員需要通過傳遞"shoppingCartBean"來調(diào)用getValueBinding。 ShoppingCartBean cart = (ShoppingCartBean) getValueBinding("#{shoppingCartBean}").getValue(facesContext); getValueBinding方法如下: private ValueBinding getValueBinding(String valueRef) { ApplicationFactory factory = (ApplicationFactory)FactoryFinder .getFactory(FactoryFinder .APPLICATION_FACTORY); Application application = factory.getApplication(); return application.createValueBinding (valueRef); } getDatabaseUtil方法返回一個(gè)對ServletContext中的DatabaseUtil實(shí)例的引用: private DatabaseUtil getDatabaseUtil() { FacesContext facesContext = FacesContext.getCurrentInstance(); ServletContext servletContext = (ServletContext) facesContext.getExternalContext() .getContext(); return (DatabaseUtil) servletContext .getAttribute("DATABASE_UTIL"); } 運(yùn)行該應(yīng)用程序 JSF開發(fā)實(shí)戰(zhàn)(二) bromon原創(chuàng) 請尊重版權(quán) JSF開發(fā)實(shí)戰(zhàn)的第一篇(jsf開發(fā)實(shí)戰(zhàn)一)演示了如何建立一個(gè)最簡單的JSF應(yīng)用,從那個(gè)例子中我們可以觀察到JSF的頁面組件是如何與后臺的javabean綁定的,也看到了JSF標(biāo)簽的最基本使用。在本文中我們會(huì)演示一個(gè)更加復(fù)雜的應(yīng)用,它包含了更豐富的JSF標(biāo)簽。 要開發(fā)的例子是一個(gè)用戶管理程序,管理員輸入帳號與密碼登陸,然后它可以看到所有用戶的列表,并且可以修改或者刪除其中的一些數(shù)據(jù)。利用myeclipse所帶的jsf-config.xml設(shè)計(jì)器,頁面流程如下: ![]() 可以看出,這個(gè)應(yīng)用設(shè)計(jì)到的業(yè)務(wù)邏輯有: 用戶登陸、修改用戶信息、增加新用戶、刪除用戶 我們在后臺的數(shù)據(jù)庫操作中使用hibernate框架來輔助開發(fā),相關(guān)的技術(shù)細(xì)節(jié)請自行查閱文檔。 首先建立pojo文件:User.java,它包含幾個(gè)基本屬性: private int id; private String name; private String password; private int power; 請自行完成set/get方法,并且編寫對應(yīng)的hbm.xml文件。 我們的主要工作之一,是要建立好供jsf頁面組件使用的javabean,把它命名為UMDelegater.java。它會(huì)調(diào)用UserManager來完成業(yè)務(wù)邏輯,這里是一個(gè)代理模式。UserManager的內(nèi)容只是簡單的增/刪/查/改的操作,這里不再具體列出。UMDelegater的內(nèi)容是: package org.bromon.jsf.control; import java.util.List; import javax.faces.model.DataModel; import javax.faces.model.ListDataModel; import org.bromon.jsf.model.UserManager;//自行建立的工具類,負(fù)責(zé)所有的hibernate操作 import org.bromon.jsf.model.pojo.User;//pojo對象 public class UMDeletager { private UserManager um=new UserManager();//所有具體的方法都由它來實(shí)現(xiàn) private User user=new User(); private DataModel allUsers=new ListDataModel();//JSF的內(nèi)置對象,用來封裝html中table的數(shù)據(jù) //----------set/get方法--------------------- public DataModel getAllUsers() { return allUsers; } public void setAllUsers(List list) { allUsers.setWrappedData(list); } public UserManager getUm() { return um; } public void setUm(UserManager um) { this.um = um; } public User getUser() { return user; } public void setUser(User user) { this.user = user; } //-----功能方法--------- public String login() { String s=um.login(this.getUser()); if(s.equals("ok")) { this.setAllUsers(um.loadAll());//如果登陸成功,就取出所有的用戶信息 return "login:ok"; }else { swapper.setLoginFailInfo(s); return "login:fail"; } } public String edit() { this.user=(User)allUsers.getRowData();//頁面中的table自動(dòng)返回含有id的user對象 this.user=um.loadById(user.getId()); if(user!=null) { return "edit"; }else { return "error"; } } public String update() { um.update(this.getUser()); this.setAllUsers(um.loadAll());//重新取一次數(shù)據(jù),目的是更新緩存 return "update:ok"; } public String addNew() { this.setUser(new User());//生成一個(gè)新的user對象,不含任何數(shù)據(jù),它會(huì)被自動(dòng)映射成一個(gè)沒有數(shù)據(jù)的form return "add"; } public String add() { um.add(this.getUser()); this.setAllUsers(um.loadAll());//重新取一次數(shù)據(jù),目的是更新緩存 return "add:ok"; } } 在jsf-config.xml中聲明這個(gè)bean: <managed-bean> <managed-bean-name>UMDelegater</managed-bean-name> <managed-bean-class>org.bromon.jsf.control.UMDeletager</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> 業(yè)務(wù)邏輯就設(shè)計(jì)完成了,下面可以開始編寫jsf文件,首先是index.jsp: 首先引入標(biāo)簽庫,并且聲明page屬性: <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> 然后是構(gòu)建頁面: <body> <f:view> <h:form> <h:panelGrid columns="3"> <h:outputLabel for="name" value="帳號:"/> <h:inputText id="name" value="#{UMDelegater.user.name}" required="true"/> <h:message for="name"/> <h:outputLabel for="password" value="密碼:"/> <h:inputSecret id="password" value="#{UMDelegater.user.password}" required="true" /> <h:message for="password"/> </h:panelGrid> <h:panelGroup> <h:commandButton value="登陸" action="#{UMDelegater.login}"/> </h:panelGroup> </h:form> </f:view> </body> 頁面中聲明了兩個(gè)文本框,分別映射UMDelegater對象中user對象的name屬性,和UMDelegater對象中user對象的password屬性。一旦這個(gè)表單被提交,這兩個(gè)文本框的值就會(huì)被自動(dòng)賦給user對象,我們只需要從user中取數(shù)據(jù)就行了,不用再去執(zhí)行麻煩的request.getParameter(“”),更改編碼,轉(zhuǎn)換數(shù)據(jù)類型等操作。 我們同時(shí)還聲明了一個(gè)button,它與UMDelegater對象的login方法綁定,點(diǎn)擊該按鈕,系統(tǒng)會(huì)執(zhí)行UMDelegater.login方法,該方法從user對象中取出name和password,和數(shù)據(jù)庫中的記錄進(jìn)行比較。如果合法,那么就取出所有的數(shù)據(jù),放到一個(gè)DataModel對象中,具體代碼是: List userList=UserManager.getAllUser();//取得所有用戶數(shù)據(jù),放到一個(gè)List中 DataModel allUser=new ListDataModel ();//DataModel是一個(gè)接口,ListDataModel是它的一個(gè)實(shí)現(xiàn) allUsers.setWrappedData(userList);//將數(shù)據(jù)填充進(jìn)去備用 使用DataModel意義何在呢?JSF中,我們可以把一個(gè)html頁面上的table和一個(gè)DataModel綁定起來,這些數(shù)據(jù)會(huì)自動(dòng)填充到table中,我們不必再自己去寫循環(huán),生成若干的<tr>、<td>來生成一個(gè)table。在list.jsp中我們會(huì)看到如何使用DataModel。 放好數(shù)據(jù)之后,登陸成功的操作就完成了,返回一個(gè)login:ok,就可以重定向到list.jsp。如果用戶登陸失敗,那么會(huì)返回login:fail,重定向到error.jsp,它的內(nèi)容就不敘說了。下面我們看看list.jsp里面有什么,下面是它的<body>代碼: <body> <f:view> <h:form> <h:dataTable id="users" value="#{UMDelegater.allUsers}" var="u" border="1" width="80%" > <h:column> <f:facet name="header"> <h:outputText value="id"/> </f:facet> <h:outputText value="#{u.id}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="帳號"/> </f:facet> <h:commandLink action="#{UMDelegater.edit}"> <h:outputText value="#{u.name}"/> </h:commandLink> </h:column> <h:column> <f:facet name="header"> <h:outputText value="密碼"/> </f:facet> <h:outputText value="#{u.password}"/> </h:column> <h:column> <f:facet name="header"> <h:outputText value="權(quán)限代碼"/> </f:facet> <h:outputText value="#{u.power}"/> </h:column> </h:dataTable> <p> <h:commandLink action="#{UMDelegater.addNew}" value="增加用戶" /> </p> </h:form> </f:view> </body> 我們使用了一個(gè)h:dataTable標(biāo)簽,它是JSF獨(dú)有的東西,它會(huì)被翻譯為一個(gè)html的table,通過指定h:dataTable的value="#{UMDelegater.allUsers}"屬性,它就與我們剛才生成的DataModel對象關(guān)聯(lián)起來,數(shù)據(jù)會(huì)被自動(dòng)填充。我們只需要聲明dataTable中的每一列的表頭,數(shù)據(jù)來自哪個(gè)字段就可以了,如下: <h:column> <f:facet name="header"> <h:outputText value="id"/> </f:facet> <h:outputText value="#{u.id}"/> </h:column> 表格最后還有一個(gè)“增加用戶”的按鈕,它與UMDelegater.addNew綁定,它會(huì)把我們重定向到add.jsp。 需要注意的是,每個(gè)用戶名都是個(gè)超鏈接,點(diǎn)擊之后可以重定向到edit.jsp,這個(gè)頁面可以修改用戶資料。這是通過如下代碼實(shí)現(xiàn)的: <h:commandLink action="#{UMDelegater.edit}"> <h:outputText value="#{u.name}"/> </h:commandLink> 可以看出,實(shí)際上系統(tǒng)調(diào)用了UMDelegater.edit方法,該方法的功能是根據(jù)頁面?zhèn)鬟^來的用戶id查詢數(shù)據(jù)庫,找到相關(guān)記錄后返回一個(gè)User對象,然后重定向到edit.jsp,由于我們后面編寫edit.jsp的時(shí)候會(huì)把form元素與User對象綁定,所以該User對象所含有的數(shù)據(jù)會(huì)自動(dòng)顯示到各個(gè)form元素上。 list.jsp的顯示效果如下: ![]() 看到這里你也許會(huì)問,那么我如何來美化這個(gè)表格?實(shí)際上這正式JSF這類框架面臨的最大問題,它大量的使用了標(biāo)簽庫,目前流行的網(wǎng)頁制作工具(如deamweaver)又沒有提供足夠的支持,所以只能依靠挖掘dataTable標(biāo)簽的各個(gè)屬性,并且大量依賴css才能實(shí)現(xiàn)頁面的美化。如果java世界能有一個(gè)強(qiáng)大的JSF IDE,能夠提供vs.net一樣的能力,那么JSF也許會(huì)更容易流行。 下面我們來看看edit.jsp的內(nèi)容,如下: <body> <f:view> <h:form> <h:inputHidden id="id" value="#{UMDelegater.user.id}"/> <h:panelGrid columns="3"> <h:outputLabel for="name" value="帳號"/> <h:inputText id="name" value="#{UMDelegater.user.name}" required="true"/> <h:message for="name"/> <h:outputLabel for="power" value="權(quán)限"/> <h:inputText id="power" value="#{UMDelegater.user.power}" required="true"/> <h:message for="power"/> </h:panelGrid> <h:panelGroup> <h:commandButton value="保存" action="#{UMDelegater.update}"/> </h:panelGroup> </h:form> </f:view> </body> 可以看出,edit.jsp并沒有什么特別需要留意的,只是一個(gè)最簡單的form與bean的綁定,“保存”按鈕與UMDelegater.update方法綁定,它的功能是把修改后的form數(shù)據(jù)寫入數(shù)據(jù)庫,然后重新取一次數(shù)據(jù),以免緩存做怪,看不到修改的效果。 Add.jsp也和edit.jsp很類似,它的內(nèi)容如下: <body> <f:view> <h:form> <h:panelGrid columns="3"> <h:outputLabel for="name" value="帳號:" /> <h:inputText id="name" value="#{UMDelegater.user.name}" required="true"/> <h:message for="name"/> <h:outputLabel for="password" value="密碼:"/> <h:inputText id="password" value="#{UMDelegater.user.password}" required="true"/> <h:message for="password"/> <h:outputLabel for="power" value="權(quán)限"/> <h:inputText id="power" value="#{UMDelegater.user.power}" required="true"/> <h:message for="power"/> </h:panelGrid> <h:panelGroup> <h:commandButton value="保存" action="#{UMDelegater.add}"/> </h:panelGroup> </h:form> </f:view> </body> JSF開發(fā)實(shí)戰(zhàn)(一) bromon原創(chuàng) 請尊重版權(quán) JSF將是J2EE5.0中所包含的web開發(fā)框架,這應(yīng)該是第一個(gè)成為jcp標(biāo)準(zhǔn),并且隨j2eesdk一起發(fā)布的web框架,可以看出sun對它的期望很高。JSF最大的競爭對手是tapestry,是apache的產(chǎn)品,但是apache又弄出了個(gè)myfaces,是對jsf標(biāo)準(zhǔn)的一個(gè)實(shí)現(xiàn)。也許你也和我一樣,在jsf和tapestry之間猶豫很久,將來從apache的態(tài)度上應(yīng)該可以看出二者的走向。在tss上有一篇比較jsf 1.0與tapestry 3.0的文章,內(nèi)容很扎實(shí)到位:http://www.theserverside.com/articles/article.tss?l=JSFTapestry JSF的競爭對手不是struts/webwork之流,它們基本上已經(jīng)是不同階段上的東西了,放在一起比較意義不大。 JSF的開發(fā)流程和asp.net中所倡導(dǎo)的code behind方式很相似,核心是事件驅(qū)動(dòng),組件和標(biāo)簽的封裝程度非常高,很多典型應(yīng)用已經(jīng)不需要開發(fā)者去處理http。頁面操作會(huì)被自動(dòng)映射到對應(yīng)的java bean中,后臺邏輯只需要同java bean發(fā)生交互。整個(gè)過程是通過“依賴注入(DI)”來實(shí)現(xiàn)的,看來這是目前解偶合的最佳途徑啊,spring的影響真是深遠(yuǎn)。不過正式因?yàn)閖sf采用了這樣的方式,導(dǎo)致開發(fā)工作和以前的jsp/struts等都有非常大的不同,需要一定的時(shí)間去學(xué)習(xí)。學(xué)習(xí)之前建議先對依賴注入有比較清楚的認(rèn)識,可以參考我的learn Spring in spring系列的第一篇。 本系列將以兩個(gè)例子來講解jsf的基本開發(fā),第一個(gè)例子當(dāng)然是hello world。目前可用的jsf ide不多,ibm要到06年才能放出支持jsf的wtp版本。所以我們的例子基本以手寫為主,這樣也能讓我們有更清楚的認(rèn)識,同時(shí)推薦目前最好的jsf開發(fā)工具:myeclipse 4.0 GA。后面的例子將會(huì)有jsf和hibernate的內(nèi)容,它都能給予很好的支持。由于myeclipse并不免費(fèi),所以我們除了講解在ide中如何操作外,還會(huì)敘述手動(dòng)操作的具體內(nèi)容,以免過于依賴開發(fā)工具。用什么服務(wù)器都可以,這里采用了jboss 4.0.2。如果你的服務(wù)器是高版本的tomcat(5.5+),那么必須要?jiǎng)h除它自帶的一些包才能很好的支持jsf,具體細(xì)節(jié)請查看它的文檔。 請自行下載jsf ri和JSTL 1.1。 廢話少說,開始了。 在myeclipse 4.0GA中新建一個(gè)web項(xiàng)目,命名為hello,為項(xiàng)目增加對JSTL的支持: ![]() 在JSTL的版本中選擇1.1。 該操作實(shí)際上是把jstl.jar和standard.jar加到工程中。 采用類似的操作為項(xiàng)目添加對jsf的支持:myeclipse?add jsf capabilities 如圖: ![]() 其中的jsf implementation是選擇使用哪中JSF實(shí)現(xiàn),我們采用的是sun的jsf ri JSF config path是配置文件的位置,保持不變 URL pattern是jsf servlet的映射方式,有兩種選擇,具體細(xì)節(jié)后面說明。 以上操作上是為項(xiàng)目加入了jsf需要的jar和tld文件,并且創(chuàng)建了一個(gè)faces-config.xml的配置文件。涉及到的jar有:commons-beanutils.jar commons-collections.jar commons-digester.jar commons-logging.jar jsf-api.jar jsf-impl.jar 涉及到了jsf中所有的tld文件。 當(dāng)前的faces-config.xml文件的內(nèi)容是: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE faces-config PUBLIC "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.1//EN" "http://java.sun.com/dtd/web-facesconfig_1_1.dtd"> <faces-config> </faces-config> 環(huán)境已經(jīng)建立好了,現(xiàn)在我們要建立一個(gè)程序,它的功能是讓用戶在表單中輸入名字,提交后系統(tǒng)會(huì)返回一個(gè)問候。使用jsf的以后好處是,開發(fā)人員會(huì)很自然的把mvc各層分開,不會(huì)像使用strtus那樣別扭,這一點(diǎn)在后面的開發(fā)中感覺得到。 首先開發(fā)model層,它是個(gè)很簡單的bean: package org.bromon.jsf.model.hello; public class SayHello { public String say(String name) { return "你好,"+name; } } 在model層中你可以隨意的實(shí)現(xiàn)業(yè)務(wù)的數(shù)據(jù)邏輯,不需要與web層有任何的關(guān)系。 下面開發(fā)控制層,它負(fù)責(zé)存取web層的數(shù)據(jù),并且調(diào)用model層的邏輯: /** * jsf的控制層方法 * @author bromon */ package org.bromon.jsf.control.hello; import org.bromon.jsf.model.hello.*; public class HelloDelegater { //------屬性--------- private String name;//表單中的文本框數(shù)據(jù)會(huì)傳到這里 private String result;//web頁會(huì)從這里取得運(yùn)行結(jié)果 private SayHello sayHello;//model層的對象,并不事例化,由系統(tǒng)注入 //-----set/get-------- public String getName() { return name; } public void setName(String name) { this.name = name; } public SayHello getSayHello() { return sayHello; } public void setSayHello(SayHello sayHello) { this.sayHello = sayHello; } public String getResult() { return result; } public void setResult(String result) { this.result = result; } //-----邏輯方法--------- public String say() { this.setResult(sayHello.say(this.getName())); return "ok"; } } 需要注意的是,屬性的名字、set/get方法的名字必須嚴(yán)格按照java bean規(guī)范編寫,因?yàn)樗鼈円蛔⑷胍蕾嚂r(shí)使用。sayHello對象并沒有被實(shí)例化,它會(huì)在運(yùn)行時(shí)由系統(tǒng)注入。 這兩個(gè)bean當(dāng)然要在系統(tǒng)中申明,否則無法實(shí)現(xiàn)DI。在faces-config.xml文件中添加內(nèi)容: <managed-bean> <managed-bean-name>SayHello</managed-bean-name> <managed-bean-class> org.bromon.jsf.model.hello.SayHello </managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> <managed-bean> <managed-bean-name>HelloDelegater</managed-bean-name> <managed-bean-class> org.bromon.jsf.control.hello.HelloDelegater </managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>sayHello</property-name> <value>#{SayHello}</value> </managed-property> </managed-bean> 在后一個(gè)bean中,它的sayHello屬性被指定要在運(yùn)行時(shí)注入一個(gè)org.bromon.jsf.model.hello.SayHello的實(shí)例。 下面要編寫表示層的頁面,只有一個(gè)index.jsp: 需要引入兩個(gè)標(biāo)簽庫: <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> 下面是構(gòu)造jsf標(biāo)簽: <body> <f:view> <h:form> <h:panelGrid columns="3"> <h:outputLabel for="name" value="姓名:"/> <h:inputText id="name" value="#{HelloDelegater.name}" required="true"/> <h:message for="name"/> <h:outputLabel value="#{HelloDelegater.result}"/> </h:panelGrid> <h:panelGroup> <h:commandButton action="#{HelloDelegater.say}" value="提交"/> </h:panelGroup> </h:form> </f:view> </body> 頁面中包含了一個(gè)文本框和一個(gè)label,他們分別被綁定到了HelloDelegater類的兩個(gè)屬性上,具體的綁定工作有系統(tǒng)通過翻轉(zhuǎn)控制的方式調(diào)用對應(yīng)的set/get方式實(shí)現(xiàn)。提交按鈕被綁定到了HelloDelegater.say方法,該方法會(huì)把計(jì)算結(jié)果賦給result屬性,它會(huì)在頁面中顯示出來。 因?yàn)槲覀冊趗rl pattern中選擇了*.faces,所以我們應(yīng)該訪問如下地址來查看程序: http://localhost:8080/hello/index.faces 相應(yīng)的,如果你選擇了/faces/*,那么就應(yīng)該是: http://localhost:8080/hello/faces/index.jsp 程序執(zhí)行結(jié)果如下: ![]() 下一篇文章中,將介紹一個(gè)更加復(fù)雜的用戶管理程序,可以更清楚的看到j(luò)sf的特征,還會(huì)涉及到j(luò)sf的model對象一些組件的使用。 |
|