2011-07-25 40 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> 
相关问题