JSF 2.0注入具有不同作用域的托管bean



我有一个无状态的控制器,负责处理表单。它被定义为ApplicationScoped。在我的页面上,我有一个与定义为ViewScoped的支持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

In my form:

       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感到困惑的原因

你的设计有缺陷。您的控制器根本不是无状态的。它有一个不同于每个请求/视图的属性,即myBean。如果支持它,那么每个新的请求/视图都将覆盖以前设置的请求/视图,最终用户将面对一个完全不同的最终用户的属性值。这将导致在高并发情况下出现问题。

你需要把它设为请求/视图范围,而不是应用程序范围。尽管如此,我相信你必须用完全不同的方式来对待它。您在操作方法中手动设置会话作用域中的属性,而不是将其设置为(注入的)会话作用域bean的属性。如何合理的解决,取决于问题本身的功能需求,而问题本身并没有明确的功能需求。

我有具有不同作用域的JSF Managed bean,它们相互引用,并且我发现Spring充分满足了我的需求。成功的关键是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的需要,并且比纯XML提供了更多的java端灵活性。

最后,我给自己一个自定义的"SmartSession"作用域。这在本质上与会话作用域类似,只是每次将bean从会话中取出时,它都会重新自动连接(这可以防止bean副本在集群中的故障转移场景中出现未连接的情况)。

我得出的结论是,要使会话(我假定是视图)作用域的bean工作,您需要使bean可序列化,并将任何@Autowired字段标记为瞬态。SmartSession给了我在这种情况下保持自动连接的信心,即使在特殊情况下也是如此。我的SmartSession自定义作用域的想法是基于这个答案的:在Spring中初始化已经创建的对象,以及如何编写自定义作用域的互联网资源。

这里有一些代码片段,希望能给你一些想法-

会话作用域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的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;
}

来自我的applicationContext.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>

相关内容

  • 没有找到相关文章

最新更新