我正在研究使用JSF 1.2的JSR-286 portlet应用程序。我正在努力将我的JSF托管bean移动到Spring bean,我注意到Spring如何处理请求范围与JSF如何处理请求范围之间的区别。
在我的portlet应用程序中,我有两个位于同一页面上的portlet,它们都使用相同的起始JSF portlet页面视图。当我使用JSF托管请求bean时,会为每个portlet创建一个单独的请求bean,这是我正在寻找的行为。当我使用Spring bean时,只创建一个请求bean并在两个portlet之间共享。这是正常的行为吗?有什么方法可以阻止它这样做吗?
我的原始faces-config.xml文件,在将我的bean移动到Spring之前:
<?xml version="1.0" encoding="UTF-8"?>
<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 http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
version="1.2">
<application>
<state-manager>com.ibm.faces.application.DevelopmentStateManager</state-manager>
<variable-resolver>com.ibm.faces.portlet.PortletVariableResolver</variable-resolver>
</application>
<factory>
<faces-context-factory>com.ibm.faces.context.AjaxFacesContextFactory</faces-context-factory>
<render-kit-factory>com.ibm.faces.renderkit.AjaxRenderKitFactory</render-kit-factory>
</factory>
<managed-bean>
<managed-bean-name>sessionBean</managed-bean-name>
<managed-bean-class>sanitycheck.SessionBean</managed-bean-class>
<managed-bean-scope>session</managed-bean-scope>
</managed-bean>
<managed-bean>
<managed-bean-name>pc_SanityCheckProjectView</managed-bean-name>
<managed-bean-class>sanitycheck.SanityCheckProjectView</managed-bean-class>
<managed-bean-scope>request</managed-bean-scope>
<managed-property>
<property-name>sessionBean</property-name>
<value>#{sessionBean}</value>
</managed-property>
</managed-bean>
<lifecycle>
<phase-listener>com.ibm.faces.webapp.ValueResourcePhaseListener</phase-listener>
</lifecycle>
</faces-config>
将bean移动到Spring之后的faces-config.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<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 http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"
version="1.2">
<application>
<state-manager>com.ibm.faces.application.DevelopmentStateManager</state-manager>
<variable-resolver>com.ibm.faces.portlet.PortletVariableResolver</variable-resolver>
<variable-resolver>org.springframework.web.jsf.DelegatingVariableResolver</variable-resolver>
</application>
<factory>
<faces-context-factory>com.ibm.faces.context.AjaxFacesContextFactory</faces-context-factory>
<render-kit-factory>com.ibm.faces.renderkit.AjaxRenderKitFactory</render-kit-factory>
</factory>
<lifecycle>
<phase-listener>com.ibm.faces.webapp.ValueResourcePhaseListener</phase-listener>
</lifecycle>
</faces-config>
我的spring-web.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd">
<bean id="sessionBean" class="sanitycheck.SessionBean" scope="session">
<aop:scoped-proxy/>
</bean>
<bean id="pc_SanityCheckProjectView" class="pagecode.SanityCheckProjectView" scope="request" init-method="init">
<aop:scoped-proxy/>
<property name="sessionBean" ref="sessionBean"/>
</bean>
</beans>
如有必要,我可以提供其他文件,请告诉我。谢谢!
编辑:在Spring bean中添加了aop:scoped-proxy。
编辑:添加portlet.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<portlet-app xmlns="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" version="2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd http://java.sun.com/xml/ns/portlet/portlet-app_2_0.xsd" id="com.ibm.faces.portlet.FacesPortlet.3a22ca3014">
<portlet>
<portlet-name>SanityCheckProject</portlet-name>
<display-name xml:lang="en">SanityCheckProject</display-name>
<display-name>SanityCheckProject</display-name>
<portlet-class>com.ibm.faces.portlet.FacesPortlet</portlet-class>
<init-param>
<name>com.ibm.faces.portlet.page.view</name>
<value>/SanityCheckProjectView.jsp</value>
</init-param>
<init-param>
<name>whichOne</name>
<value>Portlet1</value>
</init-param>
<init-param>
<name>wps.markup</name>
<value>html</value>
</init-param>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<resource-bundle>com.ibm.sanitycheckproject.nl.SanityCheckProjectPortletResource</resource-bundle>
<portlet-info>
<title>SanityCheckProject</title>
<short-title>SanityCheckProject</short-title>
<keywords>SanityCheckProject</keywords>
</portlet-info>
</portlet>
<portlet>
<portlet-name>SanityCheckPortlet2</portlet-name>
<display-name xml:lang="en">SanityCheckPortlet2</display-name>
<display-name>SanityCheckPortlet2</display-name>
<portlet-class>com.ibm.faces.portlet.FacesPortlet</portlet-class>
<init-param>
<name>com.ibm.faces.portlet.page.view</name>
<value>/SanityCheckProjectView.jsp</value>
</init-param>
<init-param>
<name>whichOne</name>
<value>Portlet2</value>
</init-param>
<init-param>
<name>wps.markup</name>
<value>html</value>
</init-param>
<expiration-cache>0</expiration-cache>
<supports>
<mime-type>text/html</mime-type>
<portlet-mode>view</portlet-mode>
</supports>
<supported-locale>en</supported-locale>
<resource-bundle>com.ibm.sanitycheckproject.nl.SanityCheckPortlet2PortletResource</resource-bundle>
<portlet-info>
<title>SanityCheckPortlet2</title>
<short-title>SanityCheckPortlet2</short-title>
<keywords>SanityCheckPortlet2</keywords>
</portlet-info>
</portlet>
<default-namespace>http://SanityCheckProject/</default-namespace>
</portlet-app>
答案 0 :(得分:0)
在Spring XML配置中,您必须使用<aop:scoped-proxy/>
标记。
<!-- an HTTP Session-scoped bean exposed as a proxy -->
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<!-- this next element effects the proxying of the surrounding bean -->
<aop:scoped-proxy/>
</bean>
答案 1 :(得分:0)
我不知道这是最好还是非常好的解决方案,但我最终做的是创建两个自定义portlet范围,一个用于请求范围,一个用于会话范围。基本上我的自定义作用域所做的是它们使用portlet的命名空间为请求对象的名称添加前缀,这似乎可以使所有内容保持分离。
以下是我用于范围的代码:
请求范围:
import javax.faces.context.FacesContext;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.web.context.request.RequestScope;
import com.ibm.faces.portlet.httpbridge.ActionResponseWrapper;
import javax.portlet.RenderResponse;
import javax.portlet.filter.RenderResponseWrapper;
public class PortletRequestScope extends RequestScope {
@Override
public Object get(String name, ObjectFactory objectFactory) {
Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
if (response instanceof RenderResponse) {
String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.get(namespace + name, objectFactory);
}
else if (response instanceof RenderResponseWrapper) {
String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.get(namespace + name, objectFactory);
}
else if (response instanceof ActionResponseWrapper) {
String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.get(namespace + name, objectFactory);
}
else {
writeError(response);
}
return super.get(name, objectFactory);
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
if (response instanceof RenderResponse) {
String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
super.registerDestructionCallback(namespace+name, callback);
}
else if (response instanceof RenderResponseWrapper) {
String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
super.registerDestructionCallback(namespace+name, callback);
}
else if (response instanceof ActionResponseWrapper) {
String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
super.registerDestructionCallback(namespace+name, callback);
}
else {
writeError(response);
}
super.registerDestructionCallback(name, callback);
}
@Override
public Object remove(String name) {
Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
if (response instanceof RenderResponse) {
String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.remove(namespace+name);
}
else if (response instanceof RenderResponseWrapper) {
String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.remove(namespace+name);
}
else if (response instanceof ActionResponseWrapper) {
String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.remove(namespace+name);
}
else {
writeError(response);
}
return super.remove(name);
}
protected void writeError(Object response) {
System.err.println("Error in PortletRequestScope");
System.err.println("Response is unrecognized class: " + response.getClass().getCanonicalName());
System.err.println("Please modify code to handle class");
}
}
会议范围:
import javax.faces.context.FacesContext;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.web.context.request.SessionScope;
import com.ibm.faces.portlet.httpbridge.ActionResponseWrapper;
import javax.portlet.RenderResponse;
import javax.portlet.filter.RenderResponseWrapper;
public class PortletSessionScope extends SessionScope {
@Override
public Object get(String name, ObjectFactory objectFactory) {
Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
if (response instanceof RenderResponse) {
String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.get(namespace + name, objectFactory);
}
else if (response instanceof RenderResponseWrapper) {
String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.get(namespace + name, objectFactory);
}
else if (response instanceof ActionResponseWrapper) {
String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.get(namespace + name, objectFactory);
}
else {
writeError(response);
}
return super.get(name, objectFactory);
}
@Override
public void registerDestructionCallback(String name, Runnable callback) {
Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
if (response instanceof RenderResponse) {
String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
super.registerDestructionCallback(namespace+name, callback);
}
else if (response instanceof RenderResponseWrapper) {
String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
super.registerDestructionCallback(namespace+name, callback);
}
else if (response instanceof ActionResponseWrapper) {
String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
super.registerDestructionCallback(namespace+name, callback);
}
else {
writeError(response);
}
super.registerDestructionCallback(name, callback);
}
@Override
public Object remove(String name) {
Object response = FacesContext.getCurrentInstance().getExternalContext().getResponse();
if (response instanceof RenderResponse) {
String namespace=((RenderResponse)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.remove(namespace+name);
}
else if (response instanceof RenderResponseWrapper) {
String namespace=((RenderResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.remove(namespace+name);
}
else if (response instanceof ActionResponseWrapper) {
String namespace=((ActionResponseWrapper)FacesContext.getCurrentInstance().getExternalContext().getResponse()).getNamespace();
return super.remove(namespace+name);
}
else {
writeError(response);
}
return super.remove(name);
}
protected void writeError(Object response) {
System.err.println("Error in PortletSessionScope");
System.err.println("Response is unrecognized class: " + response.getClass().getCanonicalName());
System.err.println("Please modify code to handle class");
}
}
然后,在我的spring-web.xml中,我定义了自定义范围:
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="portletRequestScope">
<bean class="com.test.scope.PortletRequestScope"/>
</entry>
<entry key="portletSessionScope">
<bean class="com.test.portlet.scope.PortletSessionScope"/>
</entry>
</map>
</property>
</bean>
当我定义我的实际Spring bean时,我使用了自定义范围而不是常规范围 - 例如:
<bean id="sessionBean" class="com.test.managedbeans.SessionBean"
scope="portletSessionScope" lazy-init="true"/>
至少,这样做似乎适用于我在WebSphere Portal上的JSF + Spring的特定情况,并希望这对其他人有用。