2011-06-28 19 views
3

我們正在使用JSF 2,Spring和Hibernate構建應用程序。 MyFaces Orchestra被用來提供我們用於應用程序中大部分頁面的對話範圍(以利用Orchestra對Hibernate Session的管理)。我們所有的bean都被聲明爲使用conversation.access作用域(根據Orchestra文檔)應該意味着只要用戶導航到不包含對該支持bean實例的任何引用的頁面,bean就會從作用域中移除。使用MyFaces Orchestra時,conversation.access beans在導航到不同視圖時未被刪除

我遇到的問題是,如果我從視圖中導航而不顯式地使對話失效,如果他們以後再回到該視圖,它仍然具有與以前相同的數據。我在所有支持bean中實現了ConversationBindingListener方法,並且可以看到它們何時從對話中移除,並且我可以看到它們在很多情況下都不是。

是什麼讓問題變得更加困惑的是,當我導航到某些頁面(視圖)而不是其他人時,支持bean被刪除。我想也許這是因爲頁面有一個意外的引用到EL中的其他支持bean,但我無法找到任何。我也認爲,也許這個問題只發生在我從一個有對話的頁面導航時。使用不同的conversation.scoped bean將scoped bean訪問到另一個頁面。但是,從對話中刪除的情況下,該頁面還包含對conversation.access作用域bean的引用。

正如我早說的,使用Conversation.getCurrentInstance()。invalidate()函數明確地使對話無效。但是,明確地使會話失效對每個用例都是不可能的,因爲用戶只需點擊其中一個導航鏈接就可以留下視圖,這是非常常見的用例。我們使用Hibernate 3.6(而不是JPA),這意味着我們不得不使用HibernatePersistenceContextFactory

  • MyFaces的樂團 (MyFaces的樂團-core20-1.4.jar)
  • JSF 2(鑽嘴魚科2.0.4)
  • 春季3.0
  • PrimeFaces 2.2.1
  • RichFaces的4.0.0

下面是我的Spring上下文配置看起來像(Orchestra)。

<!-- 1. initialization of all orchestra modules (required for core15 module) --> 
<import resource="classpath*:/META-INF/spring-orchestra-init.xml" /> 

<!-- 2. the conversation scopes --> 
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> 
    <property name="scopes"> 
     <map> 
      <entry key="conversation.manual"> 
       <bean 
        class="org.apache.myfaces.orchestra.conversation.spring.SpringConversationScope"> 
        <property name="timeout" value="30" /> 
        <property name="advices"> 
         <list> 
          <ref bean="persistentContextConversationInterceptor" /> 
         </list> 
        </property> 
       </bean> 
      </entry> 
      <entry key="conversation.access"> 
       <bean 
        class="org.apache.myfaces.orchestra.conversation.spring.SpringConversationScope"> 
        <property name="timeout" value="30" /> 
        <property name="advices"> 
         <list> 
          <ref bean="persistentContextConversationInterceptor" /> 
         </list> 
        </property> 
        <property name="lifetime" value="access" /> 
       </bean> 
      </entry> 
     </map> 
    </property> 
</bean>  


<!-- 3. the "entity manager" manager --> 
<bean id="persistentContextConversationInterceptor" 
    class="org.apache.myfaces.orchestra.conversation.spring.PersistenceContextConversationInterceptor"> 
    <property name="persistenceContextFactory" ref="persistentContextFactory" /> 
</bean> 



<!-- 4. conversation - persistence adapter --> 
<bean id="persistentContextFactory" 
    class="com.acme.infra.orchestra.hibernate.HibernatePersistenceContextFactory"> 
    <property name="entityManagerFactory" ref="sessionFactory" /> 
</bean> 

<!-- 5. persistence --> 
<bean id="managedDataSource" 
    class="org.apache.myfaces.orchestra.connectionManager.ConnectionManagerDataSource"> 
    <property name="dataSource" ref="dataSource" /> 
</bean> 

這裏有幾個JSF輔助bean聲明的例子。

<bean id="quoteSummaryBackingBean" class="com.acme.ui.backing.QuoteSummaryBackingBean" 
     scope="conversation.access" orchestra:conversationName="QuoteSummaryConversation"> 
    <property name="quotingBusinessService" ref="quotingBusinessService"/> 
    <property name="customerBusinessService" ref="customerBusinessService"/> 
    <property name="referenceDataBusinessService" ref="referenceDataBusinessService"/> 
    <property name="quoteExportBusinessService" ref="quoteExportBusinessService" /> 
</bean> 

<bean id="createQuoteBackingBean" class="com.acme.ui.backing.CreateQuoteBackingBean" 
     scope="conversation.access" orchestra:conversationName="CreateQuoteConversation"> 
    <property name="quotingBusinessService" ref="quotingBusinessService"/> 
    <property name="customerBusinessService" ref="customerBusinessService"/> 
    <property name="referenceDataBusinessService" ref="referenceDataBusinessService"/> 


+2

更新:我發現這個問題在導航到使用標記時顯示。出於某種原因,在下一個視圖中出現會觸發一個條件,導致Orchestra *從對話中刪除前一個視圖的支持bean。相關的代碼在Orchestra的AccessScopePhaseListener中;它設置一個名爲org.apache.myfaces.orchestra.conversation.jsf.AccessScopePhaseListener:oldView的請求作用域變量,當此變量的值與當前視圖匹配時,Orchestra跳過清理未使用組件的任務。 –

回答

2

這是不是最優雅的解決方案,我猜它可能引入新的錯誤(因爲由樂團使用的檢查是爲了處理與AJAX情況要求)。我向backing bean添加了一個新方法(使用基類),該方法處理將org.apache.myfaces.orchestra.conversation.jsf.AccessScopePhaseListener:oldView請求範圍變量重置爲null。

public void clearPreviousConversation() { 
    if (firstHit) { 
     String keyName = 
      "org.apache.myfaces.orchestra.conversation.jsf.AccessScopePhaseListener:oldView"; 

     FacesContext.getCurrentInstance().getExternalContext() 
     .getRequestMap().put(keyName, null); 

     firstHit = false; 
    } 
} 

爲了確保此方法每個視圖只被調用一次,我有一個「firstHit」標誌,它是一個布爾成員變量。

然後,由於這個特定的問題僅僅表現在使用f:metadata的視圖上,我利用這個事實只在需要時調用這個方法。我將它添加爲我的f:元數據中的預渲染調用。

<f:metadata> 
    <f:event type="preRenderView" listener="#{controlPanelBackingBean.clearPreviousConversation}" /> 
</f:metadata> 

如果您使用f:viewParam或其他f:event元素,則可以將它們混合在一起。

<f:metadata> 
    <f:viewParam name="tabIndex" value="#{controlBackingBean.tabIndex}" /> 
    <f:event type="preRenderView" listener="#{controlPanelBackingBean.clearPreviousConversation}" /> 
    <f:event type="preRenderView" listener="#{controlPanelBackingBean.init}" /> 
</f:metadata> 
+0

我發佈了我的解決方案,它與您的解決方案完全相同,但足以在不影響視圖定義的情況下放置另一個階段偵聽器。多年過去了,但問題仍然存在,樂團似乎沒有積極發展。不知道它是否適合你,當然如果JSF對你來說仍然是實際的;) –

0

至於貝尼託·維加說,正確的在他的評論,當你做一個GET請求到包含<f:metadata/>標籤視圖中的問題出現。這是因爲Orchestra的AccessScopePhaseListener.doAfterRestoreView()通過測試FacesContext.getRenderResponse()來區分POST和GET。但是,如果對包含<f:metadata/>標記的視圖發出GET請求,則不是true(請參閱RestoreViewPhase.java的第244行,以瞭解它的原因)。這就是爲什麼這種情況下AccessScopePhaseListener.doAfterRenderResponse()後面的代碼看起來像回發到相同的視圖,這是跳過未訪問的bean的失效的原因。

我已經創建了我自己的相位偵聽器來解決這個問題。它增加了AccessScopePhaseListener.doAfterRestoreView()一個片段的結果,該片段使任何GET請求的'oldView'請求屬性的狀態看起來相同,無論該視圖是否包含<f:metadata/>。片段在RENDER_RESPONSE階段之前運行,因此樂團聽衆和礦山的相互順序並不重要。

import javax.faces.context.FacesContext; 
import javax.faces.event.PhaseEvent; 
import javax.faces.event.PhaseId; 
import javax.faces.event.PhaseListener; 
import org.apache.myfaces.orchestra.conversation.jsf.AccessScopePhaseListener; 

public class OrchestraAccessScopeBugFixer implements PhaseListener { 
    /** 
    * @see AccessScopePhaseListener#OLD_VIEW_KEY 
    */ 
    private static final String OLD_VIEW_KEY = AccessScopePhaseListener.class.getName() + ":oldView"; 

    @Override 
    public void beforePhase(PhaseEvent event) { 
     FacesContext facesContext = event.getFacesContext(); 
     if (!facesContext.isPostback()) { 
      // this makes it think that we are on a new view, not posting back to the same one 
      facesContext.getExternalContext().getRequestMap().put(OLD_VIEW_KEY, null); 
     } 
    } 

    @Override 
    public void afterPhase(PhaseEvent event) { 
    } 

    @Override 
    public PhaseId getPhaseId() { 
     return PhaseId.RENDER_RESPONSE; 
    } 
} 

我測試過這個解決方案獲得一個視圖帶或不帶<f:metadata/>使POST相同的觀點和POST導航到另一個,它的工作如預期。然而我不確定爲什麼樂團開發者不能使用FacesContext.isPostback()而不是FacesContext.getRenderResponse()AccessScopePhaseListener.doAfterRestoreView()爲了區分回發和不是一個。

相關問題