征服jsf

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            6 隨筆 :: 0 文章 :: 27 評論 :: 0 Trackbacks

          2007年9月14日 #

          UIComponent實現了StateHolder接口,StateHolder接口表示組件具有了狀態。
          StateHolder接口中常用到saveState,restoreState兩個方法,在開發自定義組件的時候,需要實現它們,具體使用上經常是把你的組件中全局變量進行狀態化,就是在Object[]中定義它們,jsf在恢復和保存兩個階段分別調用組件的這兩個方法,把頁面的狀態數據恢復在Object[],把組件的Object[]數據渲染到頁面。isTransient,setTransient方法表式,你的組件是否想狀態化。

          UIComponent組件主要api
          1.public abstract java.util.Map getAttributes()  該方法會獲得該組件的所有屬性值

          2. public abstract javax.faces.el.ValueBinding getValueBinding(java.lang.String name) 該方法會從組件的值綁定集合表中獲取對應的值綁定對象,值綁定對象(ValueBinding )的目的是使用EL表達式解析你綁定的模型值,例如頁面有個(#use.name),那么值綁定就會解析這個字符串,通過變量解析獲取User對象,再用值解析獲取name值。

          3.public abstract java.lang.String getClientId(javax.faces.context.FacesContext context) 主要是在UIViewRoot組件中生成組件在頁面的映射Id,如果你為組件定義了一個id,那么他會基于這個id生成一個具有組件層次的客戶端Id,使用':'來分隔,如果沒有為組件定義id,那么會動態生成一個id,例如會在頁面渲染結果看到'form1:input1'這樣的Id,就是告訴你一個Input組件的id是'input',它在id是'form1'的form組件中。clientId常常在jsf內部對組件用來整理,定位。

           4.public abstract java.lang.String getFamily() 告訴你這個組件屬于那個家族的,用的不多。主要是內部創建渲染類使用。

          5. public abstract java.lang.String getId() 這個Id就是頁面上你指定的組件Id,它可以讓你方便的查找組件樹上的組件,最好給你的頁面組件都設定一個id,在服務端開發有時候會用到,如果沒有設定,jsf會自動生成一個唯一的id,如果自定義id在組件中有沖突,那么jsf會拋出id沖突異常。

          6.public abstract javax.faces.component.UIComponent getParent() 得到該組件的父組件,注意:html標記不會成為一個組件。

          7. public abstract boolean isRendered() 如果該組件這個方法設定為false,那么該組件以及它的子組件都會停止解碼,驗證,值更新,渲染等操作

          8. public abstract java.lang.String getRendererType()  指定了該組件使用哪個渲染類進行渲染,這就是組件與渲染分離,并且互相可以復用的機制。

          9. public abstract boolean getRendersChildren();  如果組件的此方法設定為真,那么jsf將繼續尋找它的子組件進行渲染。否者渲染只渲染到當前組件。

          10. public abstract java.util.List getChildren(); 獲得該組件的直屬子組件列表

          11. public abstract int getChildCount() 獲得獲得該組件的直屬子組件列表數

          12.public abstract javax.faces.component.UIComponent findComponent(java.lang.String expr) 尋找該組件的子組件,通過頁面上指定的組件Id,在它的父組件的findComponent去查找這個組件,一般用于自定義組件開發上,例如:jsf消息渲染的時候就是通過findComponent方法去尋找for屬性定義的組件。

          13.public abstract java.util.Map getFacets() 得到該組件的所有facet類型的組件,facet組件主要作用:一個功能強大的組件中會有很多增強功能,這些增強功能可以定義成多個小的facet組件,這樣可以從組件的主功能中分解開,降低組件內部功能的耦合性,同時也可以做為組件的插件機制來擴展。

          14.public abstract java.util.Iterator getFacetsAndChildren() 獲取自己所以的直屬子組件和facet組件,常用在子組件查找操作上,例如jsf核心機制中,遍歷組件增加事件時就要遞歸使用此方法。

          15.    public abstract void broadcast(javax.faces.event.FacesEvent event) 對于jsf生命周期的幾個階段都會用到遞歸遍歷組件的事件,使用的就是這個方法,常用在內部機制上。標準的觀察者模式。

          16.    public abstract void decode(javax.faces.context.FacesContext context) jsf聲明周期中的解碼部分就是調用此方法,主要用在應用請求值階段,通過遞歸遍歷,每一個組件會在這個階段操作request等容器對象來實現并隱藏request請求的表現邏輯,并封裝成事件交給組件的事件列表,由后面的階段來處理。需要注意的是很可能此方法會把工作委托給渲染類的decode上,所以jsf的渲染機制不僅僅是簡單的輸出html等展現標記,還用來封裝和隱藏容器內的通信機制,讓容器內對象從我們眼前消失!

          17. 

              public abstract void encodeBegin(javax.faces.context.FacesContext context)
                      throws java.io.IOException;

              public abstract void encodeChildren(javax.faces.context.FacesContext context)
                      throws java.io.IOException;

              public abstract void encodeEnd(javax.faces.context.FacesContext context)
                      throws java.io.IOException;

          組件渲染階段需要經歷的步驟,渲染機制是一個復雜的過程,原理上是把tag標記渲染的工作委托給了servlet之上來處理,jsf 的tag標記只作為組件,視圖之間的映射之用。這三個階段更明確了渲染成視圖標記這樣一個工作,同樣可以委托給渲染類去做。

           18.protected abstract void addFacesListener(javax.faces.event.FacesListener listener) 為組件增加監聽,標準的觀察者模式。

          19.protected abstract javax.faces.event.FacesListener[] getFacesListeners(java.lang.Class clazz) 得到組件的監聽列表,監聽器在組件的事件列表遍歷時會被調用,clazz參數是用來區分不同的監聽器,目前常用ActionListener.class,ValueChangeListener.class

          20.protected abstract void removeFacesListener(javax.faces.event.FacesListener listener);從組件的監聽列表中刪除不需要的監聽器。

          21.public abstract void queueEvent(javax.faces.event.FacesEvent event) 把新創建的事件增加到組件的事件隊列,此方法常常用在應用請求值階段,每個組件會根據條件創建自己的事件,加入到事件列表,所有事件會全局的放入UIViewRoot組件中,UIViewRoot組件是jsf各個階段對組件樹進行遞歸遍歷處理的啟動點。

          22.

             public abstract void processRestoreState(javax.faces.context.FacesContext context,
                                                       java.lang.Object state);

              public abstract void processDecodes(javax.faces.context.FacesContext context);

              public abstract void processValidators(javax.faces.context.FacesContext context);

              public abstract void processUpdates(javax.faces.context.FacesContext context);

              public abstract java.lang.Object processSaveState(javax.faces.context.FacesContext context);

              jsf生命周期中組件需要處理的五個重要階段:恢復狀態,解碼,驗證,更新,保持狀態(另外包括調用應用,渲染),開發自定義組件,需要特別關注這些方面,渲染階段由組件的編碼方法實現,調用應用階段則是actionListener監聽器集合的處理期。

          23. protected abstract javax.faces.context.FacesContext getFacesContext() 獲取jsf上下文環境,FacesContext 是一個線程安全模型,在設計上接近pojo,所以在jsf環境對FacesContext的調用得到了簡化,但是jsf整體的編程規則上更希望你能把FacesContext 作為命令參數傳遞給各個實現細節,以保證上下文的真實性,這一點在開發自定義組件上要注意!
          24. protected abstract javax.faces.render.Renderer getRenderer(javax.faces.context.FacesContext context) 獲取當前組件的渲染類。
          posted @ 2007-11-02 16:38 方順 閱讀(3015) | 評論 (0)編輯 收藏

             1.不要盲目使用jsf1.2規范的實現框架:
                  目前sun,myfaces都推出了jsf1.2規范的實現,但是各種jsf1.2支持并沒有跟上,所以小心使用。
                 jsf1.2的優勢主要體現在統一的EL表達式,也就是說你可以在頁面上,混合使用只要支持了統一表達式的各種tag標記,同時可以享受jsf在表現層開發上帶來的強大支持,這就又需要兩個規范的支持,一個是jsp規范,一個是jstl規范,jsp2.1規范支持統一EL,它在tomat6.0.14以后才開始支持,glassFish默認支持,這些都是基礎設施,jstl把不同的taglibs定義了一個規范,你可以選擇各種實現了jstl規范的taglibs框架,目前大多數使用的是apache 帶的標準實現,但是apache仍然沒有推出jstl1.2規范的實現,只有jstl1.2規范以后開始支持統一EL,目前可能只有glassfish有jstl1.2的默認實現,我沒有試過能不能復用在其他容器下,但是在沒有驗證之前,請斟酌使用,jsf1.2在統一EL方面的常用應用就是和jstl標記混合。如果你的項目仍然是tomcat5.5或者jboss的容器,使用jsf1.2沒有多大優勢!而且jsf1.2雖然推出了不同的實現框架,但是增強框架仍然沒有看到,myfaces基于jsf1.2的 Tomahawk 還沒有推出支持1.2規范的實現,如果僅僅使用sun的標準實現和myfaces的標準實現,在項目中你會步履維艱!

          2.如果沒有商業jsf框架選擇,請選擇myfaces的增強框架Tomahawk
            sun的標準實現和myfaces的標準實現都是針對規范該有的內容進行實現,并沒有在組件方面進行必要的增強,這也是我主要使用Tomahawk這種對組件增強框架的原因。

          3.現階段不要相信IDE工具支持
             netbeans的可視化操作使用的是自己的jsf實現,沒有myfaces框架的支持,需要等到6.0推出才能驗證它的優勢,其他包括商業工具,使用后錯誤很多,感覺就是在測試我們的接受能力,現階段我對jsf的可視化工具不抱希望,如果你有信心,可以試試工具!現階段我推薦的方式是jsf手動開發,在開發中積累經驗!

          4.不要隨意使用ajax支持的jsf組件。
            如果你是web2.0的支持者,現階段還是不要用jsf好,jsf是下一代web2.0的主導。如果你是標準的企業開發,建議使用標準方式進行頁面請求。jsf最大的貢獻不是支持ajax,而是內部實現了一個可以透明化http無狀態的機制,這種機制讓我們在開發上高度關注組件化業務,讓我們的開發能走的更遠,而使用ajax在理論上和標準方式一樣,在服務端具有統一的模型處理,但是javascript在工程開發上是高度的復雜和麻煩,jsf在處理純html上在現階段也是常常出現一些讓人接受不了的問題,有時候需要自己手動Hack,但是好在html還不復雜,如果頁面端大量javascript,你怎么辦,這還不算請求帶來的問題,ajax框架本身的質量!

          6.如果項目中決定使用jsf,請找一個能拿的住jsf的人,整個開發不僅僅會使用jsf,還有混合使用其他頁面技術,如果這個人對表現層的理解包括jsf的理解不夠,項目在很多方面的質量會有折扣,但是對于整個開發團隊使用jsf會比使用其他表現層技術更簡單,更高效,開發質量也會更高,這都要看jsf負責人的技術應變能力了!

          5.還是老調重提,在項目中最好加入seam的支持,會讓jsf的開發變的簡單!
          posted @ 2007-10-21 11:38 方順 閱讀(2031) | 評論 (7)編輯 收藏

               前一段時間我寫過一篇共享我在項目中使用jsf的一些經驗,主要是概要的提出了一些jsf使用上的建議,這次我想在文章里主要是把seam在jsf中的使用經驗提一下,能讓更多的人了解seam的實際應用和優勢。
              1.seam配置時要注意的地方:
              (1)faces-config.xml里面要加入一個seam的階段監聽:
              
          <lifecycle>
          <phase-listener>org.jboss.seam.jsf.SeamPhaseListener</phase-listener>
          <!-- <phase-listener>com.future.egov.jsf.ext.event.DebugPhaseListener</phase-listener> -->
          </lifecycle>

               seam動起來的條件就是從這里發起的,seam通過這個監聽器對jsf的各個階段進行必要的增強以及植入自己的CONVERSATION生命周期,對于這個監聽器的具體細節工作,我還需要更多時間研究,仍在了解中!
                (2) 一定要在工程類路徑的根下放置一個seam.properties文件,你可以設置為空內容,主要是引導seam在初始化的時候加載這個路徑下所有標注為seam組件的對象(通過@Name注釋),http://www.ibm.com/developerworks/cn/java/j-seam1/  seam無縫集成jsf給我了提示!
                (3)web.xml下最小配置是加入seam監聽器
                  
              <listener>
                    
          <listener-class>org.jboss.seam.servlet.SeamListener</listener-class>
              
          </listener>
                 在容器加載工程的時候,初始化seam框架。
                以上三處的配置,你就可以在任何容器中使用seam了!更多的配置大家可以找參考了解吧,我目前在項目中就使用了以上配置。其他配置主要是在seam對ajax,ejb3的支持上,不過seam很新,什么事都會發生!
             2.常用的注釋:(所有被seam定義的領域對象都可以認為是seam組件)
               (1)@Name(XXX),需要在你的領域類上定義,定義了seam組件的名稱,對于jsf來說就是backing-bean,也就是說你不用在faces-config中配置managedbean了!

                (2)@Scope(ScopeType.XXX),可以在你的領域類上定義,表示這個被定義的seam組件在什么上下文中,jsf中主要包括page,event,session,application,conversation這些Scope。我在項目中主要使用event,session,conversation。event就是把組件放入了request中,session同理,conversation是seam獨創的聲命周期,conversation短聲命周期類似request,但是會保存一些jsf容易在請求中丟失的數據(jsf只是保存組件,不保存組件渲染的數據,除非是EditableValueHolder組件,有時候需要通過myfaces的save組件和updateActionListener組件來恢復這些數據),具體保存細節,需要看使用的情況,我有這樣一個經驗:當定義成event上下文,在頁面的一次請求中,有些數據請求時還在,但是到渲染時就不見了,常見在dataTable組件,myfaces的commandNavigation組件,但是換成conversation上下文,這些數據在渲染時又找回來了。但是對于跨度比較大層面,我還是推薦使用myfaces提供的保持機制,我一般使用updateActionListener,而save組件在seam1.2.1的環境下會出錯。如果再有更大跨度,就可以使用conversation上下文的長會話了。

                 (3)@Begin(join=true),@End(beforeRedirect=true),當觸發了帶有@Begin標記的方法,conversation的長會話就這樣開始了,主要是為了長時間使用已經加載到conversation域中的對象或者屬性(如果定義了conversation但并沒有加載的可不算):join=true就是告訴你會話中有同名值時繼續賦值,還有一個注釋參數ifOutcome=XXX,就是看你的方法返回的字符串是否和ifOutcome定義的字符串相匹配,如果匹配就開始長會話。當在長會話期間執行到某個方法帶有@End標記那么這個長會話就會結束,這樣防止了內存泄漏問題,我認為這是一個權衡的結果,也許用戶真的會點擊那個帶有結束標記方法的按鈕。beforeRedirect為真就會在結束時清掉conversation上下文所有的信息,如果beforeRedirect=false,conversation只是變成短會話,在結束后的那次請求中還可以使用conversation中的數據,一般會用在messages提示這個應用場景中使用,但是如果要返回數據列表有時就需要清空所有數據,防止數據列表還會重現長會話開始前的情況。

                 (4)In(value=XXX,rquest=false,ScopeType=XXX),Out(value=XXX,rquest=false,ScopeType=XXX) seam把它定義為雙射。In是最常用的標注,你可以使用In導入一個jsf的EL變量來獲取jsf模型,例如我要獲取spring的業務bean,而且業務bean已經定義成backing bean,利用spring與jsf集成的方法:
                 
              <!-- Managed Beans for options.jsp -->
              
          <application>
                  
          <variable-resolver>
                      org.springframework.web.jsf.DelegatingVariableResolver
                  
          </variable-resolver>
                  
          <locale-config>
                      
          <default-locale>gbk</default-locale>
                  
          </locale-config>
                  
          <message-bundle>resources/MessageBundle</message-bundle>
                  
          <!--  <view-handler>com.icesoft.faces.facelets.D2DFaceletViewHandler
                  </view-handler>        
          -->
              
          </application>

               在seam組件中這樣聲明:
          @In(value="#{userService}", request=false)
          private UserService userService;

           這個示例為seam組件注入了由spring管理的用戶服務對象,它的value是從jsf EL變量中獲取,request=false在告訴seam,如果當前的值沒有找到,那么設置為空,否者當出現沒有找到的情況,seam會拋出空異常。
          @Out屬性主要是把處理過的屬性值會由seam重新再付給上下文也就是Out中所定義的ScopeType上下文,我認為雖然是seam的一個特點,但是在我的應用中不多,主要是注入而非雙射!如果它真的能在短期Conversation中有所作用來代替Myfaces的數據保持機制,我想會好些,我目前只是在長Conversation有所應用。

          3 .@Factory,@DataModelSelection,@DataModel,它們主要來代替數據列表的使用,主要是減少了代碼量,Factory是在請求值階段就對需要實例化的對象進行創建,DataModelSelection定義的屬性,可以透明的抓取數據列表選擇的單行數據,DataModel屬性減少了不必要的get,set。然而我在實際的使用中由于很多不定的情況,大部分的使用上又回到jsf標準的get方式。 這種開發方式我認為seam的目的是想屏蔽與頁面不必要的關系細節,讓開發只需要重視真正的業務,是一個標準的面向對象式結構,當jsf的體系結構的不斷優化,類似這種開發方式我想會越來越有用。
          4. @RequestParameter是個很有用的注釋,它自動把當前屬性和頁面同名的request提交值綁定在一起,雖然這樣使用違背了jsf所追求的面向對象化,http透明化,但是實際開發中會后很多意想不到的情況,有時候在集成式頁面這樣的做法會很有用,當你的頁面中不僅僅有jsf標簽就清楚了!

          seam的其他方面問題我會抽空整理一下,seam目前也是在不斷更新當中,明年出臺的webBeans規范的前身就是seam,其實我更關注的是seam在整個j2ee體系中的角色,它到底是想替代struts的application?還是想替代spring的manager?也許有更多的想法!
          posted @ 2007-10-20 13:55 方順 閱讀(2752) | 評論 (4)編輯 收藏

                  ‘jsf的dataTable組件自定義控制指定行樣式的方法’  這篇文章我主要通過覆寫DataModel對象的getRowData()方法來控制DataTable每一行的行為,其實還有個標準的方式,也更加優雅,那就是使用DataModelListener,典型的監聽事件,jsf內置了數據模型監聽機制以便開發者對于數據模型每一行的控制,而且這種監聽可以無限增加,使得對每一行控制的方式變成一個個監聽對象,非常好用,下面就是更改過得樣例:
               
          package net.blogjava.fangshun.book;

          import java.util.List;

          import javax.faces.context.FacesContext;
          import javax.faces.el.VariableResolver;
          import javax.faces.model.DataModelEvent;
          import javax.faces.model.DataModelListener;
          import javax.faces.model.ListDataModel;

          import org.apache.myfaces.component.html.ext.HtmlDataTable;

          /**
           * 集成了ListDataModel為Book模型提供自定義樣式的支持
           * 
          @author shun.fang
           *
           
          */

          public class BookListDataModel extends ListDataModel {
              
              
          public BookListDataModel(List<Book> books) {
                  
          super(books);
                  
          super.addDataModelListener(new BookListDataModelListener());
              }

              
              
          /**
               * 覆寫了DataModel對象的getRowData方法,每次uiData組件從模型列表獲取新一行記錄
               * 的時候,是從這里發起的,所以在這里可以通過獲取uiData組件,來對uiData組件的狀態進行
               * 調整。目前的調整就是根據業務的需要對uiData組件的每一行樣式進行特殊指定。
               
          */

          //    @Override
          //    public Object getRowData() {
          //        // TODO Auto-generated method stub
          //        
          //        /////////////////////////////////////////////////////////////
          //        //通過變量解析獲取request scope中的BookList列表模型
          //        FacesContext facesContext = FacesContext.getCurrentInstance();
          //        VariableResolver vr = facesContext.getApplication().getVariableResolver();
          //        BookList bookList = (BookList)vr.resolveVariable(facesContext, "booking");    
          //        /////////////////////////////////////////////////////////////
          //        
          //        //間接得到綁定BookList列表模型的uiData組件,并向下轉型成HtmlDataTable(myfaces擴展類型)組件
          //        HtmlDataTable table = (HtmlDataTable)bookList.getUiData();
          //        
          //        //獲取當前行的Book實例
          //        Book book = (Book)super.getRowData();
          //        
          //        //根據當前行Book實例的業務特征為HtmlDataTable組件設置渲染行樣式
          //        setCurrentRowStyle(table, book.getType());
          //        
          //        return book;
          //    }
              
              
          /**
               * 根據不同的類型,設置當前行的樣式
               * 
          @param table
               * 
          @param type
               
          */

              
          private void setCurrentRowStyle(HtmlDataTable table, int type) {
                  
          switch (type) {
                      
          case Book.COMPUTER_BOOK:
                          
          //System.out.println("computers");
                          table.setRowStyleClass("computers");    //設置計算機書籍樣式
                          break;
                      
          case Book.ART_BOOK:
                          
          //System.out.println("arts");
                          table.setRowStyleClass("arts");        //設置文學書籍樣式
                          break;
                      
          case Book.ECONOMY_BOOK:
                          
          //System.out.println("C");
                          table.setRowStyleClass("economy");        //設置經濟書籍樣式
                          break;
                      
          default:
                          
          break;
                  }

              }

              
              
          class BookListDataModelListener implements DataModelListener {

                  
          public void rowSelected(DataModelEvent event) {
                      
          // TODO Auto-generated method stub
                      
                      
          /////////////////////////////////////////////////////////////
                      
          //通過變量解析獲取request scope中的BookList列表模型
                      FacesContext facesContext = FacesContext.getCurrentInstance();
                      VariableResolver vr 
          = facesContext.getApplication().getVariableResolver();
                      BookList bookList 
          = (BookList)vr.resolveVariable(facesContext, "booking");    
                      
          /////////////////////////////////////////////////////////////
                      
                      
          //間接得到綁定BookList列表模型的uiData組件,并向下轉型成HtmlDataTable(myfaces擴展類型)組件
                      HtmlDataTable table = (HtmlDataTable)bookList.getUiData();
                      
                      
          //獲取當前行的Book實例
                      Book book = (Book)event.getRowData();
                      
          if(book != null{
                          
          //根據當前行Book實例的業務特征為HtmlDataTable組件設置渲染行樣式
                          setCurrentRowStyle(table, book.getType());
                      }

                  }

                  
              }

          }

           我把監聽器BookListDataModelListener作為我的自定義模型的一個內部類來增強業務聚合性,大家在使用的時候可以直接使用一些標準的DataModel,例如ListDataModel等,可以為每個監聽器新建一個對象,然后在jsf業務層去實例化調用,不一定要向樣例那樣放在一起。但是一定要記住,event.getRowData()可能有空值(最后一行的終結處),所以要向樣例那樣,進行非空判斷!

           

          posted @ 2007-09-21 16:28 方順 閱讀(1929) | 評論 (2)編輯 收藏

               摘要:      HtmlDataTable組件屬于UIData家族的HTML數據列表實現,組件中提供了rowStyleClass等方法來控制行的顯示,,如果你在rowStyleClass中加入兩個樣式類,并使用逗號分開,那么渲染的每一行會交替使用這兩個樣式類進行顯示,同理多個樣式類也會循環交替顯示。但是如果我想根據業務的情況具體指定某行顯示成特定的樣式,...  閱讀全文
          posted @ 2007-09-14 15:48 方順 閱讀(3640) | 評論 (2)編輯 收藏

          主站蜘蛛池模板: 正阳县| 河东区| 栖霞市| 甘洛县| 马鞍山市| 金堂县| 玉山县| 瑞丽市| 无极县| 松潘县| 红桥区| 常熟市| 胶南市| 孟州市| 嘉鱼县| 宁德市| 广丰县| 灌云县| 张北县| 张掖市| 汤阴县| 贞丰县| 太康县| 通榆县| 盐山县| 堆龙德庆县| 洛宁县| 大石桥市| 绥棱县| 临潭县| 长汀县| 本溪| 枣庄市| 修文县| 延川县| 乌什县| 文成县| 桐城市| 宁津县| 栖霞市| 西华县|