我有一个无状态的控制器,负责处理表单。它被定义为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>