在本系列文章中的第二篇中,介紹了一種 Load On Demand的方式,我們在這里需要繼續利用這種方式,并對其做一些小小的擴展。這里我們使用 Hibernate3 作為持久化方案。
簡單的介紹一下應用情景,一個系統中包含了一些 Customer 的信息,我們需要對其進行查詢并對查詢結果進行分頁。
首先處理條件查詢的情況,通常會根據 VO 中的字段進行 like 型查詢,有時候時間或數字之類的會使用Between查詢,因為查詢條件一般不會很復雜,在這里,使用 Hibernate3 中的 Criteria 查詢來處理這樣的情況,我們把所有的查詢條件通過 Customer 這個 VO 傳進來,然后只對非空字段進行 like 查詢,我們用到這樣的方法。
另外對應的一個count方法略去,只需要在前面加入一個
criteria.setProjection(Projections.count("customerId"));
因為考慮到以后的擴展,使用了一個Utils方法,QueryUtils.getCriteriaParam方法
我們可以很容易的在 Backing Bean 上通過 Service 層拿到這個查詢結果的 List 和 Count 值,相關的getDatePage方法如下。
如果你不了解這個 getDataPage 方法的含義,請仔細閱讀“在JSF中實現分頁(二)”一文,并仔細思考該方法的含義。
在同一個 Backing Bean 中,我們放了一個存放查詢條件的VO - Customer,并在頁面中使用<t:saveState>保存其狀態,使其查詢條件不會隨著翻頁而丟失。
<t:saveState value="#{customerListBean.customer}"/>
在頁面中,我們把所有的查詢條件都放到該 VO 中,在 getDataPage 方法中就會在適當的時候調用新的查詢條件來查詢新的數據,這一切都不需要我們動手的。
在 Backing Bean 中有這樣的一個方法:
只是把數據清空,并強制 PagedListDataModel 讀取數據,然后我們返回相同的頁面,這個時候,系統按照用戶輸入的查詢條件拿到查詢結果以后,返回同一頁面,該頁面中的使用 LocalDataModel 的那個 DataTable 就會把結果顯示出來。
請注意,這里 LocalDataModel 和 Customer 都在同一個 Backing Bean 中。
是不是覺得很簡單呢,一切都歸功于 getDataPage 這個方法,我們幾乎不需要做什么額外的操作就可以達到我們的目的。
因為該方法在一個商業項目中使用,代碼不便公布,只能把里面的一些代碼抽取出來,零零碎碎的拿給大家看,如果大家有什么疑問的話,可以在論壇上提出來,我會盡力解答的,另外這個方法也會在空閑的時候 Merge 到 MyPSP 項目中去。
Myfaces是Apache基金會中的一個一級項目,除了實現JSF標準外,做了很多的擴展工作,在Myfaces包中有一個擴展包Tomahawk,我們將主要使用其中的兩個Component實現分頁:一個是<t:dataTable>,另一個是<t:dataScroller>,在第一篇里面,我們簡易的組合這兩個Component來實現一種簡單,但并不高效的分頁。
下面的例子來自于Myfaces-Sample,我省去了其中和分頁邏輯無關的內容,詳細的例子可以下載Myfaces-Sample包或者訪問http://www.irian.at/myfaces/home.jsf 查看。
第一部分:dataTable
在這一部分中,dataTable綁定了一個backing bean - pagedSort中的cars屬性,我們可以在這個屬性中加入數據訪問邏輯,從數據庫或者其他來源取得用于顯示的數據。比如我們可以通過Hibernate獲取一個List,其中包含有我們用于顯示的POJOs。
注意,dataTable中的rows屬性指的是每頁的行數,是必須指定的,否則是無法進行分頁的,如果在項目中會使用固定行數的分頁,建議把這個值寫在BaseBackingBean中,并暴露一個property,供頁面調用,所以每次在頁面中就可以這么寫#{backingBean.pageSize}。
第二部分:dataScroller
這里定義了我們用于分頁的<t:dataScroller>,最主要的是配置該分頁Component針對哪個dataTable進行分頁的“for”屬性,該屬性與dataTable綁定,并對其進行分頁,在這里,綁定了第一部分中的id="data"的dataTable,下面有很多的<t:facet>是指定分頁的導航樣式的,這里使用了圖片作為導航,可以把他們改成文字形式的導航。
當然這只是最簡單,也是一種不推薦的分頁方式,因為在每次進行分頁的時候,將會從數據庫中取回所有的記錄放入List中,然后,dataScroller在對這個List進行分頁,如果在數據量很大的情況下,這種方式顯然是不符合要求的,假設每條記錄占用1k內存,數據庫中有100萬條記錄,每次要把這個List全部讀取出來將占用1G內存。我們需要一種Load on demand方式的讀取,也就是只在需要查看某頁的時候讀取該頁的數據。
另外一方面,JSF的生命周期中有多個階段會調用到#{pagedSort.cars}中對應的方法,如果在這里調用了數據訪問邏輯,就會在只顯示一次頁面的情況下進行多次數據庫操作,也是相當的耗費資源的。
所以我們需要有更好的分頁方式去解決以上問題,下一篇我將介紹另一種方法以改善這些問題。
入門
藉由以下的幾個主題,可以大致了解JSF的輪廓與特性,我們來看看網頁設計人員與應用程序設計人員各負責什么。
o 簡介JSF
o 第一個JSF程序
o 簡單的導航 Navigation
o 導航規則設置
o JSF Expression Language
o 國際化訊息
Managed Beans
JSF 使用 Bean 來達到邏輯層與表現層分離的目的,Bean 的管理集中在組態檔案中,您只要修改組態檔案,就可以修改 Bean 之間的相依關系。
o Backing Beans
o Beans 的組態與設定
o Beans 上的 List, Map
數據轉換與驗證
轉換器(Converter)協助模型與視圖之間的數據轉換,驗證器(Validator)協助進行語意檢驗(Semantic Validation)。
o 標準轉換器
o 自訂轉換器
o 標準驗證器
o 自訂驗證器
o 錯誤訊息處理
o 自訂轉換, 驗證標簽
事件處理
JSF的事件模型提供一個近似的桌面GUI事件模式,讓熟悉GUI設計的人員也能快速上手Web程序設計。
o 動作事件
o 實時事件
o 值變事件
o Phase 事件
JSF 標簽
網頁設計人員要作的就是了解JSF的標簽的使用方式,這就像是學習進階的HTML標簽,另一件事就是與程序設計人員溝通好各個Bean的名稱綁定。
標簽入門
卷標的相關屬性查詢,您可以參考 Tag Library Documentation,這邊的介紹只是一些簡單的入門實例。
o 簡介JSF標準標簽
o 輸出類標簽
o 輸入類標簽
o 命令類標簽
o 選擇類標簽 一
o 選擇類標簽 二
o 其它標簽
表格處理
對于必須使用表格方式呈現的數據,JSF 的 <h:dataTable> 卷標協助您進行動態表格數據的輸出。
o 簡單的表格
o 表頭, 表尾
o TableModel 類別
自訂組件
JSF 讓您可以自訂組件,每個組件都是可替換的,這使得組件在搭配時更有彈性,但相對的卻使開發組件的過程復雜的多,這邊對自訂JSF 組件只是個入門磚,更多有關自訂組件的細節可得要專書來說明。
JSF 生命周期與組件概述
要開發 JSF 組件,您需要更深入了解 JSF 的一些處理細節,包括了 JSF 生命周期以及 JSF 框架。
o JSF 生命周期
o 概述自訂組件
簡單實例
在不考慮組件有子組件的情況下,這邊以實際的一個例子來說明開發組件的過程,至于考慮子組件的情況請參考專書介紹。
o 編碼, 譯碼
o 組件卷標
o 使用自訂組件
o 自訂 Renderer
![]() 圖1.階段1:選擇文件上傳 ![]() 圖2.階段2:上傳該文件到服務器 ![]() 圖3.階段3:上傳完成 ![]() 圖4.階段4:文件上傳摘要 |
public class UploadMultipartFilter implements Filter{ public void doFilter(ServletRequest request,ServletResponse response,FilterChain chain) throws IOException, ServletException { HttpServletRequest hRequest = (HttpServletRequest)request; //檢查是否我們在處理一個多部分請求 String contentHeader = hRequest.getHeader("content-type"); boolean isMultipart = ( contentHeader != null && contentHeader.indexOf("multipart/form-data") != -1); if(isMultipart == false){ chain.doFilter(request,response); }else{ UploadMultipartRequestWrapper wrapper = new UploadMultipartRequestWrapper(hRequest); chain.doFilter(wrapper,response); } ... } |
public class UploadMultipartRequestWrapper extends HttpServletRequestWrapper{ private Map<String,String> formParameters; private Map<String,FileItem> fileParameters; public UploadMultipartRequestWrapper(HttpServletRequest request) { super(request); try{ ServletFileUpload upload = new ServletFileUpload(); upload.setFileItemFactory(new ProgressMonitorFileItemFactory(request)); List fileItems = upload.parseRequest(request); formParameters = new HashMap<String,String>(); fileParameters = new HashMap<String,FileItem>(); for(int i=0;i<fileItems.size();i++){ FileItem item = (FileItem)fileItems.get(i); if(item.isFormField() == true){ formParameters.put(item.getFieldName(),item.getString()); }else{ fileParameters.put(item.getFieldName(),item); request.setAttribute(item.getFieldName(),item); } } }catch(FileUploadException fe){ //請求時間超過-用戶可能已經轉到另一個頁面。 //作一些記錄 //... } ... |
public class ProgressMonitorFileItemFactory extends DiskFileItemFactory { private File temporaryDirectory; private HttpServletRequest requestRef; private long requestLength; public ProgressMonitorFileItemFactory(HttpServletRequest request) { super(); temporaryDirectory = (File)request.getSession().getServletContext().getAttribute("javax.servlet.context.tempdir"); requestRef = request; String contentLength = request.getHeader("content-length"); if(contentLength != null){requestLength = Long.parseLong(contentLength.trim());} } public FileItem createItem(String fieldName, String contentType,boolean isFormField, String fileName) { SessionUpdatingProgressObserver observer = null; if(isFormField == false) //這必須是一文件上傳. observer = new SessionUpdatingProgressObserver(fieldName,fileName); ProgressMonitorFileItem item = new ProgressMonitorFileItem( fieldName,contentType,isFormField, fileName,2048,temporaryDirectory, observer,requestLength); return item; } ... public class SessionUpdatingProgressObserver implements ProgressObserver { private String fieldName; private String fileName; ... public void setProgress(double progress) { if(request != null){ request.getSession().setAttribute("FileUpload.Progress."+fieldName,progress); request.getSession().setAttribute("FileUpload.FileName."+fieldName,fileName); } } } } |
public class ProgressMonitorFileItem extends DiskFileItem { private ProgressObserver observer; private long passedInFileSize; ... private boolean isFormField; ... @Override public OutputStream getOutputStream() throws IOException { OutputStream baseOutputStream = super.getOutputStream(); if(isFormField == false){ return new BytesCountingOutputStream(baseOutputStream); }else{return baseOutputStream;} } ... private class BytesCountingOutputStream extends OutputStream{ private long previousProgressUpdate; private OutputStream base; public BytesCountingOutputStream(OutputStream ous){ base = ous; } ... private void fireProgressEvent(int b){ bytesRead += b; ... double progress = (((double)(bytesRead)) / passedInFileSize); progress *= 100.0 observer.setProgress(); } } } |
<comp:fileUpload value="#{uploadPageBean.uploadedFile}" uploadIcon="images/upload.png" styleClass="progressBarDiv" progressBarStyleClass="progressBar" cellStyleClass="progressBarCell" activeStyleClass="progressBarActiveCell"> <%--下面是一旦文件上傳完成將成為可見的組件--%> <h:panelGrid columns="2" cellpadding="2" cellspacing="0" width="100%"> <f:facet name="header"> <h:outputText styleClass="text" value="文件上傳成功." /> </f:facet> <h:panelGroup style="text-align:left;display:block;width:100%;"> <h:commandButton action="#{uploadPageBean.reset}" image="images/reset.png"/> </h:panelGroup> <h:panelGroup style="text-align:right;display:block;width:100%;"> <h:commandButton action="#{uploadPageBean.nextPage}" image="images/continue.png"/> </h:panelGroup> </h:panelGrid> </comp:fileUpload> |
![]() 圖5.選擇文件上傳 |
//文件上傳組件 writer.startElement("input", component); writer.writeAttribute("type", "file", null); writer.writeAttribute("name", component.getClientId(context), "id"); writer.writeAttribute("id", component.getClientId(context),"id"); if(input.getValue() != null){ //如果可用,則生成該文件名. FileItem fileData = (FileItem)input.getValue(); writer.writeAttribute("value", fileData.getName(), fileData.getName()); } writer.endElement("input"); String iconURL = input.getUploadIcon(); //生成圖像,并把JavaScript事件依附到其上. writer.startElement("div", component); writer.writeAttribute("style","display:block;width:100%;text-align:center;", "style"); writer.startElement("img", component); writer.writeAttribute("src",iconURL,"src"); writer.writeAttribute("type","image","type"); writer.writeAttribute("style","cursor:hand;cursor:pointer;","style"); UIForm form = FacesUtils.getForm(context,component); if(form != null) { String getFormJS = "document.getElementById('" + form.getClientId(context) + "')"; String jsFriendlyClientID = input.getClientId(context).replace(":","_"); //設置表單的編碼為multipart以用于文件上傳,并且通過一個IFRAME //來提交它的內容。該組件的第二個階段也在500毫秒后被初始化. writer.writeAttribute("onclick",getFormJS + ".encoding='multipart/form-data';" + getFormJS + ".target='" + iframeName + "';" + getFormJS + ".submit();" + getFormJS + ".encoding='application/x-www-form-urlencoded';" + getFormJS + ".target='_self';" + "setTimeout('refreshProgress" + jsFriendlyClientID + "();',500);",null); } ... writer.endElement("img"); //現在實現我們將要把該文件/表單提交到的IFRAME. writer.startElement("iframe", component); writer.writeAttribute("id", iframeName, null); writer.writeAttribute("name",iframeName,null); writer.writeAttribute("style","display:none;",null); writer.endElement("iframe"); writer.endElement("div"); writer.endElement("div"); //階段1結束 |
![]() 圖6.上傳文件到服務器 |
writer.startElement("div",component); writer.writeAttribute("id", input.getClientId(context) + "_stage2", "id"); ... writer.writeAttribute("style","display:none", "style"); String progressBarID = component.getClientId(context) + "_progressBar"; String progressBarLabelID = component.getClientId(context) + "_progressBarlabel"; writer.startElement("div", component); writer.writeAttribute("id",progressBarID,"id"); String progressBarStyleClass = input.getProgressBarStyleClass(); if(progressBarStyleClass != null) writer.writeAttribute("class",progressBarStyleClass,"class"); for(int i=0;i<100;i++){ writer.write("<span> </span>"); } writer.endElement("div"); writer.startElement("div",component); writer.writeAttribute("id",progressBarLabelID,"id"); ... writer.endElement("div"); writer.endElement("div"); //階段2結束 |
![]() 圖7.上傳完成 |
public void encodeChildren(FacesContext context, UIComponent component) throws IOException { ResponseWriter writer = context.getResponseWriter(); UIFileUpload input = (UIFileUpload)component; //一旦文件上傳成功,處理將被顯示的子結點 writer.startElement("div", component); writer.writeAttribute("id", input.getClientId(context) + "_stage3", "id"); //階段3. if(input.getValue() == null){ writer.writeAttribute("style","display:none;",null); }else{ writer.writeAttribute("style","display:block",null); } List<UIComponent> children = input.getChildren(); for(UIComponent child : children){ FacesUtils.encodeRecursive(context,child); } writer.endElement("div"); //階段3結束 } |
public void decode(FacesContext context, UIComponent component) { UIFileUpload input = (UIFileUpload) component; //檢查是否這是一個上傳進度請求,或是一個實際的上傳請求. ExternalContext extContext = context.getExternalContext(); Map parameterMap = extContext.getRequestParameterMap(); String clientId = input.getClientId(context); Map requestMap = extContext.getRequestParameterMap(); if(requestMap.get(clientId) == null){ return;//什么也不做,返回 } if(parameterMap.containsKey(PROGRESS_REQUEST_PARAM_NAME)){ //這是一個在該文件請求中的得到進度信息的請求. //得到該進度信息并把它生成為XML HttpServletResponse response = (HttpServletResponse)context.getExternalContext().getResponse(); //設置響應的頭信息 response.setContentType("text/xml"); response.setHeader("Cache-Control", "no-cache"); try { ResponseWriter writer = FacesUtils.setupResponseWriter(context); writer.startElement("progress", input); writer.startElement("percentage", input); //從會話中獲得當前進度百分數(由過濾器所設置). Double progressCount = (Double)extContext.getSessionMap(). get("FileUpload.Progress." +input.getClientId(context)); if(progressCount != null){ writer.writeText(progressCount, null); }else{ writer.writeText("1", null);//我們還沒有收到上傳 } writer.endElement("percentage"); writer.startElement("clientId", input); writer.writeText(input.getClientId(context), null); writer.endElement("clientId"); writer.endElement("progress"); } catch(Exception e){ //做一些錯誤記錄... } }else{ //正常的譯碼請求. ... |
//正常的譯碼請求. if(requestMap.get(clientId).toString().equals("file")){ try{ HttpServletRequest request = (HttpServletRequest)extContext.getRequest(); FileItem fileData = (FileItem)request.getAttribute(clientId); if(fileData != null) input.setSubmittedValue(fileData); //現在我們需要清除與該項相關的任何進度 extContext.getSessionMap().put("FileUpload.Progress." + input.getClientId(context),new Double(100)); }catch(Exception e){ throw new RuntimeException("不能處理文件上傳" +" - 請配置過濾器.",e); } } |
function refreshProgress(){ // 假定我們正在進入到階段2. document.getElementById('fileUpload1_stage1').style.display = 'none'; document.getElementById('fileUpload1_stage2').style.display = ''; document.getElementById('fileUpload1_stage3').style.display = 'none'; //創建AJAX寄送 AjaxRequest.post( { //指定正確的參數,以便 //該組件在服務器端被正確處理 'parameters':{ 'uploadForm':'uploadForm', 'fileUpload1':'fileUpload1', 'jsf.component.UIFileUpload':'1', 'ajax.abortPhase':'4' } //Abort at Phase 4. //指定成功處理相應的回調方法. ,'onSuccess':function(req) { var xml = req.responseXML; if( xml.getElementsByTagName('clientId').length == 0) { setTimeout('refreshProgress()',200); return; } var clientId = xml.getElementsByTagName('clientId'); clientId = clientId[0].firstChild.nodeValue + '_progressBar'; //從XML獲取百分比 var percentage = xml.getElementsByTagName('percentage')[0].firstChild.nodeValue; var innerSpans = document.getElementById(clientId).getElementsByTagName('span'); document.getElementById(clientId + 'label').innerHTML = Math.round(percentage) + '%'; //基于當前進度,設置這些span的式樣類。 for(var i=0;i<innerSpans.length;i++){ if(i < percentage){ innerSpans[i].className = 'active'; }else{ innerSpans[i].className = 'passive'; } } //如果進度不是100,我們需要繼續查詢服務器以實現更新. if(percentage != 100){ setTimeout('refreshProgress()',400); } else { //文件上傳已經完成,我們現在需要把該組件送入到第3個階段. document.getElementById('fileUpload1_stage1').style.display = 'none'; document.getElementById('fileUpload1_stage2').style.display = 'none'; document.getElementById('fileUpload1_stage3').style.display = ''; } } }); } return builder.toString(); |
程序代碼: |
<%@ page contentType="text/html;charset=GBK"%> <HTML> <HEAD> <title>Hello</title> </HEAD> <%@ taglib uri="http://java.sun.com/jsf/html" prefix="h" %> <%@ taglib uri="http://java.sun.com/jsf/core" prefix="f" %> <body bgcolor="white"> <f:view> <h:form id="helloForm" > <h2>我的名字叫Duke. 我想好了一個數字,范圍是 <h:outputText value="#{UserNumberBean.minimum}"/> 到 <h:outputText value="#{UserNumberBean.maximum}"/>. 你能猜出它? </h2> <h:graphicImage id="waveImg" url="/wave.med.gif" /> <h:inputText id="userNo" value="#{UserNumberBean.userNumber}" validator="#{UserNumberBean.validate}"/> <h:commandButton id="submit" action="success" value="Submit" /> <p> <h:message style="color: red; font-family: 'new Century Schoolbook', serif; font-style: oblique; text-decoration: overline" id="errors1" for="userNo"/> </h:form> </f:view> </body> </HTML> |
程序代碼: |
package guessNumber; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.validator.LongRangeValidator; import javax.faces.validator.Validator; import javax.faces.validator.ValidatorException; import java.util.Random; public class UserNumberBean { Integer userNumber = null; Integer randomint = null; String response = null; protected String[] status = null; private int minimum = 0; private boolean minimumSet = false; private int maximum = 0; private boolean maximumSet = false; public UserNumberBean() { Random randomGR = new Random(); randomint = new Integer(randomGR.nextInt(10)); System.out.println("Duke's number: " + randomInt); } public void setUserNumber(Integer user_number) { userNumber = user_number; System.out.println("Set userNumber " + userNumber); } public Integer getUserNumber() { System.out.println("get userNumber " + userNumber); return userNumber; } public String getResponse() { if (userNumber != null && userNumber.compareTo(randomInt) == 0) { return "Yay! You got it!"; } else { return "Sorry, " + userNumber + " is incorrect."; } } public String[] getStatus() { return status; } public void setStatus(String[] newStatus) { status = newStatus; } public int getMaximum() { return (this.maximum); } public void setMaximum(int maximum) { this.maximum = maximum; this.maximumSet = true; } public int getMinimum() { return (this.minimum); } public void setMinimum(int minimum) { this.minimum = minimum; this.minimumSet = true; } public void validate(FacesContext context, UIComponent component, Object value) throws ValidatorException { if ((context == null) || (component == null)) { throw new nullPointerException(); } if (value != null) { try { int converted = intValue(value); if (maximumSet && (converted > maximum)) { if (minimumSet) { throw new ValidatorException( MessageFactory.getMessage (context, Validator.NOT_IN_RANGE_MESSAGE_ID, new Object[]{ new Integer(minimum), new Integer(maximum) })); } else { throw new ValidatorException( MessageFactory.getMessage (context, LongRangeValidator.MAXIMUM_MESSAGE_ID, new Object[]{ new Integer(maximum) })); } } if (minimumSet && (converted < minimum)) { if (maximumSet) { throw new ValidatorException(MessageFactory.getMessage (context, Validator.NOT_IN_RANGE_MESSAGE_ID, new Object[]{ new Double(minimum), new Double(maximum) })); } else { throw new ValidatorException( MessageFactory.getMessage (context, LongRangeValidator.MINIMUM_MESSAGE_ID, new Object[]{ new Integer(minimum) })); } } } catch (NumberFormatException e) { throw new ValidatorException( MessageFactory.getMessage (context, LongRangeValidator.TYPE_MESSAGE_ID)); } } } private int intValue(Object attributeValue) throws NumberFormatException { if (attributeValue instanceof Number) { return (((Number) attributeValue).intValue()); } else { return (Integer.parseInt(attributeValue.toString())); } } } |
程序代碼: |
package guessNumber; import javax.faces.application.Application; import javax.faces.application.FacesMessage; import javax.faces.context.FacesContext; import java.text.MessageFormat; import java.util.Locale; import java.util.MissingResourceException; import java.util.ResourceBundle; public class MessageFactory extends Object { private MessageFactory() { } //這個方法是用做參數替換 public static String substituteParams(Locale locale, String msgtext, Object params[]) { String localizedStr = null; if (params == null || msgtext == null) { return msgtext; } StringBuffer b = new StringBuffer(100); MessageFormat mf = new MessageFormat(msgtext); if (locale != null) { mf.setLocale(locale); b.append(mf.format(params)); localizedStr = b.toString(); } return localizedStr; } //上面方法的用法例子 public static void main(String args[]){ String msgtext="我由于{0},不能去{1}"; String params[]={"有事","北京"}; String s=substituteParams(Locale.CHINESE,msgtext,params); System.out.println(s); } /** 運行結果: D:\java>java Test 我由于有事,不能去北京 D:\java> */ /** * This version of getMessage() is used in the RI for localizing RI * specific messages. */ public static FacesMessage getMessage(String messageId, Object params[]) { Locale locale = null; FacesContext context = FacesContext.getCurrentInstance(); // context.getViewRoot() may not have been initialized at this point. if (context != null && context.getViewRoot() != null) { locale = context.getViewRoot().getLocale(); if (locale == null) { locale = Locale.getDefault(); } } else { locale = Locale.getDefault(); } return getMessage(locale, messageId, params); } //從資源包中獲取信息,從而構造jsf頁面標記<h:message>與<h:messages>中所要顯示的詳細和摘要信息 public static FacesMessage getMessage(Locale locale, String messageId, Object params[]) { FacesMessage result = null; String summary = null, detail = null, bundleName = null; ResourceBundle bundle = null; // see if we have a user-provided bundle if (null != (bundleName = getApplication().getMessageBundle())) {//資源包的名字 if (null != (bundle = ResourceBundle.getBundle(bundleName, locale, getCurrentLoader(bundleName)))) {//獲取資源包 // see if we have a hit try { summary = bundle.getString(messageId);//從資源包中獲取信息摘要 } catch (MissingResourceException e) { } } } // 如果不能在用戶提供的資源包中獲取信息摘要,再從默認的資源包中查找 if (null == summary) { // see if we have a summary in the app provided bundle bundle = ResourceBundle.getBundle(FacesMessage.FACES_MESSAGES, locale, getCurrentLoader(bundleName)); if (null == bundle) { throw new nullPointerException(" bundle " + bundle); } // see if we have a hit try { summary = bundle.getString(messageId); } catch (MissingResourceException e) { } } // we couldn't find a summary anywhere! return null if (null == summary) { return null; } // At this point, we have a summary and a bundle. if (null == summary || null == bundle) { throw new nullPointerException(" summary " + summary + " bundle " + bundle); } //參數替換,用參數params替換摘要中的{0},{1}等占位符。 summary = substituteParams(locale, summary, params); try { //詳細信息 detail = substituteParams(locale, bundle.getString(messageId + "_detail"), params); } catch (MissingResourceException e) { } return (new FacesMessage(summary, detail)); } // // Methods from MessageFactory // public static FacesMessage getMessage(FacesContext context, String messageId) { return getMessage(context, messageId, null); } public static FacesMessage getMessage(FacesContext context, String messageId, Object params[]) { if (context == null || messageId == null) { throw new nullPointerException(" context " + context + " messageId " + messageId); } Locale locale = null; // viewRoot may not have been initialized at this point. if (context != null && context.getViewRoot() != null) { locale = context.getViewRoot().getLocale(); } else { locale = Locale.getDefault(); } if (null == locale) { throw new nullPointerException(" locale " + locale); } FacesMessage message = getMessage(locale, messageId, params); if (message != null) { return message; } locale = Locale.getDefault(); return (getMessage(locale, messageId, params)); } public static FacesMessage getMessage(FacesContext context, String messageId, Object param0) { return getMessage(context, messageId, new Object[]{param0}); } public static FacesMessage getMessage(FacesContext context, String messageId, Object param0, Object param1) { return getMessage(context, messageId, new Object[]{param0, param1}); } public static FacesMessage getMessage(FacesContext context, String messageId, Object param0, Object param1, Object param2) { return getMessage(context, messageId, new Object[]{param0, param1, param2}); } public static FacesMessage getMessage(FacesContext context, String messageId, Object param0, Object param1, Object param2, Object param3) { return getMessage(context, messageId, new Object[]{param0, param1, param2, param3}); } protected static Application getApplication() { return (FacesContext.getCurrentInstance().getApplication()); } //獲取當前的類加載器,需要該類加載器來查找資源包 protected static ClassLoader getCurrentLoader(Object fallbackClass) { ClassLoader loader = Thread.currentThread().getContextClassLoader(); if (loader == null) { loader = fallbackClass.getClass().getClassLoader(); } return loader; } } // end of class MessageFactory |
程序代碼: |
<application> <locale-config> <default-locale>zh_CN</default-locale> <supported-locale>en</supported-locale> <supported-locale>fr</supported-locale> <supported-locale>es</supported-locale> </locale-config> <message-bundle>guessNumber.messages</message-bundle> </application> |