2011-07-25 73 views
4

我有一個無狀態的控制器,它負責處理表單。這被定義爲ApplicationScoped。在我的頁面上,我有一個與支持bean關聯的表單,其定義爲ViewScopedJSF 2.0使用不同範圍注入受管理的bean

我時,我想處理表單的錯誤:

serverError: class com.sun.faces.mgbean.ManagedBeanCreationException Unable to create managed bean myController. The following problems were found: 
    - The scope of the object referenced by expression #{myFormBean}, view, is shorter than the referring managed beans (myController) scope of application 

在我的形式:

 Name: <h:inputText value="#{myFormBean.name}" id="name" /> 
     <h:commandButton value="Save Name" action="#{myController.processForm}"> 
      <f:ajax render="nameResult" /> 
     </h:commandButton> 
     Your name is <h:outputText value="#{myFormBean.name}" id="nameResult"/> 

控制器:

@ManagedBean 
@ApplicationScoped 
public class MyController { 
    @ManagedProperty("#{myFormBean}") 
    private MyFormBean myBean; 
    public void processForm() { 
     System.out.println(myBean.getName()); 
     // Save current name in session 
     FacesContext.getCurrentInstance().getExternalContext().getSessionMap().put(
       "name", myBean.getName()); 
    } 
} 

的支持bean:

@ManagedBean 
@ViewScoped 
public class MyFormBean { 
    private String name; 
    public String getName() { 
     return name; 
    } 
    public void setName(String name) { 
     this.name = name; 
    } 
} 

我可以通過將控制器設置爲SessionScoped來解決這個問題,但由於控制器是無狀態的,所以它不是一個乾淨的方法,所以我不需要每個會話都有一個控制器。整個應用程序的一個控制器應該足夠了。

我有一個Spring MVC的背景,這就是爲什麼我很困惑如何做的事情與JSF 2.0

回答

11

有一個在你的設計中的缺陷。你的控制器根本不是無狀態的。它對每個請求/視圖都有不同的屬性,即myBean。如果它被支持,那麼每個新的請求/視圖將覆蓋先前設置的一個,並且最終用戶將面對完全不同的最終用戶的屬性值。這導致高併發情況下的問題。

您需要使其請求/查看作用域而不是應用程序作用域。那麼,我相信你必須完全不同於它。您正在操作方法中的會話作用域中手動設置屬性,而不是將其設置爲(注入的)會話作用域bean的屬性。如何正確解決這個問題取決於問題中不明確的功能要求。

+0

BalusC是正確的,請不要修改上面的答案以實現應用程序範圍的bean引用視圖範圍的bean。當多個用戶同時引用該應用程序範圍的bean時,它會崩潰。什麼應用程序 - >視圖基本上相當於是一個有狀態的單身人士,這從來沒有一個可行的設計。嘗試重新設計,以便您可以請求 - >查看參考。 – DWoldrich

+0

我要重做我的設計。 – Sydney

2

我有不同的範圍引用對方JSF託管Bean,而我發現春天充分滿足我的需求。成功的關鍵是Spring AOP代表了bean引用,並且給我更靈活的自動裝配。我認爲將JSF和Spring混合起來以達到目標是合理的。

我不使用JSF範圍聲明註釋來聲明我的bean。相反,我使用Spring來聲明我的bean,分配它們的作用域,並指定我希望奇怪範圍的bean爲它們生成aop代理(這樣它們可以在引用時適當地自動裝配)。我使用spring el-解析器使我的Spring bean可以在EL中作爲JSF2託管bean尋址。

我沒有在我的程序中使用視圖範圍,我使用會話範圍和引用它們的請求範圍的bean。但是,我懷疑我的方法也可能適用於您的視圖範圍的bean。

我不使用註釋來聲明我的bean,我使用XML來聲明我的bean和它們的作用域。我發現把所有的bean聲明編入一個地方很方便。我確信有一個純粹的基於註解的方法來實現我所擁有的。我在我的bean中使用@Autowired註釋來指示引用其他bean的位置。這使得我的XML配置簡短,無需getter/setter,並且給了我更多的Java靈活性,已經能夠從純XML獲得。

最後,我給自己定製了一個「SmartSession」範圍。這基本上就像會話範圍一樣,除了每次bean退出會話時重新自動裝入(這樣可以防止bean副本在羣集中的故障切換場景中顯示爲未連線)。

我已經明白了對於會話 - (我認爲視圖)有限範圍的bean可以工作,您需要使bean可序列化並將任何@Autowired字段標記爲瞬態。 SmartSession使我有信心在這種情況下確保我即使在特殊情況下也能保持自動裝配。我將我的SmartSession自定義作用域想法基於這個答案:Initialize already created objects in Spring以及如何編寫自定義作用域的Internet源代碼。

下面是一些代碼片段,希望能夠給你一些想法 -

樣品會話範圍的bean:

public class UserProfileContainer implements Serializable { 
    private static final long serialVersionUID = -6765013004669200867L; 

    private User userProfile; 

    public void setUserProfile(User userProfile) { 
     this.userProfile = userProfile; 
    } 

    public User getUserProfile() { 
     return this.userProfile; 
    } 
} 

豆引用我smartSession作用域的bean:

public class KidProfileEditor implements Serializable { 
    private static final long serialVersionUID = 1552049926125644314L; 

    private String screenName; 
    private String password; 
    private String confirmPassword; 
    private String firstName; 
    private String lastName; 
    private String city; 
    private String state; 
    private String notes; 
    private String country; 

    @Autowired 
    private transient UserProfileContainer userProfileContainer; 
} 

從我的applicationConte中摘錄xt.xml:

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:tx="http://www.springframework.org/schema/tx" 
    xmlns:jee="http://www.springframework.org/schema/jee" 
    xmlns:util="http://www.springframework.org/schema/util"  
    xmlns:lang="http://www.springframework.org/schema/lang" 
    xmlns:jms="http://www.springframework.org/schema/jms" 
    xmlns:context="http://www.springframework.org/schema/context"    
    xsi:schemaLocation=" 
     http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
     http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd 
     http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd 
     http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang-3.0.xsd 
     http://www.springframework.org/schema/jms http://www.springframework.org/schema/jms/spring-jms-3.0.xsd 
     http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
     http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
     http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd" 

    default-lazy-init="true" > 

    <!-- BOILERPLATE magic AOP setup tags --> 
    <context:annotation-config /> 
    <context:component-scan base-package="com.woldrich.kidcompy" /> 
    <aop:aspectj-autoproxy /> 

    <!-- JSF2+Spring custom scope configurations --> 
    <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> 
     <property name="scopes"> 
      <map> 
       <entry key="safetySession"> 
        <bean class="com.woldrich.kidcompy.faces.util.SpringSafetySessionScope"/> 
       </entry> 
      </map> 
     </property> 
    </bean> 

    <bean id="userProfileContainer" class="com.woldrich.kidcompy.auth.UserProfileContainer" scope="safetySession">  
     <aop:scoped-proxy /> 
    </bean> 
    <bean id="kidProfileEditor" class="com.woldrich.kidcompy.faces.actionview.KidProfileEditor" scope="request" /> 
</beans> 

的web.xml片段:

<web-app xsi:schemaLocation="http://java.sun.com/xml/ns/javaee /WEB-INF/includes/schema/web-app_2_5.xsd" id="KidCompy" version="2.5" metadata-complete="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee"> 
    <distributable/> 

    <context-param> 
     <description>Allows the Spring Context to load multiple application context files</description> 
     <param-name>contextConfigLocation</param-name> 
     <param-value>classpath:/mainApplicationContext.xml</param-value> 
    </context-param> 

    <listener> 
     <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class> 
    </listener> 
</web-app> 

faces-config.xml中的代碼片段:

<faces-config xmlns="http://java.sun.com/xml/ns/javaee" 
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
       xsi:schemaLocation="http://java.sun.com/xml/ns/javaee /WEB-INF/includes/schema/web-facesconfig_2_0.xsd" 
       version="2.0"> 
    <application> 
     <el-resolver> 
      org.springframework.web.jsf.el.SpringBeanFacesELResolver 
     </el-resolver> 
    </application> 
</faces-config> 
相關問題